enigmare/v2-crawler
1904
1{"id":"doc-google_for_developers_build_with_gemini-af7aa76e","source":"documentation","title":"Google for Developers | Build with Gemini","url":"https://developers.google.com/","text":"Example:\n```text\nfrom google import genai\n\nclient = genai.Client()\n\nresponse = client.models.generate_content(\n model=\"gemini-3.5-flash\",\n contents=\"Explain how AI works in a few words\",\n)\n\nprint(response.text)\n```\n\nExample:\n```text\nimport { GoogleGenAI } from \"@google/genai\";\n\nconst ai = new GoogleGenAI({});\n\nasync function main() {\n const response = await ai.models.generateContent({\n model: \"gemini-3.5-flash\",\n contents: \"Explain how AI works in a few words\",\n });\n console.log(response.text);\n}\n\nawait main();\n```\n\nExample:\n```text\npackage main\n\nimport (\n \"context\"\n \"fmt\"\n \"log\"\n \"google.golang.org/genai\"\n)\n\nfunc main() {\n ctx := context.Background()\n client, err := genai.NewClient(ctx, nil)\n if err != nil {\n log.Fatal(err)\n }\n\n result, err := client.Models.GenerateContent(\n ctx,\n \"gemini-3.5-flash\",\n genai.Text(\"Explain how AI works in a few words\"),\n nil,\n )\n if err != nil {\n log.Fatal(err)\n }\n fmt.Println(result.Text())\n}\n```\n\nExample:\n```text\npackage com.example;\n\nimport com.google.genai.Client;\nimport com.google.genai.types.GenerateContentResponse;\n\npublic class GenerateTextFromTextInput {\n public static void main(String[] args) {\n Client client = new Client();\n\n GenerateContentResponse response =\n client.models.generateContent(\n \"gemini-3.5-flash\",\n \"Explain how AI works in a few words\",\n null);\n\n System.out.println(response.text());\n }\n}\n```\n\nExample:\n```text\ncurl \"https://generativelanguage.googleapis.com/v1beta/models/gemini-3-5-flash:generateContent\" \\\n -H \"x-goog-api-key: $GEMINI_API_KEY\" \\\n -H 'Content-Type: application/json' \\\n -X POST \\\n -d '{\n \"contents\": [\n {\n \"parts\": [\n {\n \"text\": \"Explain how AI works in a few words\"\n }\n ]\n }\n ]\n }'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.065Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":104,"estimatedTokens":477}}2{"id":"doc-women_techmakers_technovation-14837be6","source":"documentation","title":"Women Techmakers | Technovation","url":"https://developers.google.com/womentechmakers/ambassadors","text":"About Strategic Plan Get Involved Run our programs Partnerships Technovation Girls Impact 2023 Annual Report Our Alumnae App Gallery Contact Us Ways to Give Individual Giving Donate Crypto Corporate Giving DONATE About Strategic Plan Get Involved Run our programs Partnerships Technovation Girls Impact 2023 Annual Report Our Alumnae App Gallery Contact Us Ways to Give Individual Giving Donate Crypto Corporate Giving Donate\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.070Z","totalSectionsIncluded":1,"totalCodeBlocksIncluded":0,"totalLines":3,"estimatedTokens":110}}3{"id":"doc-women_techmakers_technovation-54d27228","source":"documentation","title":"Women Techmakers | Technovation","url":"https://developers.google.com/womentechmakers/initiatives/iwd","text":"About Strategic Plan Get Involved Run our programs Partnerships Technovation Girls Impact 2023 Annual Report Our Alumnae App Gallery Contact Us Ways to Give Individual Giving Donate Crypto Corporate Giving DONATE About Strategic Plan Get Involved Run our programs Partnerships Technovation Girls Impact 2023 Annual Report Our Alumnae App Gallery Contact Us Ways to Give Individual Giving Donate Crypto Corporate Giving Donate\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.072Z","totalSectionsIncluded":1,"totalCodeBlocksIncluded":0,"totalLines":3,"estimatedTokens":110}}4{"id":"doc-learn_how_to_build_android_apps_faster_with_goog-9460fc9c","source":"documentation","title":"Learn how to build Android apps faster with Google | Solutions for Developers | Google for Developers","url":"https://developers.google.com/solutions/pages/android-with-ai","text":"Example:\n```text\nscope.launch {\n val response = model.generateContent(\n \"Create a shopping list with $cuisineStyle ingredients\")\n}\n```\n\nExample:\n```text\nimplementation(\"com.google.ai.edge.aicore:aicore:0.0.1-exp01\")\n```\n\nExample:\n```text\nval generationConfig = generationConfig {\n context = ApplicationProvider.getApplicationContext()\n temperature = 0.2f\n topK = 16\n maxOutputTokens = 256\n}\n```\n\nExample:\n```text\nval downloadConfig = DownloadConfig(downloadCallback)\nval generativeModel = GenerativeModel(\n generationConfig = generationConfig,\n downloadConfig = downloadConfig // optional\n)\n```\n\nExample:\n```text\nscope.launch {\n val input = \"Suggest different types of cuisines and easy to cook dishes that are not $recentMealList\"\n val response = generativeModel.generateContent(input)\n print(response.text)\n}\n```\n\nExample:\n```text\ndependencies {\n...\n// Import the BoM for the Firebase platform\nimplementation(platform(\"com.google.firebase:firebase-bom:\"))\n\n// Add the dependency for the Vertex AI in Firebase library\n// When using the BoM, you don't specify versions in Firebase\n// library dependencies\nimplementation(\"com.google.firebase:firebase-vertexai\")\n}\n```\n\nExample:\n```text\nval generativeModel = Firebase.vertexAI\n .generativeModel(\n \"gemini-2.0-flash\",\n generationConfig = generationConfig {\n responseMimeType = \"application/json\"\n responseSchema = jsonSchema\n }\n )\n```\n\nExample:\n```text\nscope.launch {\n val response = model.generateContent(\"\n Create a shopping list with $cuisineStyle ingredients\")\n}\n```\n\nExample:\n```text\ndependencies {\n implementation(platform(\"com.google.firebase:firebase-bom:33.10.0\"))\n\n implementation(\"com.google.firebase:firebase-vertexai\")\n}\n```\n\nExample:\n```text\nval imageModel = Firebase.vertexAI.imagenModel(\nmodelName = \"imagen-3.0-generate-001\",\ngenerationConfig = ImagenGenerationConfig(\n imageFormat = ImagenImageFormat.jpeg(compresssionQuality = 75),\n addWatermark = true,\n numberOfImages = 1,\n aspectRatio = ImagenAspectRatio.SQUARE_1x1\n)\n```\n\nExample:\n```text\nval imageResponse = imageModel.generateImages(\nprompt = \"A cartoon style illustration of a top overview of a kitchen countertop\n with beautiful ingredients for a $cuisineStyle meal.\"\n)\n```\n\nExample:\n```text\nval image = imageResponse.images.first()\nval uiImage = image.asBitmap()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.077Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":111,"estimatedTokens":589}}5{"id":"doc-custom_function_quickstart_apps_script_google_fo-c1f6c6cf","source":"documentation","title":"Custom function quickstart | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/quickstart/custom-functions","text":"Example:\n```text\n/**\n * Calculates the sale price of a value at a given discount.\n * The sale price is formatted as US dollars.\n *\n * @param {number} input The value to discount.\n * @param {number} discount The discount to apply, such as .5 or 50%.\n * @return The sale price formatted as USD.\n * @customfunction\n */\nfunction salePrice(input, discount) {\n let price = input - (input * discount);\n let dollarUS = Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: \"USD\",\n});\n return dollarUS.format(price);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.095Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":22,"estimatedTokens":136}}6{"id":"doc-prompting_with_images_and_text_using_the_gemini_-b92fe309","source":"documentation","title":"Prompting with images and text using the Gemini API for accessibility | Google for Developers","url":"https://developers.google.com/codelabs/solutions/ai-gemini-images/codelab-1","text":"Example:\n```text\nnpm install @google-ai/generativelanguage\n```\n\nExample:\n```text\nconst { GoogleGenerativeAI } = require(\"@google/generative-ai\");\n\nconst generationConfig = {\n temperature: 0.7,\n candidateCount: 1,\n topK: 40,\n topP: 0.95,\n maxOutputTokens: 1024,\n};\n\nconst safetySettings = [\n {\n category: 'HARM_CATEGORY_DANGEROUS_CONTENT',\n threshold: 'BLOCK_NONE'\n },\n];\n\nconst genAI = new GoogleGenerativeAI(process.env.API_KEY);\n\nconst model = genAI.getGenerativeModel({\n model: \"gemini-pro\",\n});\n\nmodel.generateContent({\n generationConfig,\n safetySettings,\n contents: [\n {\n role: \"user\",\n parts: [\n { text: 'On what planet do humans live? ' }\n },\n ],\n}).then(result => {\n console.log(JSON.stringify(result, null, 2));\n});\n```\n\nExample:\n```text\nnode script.js\n```\n\nExample:\n```text\nexport API_KEY=<YOUR API KEY GOES HERE>\n```\n\nExample:\n```text\n{\n \"response\": {\n \"candidates\": [\n {\n \"content\": {\n \"parts\": [\n {\n \"text\": \"Humans currently only live on one planet: Earth. There are no known human colonies or permanent settlements on any other planets in our solar system or beyond.\"\n }\n ],\n \"role\": \"model\"\n },\n \"finishReason\": \"STOP\",\n \"index\": 0,\n \"safetyRatings\": [\n {\n \"category\": \"HARM_CATEGORY_SEXUALLY_EXPLICIT\",\n \"probability\": \"NEGLIGIBLE\"\n },\n {\n \"category\": \"HARM_CATEGORY_HATE_SPEECH\",\n \"probability\": \"NEGLIGIBLE\"\n },\n {\n \"category\": \"HARM_CATEGORY_HARASSMENT\",\n \"probability\": \"NEGLIGIBLE\"\n },\n {\n \"category\": \"HARM_CATEGORY_DANGEROUS_CONTENT\",\n \"probability\": \"NEGLIGIBLE\"\n }\n ]\n }\n ],\n \"promptFeedback\": {\n \"safetyRatings\": [\n {\n \"category\": \"HARM_CATEGORY_SEXUALLY_EXPLICIT\",\n \"probability\": \"NEGLIGIBLE\"\n },\n {\n \"category\": \"HARM_CATEGORY_HATE_SPEECH\",\n \"probability\": \"NEGLIGIBLE\"\n },\n {\n \"category\": \"HARM_CATEGORY_HARASSMENT\",\n \"probability\": \"NEGLIGIBLE\"\n },\n {\n \"category\": \"HARM_CATEGORY_DANGEROUS_CONTENT\",\n \"probability\": \"NEGLIGIBLE\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\nconst model = genAI.getGenerativeModel({\n model: \"gemini-pro-vision\",\n});\n```\n\nExample:\n```text\ncontents: [\n {\n role: \"user\",\n parts: [\n { text: 'Can you see an image attached to this message?' },\n ]\n }\n]\n```\n\nExample:\n```text\nnode:internal/process/promises:288\n triggerUncaughtException(err, true /* fromPromise */);\n ^\n\nError: [400 Bad Request] Add an image to use models/gemini-pro-vision, or switch your model to a text model.\n```\n\nExample:\n```text\nconst { join } = require('path');\n```\n\nExample:\n```text\nconst base64Image = Buffer.from(fs.readFileSync(join(__dirname, src))).toString(\"base64\");\n```\n\nExample:\n```text\ncontents: [\n {\n role: \"user\",\n parts: [\n { text: 'Can you see an image attached to this message?' },\n {\n inlineData: {\n mimeType: 'image/png',\n data: base64Image\n }\n },\n ]\n },\n ],\n```\n\nExample:\n```text\n{\n \"response\": {\n \"candidates\": [\n {\n \"content\": {\n \"parts\": [\n {\n \"text\": \" Yes, I can see a cat sleeping in the grass.\"\n }\n ],\n \"role\": \"model\"\n },\n...\n```\n\nExample:\n```text\n<html>\n <body>\n <img src=\"cat.png\" />\n\n <img src=\"cat.png\" alt=\"It's a cat.\" />\n\n <img src=\"cat.png\" alt=\"It's a dog.\" />\n </body>\n</html>\n```\n\nExample:\n```text\nconst cheerio = require(\"cheerio\");\nconst fs = require(\"fs\");\n```\n\nExample:\n```text\nasync function main() {\n const html = fs.readFileSync(\"input.html\", \"utf8\");\n\n const $ = cheerio.load(html);\n\n $(\"img\").each(function (i, element) {\n const src = $(element).attr(\"src\");\n const originalAlt = $(element).attr(\"alt\");\n \n if (!fs.existsSync(src)) {\n console.log(\"Image doesn't exist\", src);\n return;\n }\n \n const base64Image = Buffer.from(fs.readFileSync(join(__dirname, src))).toString(\"base64\");\n \n if (originalAlt !== undefined) {\n console.log(\"Alt text is already defined:\", originalAlt);\n return;\n }\n\n // TODO: Query Gemini API with the image and ask for a description\n // to update the missing alt text\n}\n\nmain();\n```\n\nExample:\n```text\nconst geminiSummary = await model\n .generateContent({\n generationConfig,\n safetySettings,\n contents: [\n {\n role: \"user\",\n parts: [\n { text: \"What is in this picture?\" },\n {\n inlineData: {\n mimeType: \"image/png\",\n data: base64Image,\n },\n },\n ],\n },\n ],\n })\n .then((result) => {\n return extractText(result, \"text\");\n });\n\n $(element).attr(\"alt\", geminiSummary.trim());\n\n console.log(\"Updated alt text for\", src, \"image:\", geminiRead.trim());\n```\n\nExample:\n```text\nfunction extractText(obj) {\n try {\n return obj.response.candidates[0].content.parts[0].text;\n } catch(e) {\n return undefined;\n }\n}\n```\n\nExample:\n```text\nAlt text is already defined: It's a cat.\nAlt text is already defined: It's a dog.\nUpdated alt text for cat.png image: This is a picture of a gray and white cat sleeping in the grass.\n```\n\nExample:\n```text\nconst cheerio = require(\"cheerio\");\nconst fs = require(\"fs\");\nconst { GoogleGenerativeAI } = require(\"@google/generative-ai\");\nconst { join } = require(\"path\");\n\nconst generationConfig = {\n temperature: 0.7,\n candidateCount: 1,\n topK: 40,\n topP: 0.95,\n maxOutputTokens: 1024,\n};\n\nconst safetySettings = [\n {\n category: \"HARM_CATEGORY_DANGEROUS_CONTENT\",\n threshold: \"BLOCK_NONE\",\n },\n];\n\nconst genAI = new GoogleGenerativeAI(process.env.API_KEY);\n\nconst model = genAI.getGenerativeModel({\n model: \"gemini-pro-vision\",\n});\n\nasync function main() {\n const html = fs.readFileSync(\"input.html\", \"utf8\");\n const $ = cheerio.load(html);\n\n const promises = [];\n\n $(\"img\").each(function (i, element) {\n // elements.push(element);\n\n promises.push(\n new Promise(async (resolve, reject) => {\n const src = $(element).attr(\"src\");\n const originalAlt = $(element).attr(\"alt\");\n\n if (!fs.existsSync(src)) {\n console.log(\"Image doesn't exist:\", src);\n resolve(false);\n return;\n }\n\n const base64Image = Buffer.from(fs.readFileSync(join(__dirname, src))).toString(\"base64\");\n\n if (originalAlt !== undefined) {\n console.log(\"Alt text is already defined:\", originalAlt);\n resolve(false);\n return;\n }\n\n const geminiRead = await model\n .generateContent({\n generationConfig,\n safetySettings,\n contents: [\n {\n role: \"user\",\n parts: [\n { text: \"What is in this picture? \" },\n {\n inlineData: {\n mimeType: \"image/png\",\n data: base64Image,\n },\n },\n ],\n },\n ],\n })\n .then((result) => {\n return extractText(result);\n });\n\n $(element).attr(\"alt\", geminiRead.trim());\n\n console.log(\"Updated alt text for\", src, \"image:\", geminiRead.trim());\n resolve(true);\n })\n );\n });\n\n Promise.all(promises).then((values) => {\n const updatedHTML = $.html();\n fs.writeFileSync(\"output.html\", updatedHTML);\n });\n}\n\nmain();\n\nfunction extractText(obj) {\n try {\n return obj.response.candidates[0].content.parts[0].text;\n } catch (e) {\n return undefined;\n }\n}\n```\n\nExample:\n```text\n<html>\n <body>\n <img src=\"cat.png\" alt=\"This is a picture of a gray and white cat sleeping in the grass.\">\n\n <img src=\"cat.png\" alt=\"It's a cat.\">\n\n <img src=\"cat.png\" alt=\"It's a dog.\">\n </body>\n</html>\n```\n\nExample:\n```text\nasync function askBoolean(question, base64Image) {\n return await model\n .generateContent({\n generationConfig,\n safetySettings,\n contents: [\n {\n role: \"user\",\n parts: [\n { text: question + \"\\nAnswer me with either 'yes' or 'no'.\" },\n {\n inlineData: {\n mimeType: \"image/png\",\n data: base64Image,\n },\n },\n ],\n },\n ],\n })\n .then((result) => {\n return extractText(result, \"text\").toLowerCase().includes(\"yes\");\n });\n}\n```\n\nExample:\n```text\nconst cheerio = require(\"cheerio\");\nconst fs = require(\"fs\");\nconst { GoogleGenerativeAI } = require(\"@google/generative-ai\");\nconst { join } = require(\"path\");\n\nconst generationConfig = {\n temperature: 0.7,\n candidateCount: 1,\n topK: 40,\n topP: 0.95,\n maxOutputTokens: 1024,\n};\n\nconst safetySettings = [\n {\n category: \"HARM_CATEGORY_DANGEROUS_CONTENT\",\n threshold: \"BLOCK_NONE\",\n },\n];\n\nconst genAI = new GoogleGenerativeAI(process.env.API_KEY);\n\nconst model = genAI.getGenerativeModel({\n model: \"gemini-pro-vision\",\n});\n\nasync function main() {\n const html = fs.readFileSync(\"input.html\", \"utf8\");\n const $ = cheerio.load(html);\n\n const promises = [];\n\n $(\"img\").each(function (i, element) {\n promises.push(\n new Promise(async (resolve, reject) => {\n const src = $(element).attr(\"src\");\n const originalAlt = $(element).attr(\"alt\");\n\n if (!fs.existsSync(src)) {\n console.log(\"Image doesn't exist:\", src);\n resolve(false);\n return;\n }\n\n const base64Image = Buffer.from(fs.readFileSync(join(__dirname, src))).toString(\"base64\");\n\n let answer = false;\n\n if (originalAlt !== undefined) {\n answer = await askBoolean(\n \"Does this description describe the image well? \" + originalAlt,\n base64Image\n );\n\n if (answer) {\n console.log(\"Alt text is already defined:\", originalAlt);\n\n resolve(false);\n return;\n } else {\n console.log(\n \"Alt text already exists but it's not good enough:\",\n originalAlt\n );\n }\n }\n\n const geminiRead = await model\n .generateContent({\n generationConfig,\n safetySettings,\n contents: [\n {\n role: \"user\",\n parts: [\n { text: \"What is in this picture? \" },\n {\n inlineData: {\n mimeType: \"image/png\",\n data: base64Image,\n },\n },\n ],\n },\n ],\n })\n .then((result) => {\n return extractText(result);\n });\n\n $(element).attr(\"alt\", geminiRead.trim());\n\n console.log(\"Updated alt text for\", src, \"image:\", geminiRead.trim());\n resolve(true);\n })\n );\n });\n\n Promise.all(promises).then((values) => {\n const updatedHTML = $.html();\n fs.writeFileSync(\"output.html\", updatedHTML);\n });\n}\n\nmain();\n\nfunction extractText(obj) {\n try {\n return obj.response.candidates[0].content.parts[0].text;\n } catch (e) {\n return undefined;\n }\n}\n\nasync function askBoolean(question, base64Image) {\n return await model\n .generateContent({\n generationConfig,\n safetySettings,\n contents: [\n {\n role: \"user\",\n parts: [\n { text: question + \"\\nAnswer me with either 'yes' or 'no'.\" },\n {\n inlineData: {\n mimeType: \"image/png\",\n data: base64Image,\n },\n },\n ],\n },\n ],\n })\n .then((result) => {\n return extractText(result, \"text\").toLowerCase().includes(\"yes\");\n });\n}\n```\n\nExample:\n```text\n<html>\n <body>\n <img src=\"cat.png\" alt=\"This is a picture of a gray and white cat sleeping in the grass.\">\n\n <img src=\"cat.png\" alt=\"It's a cat.\">\n\n <img src=\"cat.png\" alt=\"This is a picture of a gray and white cat sleeping in the grass.\">\n </body>\n</html>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.101Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":23,"totalLines":586,"estimatedTokens":3123}}7{"id":"doc-translate_text_from_google_docs_sheets_and_slide-f1587780","source":"documentation","title":"Translate text from Google Docs, Sheets, and Slides | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/samples/translate-addon-sample","text":"Example:\n```text\nconst DEFAULT_INPUT_TEXT = '';\nconst DEFAULT_OUTPUT_TEXT = '';\nconst DEFAULT_ORIGIN_LAN = ''; // Empty string means detect langauge\nconst DEFAULT_DESTINATION_LAN = 'en' // English\n\nconst LANGUAGE_MAP =\n [\n { text: 'Detect Language', val: '' },\n { text: 'Afrikaans', val: 'af' },\n { text: 'Albanian', val: 'sq' },\n { text: 'Amharic', val: 'am' },\n { text: 'Arabic', val: 'ar' },\n { text: 'Armenian', val: 'hy' },\n { text: 'Azerbaijani', val: 'az' },\n { text: 'Basque', val: 'eu' },\n { text: 'Belarusian', val: 'be' },\n { text: 'Bengali', val: 'bn' },\n { text: 'Bosnian', val: 'bs' },\n { text: 'Bulgarian', val: 'bg' },\n { text: 'Catalan', val: 'ca' },\n { text: 'Cebuano', val: 'ceb' },\n { text: 'Chinese (Simplified)', val: 'zh-CN' },\n { text: 'Chinese (Traditional)', val: 'zh-TW' },\n { text: 'Corsican', val: 'co' },\n { text: 'Croatian', val: 'hr' },\n { text: 'Czech', val: 'cs' },\n { text: 'Danish', val: 'da' },\n { text: 'Dutch', val: 'nl' },\n { text: 'English', val: 'en' },\n { text: 'Esperanto', val: 'eo' },\n { text: 'Estonian', val: 'et' },\n { text: 'Finnish', val: 'fi' },\n { text: 'French', val: 'fr' },\n { text: 'Frisian', val: 'fy' },\n { text: 'Galician', val: 'gl' },\n { text: 'Georgian', val: 'ka' },\n { text: 'German', val: 'de' },\n { text: 'Greek', val: 'el' },\n { text: 'Gujarati', val: 'gu' },\n { text: 'Haitian Creole', val: 'ht' },\n { text: 'Hausa', val: 'ha' },\n { text: 'Hawaiian', val: 'haw' },\n { text: 'Hebrew', val: 'he' },\n { text: 'Hindi', val: 'hi' },\n { text: 'Hmong', val: 'hmn' },\n { text: 'Hungarian', val: 'hu' },\n { text: 'Icelandic', val: 'is' },\n { text: 'Igbo', val: 'ig' },\n { text: 'Indonesian', val: 'id' },\n { text: 'Irish', val: 'ga' },\n { text: 'Italian', val: 'it' },\n { text: 'Japanese', val: 'ja' },\n { text: 'Javanese', val: 'jv' },\n { text: 'Kannada', val: 'kn' },\n { text: 'Kazakh', val: 'kk' },\n { text: 'Khmer', val: 'km' },\n { text: 'Korean', val: 'ko' },\n { text: 'Kurdish', val: 'ku' },\n { text: 'Kyrgyz', val: 'ky' },\n { text: 'Lao', val: 'lo' },\n { text: 'Latin', val: 'la' },\n { text: 'Latvian', val: 'lv' },\n { text: 'Lithuanian', val: 'lt' },\n { text: 'Luxembourgish', val: 'lb' },\n { text: 'Macedonian', val: 'mk' },\n { text: 'Malagasy', val: 'mg' },\n { text: 'Malay', val: 'ms' },\n { text: 'Malayalam', val: 'ml' },\n { text: 'Maltese', val: 'mt' },\n { text: 'Maori', val: 'mi' },\n { text: 'Marathi', val: 'mr' },\n { text: 'Mongolian', val: 'mn' },\n { text: 'Myanmar (Burmese)', val: 'my' },\n { text: 'Nepali', val: 'ne' },\n { text: 'Norwegian', val: 'no' },\n { text: 'Nyanja (Chichewa)', val: 'ny' },\n { text: 'Pashto', val: 'ps' },\n { text: 'Persian', val: 'fa' },\n { text: 'Polish', val: 'pl' },\n { text: 'Portuguese (Portugal, Brazil)', val: 'pt' },\n { text: 'Punjabi', val: 'pa' },\n { text: 'Romanian', val: 'ro' },\n { text: 'Russian', val: 'ru' },\n { text: 'Samoan', val: 'sm' },\n { text: 'Scots Gaelic', val: 'gd' },\n { text: 'Serbian', val: 'sr' },\n { text: 'Sesotho', val: 'st' },\n { text: 'Shona', val: 'sn' },\n { text: 'Sindhi', val: 'sd' },\n { text: 'Sinhala (Sinhalese)', val: 'si' },\n { text: 'Slovak', val: 'sk' },\n { text: 'Slovenian', val: 'sl' },\n { text: 'Somali', val: 'so' },\n { text: 'Spanish', val: 'es' },\n { text: 'Sundanese', val: 'su' },\n { text: 'Swahili', val: 'sw' },\n { text: 'Swedish', val: 'sv' },\n { text: 'Tagalog (Filipino)', val: 'tl' },\n { text: 'Tajik', val: 'tg' },\n { text: 'Tamil', val: 'ta' },\n { text: 'Telugu', val: 'te' },\n { text: 'Thai', val: 'th' },\n { text: 'Turkish', val: 'tr' },\n { text: 'Ukrainian', val: 'uk' },\n { text: 'Urdu', val: 'ur' },\n { text: 'Uzbek', val: 'uz' },\n { text: 'Vietnamese', val: 'vi' },\n { text: 'Welsh', val: 'cy' },\n { text: 'Xhosa', val: 'xh' },\n { text: 'Yiddish', val: 'yi' },\n { text: 'Yoruba', val: 'yo' },\n { text: 'Zulu', val: 'zu' }\n ];\n\n\n/**\n * Callback for rendering the main card.\n * @return {CardService.Card} The card to show the user.\n */\nfunction onHomepage(e) {\n return createSelectionCard(e, DEFAULT_ORIGIN_LAN, DEFAULT_DESTINATION_LAN, DEFAULT_INPUT_TEXT, DEFAULT_OUTPUT_TEXT);\n}\n\n/**\n * Main function to generate the main card.\n * @param {String} originLanguage Language of the original text.\n * @param {String} destinationLanguage Language of the translation.\n * @param {String} inputText The text to be translated.\n * @param {String} outputText The text translated.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction createSelectionCard(e, originLanguage, destinationLanguage, inputText, outputText) {\n var hostApp = e['hostApp'];\n var builder = CardService.newCardBuilder();\n\n // \"From\" language selection & text input section\n var fromSection = CardService.newCardSection()\n .addWidget(generateLanguagesDropdown('origin', 'From: ', originLanguage))\n .addWidget(CardService.newTextInput()\n .setFieldName('input')\n .setValue(inputText)\n .setTitle('Enter text...')\n .setMultiline(true));\n\n if (hostApp === 'docs') {\n fromSection.addWidget(CardService.newButtonSet()\n .addButton(CardService.newTextButton()\n .setText('Get Selection')\n .setOnClickAction(CardService.newAction().setFunctionName('getDocsSelection'))\n .setDisabled(false)))\n } else if (hostApp === 'sheets') {\n fromSection.addWidget(CardService.newButtonSet()\n .addButton(CardService.newTextButton()\n .setText('Get Selection')\n .setOnClickAction(CardService.newAction().setFunctionName('getSheetsSelection'))\n .setDisabled(false)))\n } else if (hostApp === 'slides') {\n fromSection.addWidget(CardService.newButtonSet()\n .addButton(CardService.newTextButton()\n .setText('Get Selection')\n .setOnClickAction(CardService.newAction().setFunctionName('getSlidesSelection'))\n .setDisabled(false)))\n }\n\n\n builder.addSection(fromSection);\n\n // \"Translation\" language selection & text input section\n builder.addSection(CardService.newCardSection()\n .addWidget(generateLanguagesDropdown('destination', 'To: ', destinationLanguage))\n .addWidget(CardService.newTextInput()\n .setFieldName('output')\n .setValue(outputText)\n .setTitle('Translation...')\n .setMultiline(true)));\n\n //Buttons section\n builder.addSection(CardService.newCardSection()\n .addWidget(CardService.newButtonSet()\n .addButton(CardService.newTextButton()\n .setText('Translate')\n .setTextButtonStyle(CardService.TextButtonStyle.FILLED)\n .setOnClickAction(CardService.newAction().setFunctionName('translateText'))\n .setDisabled(false))\n .addButton(CardService.newTextButton()\n .setText('Clear')\n .setOnClickAction(CardService.newAction().setFunctionName('clearText'))\n .setDisabled(false))));\n\n return builder.build();\n\n}\n\n/**\n * Helper function to generate the drop down language menu. It checks what language the user had selected.\n * @param {String} fieldName\n * @param {String} fieldTitle\n * @param {String} previousSelected The language the user previously had selected.\n * @return {CardService.SelectionInput} The card to show to the user.\n */\nfunction generateLanguagesDropdown(fieldName, fieldTitle, previousSelected) {\n var selectionInput = CardService.newSelectionInput().setTitle(fieldTitle)\n .setFieldName(fieldName)\n .setType(CardService.SelectionInputType.DROPDOWN);\n\n LANGUAGE_MAP.forEach((language, index, array) => {\n selectionInput.addItem(language.text, language.val, language.val == previousSelected);\n })\n\n return selectionInput;\n}\n\n/**\n * Helper function to translate the text. If the originLanguage is an empty string, the API detects the language\n * @return {CardService.Card} The card to show to the user.\n */\nfunction translateText(e) {\n var originLanguage = e.formInput.origin;\n var destinationLanguage = e.formInput.destination;\n var inputText = e.formInput.input;\n\n if (originLanguage !== destinationLanguage && inputText !== undefined) {\n var translation = LanguageApp.translate(e.formInput.input, e.formInput.origin, e.formInput.destination);\n return createSelectionCard(e, originLanguage, destinationLanguage, inputText, translation);\n }\n}\n\n/**\n * Helper function to clean the text.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction clearText(e) {\n var originLanguage = e.formInput.origin;\n var destinationLanguage = e.formInput.destination;\n return createSelectionCard(e, originLanguage, destinationLanguage, DEFAULT_INPUT_TEXT, DEFAULT_OUTPUT_TEXT);\n}\n\n/**\n * Helper function to get the text selected.\n * @return {CardService.Card} The selected text.\n */\nfunction getDocsSelection(e) {\n var text = '';\n var selection = DocumentApp.getActiveDocument().getSelection();\n Logger.log(selection)\n if (selection) {\n var elements = selection.getRangeElements();\n for (var i = 0; i < elements.length; i++) {\n Logger.log(elements[i]);\n var element = elements[i];\n // Only modify elements that can be edited as text; skip images and other non-text elements.\n if (element.getElement().asText() && element.getElement().asText().getText() !== '') {\n text += element.getElement().asText().getText() + '\\n';\n }\n }\n }\n\n if (text !== '') {\n var originLanguage = e.formInput.origin;\n var destinationLanguage = e.formInput.destination;\n var translation = LanguageApp.translate(text, e.formInput.origin, e.formInput.destination);\n return createSelectionCard(e, originLanguage, destinationLanguage, text, translation);\n }\n}\n\n/**\n * Helper function to get the text of the selected cells.\n * @return {CardService.Card} The selected text.\n */\nfunction getSheetsSelection(e) {\n var text = '';\n var ranges = SpreadsheetApp.getActive().getSelection().getActiveRangeList().getRanges();\n for (var i = 0; i < ranges.length; i++) {\n const range = ranges[i];\n const numRows = range.getNumRows();\n const numCols = range.getNumColumns();\n for (let i = 1; i <= numCols; i++) {\n for (let j = 1; j <= numRows; j++) {\n const cell = range.getCell(j, i);\n if (cell.getValue()) {\n text += cell.getValue() + '\\n';\n }\n }\n }\n }\n if (text !== '') {\n var originLanguage = e.formInput.origin;\n var destinationLanguage = e.formInput.destination;\n var translation = LanguageApp.translate(text, e.formInput.origin, e.formInput.destination);\n return createSelectionCard(e, originLanguage, destinationLanguage, text, translation);\n }\n}\n\n/**\n * Helper function to get the selected text of the active slide.\n * @return {CardService.Card} The selected text.\n */\nfunction getSlidesSelection(e) {\n var text = '';\n var selection = SlidesApp.getActivePresentation().getSelection();\n var selectionType = selection.getSelectionType();\n if (selectionType === SlidesApp.SelectionType.TEXT) {\n var textRange = selection.getTextRange();\n if (textRange.asString() !== '') {\n text += textRange.asString() + '\\n';\n }\n }\n if (text !== '') {\n var originLanguage = e.formInput.origin;\n var destinationLanguage = e.formInput.destination;\n var translation = LanguageApp.translate(text, e.formInput.origin, e.formInput.destination);\n return createSelectionCard(e, originLanguage, destinationLanguage, text, translation);\n }\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/New_York\",\n \"dependencies\": {},\n \"exceptionLogging\": \"STACKDRIVER\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/documents.currentonly\",\n \"https://www.googleapis.com/auth/spreadsheets.currentonly\",\n \"https://www.googleapis.com/auth/presentations.currentonly\"\n ],\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Translate\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/product/1x/translate_24dp.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#2772ed\"\n },\n \"homepageTrigger\": {\n \"runFunction\": \"onHomepage\"\n }\n },\n \"docs\" : {},\n \"slides\" : {},\n \"sheets\" : {}\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.103Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":349,"estimatedTokens":3069}}8{"id":"doc-iterators_google_ads_scripts_google_for_develope-ecf674b2","source":"documentation","title":"Iterators | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/elements/iterators","text":"Example:\n```text\nfor (var i = 0; i < myArray.length; i++) {\n let myObject = myArray[i];\n}\n```\n\nExample:\n```text\nwhile (myIterator.hasNext()) {\n let myObject = myIterator.next();\n}\n```\n\nExample:\n```text\nvar campaignIterator = AdsApp.campaigns().get();\n\nwhile (campaignIterator.hasNext()) {\n let campaign = campaignIterator.next();\n console.log(`${campaign.getName()}; active? ${campaign.isEnabled()}; ` +\n `budget=${campaign.getBudget().getAmount()}`);\n}\n```\n\nExample:\n```text\nfor (const campaign of AdsApp.campaigns()) {\n console.log(`${campaign.getName()}; active? ${campaign.isEnabled()}; ` +\n `budget=${campaign.getBudget().getAmount()}`);\n}\n```\n\nExample:\n```text\nvar x = AdsApp.keywords().get().totalNumEntities();\nvar y = AdsApp.keywords().withLimit(5).get().totalNumEntities();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.109Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":40,"estimatedTokens":204}}9{"id":"doc-selectors_google_ads_scripts_google_for_develope-b604bd85","source":"documentation","title":"Selectors | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/elements/selectors","text":"Example:\n```text\nselector = selector.forDateRange(\"LAST_14_DAYS\")\n .orderBy(\"metrics.clicks DESC\")\n .orderBy(\"metrics.ctr ASC\");\n```\n\nExample:\n```text\nvar campaignSelector = AdsApp.campaigns();\ncampaignSelector.withCondition(\"metrics.clicks > 10\");\ncampaignSelector.withCondition(\"metrics.impressions > 1000\");\ncampaignSelector.orderBy(\"metrics.impressions DESC\");\ncampaignSelector.forDateRange(\"YESTERDAY\");\n```\n\nExample:\n```text\nvar campaignSelector = AdsApp.campaigns()\n .withCondition(\"metrics.clicks > 10\")\n .withCondition(\"metrics.impressions > 1000\")\n .orderBy(\"metrics.impressions DESC\")\n .forDateRange(\"YESTERDAY\");\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.109Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":26,"estimatedTokens":163}}10{"id":"doc-ids_google_ads_scripts_google_for_developers-3c7fe56d","source":"documentation","title":"IDs | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/elements/ids","text":"Example:\n```text\nlet campaigns = AdsApp.campaigns()\n .withIds([678678])\n .get();\n// versus\nlet campaigns = AdsApp.campaigns()\n .withCondition(\"Name='My Campaign'\")\n .get();\n```\n\nExample:\n```text\nlet ids = [123123, 234234, 345345];\nlet campaignSelector = AdsApp.campaigns().withIds(ids);\n```\n\nExample:\n```text\nlet adGroupId = 123123;\nlet keywordSelector = AdsApp.keywords().withIds([\n [adGroupId, 234234],\n [adGroupId, 345345],\n [adGroupId, 456456]\n]);\n```\n\nExample:\n```text\nlet nextId = -1;\n\nfunction getNextTempId() {\n const ret = nextId;\n nextId -= 1;\n return ret;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.109Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":39,"estimatedTokens":153}}11{"id":"doc-oauth_2_0_for_ios_desktop_apps_google_for_develo-7ec639c1","source":"documentation","title":"OAuth 2.0 for iOS & Desktop Apps | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/native-app","text":"Example:\n```text\nms-app://YOUR_APP_PACKAGE_SID\n```\n\nExample:\n```text\ncode_challengecode_verifier\n```\n\nExample:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth?\n scope=email%20profile&\n response_type=code&\n state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2.example.com%2Ftoken&\n redirect_uri=com.example.app%3A/oauth2redirect&\n client_id=client_id\n```\n\nExample:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth?\n scope=email%20profile&\n response_type=code&\n state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2.example.com%2Ftoken&\n redirect_uri=http%3A//127.0.0.1%3A9004&\n client_id=client_id\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.googleapis.com\nContent-Type: application/x-www-form-urlencoded\nDPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik\\\n VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR\\\n nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE\\\n QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj\\\n oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia\\\n WF0IjoxNTYyMjYyNjE2fQ.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg\\\n 4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg\n\ncode=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&\nclient_id=your_client_id&\nredirect_uri=http://127.0.0.1:9004&\ngrant_type=authorization_code\n```\n\nExample:\n```text\nopenssl ecparam -name prime256v1 -genkey -noout -out dpop_private.pem\nopenssl ec -in dpop_private.pem -pubout -out dpop_public.pem\n```\n\nExample:\n```text\n{\n \"typ\":\"dpop+jwt\",\n \"alg\":\"ES256\",\n \"jwk\": {\n \"kty\":\"EC\",\n \"x\":\"YOUR_PUBLIC_KEY_X\",\n \"y\":\"YOUR_PUBLIC_KEY_Y\",\n \"crv\":\"P-256\"\n }\n}\n```\n\nExample:\n```text\n{\n \"jti\":\"JTI_VALUE\",\n \"htm\":\"POST\",\n \"htu\":\"https://oauth2.googleapis.com/token\",\n \"iat\":YOUR_JWT_ISSUED_TIME,\n \"nonce\":\"SERVER_PROVIDED_NONCE\"\n}\n```\n\nExample:\n```text\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nDPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI\n\n{\n \"access_token\": \"1/fFAGRNJru1FTz70BzhT3Zg\",\n \"expires_in\": 3920,\n \"token_type\": \"Bearer\",\n \"scope\": \"https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly\",\n \"refresh_token\": \"1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI\"\n}\n```\n\nExample:\n```text\n{\n \"access_token\": \"1/fFAGRNJru1FTz70BzhT3Zg\",\n \"expires_in\": 3920,\n \"token_type\": \"Bearer\",\n \"scope\": \"https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly\",\n \"refresh_token\": \"1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI\"\n }\n```\n\nExample:\n```text\nGET /drive/v2/files HTTP/1.1\nHost: www.googleapis.com\nAuthorization: Bearer access_token\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v2/files?access_token=access_token\n```\n\nExample:\n```text\ncurl -H \"Authorization: Bearer access_token\" https://www.googleapis.com/drive/v2/files\n```\n\nExample:\n```text\ncurl https://www.googleapis.com/drive/v2/files?access_token=access_token\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.googleapis.com\nContent-Type: application/x-www-form-urlencoded\nDPoP: DPOP_PROOF_JWT\n\nclient_id=your_client_id&\nrefresh_token=refresh_token&\ngrant_type=refresh_token\n```\n\nExample:\n```text\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nDPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI\n\n{\n \"access_token\": \"1/fFAGRNJru1FTz70BzhT3Zg\",\n \"expires_in\": 3920,\n \"scope\": \"https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly\",\n \"token_type\": \"Bearer\"\n}\n```\n\nExample:\n```text\ncurl -d -X -POST --header \"Content-type:application/x-www-form-urlencoded\" \\\n https://oauth2.googleapis.com/revoke?token={token}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.112Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":17,"totalLines":161,"estimatedTokens":950}}12{"id":"doc-get_started_google_ads_scripts_google_for_develo-6fcaa57f","source":"documentation","title":"Get started | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/getting-started","text":"Example:\n```text\nfunction main() {\n // Get the campaign names from all the campaigns\n const rows = AdsApp.search('SELECT campaign.name FROM campaign');\n\n console.log('My campaigns:');\n // Iterate through the campaigns and print the campaign names\n for (const row of rows) {\n console.log(row.campaign.name);\n }\n}\n```\n\nExample:\n```text\nfunction main() {\n // Retrieve all children accounts.\n const accountIterator = AdsManagerApp.accounts().get();\n\n // Iterate through the account list.\n for (const account of accountIterator) {\n // Get stats for the child account.\n const stats = account.getStatsFor(\"THIS_MONTH\");\n // And log it.\n console.log(`${account.getCustomerId()},${stats.getClicks()},` +\n `${stats.getImpressions()},${stats.getCost()}`);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.112Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":32,"estimatedTokens":200}}13{"id":"doc-preview_mode_google_ads_scripts_google_for_devel-0f26321c","source":"documentation","title":"Preview Mode | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/preview","text":"Example:\n```text\n// Suppose the ad group has no keywords.\nlet adGroup = findAnEmptyAdGroup();\n\n// Create a keyword.\nadGroup.createKeyword(\"test\");\n\n// Fetch all keywords in the ad group.\nlet keywords = adGroup.keywords().get();\n\n// In preview mode, this will log \"false\" since the keyword was not actually created.\n// In real execution, this will log \"true\".\nconsole.log(\"Are there keywords in the ad group? \" + keywords.hasNext());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.112Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":17,"estimatedTokens":113}}14{"id":"doc-builders_google_ads_scripts_google_for_developer-2e61bb1b","source":"documentation","title":"Builders | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/elements/builders","text":"Example:\n```text\n// Retrieve your ad group.\nlet adGroup = AdsApp.adGroups().get().next();\n\n// Create a keyword operation.\nlet keywordOperation = adGroup.newKeywordBuilder()\n .withCpc(1.2)\n .withText(\"shoes\")\n .withFinalUrl(\"http://www.example.com/shoes\")\n .build();\n\n// Optional: examine the outcome. The call to isSuccessful()\n// will block until the operation completes.\nif (keywordOperation.isSuccessful()) {\n // Get the result.\n let keyword = keywordOperation.getResult();\n} else {\n // Handle the errors.\n let errors = keywordOperation.getErrors();\n}\n```\n\nExample:\n```text\nfor (let i = 0; i < keywords.length; i++)\n let keywordOperation = adGroup\n .newKeywordBuilder()\n .withText(keywords[i])\n .build();\n\n // Bad: retrieving the result in the same\n // loop that creates the operation\n // leads to poor performance.\n let newKeyword =\n keywordOperation.getResult();\n newKeyword.applyLabel(\"New keywords\");\n}\n```\n\nExample:\n```text\n// Create an array to hold the operations\nlet operations = [];\n\nfor (let i = 0; i < keywords.length; i++) {\n let keywordOperation = adGroup\n .newKeywordBuilder()\n .withText(keywords[i])\n .build();\n operations.push(keywordOperation);\n}\n\n// Process the operations separately. Allows\n// Google Ads scripts to group operations into\n// batches.\nfor (let i = 0; i < operations.length; i++) {\n let newKeyword = operations[i].getResult();\n newKeyword.applyLabel(\"New keywords\");\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.113Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":63,"estimatedTokens":368}}15{"id":"doc-google_workspace_developer_release_notes_google_-263711ca","source":"documentation","title":"Google Workspace developer release notes | Google for Developers","url":"https://developers.google.com/workspace/release-notes","text":"Example:\n```text\n\"eventTime\": {\n \"seconds\": 1.742601948E9\n \"nanos\": 7.01868E8\n}\n```\n\nExample:\n```text\n\"eventTime\": \"2025-03-24T16:31:21.165203Z\"\n```\n\nExample:\n```text\naddonClient.newSessionBuilder(appContext, new MyAddonSessionDelegate())\n .withParticipantMetadata(new MyMetadataDelegate(), initialMetadata)\n .withCoWatching(new MyCoWatchingHandler())\n .withCoDoing(new MyCoDoingHandler())\n .verifyRecordingInfo() // Newly added method\n .begin();\n```\n\nExample:\n```text\n+ `LiveSharing*` → `Addon*`\n+ `*Delegate` → `*Handler`\n+ `CoWatchingSession` → `CoWatchingClient`\n+ `CoDoingSession` → `CoDoingClient`\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.126Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":32,"estimatedTokens":165}}16{"id":"doc-assets_google_ads_scripts_google_for_developers-a6adfd42","source":"documentation","title":"Assets | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/demand-gen/assets","text":"Example:\n```text\nconst file = DriveApp.getFileById(fileId);\nconst imageAsset = {\n \"assetOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/assets/${getNextTempId()}`,\n \"name\": \"Marketing Logo\",\n \"type\": \"IMAGE\",\n \"imageAsset\": {\n \"data\": Utilities.base64Encode(file.getBlob().getBytes())\n }\n }\n }\n}\n```\n\nExample:\n```text\nconst file = UrlFetchApp.fetch(imageUrl);\n```\n\nExample:\n```text\nconst videoAsset = {\n \"assetOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/assets/${getNextTempId()}`,\n \"name\": \"Marketing video\",\n \"type\": \"YOUTUBE_VIDEO\",\n \"youtube_video_asset\": {\n \"youtube_video_title\": \"Demand Gen video\",\n \"youtube_video_id\": \"123456789\"\n }\n }\n }\n}\noperations.push(videoAsset);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.127Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":41,"estimatedTokens":208}}17{"id":"doc-setup_web_guides_google_for_developers-243db62f","source":"documentation","title":"Setup | Web guides | Google for Developers","url":"https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid","text":"Example:\n```text\n<script src=\"https://accounts.google.com/gsi/client\" async></script>\n```\n\nExample:\n```text\nContent-Security-Policy-Report-Only: script-src\nhttps://accounts.google.com/gsi/client; frame-src\nhttps://accounts.google.com/gsi/; connect-src https://accounts.google.com/gsi/;\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.127Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":13,"estimatedTokens":76}}18{"id":"doc-bulk_upload_google_ads_scripts_google_for_develo-59f0c3e8","source":"documentation","title":"Bulk Upload | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/concepts/bulk-upload","text":"Example:\n```text\nconst file = DriveApp.getFilesByName(\"BulkCampaignUpload.csv\")\n .next();\nconst upload = AdsApp.bulkUploads().newFileUpload(file);\nupload.forCampaignManagement();\nupload.preview();\n```\n\nExample:\n```text\n// The best way to find column names is to consult a template\n// as described in the last section of this guide.\nconst columns = [\n \"Campaign\", \"Budget\", \"Bid Strategy type\", \"Campaign type\"\n];\n\nconst upload = AdsApp.bulkUploads().newCsvUpload(columns);\n\n// Call append once for each row you'd like to upload\nupload.append({\n \"Campaign\": \"Test Campaign 1\",\n \"Budget\": 2.34,\n \"Bid Strategy type\": \"cpc\",\n \"Campaign type\": \"Search Only\"\n});\nupload.forCampaignManagement();\nupload.preview();\n```\n\nExample:\n```text\nconst upload = AdsApp.bulkUploads().newCsvUpload(columns,\n {moneyInMicros: true});\n```\n\nExample:\n```text\nconst upload = AdsApp.bulkUploads().newCsvUpload(columns,\n {fileLocale: \"fr_FR\"});\n```\n\nExample:\n```text\nconst upload = AdsApp.bulkUploads().newCsvUpload(columns,\n {timeZone: \"America/New_York\"});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.127Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":49,"estimatedTokens":268}}19{"id":"doc-execution_logs_google_ads_scripts_google_for_dev-7ca8e206","source":"documentation","title":"Execution Logs | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/troubleshooting/execution-logs","text":"Example:\n```text\nlet spreadsheet = SpreadsheetApp.create(\"Daily Report\");\n// Populate the spreadsheet.\n// ...\nconsole.log(\"Daily report ready!\");\nconsole.log(spreadsheet.getUrl());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.128Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":50}}20{"id":"doc-execution_info_google_ads_scripts_google_for_dev-4782a604","source":"documentation","title":"Execution info | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/troubleshooting/execution-info","text":"Example:\n```text\n// Code that generates a report.\n// ...\nif (!AdsApp.getExecutionInfo().isPreview()) {\n // Do not email the report when in preview mode!\n MailApp.sendEmail(\"customer@example.com\", \"Report is ready!\", report);\n}\n```\n\nExample:\n```text\nlet accountId = AdsApp.currentAccount().getCustomerId();\nMailApp.sendEmail(\"customer@example.com\",\n \"Report is ready for \" + accountId, report);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.128Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":18,"estimatedTokens":104}}21{"id":"doc-errors_and_warnings_google_ads_scripts_google_fo-ccac6c6e","source":"documentation","title":"Errors and Warnings | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/troubleshooting/errors","text":"Example:\n```text\n// Attempt an invalid change.\nlet amount = 999999999999;\ncampaign.getBudget().setAmount(amount);\n// Error is logged into Changes log, but the script keeps running.\n\n// Suppose we must know whether the change actually happened.\nif (campaign.getBudget() != amount) {\n // The current value of budget is not the one we expected.\n // The change must have failed.\n}\n```\n\nExample:\n```text\nlet keywords = AdsApp.keywords()\n .withCondition(\"metrics.clicks > 10\")\n // Forgot forDateRange().\n .get();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.128Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":23,"estimatedTokens":134}}22{"id":"doc-mutate_google_ads_scripts_google_for_developers-cd5898e1","source":"documentation","title":"Mutate | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/concepts/mutate","text":"Example:\n```text\nconst budgetResult = AdsApp.mutate({\n campaignBudgetOperation: {\n create: {\n amountMicros: 10000000,\n explicitlyShared: false\n }\n }\n });\n```\n\nExample:\n```text\nAdsApp.mutate({\n adGroupOperation: {\n remove: \"customers/[CUSTOMER_ID]/adGroups/[AD_GROUP_ID]\"\n }\n});\n```\n\nExample:\n```text\nconst campaignResult = AdsApp.mutate({\n campaignOperation: {\n update: {\n resourceName: \"customers/[CUSTOMER_ID]/campaigns/[CAMPAIGN_ID]\",\n status: \"PAUSED\",\n name: \"[Paused] My campaign\"\n },\n updateMask: \"name,status\"\n }\n});\n```\n\nExample:\n```text\nconst result = AdsApp.mutate( ... );\nif (result.isSuccessful()) {\n console.log(`Resource ${result.getResourceName()} successfully mutated.`);\n} else {\n console.log(\"Errors encountered:\");\n for (const error of result.getErrorMessages()) {\n console.log(error);\n }\n}\n```\n\nExample:\n```text\nconst operations = [];\nconst customerId = 'INSERT_CUSTOMER_ID_HERE';\nconst budgetId = `customers/${customerId}/campaignBudgets/-1`;\nconst campaignId = `customers/${customerId}/campaigns/-2`;\noperations.push({\n campaignBudgetOperation: {\n create: {\n resourceName: budgetId,\n amountMicros: 10000000,\n explicitlyShared: false\n }\n }\n });\noperations.push({\n campaignOperation: {\n create: {\n resourceName: campaignId,\n name: 'New Campaign ' + new Date(),\n advertisingChannelType: 'SEARCH',\n manualCpc: {},\n campaignBudget: budgetId,\n advertisingChannelType: 'DISPLAY',\n networkSettings: {\n targetContentNetwork: true\n }\n }\n }\n });\noperations.push({\n adGroupOperation: {\n create: {\n campaign: campaignId,\n name: 'New AdGroup ' + new Date(),\n optimizedTargetingEnabled: true\n }\n }\n });\nconst results = AdsApp.mutateAll(\n operations, {partialFailure: false});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.129Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":92,"estimatedTokens":497}}23{"id":"doc-openid_connect_sign_in_with_google_google_for_de-4f815acc","source":"documentation","title":"OpenID Connect | Sign in with Google | Google for Developers","url":"https://developers.google.com/identity/openid-connect/openid-connect","text":"Example:\n```text\n// Create a state token to prevent request forgery.\n// Store it in the session for later validation.\n$state = bin2hex(random_bytes(128/8));\n$app['session']->set('state', $state);\n// Set the client ID, token state, and application name in the HTML while\n// serving it.\nreturn $app['twig']->render('index.html', array(\n 'CLIENT_ID' => CLIENT_ID,\n 'STATE' => $state,\n 'APPLICATION_NAME' => APPLICATION_NAME\n));\n```\n\nExample:\n```text\n// Create a state token to prevent request forgery.\n// Store it in the session for later validation.\nString state = new BigInteger(130, new SecureRandom()).toString(32);\nrequest.session().attribute(\"state\", state);\n// Read index.html into memory, and set the client ID,\n// token state, and application name in the HTML before serving it.\nreturn new Scanner(new File(\"index.html\"), \"UTF-8\")\n .useDelimiter(\"\\\\A\").next()\n .replaceAll(\"[{]{2}\\\\s*CLIENT_ID\\\\s*[}]{2}\", CLIENT_ID)\n .replaceAll(\"[{]{2}\\\\s*STATE\\\\s*[}]{2}\", state)\n .replaceAll(\"[{]{2}\\\\s*APPLICATION_NAME\\\\s*[}]{2}\",\n APPLICATION_NAME);\n```\n\nExample:\n```text\n# Create a state token to prevent request forgery.\n# Store it in the session for later validation.\nstate = hashlib.sha256(os.urandom(1024)).hexdigest()\nsession['state'] = state\n# Set the client ID, token state, and application name in the HTML while\n# serving it.\nresponse = make_response(\n render_template('index.html',\n CLIENT_ID=CLIENT_ID,\n STATE=state,\n APPLICATION_NAME=APPLICATION_NAME))\n```\n\nExample:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth?\n response_type=code&\n client_id=424911365001.apps.googleusercontent.com&\n scope=openid%20email&\n redirect_uri=https%3A//developers.google.com/oauthplayground&\n state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2-login-demo.example.com%2FmyHome&\n login_hint=jsmith@example.com&\n nonce=0394852-3190485-2490358&\n hd=example.com\n```\n\nExample:\n```text\nhttps://developers.google.com/oauthplayground?state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foa2cb.example.com%2FmyHome&code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&scope=openid%20email%20https://www.googleapis.com/auth/userinfo.email\n```\n\nExample:\n```text\n// Ensure that there is no request forgery going on, and that the user\n// sending us this connect request is the user that was supposed to.\nif ($request->get('state') != ($app['session']->get('state'))) {\n return new Response('Invalid state parameter', 401);\n}\n```\n\nExample:\n```text\n// Ensure that there is no request forgery going on, and that the user\n// sending us this connect request is the user that was supposed to.\nif (!request.queryParams(\"state\").equals(\n request.session().attribute(\"state\"))) {\n response.status(401);\n return GSON.toJson(\"Invalid state parameter.\");\n}\n```\n\nExample:\n```text\n# Ensure that the request is not a forgery and that the user sending\n# this connect request is the expected user.\nif request.args.get('state', '') != session['state']:\n response = make_response(json.dumps('Invalid state parameter.'), 401)\n response.headers['Content-Type'] = 'application/json'\n return response\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.googleapis.com\nContent-Type: application/x-www-form-urlencoded\n\ncode=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&\nclient_id=your-client-id&\nclient_secret=your-client-secret&\nredirect_uri=https%3A//developers.google.com/oauthplayground&\ngrant_type=authorization_code\n```\n\nExample:\n```text\n{\n \"iss\": \"https://accounts.google.com\",\n \"azp\": \"1234987819200.apps.googleusercontent.com\",\n \"aud\": \"1234987819200.apps.googleusercontent.com\",\n \"sub\": \"10769150350006150715113082367\",\n \"at_hash\": \"HK6E_P6Dh8Y93mRNtsDB1Q\",\n \"hd\": \"example.com\",\n \"email\": \"jsmith@example.com\",\n \"email_verified\": \"true\",\n \"iat\": 1353601026,\n \"exp\": 1353604926,\n \"nonce\": \"0394852-3190485-2490358\"\n}\n```\n\nExample:\n```text\nscope=openid%20profile%20email\n```\n\nExample:\n```text\nhttps://accounts.google.com/.well-known/openid-configuration\n```\n\nExample:\n```text\n{\n \"issuer\": \"https://accounts.google.com\",\n \"authorization_endpoint\": \"https://accounts.google.com/o/oauth2/v2/auth\",\n \"device_authorization_endpoint\": \"https://oauth2.googleapis.com/device/code\",\n \"token_endpoint\": \"https://oauth2.googleapis.com/token\",\n \"userinfo_endpoint\": \"https://openidconnect.googleapis.com/v1/userinfo\",\n \"revocation_endpoint\": \"https://oauth2.googleapis.com/revoke\",\n \"jwks_uri\": \"https://www.googleapis.com/oauth2/v3/certs\",\n \"response_types_supported\": [\n \"code\",\n \"token\",\n \"id_token\",\n \"code token\",\n \"code id_token\",\n \"token id_token\",\n \"code token id_token\",\n \"none\"\n ],\n \"subject_types_supported\": [\n \"public\"\n ],\n \"id_token_signing_alg_values_supported\": [\n \"RS256\"\n ],\n \"scopes_supported\": [\n \"openid\",\n \"email\",\n \"profile\"\n ],\n \"token_endpoint_auth_methods_supported\": [\n \"client_secret_post\",\n \"client_secret_basic\"\n ],\n \"claims_supported\": [\n \"aud\",\n \"email\",\n \"email_verified\",\n \"exp\",\n \"family_name\",\n \"given_name\",\n \"iat\",\n \"iss\",\n \"locale\",\n \"name\",\n \"picture\",\n \"sub\"\n ],\n \"code_challenge_methods_supported\": [\n \"plain\",\n \"S256\"\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.131Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":191,"estimatedTokens":1311}}24{"id":"doc-reporting_google_ads_scripts_google_for_develope-8945dd8f","source":"documentation","title":"Reporting | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/concepts/reports","text":"Example:\n```text\nSELECT\n campaign.id,\n campaign.status,\n metrics.clicks,\n metrics.impressions,\n customer.id\nFROM campaign\nWHERE\n metrics.impressions > 0\n```\n\nExample:\n```text\nlet report = AdsApp.report(\n \"SELECT \" +\n \" ad_group.id, search_term_view.search_term, metrics.ctr, metrics.cost_micros, metrics.impressions \" +\n \"FROM search_term_view \" +\n \"WHERE metrics.impressions < 10 AND segments.date DURING LAST_30_DAYS\");\n\nlet rows = report.rows();\nwhile (rows.hasNext()) {\n let row = rows.next();\n let query = row[\"search_term_view.search_term\"];\n let impressions = row[\"metrics.impressions\"];\n}\n```\n\nExample:\n```text\nlet search = AdsApp.search(\n \"SELECT \" +\n \" ad_group.id, search_term_view.search_term, metrics.ctr, metrics.cost_micros, metrics.impressions \" +\n \"FROM search_term_view \" +\n \"WHERE metrics.impressions < 10 AND segments.date DURING LAST_30_DAYS\");\n\nwhile (search.hasNext()) {\n let row = search.next();\n let query = row.searchTermView.searchTerm;\n let impressions = row.metrics.impressions;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.133Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":45,"estimatedTokens":269}}25{"id":"doc-working_with_dates_and_times_google_ads_scripts_-0ab029dd","source":"documentation","title":"Working with Dates and Times | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/elements/dates","text":"Example:\n```text\n// Create a date object for the current date and time.\nconst now = new Date();\n\n// Create a date object for a past date and time using a formatted string.\nconst date = new Date('February 17, 2025 13:00:00 -0500');\n\n// Create a copy of an existing date object.\nlet copy = new Date(date);\n```\n\nExample:\n```text\n// Create two date objects with different times and time zone offsets.\nconst date1 = new Date('February 17, 2025 13:00:00 -0500');\nconst date2 = new Date('February 17, 2025 10:00:00 -0800');\n\n// getTime() returns the number of milliseconds since the beginning of\n// January 1, 1970 UTC.\n// True, as the dates represent the same moment in time.\nconsole.log(date1.getTime() == date2.getTime());\n\n// False, as the dates are separate objects, though they happen to\n// represent the same moment in time.\nconsole.log(date1 == date2);\n```\n\nExample:\n```text\nconst date = new Date('February 17, 2025 13:00:00 -0500');\n\n// February 17, 2025 13:00:00 -0500\nconsole.log(Utilities.formatDate(date, 'America/New_York', 'MMMM dd, yyyy HH:mm:ss Z'));\n\n// February 17, 2025 10:00:00 -0800\nconsole.log(Utilities.formatDate(date, 'America/Los_Angeles', 'MMMM dd, yyyy HH:mm:ss Z'));\n\n// 2025-02-17T18:00:00.000Z\nconsole.log(Utilities.formatDate(date, 'Etc/GMT', 'yyyy-MM-dd\\'T\\'HH:mm:ss.SSS\\'Z\\''));\n```\n\nExample:\n```text\nconst date = new Date('February 17, 2025 13:00:00 -0500');\n\n// Mon Feb 17 10:00:00 GMT-08:00 2025\nconsole.log(date);\n```\n\nExample:\n```text\n// Create a date without specifying the time zone offset.\nconst date = new Date('February 17, 2025 13:00:00');\n\n// Mon Feb 17 13:00:00 GMT-08:00 2025\nconsole.log(date);\n```\n\nExample:\n```text\nconst date = new Date('February 17, 2025 13:00:00 -0500');\n```\n\nExample:\n```text\nconst now = new Date();\nconst timeZone = AdsApp.currentAccount().getTimeZone();\nconst noonString = Utilities.formatDate(now, timeZone, 'MMMM dd, yyyy 12:00:00 Z');\nconst noon = new Date(noonString);\n```\n\nExample:\n```text\nconst MILLIS_PER_DAY = 1000 * 60 * 60 * 24;\nconst now = new Date();\nconst yesterday = new Date(now.getTime() - MILLIS_PER_DAY);\n```\n\nExample:\n```text\nconst MILLIS_PER_DAY = 1000 * 60 * 60 * 24;\nconst now = new Date();\nconst from = new Date(now.getTime() - 3 * MILLIS_PER_DAY);\nconst to = new Date(now.getTime() - 1 * MILLIS_PER_DAY);\n\nconst timeZone = AdsApp.currentAccount().getTimeZone();\nconst results = AdsApp.search(\n 'SELECT campaign.name, metrics.clicks' +\n 'FROM campaign ' +\n 'WHERE segments.date BETWEEN ' +\n Utilities.formatDate(from, timeZone, 'yyyy-MM-dd') + ' AND ' +\n Utilities.formatDate(to, timeZone, 'yyyy-MM-dd'));\n```\n\nExample:\n```text\n// Suppose today is February 17, 2025 13:00:00 -0500 (Eastern Time)\nconst now = new Date();\nspreadsheet.getRange('A1').setValue(now);\n```\n\nExample:\n```text\nspreadsheet.setSpreadsheetTimeZone(AdsApp.currentAccount().getTimeZone());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.133Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":108,"estimatedTokens":720}}26{"id":"doc-ads_manager_scripts_google_ads_scripts_google_fo-47381fdf","source":"documentation","title":"Ads Manager Scripts | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/concepts/manager-scripts","text":"Example:\n```text\nconst accountSelector = AdsManagerApp.accounts()\n .withCondition('customer_client.descriptive_name = \"My Account\"');\n\nconst accountIterator = accountSelector.get();\n```\n\nExample:\n```text\n// Hyphens in the account ID are optional.\nconst accountSelector = AdsManagerApp.accounts()\n .withIds(['123-456-7890', '234-567-8901', '345-678-9012']);\n```\n\nExample:\n```text\n// Keep track of the manager account for future reference.\nconst managerAccount = AdsApp.currentAccount();\n\n// Select your accounts\nconst accountIterator = AdsManagerApp.accounts()\n// ... Write some logic here to select the accounts you want using\n// withCondition or withIds\n\n// Iterate through the list of accounts\nfor (const account of accountIterator) {\n // Select the client account.\n AdsManagerApp.select(account);\n\n // Select Search and Display campaigns under the client account\n const campaignIterator = AdsApp.campaigns().get();\n\n // Operate on client account\n ...\n}\n```\n\nExample:\n```text\nfunction executeInParallel(functionName, optionalCallbackFunctionName, optionalInput);\n```\n\nExample:\n```text\nfunction main() {\n const accountSelector = AdsManagerApp.accounts()\n .withLimit(50)\n .withCondition('customer_client.currency_code = \"USD\"');\n\n accountSelector.executeInParallel(\"processClientAccount\", \"afterProcessAllClientAccounts\");\n}\n\nfunction processClientAccount() {\n const clientAccount = AdsApp.currentAccount();\n\n // Process your client account here.\n ...\n\n // optionally, return a result, as text.\n return \"\";\n}\n\nfunction afterProcessAllClientAccounts(results) {\n for (const result of results) {\n // Process the result further\n ...\n }\n}\n```\n\nExample:\n```text\nfunction main() {\n const accountSelector = AdsManagerApp.accounts().withIds([1234567890, 3456787890]);\n const sharedParameter = \"INSERT_SHARED_PARAMETER_HERE\";\n accountSelector.executeInParallel(\"processClientAccount\", null, sharedParameter);\n}\n\nfunction processClientAccount(sharedParameter) {\n // Process your client account here.\n ...\n}\n```\n\nExample:\n```text\nfunction main() {\n ...\n const accountFlags = {\n '1234567890': {\n 'label': 'Brand 1 campaigns',\n },\n '3456787890': {\n 'label': 'Brand 2 campaigns',\n }\n };\n accountSelector.executeInParallel(\"processClientAccount\", null,\n JSON.stringify(accountFlags));\n ...\n}\n\nfunction processClientAccount(sharedParameter) {\n const accountFlags = JSON.parse(sharedParameter);\n // Process your client account here.\n ...\n}\n```\n\nExample:\n```text\nfunction processClientAccount() {\n ...\n const jsonObj = {value: 10, list: [1,2,3,4,5,6], name: \"Joe Smith\"};\n return JSON.stringify(jsonObj);\n}\n```\n\nExample:\n```text\nfunction callbackFunctionName(results) {\n for (var i = 0; i < results.length; i++) {\n var resultObj = JSON.parse(results[i].getReturnValue());\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.134Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":128,"estimatedTokens":716}}27{"id":"doc-make_google_ads_api_calls_with_the_mutate_strate-1b09038e","source":"documentation","title":"Make Google Ads API calls with the mutate strategy | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/demand-gen/mutate-strategy","text":"Example:\n```text\nconst operations = [];\n```\n\nExample:\n```text\nconst customerId = AdsApp.currentAccount().getCustomerId();\n```\n\nExample:\n```text\nconst newOperation = {\n [OPERATION_TYPE_VARIES]: {\n create: {\n resourceName: `customers/${customerId}/[EXACT_PATH_VARIES]/${getNextTempId()}`\n // Other fields, relevant to the resource being created.\n }\n }\n}\noperations.push(newOperation);\n```\n\nExample:\n```text\nAdsApp.mutateAll(operations);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.134Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":29,"estimatedTokens":124}}28{"id":"doc-labels_google_ads_scripts_google_for_developers-e1bb7b60","source":"documentation","title":"Labels | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/concepts/labels","text":"Example:\n```text\nconst labelName = 'High spending accounts';\nAdsManagerApp.createAccountLabel(labelName);\n```\n\nExample:\n```text\nconst accountIds = ['123-456-7890', '345-6789-2100'];\nconst labelName = 'High spending accounts';\n\nconst accounts = AdsManagerApp.accounts().withIds(accountIds).get();\nfor (const account of accounts) {\n account.applyLabel(labelName);\n}\n```\n\nExample:\n```text\nconst accountIds = ['123-456-7890', '345-6789-2100'];\nconst labelName = 'High spending accounts';\n\nconst accounts = AdsManagerApp.accounts().withIds(accountIds).get();\nfor (const account of accounts) {\n account.removeLabel(labelName);\n}\n```\n\nExample:\n```text\nconst labelName = 'High spending accounts';\n\nconst accounts = AdsManagerApp.accounts()\n .withCondition(`LabelNames CONTAINS \"${labelName}\"`)\n .get();\n```\n\nExample:\n```text\nconst campaign = AdsApp.campaigns()\n .withCondition('campaign.name = \"My first campaign\"').get().next();\ncampaign.applyLabel('High performing campaign');\n```\n\nExample:\n```text\nconst campaign = AdsApp.campaigns()\n .withCondition('campaign.name = \"My first campaign\"').get().next();\ncampaign.removeLabel('High performing campaign');\n```\n\nExample:\n```text\nconst label = AdsApp.labels()\n .withCondition('label.name = \"Christmas promotions\"')\n .get().next();\nvar campaignIterator = label.campaigns().get();\nfor (const campaign of campaignIterator) {\n campaign.pause();\n}\n```\n\nExample:\n```text\ncustomers/[customer id]/labels/[label id]\n```\n\nExample:\n```text\nconst label = AdsApp.labels()\n .withCondition(\"label.name = 'Christmas promotions'\")\n .get().next();\nconst query = `SELECT campaign.name, metrics.clicks, metrics.impressions, metrics.cost ` +\n `FROM campaign WHERE campaign.labels CONTAINS ANY ` +\n `[\"${label.getResourceName()}\"] AND segments.date DURING THIS_MONTH`;\nconst result = AdsApp.search(query);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.135Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":79,"estimatedTokens":469}}29{"id":"doc-best_practices_google_for_developers-37bacb51","source":"documentation","title":"Best Practices | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/best-practices","text":"Example:\n```text\nvar keywords = AdsApp.keywords()\n .withCondition('Clicks > 10')\n .forDateRange('LAST_MONTH')\n .get();\nwhile (keywords.hasNext()) {\n var keyword = keywords.next();\n // Do work here.\n}\n```\n\nExample:\n```text\nvar keywords = AdsApp.keywords().get();\n\nwhile (keywords.hasNext()) {\n var keyword = keywords.next();\n var stats = keyword.getStatsFor(\n 'LAST_MONTH');\n if (stats.getClicks() > 10) {\n // Do work here.\n }\n}\n```\n\nExample:\n```text\nvar ads = AdsApp.ads();\n```\n\nExample:\n```text\nvar campaigns = AdsApp.campaigns().get();\nwhile (campaigns.hasNext()) {\n var adGroups = campaigns.next().\n adGroups().get();\n while (adGroups.hasNext()) {\n var ads = adGroups.next().ads().get();\n // Do your work here.\n }\n}\n```\n\nExample:\n```text\nvar ads = AdsApp.ads()\n .withCondition('Clicks > 50')\n .forDateRange('LAST_MONTH')\n .get();\n\nwhile (ads.hasNext()) {\n var ad = ads.next();\n var adGroup = ad.getAdGroup();\n var campaign = ad.getCampaign();\n // Store (campaign, adGroup) to an array.\n}\n```\n\nExample:\n```text\nvar campaigns = AdsApp.campaigns().get();\nwhile (campaigns.hasNext()) {\n var adGroups = campaigns.next()\n .adGroups()\n .get();\n while (adGroups.hasNext()) {\n var ads = adGroups.ads()\n .withCondition('Clicks > 50')\n .forDateRange('LAST_MONTH')\n .get();\n if (ads.totalNumEntities() > 0) {\n // Store (campaign, adGroup) to an array.\n }\n }\n}\n```\n\nExample:\n```text\nvar ads = AdsApp.ads()\n .withCondition('CampaignName = \"Campaign 1\"')\n .withCondition('AdGroupName = \"AdGroup 1\"')\n .withCondition('Clicks > 50')\n .forDateRange('LAST_MONTH')\n .get();\n\nwhile (ads.hasNext()) {\n var ad = ads.next();\n var adGroup = ad.getAdGroup();\n var campaign = ad.getCampaign();\n // Store (campaign, adGroup, ad) to\n // an array.\n}\n```\n\nExample:\n```text\nvar campaigns = AdsApp.campaigns()\n .withCondition('Name = \"Campaign 1\"')\n .get();\n\nwhile (campaigns.hasNext()) {\n var adGroups = campaigns.next()\n .adGroups()\n .withCondition('Name = \"AdGroup 1\"')\n .get();\n while (adGroups.hasNext()) {\n var ads = adGroups.ads()\n .withCondition('Clicks > 50')\n .forDateRange('LAST_MONTH')\n .get();\n while (ads.hasNext()) {\n var ad = ads.next();\n // Store (campaign, adGroup, ad) to\n // an array.\n }\n }\n}\n```\n\nExample:\n```text\nvar campaign = AdsApp.campaigns()\n .withIds([12345])\n .get()\n .next();\n```\n\nExample:\n```text\nvar campaign = AdsApp.campaigns()\n .withCondition('Name=\"foo\"')\n .get()\n .next();\n```\n\nExample:\n```text\nvar adGroup = AdsApp.adGroups()\n .withIds([12345])\n .withCondition('CampaignId=\"54678\"')\n .get()\n .next();\n```\n\nExample:\n```text\nvar adGroup = AdsApp.adGroups()\n .withIds([12345])\n .get()\n .next();\n```\n\nExample:\n```text\nvar label = AdsApp.labels()\n .withCondition('Name = \"My Label\"')\n .get()\n .next();\nvar campaigns = label.campaigns.get();\nwhile (campaigns.hasNext()) {\n var campaign = campaigns.next();\n // Do more work\n}\n```\n\nExample:\n```text\nvar campaignNames = ['foo', 'bar', 'baz'];\n\nfor (var i = 0; i < campaignNames.length; i++) {\n campaignNames[i] = '\"' + campaignNames[i] + '\"';\n}\n\nvar campaigns = AdsApp.campaigns\n .withCondition('CampaignName in [' + campaignNames.join(',') + ']')\n .get();\n\nwhile (campaigns.hasNext()) {\n var campaign = campaigns.next();\n // Do more work.\n}\n```\n\nExample:\n```text\n// The label applied to the entity is \"Report Entities\"\nvar label = AdsApp.labels()\n .withCondition('LabelName contains \"Report Entities\"')\n .get()\n .next();\n\nvar report = AdsApp.report('SELECT AdGroupId, Id, Clicks, ' +\n 'Impressions, Cost FROM KEYWORDS_PERFORMANCE_REPORT ' +\n 'WHERE LabelId = \"' + label.getId() + '\"');\n```\n\nExample:\n```text\nvar report = AdsApp.report('SELECT AdGroupId, Id, Clicks, ' +\n 'Impressions, Cost FROM KEYWORDS_PERFORMANCE_REPORT WHERE ' +\n 'AdGroupId IN (123, 456) and Id in (123,345, 456…)');\n```\n\nExample:\n```text\nvar keywords = AdsApp.keywords()\n .withCondition('Clicks > 50')\n .withCondition('CampaignName = \"Campaign 1\"')\n .withCondition('AdGroupName = \"AdGroup 1\"')\n .forDateRange('LAST_MONTH')\n .get();\n\nvar list = [];\nwhile (keywords.hasNext()) {\n var keyword = keywords.next();\n keyword.bidding().setCpc(1.5);\n list.push(keyword);\n}\n\nfor (var i = 0; i < list.length; i++) {\n var keyword = list[i];\n Logger.log('%s, %s', keyword.getText(),\n keyword.bidding().getCpc());\n}\n```\n\nExample:\n```text\nvar keywords = AdsApp.keywords()\n .withCondition('Clicks > 50')\n .withCondition('CampaignName = \"Campaign 1\"')\n .withCondition('AdGroupName = \"AdGroup 1\"')\n .forDateRange('LAST_MONTH')\n .get();\n\nwhile (keywords.hasNext()) {\n var keyword = keywords.next();\n keyword.bidding().setCpc(1.5);\n Logger.log('%s, %s', keyword.getText(),\n keyword.bidding().getCpc());\n}\n```\n\nExample:\n```text\nvar operation = adGroup.newKeywordBuilder()\n .withText('shoes')\n .build();\nvar keyword = operation.getResult();\n```\n\nExample:\n```text\nadGroup.createKeyword('shoes');\nvar keyword = adGroup.keywords()\n .withCondition('KeywordText=\"shoes\"')\n .get()\n .next();\n```\n\nExample:\n```text\nvar keywords = ['foo', 'bar', 'baz'];\n\nvar list = [];\nfor (var i = 0; i < keywords.length; i++) {\n var operation = adGroup.newKeywordBuilder()\n .withText(keywords[i])\n .build();\n list.push(operation);\n}\n\nfor (var i = 0; i < list.length; i++) {\n var operation = list[i];\n var result = operation.getResult();\n Logger.log('%s %s', result.getId(),\n result.getText());\n}\n```\n\nExample:\n```text\nvar keywords = ['foo', 'bar', 'baz'];\n\nfor (var i = 0; i < keywords.length; i++) {\n var operation = adGroup.newKeywordBuilder()\n .withText(keywords[i])\n .build();\n var result = operation.getResult();\n Logger.log('%s %s', result.getId(),\n result.getText());\n}\n```\n\nExample:\n```text\nvar report = AdsApp.report(\n 'SELECT AdGroupId, Id, CpcBid FROM KEYWORDS_PERFORMANCE_REPORT ' +\n 'WHERE TopImpressionPercentage > 0.4 DURING LAST_MONTH');\n\nvar upload = AdsApp.bulkUploads().newCsvUpload([\n report.getColumnHeader('AdGroupId').getBulkUploadColumnName(),\n report.getColumnHeader('Id').getBulkUploadColumnName(),\n report.getColumnHeader('CpcBid').getBulkUploadColumnName()]);\nupload.forCampaignManagement();\n\nvar reportRows = report.rows();\nwhile (reportRows.hasNext()) {\n var row = reportRows.next();\n row['CpcBid'] = row['CpcBid'] + 0.02;\n upload.append(row.formatForUpload());\n}\n\nupload.apply();\n```\n\nExample:\n```text\nvar reportRows = AdsApp.report('SELECT AdGroupId, Id, CpcBid FROM ' +\n 'KEYWORDS_PERFORMANCE_REPORT WHERE TopImpressionPercentage > 0.4 ' +\n ' DURING LAST_MONTH')\n .rows();\n\nvar map = {\n};\n\nwhile (reportRows.hasNext()) {\n var row = reportRows.next();\n var adGroupId = row['AdGroupId'];\n var id = row['Id'];\n\n if (map[adGroupId] == null) {\n map[adGroupId] = [];\n }\n map[adGroupId].push([adGroupId, id]);\n}\n\nfor (var key in map) {\n var keywords = AdsApp.keywords()\n .withCondition('AdGroupId=\"' + key + '\"')\n .withIds(map[key])\n .get();\n\n while (keywords.hasNext()) {\n var keyword = keywords.next();\n keyword.bidding().setCpc(keyword.bidding().getCpc() + 0.02);\n }\n}\n```\n\nExample:\n```text\nreport = AdsApp.search(\n 'SELECT ' +\n ' ad_group_criterion.keyword.text, ' +\n ' metrics.clicks, ' +\n ' metrics.cost_micros, ' +\n ' metrics.impressions ' +\n 'FROM ' +\n ' keyword_view ' +\n 'WHERE ' +\n ' segments.date DURING LAST_MONTH ' +\n ' AND metrics.clicks > 50');\n while (report.hasNext()) {\n var row = report.next();\n Logger.log('Keyword: %s Impressions: %s ' +\n 'Clicks: %s Cost: %s',\n row.adGroupCriterion.keyword.text,\n row.metrics.impressions,\n row.metrics.clicks,\n row.metrics.cost);\n }\n```\n\nExample:\n```text\nvar keywords = AdsApp.keywords()\n .withCondition('metrics.clicks > 50')\n .forDateRange('LAST_MONTH')\n .get();\nwhile (keywords.hasNext()) {\n var keyword = keywords.next();\n var stats = keyword.getStatsFor('LAST_MONTH');\n Logger.log('Keyword: %s Impressions: %s ' +\n 'Clicks: %s Cost: %s',\n keyword.getText(),\n stats.getImpressions(),\n stats.getClicks(),\n stats.getCost());\n}\n```\n\nExample:\n```text\nvar adGroups = []\nvar report = AdsApp.search(\n 'SELECT ad_group.name, ad_group.cpc_bid_micros' +\n ' FROM ad_group WHERE ad_group.cpc_bid_micros < 1000000');\n\nwhile (report.hasNext()) {\n var row = report.next();\n adGroups.push(row.adGroup);\n}\nvar report = AdsApp.search(\n 'SELECT ad_group.name, ad_group.cpc_bid_micros' +\n ' FROM ad_group WHERE ad_group.cpc_bid_micros > 2000000');\n\nwhile (report.hasNext()) {\n var row = report.next();\n adGroups.push(row.adGroup);\n}\n```\n\nExample:\n```text\nvar adGroups = []\nvar report = AdsApp.search(\n 'SELECT ad_group.name, ad_group.cpc_bid_micros' +\n ' FROM ad_group');\n\nwhile (report.hasNext()) {\n var row = report.next();\n var cpcBidMicros = row.adGroup.cpcBidMicros;\n if (cpcBidMicros < 1000000 || cpcBidMicros > 2000000) {\n adGroups.push(row.adGroup);\n }\n}\n```\n\nExample:\n```text\nvar colors = new Array(100);\nfor (var y = 0; y < 100; y++) {\n xcoord = xmin;\n colors[y] = new Array(100);\n for (var x = 0; x < 100; x++) {\n colors[y][x] = getColor_(xcoord, ycoord);\n xcoord += xincrement;\n }\n ycoord -= yincrement;\n}\nsheet.getRange(1, 1, 100, 100).setBackgroundColors(colors);\n```\n\nExample:\n```text\nvar cell = sheet.getRange('a1');\nfor (var y = 0; y < 100; y++) {\n xcoord = xmin;\n for (var x = 0; x < 100; x++) {\n var c = getColor_(xcoord, ycoord);\n cell.offset(y, x).setBackgroundColor(c);\n xcoord += xincrement;\n }\n ycoord -= yincrement;\n SpreadsheetApp.flush();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.136Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":30,"totalLines":462,"estimatedTokens":2460}}30{"id":"doc-campaign_targeting_google_ads_scripts_google_for-b19caf43","source":"documentation","title":"Campaign Targeting | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/campaign-targeting","text":"Example:\n```text\nconst campaign = AdsApp.campaigns()\n .withCondition(\"campaign.name = 'My campaign'\")\n .get()\n .next();\n\nconst adSchedules = campaign.targeting().adSchedules().get();\nfor (const adSchedule of adSchedules) {\n // Process your ad schedule.\n ...\n}\n```\n\nExample:\n```text\nadSchedule.setBidModifier(1.1);\n```\n\nExample:\n```text\ncampaign.addAdSchedule({\n dayOfWeek: \"SATURDAY\",\n startHour: 7,\n startMinute: 0,\n endHour: 11,\n endMinute: 0,\n bidModifier: 1.1\n});\n```\n\nExample:\n```text\nconst adSchedules = campaign.adSchedules().get();\nfor (const adSchedule of adSchedules) {\n adSchedule.remove();\n}\n```\n\nExample:\n```text\nconst locations = AdsApp.targeting()\n .targetedLocations()\n .withCondition(\"metrics.impressions > 100\")\n .forDateRange(\"LAST_MONTH\")\n .orderBy(\"metrics.clicks DESC\")\n .get();\n\nfor (const location of locations) {\n // Process the campaign target here.\n ...\n}\n```\n\nExample:\n```text\nlocation.setBidModifier(1.1);\n```\n\nExample:\n```text\ncampaign.addLocation(2840, 1.15); // United States\ncampaign.excludeLocation(1023191); // New York city\n```\n\nExample:\n```text\nconst proximities = AdsApp.targeting()\n .targetedProximities()\n .withCondition(\"metrics.impressions > 100\")\n .forDateRange(\"LAST_MONTH\")\n .orderBy(\"metrics.clicks DESC\")\n .get();\n\nfor (const proximity of proximities) {\n ...\n}\n```\n\nExample:\n```text\ncampaign.addProximity(37.423021, -122.083739, 20, \"KILOMETERS\");\n```\n\nExample:\n```text\ncampaign.addProximity(37.423021, -122.083739, 20, \"KILOMETERS\", {\n bidModifier: 1.15,\n address: {\n streetAddress: \"1600 Amphitheatre Parkway\",\n cityName: \"Mountain View\",\n provinceName: \"California\",\n provinceCode: \"CA\",\n postalCode: \"94043\",\n countryCode: \"US\"\n }\n});\n```\n\nExample:\n```text\ncampaign.targeting()\n .platforms()\n .mobile()\n .get()\n .next().\n setBidModifier(1.2);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.136Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":110,"estimatedTokens":475}}31{"id":"doc-optional_components_of_demand_gen_google_ads_scr-e923a6cf","source":"documentation","title":"Optional components of Demand Gen | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/demand-gen/optional-components","text":"Example:\n```text\nconst userListOperation = {\n \"userListOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/userLists/${getNextTempId()}`,\n \"type\": \"LOOKALIKE\",\n \"name\": \"Demand Gen Lookalike audience\",\n \"lookalikeUserList\": {\n \"expansionLevel\": \"BALANCED\",\n \"countryCodes\": [\n \"US\", \"UM\"\n ],\n \"seedUserListIds\": [\n 1, 2, 3, 4, 5, 6, 7, 8, 9, ...\n ]\n }\n }\n }\n}\noperations.push(userListOperation);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.137Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":24,"estimatedTokens":129}}32{"id":"doc-required_components_of_performance_max_google_ad-bb7f55b6","source":"documentation","title":"Required components of Performance Max | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/performance-max/required-components","text":"Example:\n```text\nconst budgetOperation = {\n \"campaignBudgetOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/campaignBudgets/${getNextTempId()}`,\n \"name\": \"Performance Max campaign budget\",\n \"amountMicros\": \"50000000\",\n \"deliveryMethod\": \"STANDARD\",\n \"explicitlyShared\": false\n }\n }\n}\noperations.push(budgetOperation);\n```\n\nExample:\n```text\nconst campaignOperation = {\n \"campaignOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/campaigns/${getNextTempId()}`,\n \"name\": \"Performance Max campaign\",\n \"status\": \"PAUSED\",\n \"advertisingChannelType\": \"PERFORMANCE_MAX\",\n \"campaignBudget\": budgetOperation.campaignBudgetOperation.create.resourceName,\n \"biddingStrategyType\": \"MAXIMIZE_CONVERSION_VALUE\",\n \"startDate\": \"20240314\",\n \"endDate\": \"20250313\",\n \"urlExpansionOptOut\": false,\n \"maximizeConversionValue\": {\n \"targetRoas\": 3.5\n },\n \"containsEuPoliticalAdvertising\": \"DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\"\n }\n }\n}\noperations.push(campaignOperation);\n```\n\nExample:\n```text\nconst assetGroupOperation = {\n \"assetGroupOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/assetGroups/${getNextTempId()}`,\n \"campaign\": campaignOperation.campaignOperation.create.resourceName,\n \"name\": \"Performance Max asset group\",\n \"finalUrls\": [\n \"http://www.example.com\"\n ],\n \"finalMobileUrls\": [\n \"http://www.example.com\"\n ],\n \"status\": \"PAUSED\"\n }\n }\n}\noperations.push(assetGroupOperation);\n```\n\nExample:\n```text\noperations.push({\n \"assetGroupAssetOperation\": {\n \"create\": {\n \"assetGroup\": assetGroupOperation.assetGroupOperation.create.resourceName,\n // assetResourceName here is a placeholder; you will need to determine\n // the correct resource name to use depending on which asset you want\n // to add to the asset group.\n \"asset\": assetResourceName,\n \"fieldType\": \"HEADLINE\"\n }\n }\n});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.137Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":78,"estimatedTokens":514}}33{"id":"doc-reporting_google_ads_scripts_google_for_develope-47c3bbc6","source":"documentation","title":"Reporting | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/demand-gen/reporting","text":"Example:\n```text\nconst searchResults = AdsApp.search(`\nSELECT\n campaign.id,\n campaign.status,\n campaign.bidding_strategy_type\nFROM campaign\nWHERE campaign.advertising_channel_type = DEMAND_GEN\n `);\n\n while (searchResults.hasNext()) {\n const row = searchResults.next();\n const campaign = row.campaign;\n // Your custom logic here, fetching the selected fields to do your analysis.\n }\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.137Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":19,"estimatedTokens":104}}34{"id":"doc-optional_components_of_performance_max_google_ad-4442134f","source":"documentation","title":"Optional components of Performance Max | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/performance-max/optional-components","text":"Example:\n```text\n// Query for a campaign by name. Update this logic to pull the campaigns you'd\n// like to edit\nconst campaignName = \"My PMax campaign\";\nlet campaignId = \"\";\n\nconst search = AdsApp.search(`SELECT campaign.id FROM campaign WHERE campaign.name = \"${campaignName}\"`);\nif (search.hasNext()) {\n campaignId = search.next().campaign.id;\n console.log(`Updating conversion goals for ${campaignName}: ${campaignId}`);\n}\nelse\n{\n console.log(`No campaign named \"${campaignName}\" found`);\n // Perform further error handling here\n}\n\n// Query for a list of customer conversion goals\nconst searchResults = AdsApp.search(\n `SELECT\n customer_conversion_goal.category,\n customer_conversion_goal.origin\n FROM customer_conversion_goal`\n);\n```\n\nExample:\n```text\noperations = [];\nwhile (searchResults.hasNext()) {\n const row = searchResults.next();\n const conversionGoal = row.customerConversionGoal;\n\n operations.push({\n \"campaignConversionGoalOperation\": {\n \"update\": {\n \"resourceName\": `customers/${customerId}/campaignConversionGoals/${campaignId}~${conversionGoal.category}~${conversionGoal.origin}`,\n // Insert your logic here to determine whether you want this particular\n // campaign conversion goal to be biddable or not.\n // This code will just default everything to being biddable, but that\n // is not necessarily best for your use case.\n \"biddable\": true\n },\n \"updateMask\": \"biddable\"\n }\n });\n}\n\nAdsApp.mutateAll(operations, {partialFailure: false});\n```\n\nExample:\n```text\noperations.push({\n \"campaignCriterionOperation\": {\n \"create\": {\n \"campaign\": campaignOperation.campaignOperation.create.resourceName,\n \"negative\": false,\n \"location\": {\n // 1023191 represents New York City\n \"geoTargetConstant\": \"geoTargetConstants/1023191\"\n }\n }\n }\n});\n```\n\nExample:\n```text\noperations.push({\n \"assetGroupSignalOperation\": {\n \"create\": {\n \"assetGroup\": assetGroupOperation.assetGroupOperation.create.resourceName,\n \"searchTheme\": {\n \"text\": \"mars cruise\"\n }\n }\n }\n});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.138Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":83,"estimatedTokens":534}}35{"id":"doc-performance_max_campaigns_using_adsapp_google_ad-8747123b","source":"documentation","title":"Performance Max campaigns using AdsApp | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/performance-max/using-ads-app","text":"Example:\n```text\nconst campaignName = \"My Performance Max campaign\";\n\nconst campaignIterator = AdsApp.performanceMaxCampaigns()\n .withCondition(`campaign.name = \"${campaignName}\"`)\n .get();\n\nfor (const campaign of campaignIterator) {\n ...\n}\n```\n\nExample:\n```text\nconst imageUrl = \"http://www.example.com/example.png\";\nconst imageBlob = UrlFetchApp.fetch(imageUrl).getBlob();\nconst assetOperation = AdsApp.adAssets().newImageAssetBuilder()\n .withName(\"new asset name\")\n .withData(imageBlob)\n .build();\nconst imageAsset = assetOperation.getResult();\n```\n\nExample:\n```text\n// First, fetch the Performance Max campaign we want to operate on.\nconst campaignIterator = AdsApp.performanceMaxCampaigns()\n .withCondition(`campaign.name = '${campaignName}'`)\n .get();\nlet campaign;\nif (campaignIterator.hasNext()) {\n campaign = campaignIterator.next();\n} else {\n throw `No campaign found with name ${campaignName}.`\n}\n\n// Then, get that campaign's asset groups.\nconst assetGroupIterator = campaign.assetGroups().get();\n\n// The campaign must have at least one asset group, so we can just assume so here.\nconst assetGroup = assetGroupIterator.next();\n\n// Add the asset from the previous step.\nassetGroup.addAsset(imageAsset, 'MARKETING_IMAGE');\n```\n\nExample:\n```text\nconst assetSelector = AdsApp.adAssets().assets();\n```\n\nExample:\n```text\nconst assetIterator = assetSelector.get();\n\nfor (const asset of assetIterator) {\n ...\n}\n```\n\nExample:\n```text\nassetGroup.addAsset('asset text here', 'HEADLINE');\n```\n\nExample:\n```text\nassetGroup.removeAsset(imageAsset, 'MARKETING_IMAGE');\n```\n\nExample:\n```text\n// The resource name is a unique identifier for this asset group.\nconst assetGroupName = assetGroup.getResourceName();\nresults = AdsApp.search(\n `SELECT asset.resource_name, asset_group_asset.field_type\n FROM asset_group_asset\n WHERE asset_group.resource_name = '${assetGroupName}'`\n);\n```\n\nExample:\n```text\n// This example assumes at least one asset is returned. We'll remove the first\n// asset, whatever it is. In your code, customize this to choose the right\n// asset to be removed.\nconst row_info = results.next().asset;\nassetGroup.remove(row_info.asset.resource_name, row_info.asset_group_asset.field_type);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.139Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":92,"estimatedTokens":563}}36{"id":"doc-make_google_ads_api_calls_with_the_mutate_strate-8233ec24","source":"documentation","title":"Make Google Ads API calls with the mutate strategy | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/performance-max/mutate-strategy","text":"Example:\n```text\nconst operations = [];\n```\n\nExample:\n```text\nconst customerId = AdsApp.currentAccount().getCustomerId();\n```\n\nExample:\n```text\nconst newOperation = {\n [OPERATION_TYPE_VARIES]: {\n create: {\n resourceName: `customers/${customerId}/[EXACT_PATH_VARIES]/${getNextTempId()}`\n // Other fields, relevant to the resource being created.\n }\n }\n}\noperations.push(newOperation);\n```\n\nExample:\n```text\nAdsApp.mutateAll(operations);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.140Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":29,"estimatedTokens":124}}37{"id":"doc-video_campaigns_google_ads_scripts_google_for_de-07d4ba36","source":"documentation","title":"Video Campaigns | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/video-campaigns","text":"Example:\n```text\nconst campaignName = \"My first video campaign\";\n\nconst campaignIterator = AdsApp.videoCampaigns()\n .withCondition(`campaign.name = \"${campaignName}\"`)\n .get();\n\nfor (const campaign of campaignIterator) {\n ...\n}\n```\n\nExample:\n```text\nconst adGroupIterator = campaign.videoAdGroups()\n .withCondition(`ad_group.name = \"${adGroupName}\"`)\n .get();\n\nfor (const adGroup of adGroupIterator) {\n ...\n}\n```\n\nExample:\n```text\nconst adGroupIterator = AdsApp.videoAdGroups()\n .withCondition(`campaign.name = \"${campaignName}\" AND ad_group.name = \"${adGroupName}\")\n .get();\n\nfor (const adGroup of adGroupIterator) {\n ...\n}\n```\n\nExample:\n```text\nconst campaignIterator = AdsApp.videoCampaigns()\n .withCondition(\"AdvertisingChannelSubType = VIDEO_ACTION\")\n .get();\n```\n\nExample:\n```text\nconst campaignIterator = AdsApp.videoCampaigns()\n .withCondition(\"AdvertisingChannelSubType = null\")\n .get();\n```\n\nExample:\n```text\nconst videoAdGroup =\n videoCampaign.newVideoAdGroupBuilder()\n .withAdGroupType(\"VIDEO_TRUE_VIEW_IN_STREAM\")\n .withName(\"Video Ad Group\")\n .build()\n .getResult();\n```\n\nExample:\n```text\nconst assetOperation = AdsApp.adAsset().newYouTubeVideoAssetBuilder()\n .withName(\"name\")\n // This is the ID in the URL for the YouTube video.\n .withYouTubeVideoId(youTubeVideoId)\n .build();\nconst videoAsset = assetOperation.getResult();\n```\n\nExample:\n```text\nconst videoAd = videoAdGroup.newVideoAd()\n .inStreamAdBuilder()\n .withAdName(\"Video Ad\")\n .withFinalUrl(\"http://www.example.com/video-ad\")\n // Specify the video asset created in the last step.\n .withVideo(video)\n .build()\n .getResult();\n```\n\nExample:\n```text\nvideoCampaign.videoTargeting().newPlacementBuilder()\n .withUrl(\"http://www.example.com\")\n .exclude();\n```\n\nExample:\n```text\nconst videoGenderIterator = videoAdGroup.videoTargeting()\n .genders()\n .withCondition('GenderType = \"GENDER_MALE\"')\n .get();\nif (videoGenderIterator.hasNext()) {\n const videoGender = videoGenderIterator.next();\n videoGender.exclude();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.140Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":101,"estimatedTokens":522}}38{"id":"doc-assets_google_ads_scripts_google_for_developers-0b26f3d1","source":"documentation","title":"Assets | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/performance-max/assets","text":"Example:\n```text\nconst textAsset = {\n \"assetOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/assets/${getNextTempId()}`,\n \"textAsset\": {\n \"text\": \"Travel the World\"\n }\n }\n }\n}\noperations.push(textAsset);\n```\n\nExample:\n```text\nconst file = DriveApp.getFileById(fileId);\nconst imageAsset = {\n \"assetOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/assets/${getNextTempId()}`,\n \"name\": \"Marketing Logo\",\n \"type\": \"IMAGE\",\n \"imageAsset\": {\n \"data\": Utilities.base64Encode(file.getBlob().getBytes())\n }\n }\n }\n}\noperations.push(imageAsset);\n```\n\nExample:\n```text\nconst file = UrlFetchApp.fetch(imageUrl);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.141Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":39,"estimatedTokens":182}}39{"id":"doc-shopping_campaigns_google_ads_scripts_google_for-2b9b86aa","source":"documentation","title":"Shopping campaigns | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/shopping-campaigns","text":"Example:\n```text\nconst campaignName = \"My first shopping campaign\";\n\nconst campaignIterator = AdsApp.shoppingCampaigns()\n .withCondition(`campaign.name = \"${campaignName}\"`)\n .get();\n\nfor (const campaign of campaignIterator) {\n ...\n}\n```\n\nExample:\n```text\nconst adGroupIterator = campaign.adGroups()\n .withCondition(`ad_group.name = \"${adGroupName}\"`)\n .get();\n\nfor (const adGroup of adGroupIterator) {\n ...\n}\n```\n\nExample:\n```text\nconst adGroupIterator = AdsApp.shoppingAdGroups()\n .withCondition(`campaign.name = \"${campaignName}\"`)\n .withCondition(`ad_group.name = \"${adGroupName}\"`)\n .get();\n\nfor (const adGroup of adGroupIterator) {\n ...\n}\n```\n\nExample:\n```text\nwalkTree(shoppingAdGroup.rootProductGroup(), 1);\n\nfunction walkTree(root, level) {\n // Logger.log(root.getDimension());\n let description = \"\";\n switch (root.getDimension()) {\n case \"ROOT\":\n description = \"Root\";\n break;\n\n case \"CATEGORY\":\n description = root.asCategory().getName();\n break;\n\n case \"BRAND\":\n description = root.asBrand().getName();\n break;\n\n // Handle more types here.\n ...\n }\n\n if (root.isOtherCase()) {\n description = \"Other\";\n }\n\n const padding = new Array(level + 1).join('-');\n console.log(\"%s, %s, %s, %s, %s, %s\",\n padding,\n description,\n root.getDimension(),\n root.getMaxCpc(),\n root.isOtherCase(),\n root.getId().toFixed());\n const children = root.children().get();\n for (const child of children) {\n walkTree(child, level + 1);\n }\n}\n```\n\nExample:\n```text\nfunction main() {\n const productGroups = AdsApp.productGroups()\n .withCondition(\"metrics.clicks > 5\")\n .withCondition(\"metrics.ctr > 0.01\")\n .forDateRange(\"LAST_MONTH\")\n .get();\n for (const productGroup of productGroups) {\n productGroup.setMaxCpc(productGroup.getMaxCpc() + 0.01);\n }\n}\n```\n\nExample:\n```text\nconst root = shoppingAdGroup.rootProductGroup();\n\n// Add a brand product group for a \"cardcow\" under root.\nconst brandProductGroup = root.newChild()\n .brandBuilder()\n .withName(\"cardcow\")\n .withBid(1.2)\n .build()\n .getResult();\n\n// Add new conditions for New and Refurbished cardcow brand items.\nconst newItems = brandProductGroup.newChild()\n .conditionBuilder()\n .withCondition(\"New\")\n .withBid(1.5)\n .build()\n .getResult();\n\n// Refurbished items will use the bid from \"cardcow\" product group.\nconst refurbishedItems = brandProductGroup.newChild()\n .conditionBuilder()\n .withCondition(\"Refurbished\")\n .build()\n .getResult();\n```\n\nExample:\n```text\nconst root = shoppingAdGroup.rootProductGroup();\n\nconst childProductGroups = root.children().get();\nlet everythingElseProductGroupFound = false;\n\nfor (const childProductGroup of childProductGroups) {\n if (childProductGroup.isOtherCase()) {\n console.log(\"'Everything else' product group found. Type of the \" +\n \"product group is %s and bid is %s.\",\n childProductGroup.getDimension(),\n childProductGroup.getMaxCpc());\n everythingElseProductGroupFound = true;\n break;\n }\n}\nif (!everythingElseProductGroupFound) {\n console.log(\"No 'Everything else' product group found under root \" +\n \"product group.\");\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.142Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":145,"estimatedTokens":830}}40{"id":"doc-reporting_google_ads_scripts_google_for_develope-a3ce3318","source":"documentation","title":"Reporting | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/performance-max/reporting","text":"Example:\n```text\nconst searchResults = AdsApp.search(`\nSELECT\n asset_group.id,\n asset_group.name,\n asset_group.primary_status,\n metrics.conversions,\n metrics.conversions_value,\n metrics.cost_micros,\n metrics.clicks,\n metrics.impressions\nFROM asset_group\nWHERE campaign.id = CAMPAIGN_ID\n AND segments.date DURING LAST_7_DAYS\n `);\n\n while (searchResults.hasNext()) {\n const row = searchResults.next();\n const assetGroup = row.assetGroup;\n // Your custom logic here, fetching the selected fields to do your analysis.\n }\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.142Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":25,"estimatedTokens":139}}41{"id":"doc-recommendations_google_ads_scripts_google_for_de-d55c388d","source":"documentation","title":"Recommendations | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/recommendations","text":"Example:\n```text\nconst selector = AdsApp.recommendations()\n .withCondition('recommendation.type IN (CAMPAIGN_BUDGET)');\nconst recommendations = selector.get();\n```\n\nExample:\n```text\nfor (const recommendation of recommendations) {\n // Perform whatever check here that works for your use case.\n // You can also potentially skip this step if you've sufficiently narrowed\n // down what recommendations you're selecting initially with customized\n // withCondition clauses in the previous step.\n if (shouldApply(recommendation)) {\n recommendation.apply();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.142Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":21,"estimatedTokens":146}}42{"id":"doc-required_components_of_demand_gen_google_ads_scr-7a15ec24","source":"documentation","title":"Required components of Demand Gen | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/demand-gen/required-components","text":"Example:\n```text\nconst budgetOperation = {\n \"campaignBudgetOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/campaignBudgets/${getNextTempId()}`,\n \"name\": \"Demand Gen campaign budget\",\n \"amountMicros\": \"50000000\",\n \"deliveryMethod\": \"STANDARD\",\n \"explicitlyShared\": false\n }\n }\n}\noperations.push(budgetOperation);\n```\n\nExample:\n```text\nconst campaignOperation = {\n \"campaignOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/campaigns/${getNextTempId()}`,\n \"name\": \"Demand Gen campaign\",\n \"status\": \"PAUSED\",\n \"advertisingChannelType\": \"DEMAND_GEN\",\n \"campaignBudget\": budgetOperation.campaignBudgetOperation.create.resourceName,\n \"biddingStrategyType\": \"TARGET_CPA\",\n \"startDate\": \"20240314\",\n \"endDate\": \"20250313\",\n \"urlExpansionOptOut\": false,\n \"targetCpa\": {\n \"targetCpaMicros\": 1000000\n },\n \"containsEuPoliticalAdvertising\": \"DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\"\n }\n }\n}\noperations.push(campaignOperation);\n```\n\nExample:\n```text\nconst adGroupId = getNextTempId();\nconst adGroupOperation = {\n \"adGroupOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/adGroups/${adGroupId}`,\n \"name\": \"Demand Gen ad group\",\n \"status\": \"PAUSED\",\n \"campaign\": campaignOperation.campaignOperation.create.resourceName,\n \"demand_gen_ad_group_settings\": {\n \"channel_controls\": {\n \"selected_channels\": {\n \"gmail\": false,\n \"discover\": false,\n \"display\": false,\n \"youtube_in_feed\": true,\n \"youtube_in_stream\": true,\n \"youtube_shorts\": true\n }\n }\n }\n }\n }\n}\noperations.push(adGroupOperation);\n```\n\nExample:\n```text\nconst adGroupAdOperation = {\n \"adGroupAdOperation\": {\n \"create\": {\n \"resourceName\": `customers/${customerId}/adGroupAds/${adGroupId}~${getNextTempId()}`,\n \"adGroup\": adGroupOperation.adGroupOperation.create.resourceName,\n \"status\": \"PAUSED\",\n \"ad\": {\n \"name\": \"Demand Gen video responsive ad\",\n \"finalUrls\": [\n \"http://www.example.com\"\n ],\n \"demandGenVideoResponsiveAd\": {\n \"businessName\": {\n \"text\": \"Demand Gen business\"\n },\n \"videos\": [\n { \"asset\": videoAsset.assetOperation.create.resourceName }\n ],\n \"logoImages\": [\n { \"asset\": imageAsset.assetOperation.create.resourceName }\n ],\n \"headlines\": [\n { \"text\": \"Demand Gen responsive video\" }\n ],\n \"longHeadlines\": [\n { \"text\": \"Make a Demand Gen video responsive ad today\" }\n ],\n \"description\": [\n { \"text\": \"This is an example of a Demand Gen video responsive ad\"}\n ]\n }\n }\n }\n }\n}\noperations.push(adGroupAdOperation);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.143Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":109,"estimatedTokens":735}}43{"id":"doc-ad_types_google_ads_scripts_google_for_developer-6fedb9c8","source":"documentation","title":"Ad Types | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/ads/ad-types","text":"Example:\n```text\nlet adOperation = adGroup.newAd().responsiveSearchAdBuilder()\n .withHeadlines([\"Headline 1\", \"Headline 2\", \"Headline 3\"])\n .withDescriptions([\"Description 1\", \"Description 2\"])\n .withFinalUrl(\"http://www.example.com\")\n .withPath1(\"path1\")\n .withPath2(\"path2\")\n .build();\n```\n\nExample:\n```text\nconst iterator = AdsApp.ads().withCondition(\"Type = RESPONSIVE_SEARCH_AD\").get();\nwhile (iterator.hasNext()) {\n let ad = iterator.next();\n let responsiveSearchAd = ad.asType().responsiveSearchAd();\n let headlines = responsiveSearchAd.getHeadlines();\n}\n```\n\nExample:\n```text\nif (ad.isType().responsiveSearchAd()) {\n let responsiveSearchAd = ad.asType().responsiveSearchAd();\n let headlines = responsiveSearchAd.getHeadlines();\n let descriptions = responsiveSearchAd.getDescriptions();\n}\n```\n\nExample:\n```text\nconst iterator = AdsApp.ads().withCondition(\"Type = RESPONSIVE_SEARCH_AD\").get();\nwhile (iterator.hasNext()) {\n let ad = iterator.next();\n let responsiveSearchAd = ad.asType().responsiveSearchAd();\n let headlines = responsiveSearchAd.getHeadlines();\n // Filter for ads containing a specific headline.\n if (headlines.some(h => h.getText().includes(\"Special Offer\"))) {\n console.log(`Found ad with ID ${ad.getId()}`);\n }\n}\n```\n\nExample:\n```text\nconst results = AdsApp.search(\n \"SELECT ad_group_ad.ad_group.id, \" +\n \"ad_group_ad.ad.id, \" +\n \"metrics.clicks, \" +\n \"metrics.impressions, \" +\n \"metrics.cost \" +\n \"FROM ad_group_ad \" +\n \"WHERE ad_group_ad.ad.type = 'RESPONSIVE_SEARCH_AD' \" +\n \"AND segments.date DURING LAST_7_DAYS\");\n\nwhile (results.hasNext()) {\n let row = results.next();\n let adId = row.adGroupAd.ad.id;\n let clicks = row.metrics.clicks;\n ...\n}\n```\n\nExample:\n```text\nconst results = AdsApp.search(\n \"SELECT ad_group_ad.ad.id, \" +\n \"asset.text_asset.text, \" +\n \"metrics.clicks, \" +\n \"metrics.impressions \" +\n \"FROM ad_group_ad_asset_view \" +\n \"WHERE asset.text_asset.text LIKE '%Special Offer%' \" +\n \"AND ad_group_ad_asset_view.field_type = 'HEADLINE'\");\n\nwhile (results.hasNext()) {\n let row = results.next();\n let adId = row.adGroupAd.ad.id;\n let text = row.asset.textAsset.text;\n let clicks = row.metrics.clicks;\n console.log(`Ad ID ${adId} with headline \"${text}\" had ${clicks} clicks.`);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.143Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":85,"estimatedTokens":590}}44{"id":"doc-bidding_google_ads_scripts_google_for_developers-5c2f3678","source":"documentation","title":"Bidding | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/bidding","text":"Example:\n```text\nconst campaign = AdsApp.campaigns()\n .withCondition(\"campaign.name = 'Test Campaign'\")\n .get()\n .next();\ncampaign.bidding().setStrategy(\"TARGET_SPEND\");\n```\n\nExample:\n```text\nconst bidding = campaign.bidding();\nbidding.setStrategy(\n 'MAXIMIZE_CONVERSION_VALUE',\n bidding.argsBuilder().withTargetRoas(5));\n```\n\nExample:\n```text\nconst biddingStrategy = AdsApp.biddingStrategies()\n .withCondition(\"bidding_strategy.name = 'My Shared Bidding Strategy'\")\n .get()\n .next();\n```\n\nExample:\n```text\nconst campaigns = biddingStrategy.campaigns().get();\n```\n\nExample:\n```text\nconst clicks = biddingStrategy.getStatsFor(\"LAST_MONTH\").getClicks();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.144Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":36,"estimatedTokens":173}}45{"id":"doc-ad_params_google_ads_scripts_google_for_develope-28104232","source":"documentation","title":"Ad Params | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/ads/ad-params","text":"Example:\n```text\nconst keywords = adGroup.keywords().get();\nfor(const keyword of keywords) {\n keyword.setAdParam(1, daysLeft);\n keyword.setAdParam(2, hoursLeft);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.144Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":46}}46{"id":"doc-ad_extensions_google_ads_scripts_google_for_deve-f8cdca6c","source":"documentation","title":"Ad Extensions | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/ads/ad-extensions","text":"Example:\n```text\nconst sitelinkIterator = AdsApp.extensions().sitelinks().get();\nfor (const sitelink of sitelinkIterator) {\n // Do something with each sitelink\n}\n```\n\nExample:\n```text\nconst phoneNumberBuilder = AdsApp.extensions().newPhoneNumberBuilder();\nconst newPhoneNumber = phoneNumberBuilder\n .withCountry(\"US\")\n .withPhoneNumber(\"6502530000\")\n .withCallOnly(false)\n .build()\n .getResult();\n```\n\nExample:\n```text\n// Add a phone number to a campaign.\ncampaign.addPhoneNumber(newPhoneNumber);\n\n// Add a phone number to an ad group.\nadGroup.addPhoneNumber(newPhoneNumber);\n```\n\nExample:\n```text\n// Account-level stats\n// Get a sitelink in the account.\nconst sitelinkIterator = AdsApp.extensions().sitelinks().get();\nconst sitelink = sitelinkIterator.next();\nconst sitelinkStats = sitelink.getStatsFor(\"LAST_30_DAYS\");\nconsole.log(sitelinkStats.getClicks());\n\n// Campaign-level stats.\n// Get a sitelink in a campaign.\nconst campaignSitelinkIterator = campaign.extensions().sitelinks().get();\nconst campaignSitelink = campaignSitelinkIterator.next();\nconst campaignSitelinkStats = campaignSitelink.getStatsFor(\"LAST_30_DAYS\");\nconsole.log(campaignSitelinkStats.getClicks());\n\n// Ad-group-level stats.\n// Get a sitelink in an ad group.\nconst adGroupSitelinkIterator = adGroup.extensions().sitelinks().get();\nconst adGroupSitelink = adGroupSitelinkIterator.next();\nconst adGroupSitelinkStats = adGroupSitelink.getStatsFor(\"LAST_30_DAYS\");\nconsole.log(adGroupSitelinkStats.getClicks());\n```\n\nExample:\n```text\n// Get a sitelink in the account.\nconst sitelinkIterator = AdsApp.extensions().sitelinks().get();\nconst sitelink = sitelinkIterator.next();\nconsole.log(sitelink.getLinkText()); // \"original text\"\n\n// Get a sitelink from a campaign. Assume it's the same one as before.\nconst campaignSitelinkIterator = campaign.extensions().sitelinks().get();\nconst campaignSitelink = campaignSitelinkIterator.next();\nconsole.log(campaignSitelink.getLinkText()); // \"original text\"\n\n// Get a sitelink from an ad group. Assume it's the same one as before.\nconst adGroupSitelinkIterator = adGroup.extensions().sitelinks().get();\nconst adGroupSitelink = adGroupSitelinkIterator.next();\nconsole.log(adGroupSitelink.getLinkText()); // \"original text\"\n\n// Change the sitelink's link text. This change will affect all the campaigns\n// and ad groups to which the sitelink belongs.\ncampaignSitelink.setLinkText(\"new link text\");\n\n// Same text!\nconsole.log(campaignSitelink.getLinkText()); // \"new link text\"\nconsole.log(adGroupSitelink.getLinkText()); // \"new link text\"\nconsole.log(sitelink.getLinkText()); // \"new link text\"\n```\n\nExample:\n```text\n// This will return phone numbers that have been explicitly added to this\n// ad group.\nconst adGroupPhoneNumberIterator = adGroup.extensions().phoneNumbers().get();\n```\n\nExample:\n```text\n// This will return callouts that have been explicitly added to your account.\nconst accountCalloutIterator =\n AdsApp.currentAccount().extensions().callouts().get();\n```\n\nExample:\n```text\n// Create a new callout in the account. Without adding the new callout as an ad\n// group, campaign or account extension, it won't actually serve.\nconst calloutBuilder = AdsApp.extensions().newCalloutBuilder();\nconst newCallout = calloutBuilder.withText(\"Sample Text\").build().getResult();\n\n// Add the new callout as an account-level extension. This enables it to serve\n// for all campaigns in the account.\nAdsApp.currentAccount().addCallout(newCallout);\n```\n\nExample:\n```text\n// Get a mobile app from a campaign.\nconst campaignMobileAppIterator = campaign.extensions().mobileApps().get();\nconst campaignMobileApp = campaignMobileAppIterator.next();\n\n// Remove the mobile app.\ncampaign.removeMobileApp(campaignMobileApp);\n\n// The mobile app still exists in the account and will be returned in the\n// following iterator.\nconst mobileAppIterator = AdsApp.extensions().mobileApps().get();\n```\n\nExample:\n```text\n// Get a mobile app from an ad group.\nconst adGroupMobileAppIterator = adGroup.extensions().mobileApps().get();\nconst adGroupMobileApp = adGroupMobileAppIterator.next();\n\n// Remove the mobile app.\nadGroup.removeMobileApp(adGroupMobileApp);\n\n// Get an account-level mobile app.\nconst accountMobileAppIterator =\n AdsApp.currentAccount().extensions().mobileApps().get();\nconst accountMobileApp = accountMobileAppIterator.next();\n\n// Remove the mobile app.\n// Note that this removes the mobile app from the account level, so it won't\n// serve as an account-level extension, but it will still exist in the\n// account. It can still be added to an AdGroup or Campaign, or again as an\n// account-level extension in the future.\nAdsApp.currentAccount().removeMobileApp(accountMobileApp);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.145Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":142,"estimatedTokens":1180}}47{"id":"doc-campaign_drafts_and_experiments_google_ads_scrip-91537bf0","source":"documentation","title":"Campaign Drafts and Experiments | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/campaigns/drafts-experiments","text":"Example:\n```text\nconst campaign = AdsApp.campaigns()\n .withCondition(`campaign.name = \"${campaignName}\"`)\n .get()\n .next();\n\nconst draftBuilder = campaign.newDraftBuilder()\n .withName(\"INSERT_DRAFT_NAME_HERE\")\n .build();\n\nconst draft = draftBuilder.getResult();\n```\n\nExample:\n```text\nconst draftCampaign = draft.getDraftCampaign();\n\ndraftCampaign.setAdRotationType(\"CONVERSION_OPTIMIZE\");\ndraftCampaign.createNegativeKeyword(\"shoes\");\n```\n\nExample:\n```text\ndraft.remove();\n```\n\nExample:\n```text\ndraft.startApplying();\n```\n\nExample:\n```text\nconst experiment = AdsApp.newExperimentBuilder()\n .withCampaign(campaign)\n .withTrafficSplitPercent(50)\n .withStartDate(\"20230501\")\n .withEndDate(\"20230601\")\n .withType(\"SEARCH_CUSTOM\")\n .withSuffix(\"experiment\")\n .withGoals([{metric: 'CLICKS', direction: 'INCREASE'}])\n .build();\n\n// The experimentCampaign represents the customizeable draft.\nconst experimentCampaign = experiment.getExperimentCampaign();\n```\n\nExample:\n```text\nconst experimentCampaign = experiment.getExperimentCampaign();\n\n// Will succeed.\nexperimentCampaign.setAdRotationType(\"ROTATE_FOREVER\");\nexperimentCampaign.createNegativeKeyword(\"sneakers\");\n\n// Will fail.\nexperimentCampaign.setName(\"INSERT_EXPERIMENT_NAME_HERE\");\n```\n\nExample:\n```text\n// Will succeed.\nexperiment.setName(\"INSERT_EXPERIMENT_NAME_HERE\");\n\n// Will succeed if date is acceptable.\nconst date = \"20220601\";\nexperiment.setStartDate(date);\n```\n\nExample:\n```text\nexperiment.finish();\nconst stats = experimentCampaign.getStatsFor(\"INSERT_TIME_PERIOD_HERE\");\n```\n\nExample:\n```text\nexperiment.remove();\n```\n\nExample:\n```text\nexperiment.startApplying();\n```\n\nExample:\n```text\nconst budget = AdsApp.budgets()\n .withCondition(`campaign_budget.id = ${budgetId}`)\n .get()\n .next();\n\n experiment.graduate(budget);\n```\n\nExample:\n```text\nconst draftCampaign = draft.getDraftCampaign();\ndraftCampaign.createNegativeKeyword(\"shoes\"); // Will fail in preview.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.145Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":103,"estimatedTokens":498}}48{"id":"doc-adsapp_google_ads_scripts_google_for_developers-c107c8c5","source":"documentation","title":"AdsApp | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/reference/adsapp/adsapp","text":"Example:\n```text\nvar campaignSelector = AdsApp.campaigns();\n```\n\nExample:\n```text\nAdsApp.createLabel(\"My Label\");\n\nAdsApp.createLabel(\n \"Modified by script\", \"These ads have been modified by a script\");\n\nAdsApp.createLabel(\n \"Bad Keywords\", \"These keywords are performing poorly\", \"red\");\n```\n\nExample:\n```devsite-click-to-copy\n// CORRECT: This will set the color to '#0088FF':\nAdsApp.createLabel(\"Good\", \"\", \"#0088FF\");\n\n// WRONG: This will set the description to '#0088FF':\nAdsApp.createLabel(\"Bad\", \"#0088FF\");\n```\n\nExample:\n```text\nconst customerId = AdsApp.currentAccount().getCustomerId();\nconst campaignId = 12345;\n\nconst createAdGroupResponse = AdsApp.mutate(\n {\n adGroupOperation: {\n create: {\n campaign: `customers/${customerId}/campaigns/${campaignId}`,\n name: 'My Ad Group Name',\n cpcBidMicros: '1230000' // $1.23 in micros\n }\n }\n });\nconst keywordText = 'Example Text';\nconst finalUrl = 'https://example.final.url.com/';\nconst createKeywordResponse = AdsApp.mutate(\n {\n adGroupCriterionOperation: {\n create: {\n adGroup: createAdGroupResponse.getResourceName(),\n keyword: {\n matchType: 'BROAD',\n text: keywordText\n },\n finalUrls: [ finalUrl ]\n }\n }\n });\n```\n\nExample:\n```text\nconst customerId = AdsApp.currentAccount().getCustomerId();\nconst campaignId = 12345;\nconst keywordText = 'Example Text';\nconst finalUrl = 'https://example.final.url.com/';\n\nconst mutateResponses = AdsApp.mutateAll([\n {\n adGroupOperation: {\n create: {\n resourceName: `customers/${customerId}/adGroups/-1`\n campaign: `customers/${customerId}/campaigns/${campaignId}`,\n name: 'My Ad Group Name',\n cpcBidMicros: '1230000' // $1.23 in micros\n }\n }\n },\n {\n adGroupCriterionOperation: {\n create: {\n adGroup: `customers/${customerId}/adGroups/-1`,\n keyword: {\n matchType: 'BROAD',\n text: keywordText\n },\n finalUrls: [ finalUrl ]\n }\n }\n }]);\n```\n\nExample:\n```text\nvar report1 = AdsApp.report(\n 'SELECT search_term_view.search_term, metrics.ctr ' +\n 'FROM search_term_view ' +\n 'WHERE segments.date BETWEEN \"2013-01-01\" AND \"2013-03-01\"');\n\nvar report2 = AdsApp.report(\n 'SELECT ad_group.id, ad_group_criterion.criterion_id, ' +\n ' ad_group_criterion.keyword.text, metrics.impressions, ' +\n ' metrics.clicks ' +\n 'FROM keyword_view ' +\n 'WHERE segments.date BETWEEN \"2013-01-01\" AND \"2013-03-01\"', {\n apiVersion: 'v25'\n });\n\nvar report3 = AdsApp.report(\n 'SELECT ad_group.id, ad_group_criterion.criterion_id, ' +\n ' ad_group_criterion.keyword.text, ' +\n ' campaign.name, metrics.impressions, metrics.clicks ' +\n 'FROM keyword_view ' +\n 'WHERE segments.date BETWEEN \"2013-01-01\" AND \"2013-03-01\"', {\n apiVersion: 'v25'\n });\n```\n\nExample:\n```text\nvar search1 = AdsApp.search(\n 'SELECT search_term_view.search_term, metrics.ctr ' +\n 'FROM search_term_view ' +\n 'WHERE segments.date BETWEEN \"2013-01-01\" AND \"2013-03-01\"');\n\nvar search2 = AdsApp.search(\n 'SELECT ad_group.id, ad_group_criterion.criterion_id, ' +\n ' ad_group_criterion.keyword.text, campaign.name, ' +\n ' metrics.impressions, metrics.clicks ' +\n 'FROM keyword_view ' +\n 'WHERE segments.date BETWEEN ' +\n ' \"2013-01-01\" AND \"2013-03-01\"', {\n apiVersion: 'v25'\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.148Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":134,"estimatedTokens":866}}49{"id":"doc-third_party_apis_google_ads_scripts_google_for_d-7b8d0826","source":"documentation","title":"Third-Party APIs | Google Ads Scripts | Google for Developers","url":"https://developers.google.com/google-ads/scripts/docs/integrations/third-party-apis","text":"Example:\n```text\nhttp://api.openweathermap.org/data/2.5/weather?q=[location]&apikey=[apikey]\n```\n\nExample:\n```text\nconst location = 'London,uk';\nconst apikey = 'da.......................81'; // Replace with your API key\nconst currentWeatherUrl = `http://api.openweathermap.org/data/2.5/weather?q=${location}&apiKey=${apiKey}`;\nconst response = UrlFetchApp.fetch(currentWeatherUrl);\nconsole.log(response.getContentText());\n```\n\nExample:\n```text\nconst json = response.getContentText();\nconst weatherData = JSON.parse(json);\nconsole.log(weatherData.name);\n// \"London\"\n```\n\nExample:\n```text\nconst options = {\n muteHttpExceptions: true\n}\nconst response = UrlFetchApp.fetch(url, options);\n// Any status code greater or equal to 400 is either a client or server error.\nif (response.getResponseCode() >= 400) {\n // Error encountered, send an email alert to the developer\n sendFailureEmail();\n}\n```\n\nExample:\n```text\nconst weatherData = JSON.parse(json);\nif (weatherData && weatherData.name) {\n console.log('Location is : ' + name);\n} else {\n console.log('Data not in expected format');\n}\n```\n\nExample:\n```text\n// Change the URL for the one issued to you from 'Setting up Slack'.\n const SLACK_URL = 'https://hooks.slack.com/services/AAAA/BBBB/CCCCCCCCCC';\n const slackMessage = {\n text: 'Hello, slack!'\n };\n\n const options = {\n method: 'POST',\n contentType: 'application/json',\n payload: JSON.stringify(slackMessage)\n };\n UrlFetchApp.fetch(SLACK_URL, options);\n```\n\nExample:\n```text\n{to: 'mail@example.com', subject:'Test', body:'Hello, World!'}\n```\n\nExample:\n```text\nsubject=Test&to=mail@example.com&body=Hello,+World!\n```\n\nExample:\n```text\nconst USERNAME = 'your_username';\nconst PASSWORD = 'your_password';\nconst API_URL = 'http://<place_api_url_here>';\n\nconst authHeader = 'Basic ' + Utilities.base64Encode(USERNAME + ':' + PASSWORD);\nconst options = {\n headers: {Authorization: authHeader}\n}\n// Include 'options' object in every request\nconst response = UrlFetchApp.fetch(API_URL, options);\n```\n\nExample:\n```text\n// Authenticate using chosen flow type\nconst urlFetchObj = OAuth2.<flow method>(args);\n// Make request(s) using obtained object.\nconst response1 = urlFetchObj.fetch(url1);\nconst response2 = urlFetchObj.fetch(url2, options);\n```\n\nExample:\n```text\n// Access token is obtained and cached.\nconst authUrlFetch = OAuth2.withClientCredentials(\n tokenUrl, clientId, clientSecret, optionalScope));\n// Use access token in each request\nconst response = authUrlFetch.fetch(url);\n// ... use response\n```\n\nExample:\n```text\nconst authUrlFetch = OAuth2.withRefreshToken(tokenUrl, clientId, clientSecret,\n refreshToken, optionalScope);\nconst response = authUrlFetch.fetch(url);\n// ... use response\n```\n\nExample:\n```text\nfunction listFiles() {\n const limit = 10;\n const files = [];\n const fileIterator = DriveApp.getFiles();\n while (fileIterator.hasNext() && limit) {\n files.push(fileIterator.next().getName());\n limit--;\n }\n return files;\n}\n```\n\nExample:\n```text\nconst json = response.getContentText();\ntry {\n const data = JSON.parse(json);\n return data;\n} catch(e) {\n // Parsing of JSON failed - handle error.\n}\n```\n\nExample:\n```text\n// Less good approach\n// Assumes JSON was in form {\"queryResponse\": ...} when parsed.\nconst answer = data.queryResponse;\n\n// Better approach\nif (data && data.queryResponse) {\n const answer = data.queryResponse;\n} else {\n // Format of API response has changed - alert developer or handle accordingly\n}\n```\n\nExample:\n```text\nconst responseText = response.getContentText();\ntry {\n const document = XmlService.parse(responseText);\n} catch(e) {\n // Error in XML representation - handle accordingly.\n}\n```\n\nExample:\n```text\nconst document = XmlService.parse(responseText);\nconst rootElement = document.getRootElement();\n```\n\nExample:\n```text\n<schedule_schedules xmlns=\"http://schemas.sportradar.com/sportsapi/soccer/v4\">\n <schedule>\n ...\n </schedule>\n</schedule_schedules>\n```\n\nExample:\n```text\nconst document = XmlService.parse(xmlText);\nconst rootElement = document.getRootElement();\n// The namespace is required for accessing child elements in the schema.\nconst namespace = rootElement.getNamespace();\nconst scheduleElement = rootElement.getChild('schedule', namespace);\nconst sportEvents = scheduleElement.getChildren('sport_event', namespace);\n```\n\nExample:\n```text\n<sport_event_status status=\"...\" ... />\n```\n\nExample:\n```text\nconst statusElement = sportEventElement.getChild('sport_event_status', namespace);\nconst status = statusElement.getAttribute('status').getValue();\n```\n\nExample:\n```text\nconst params = {\n muteHttpExceptions: true\n};\nconst response = UrlFetchApp.fetch(url, params);\nif (response.getResponseCode() >= 400) {\n // ... inspect error details...\n}\n```\n\nExample:\n```text\nconst request = UrlFetchApp.getRequest(url, params);\nconsole.log(request);\n// Now make the fetch:\nconst response = UrlFetchApp.fetch(url, params);\n// ...\n```\n\nExample:\n```text\nfunction logUrlFetch(url, opt_params) {\n const params = opt_params || {};\n params.muteHttpExceptions = true;\n const request = UrlFetchApp.getRequest(url, params);\n console.log('Request: >>> ' + JSON.stringify(request));\n const response = UrlFetchApp.fetch(url, params);\n console.log('Response Code: <<< ' + response.getResponseCode());\n console.log('Response text: <<< ' + response.getContentText());\n if (response.getResponseCode() >= 400) {\n throw Error('Error in response: ' + response);\n }\n return response;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.149Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":24,"totalLines":235,"estimatedTokens":1373}}50{"id":"doc-client_libraries_google_ads_api_google_for_devel-f1d09fd7","source":"documentation","title":"Client Libraries | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/client-libs","text":"Example:\n```text\n# Append the line \"export GOOGLE_ADS_CLIENT_ID=1234567890\" to\n# the bottom of your .bashrc file.\necho \"export GOOGLE_ADS_CLIENT_ID=1234567890\" >> ~/.bashrc\n\n# Update your bash environment to use the most recently updated\n# version of your .bashrc file.\nsrc ~/.bashrc\n```\n\nExample:\n```text\nexport GOOGLE_ADS_CLIENT_ID=1234567890\n```\n\nExample:\n```text\nGOOGLE_ADS_CLIENT_ID=1234567890 php /path/to/script/that/uses/envvar.php\n```\n\nExample:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient, long customerId) {\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n String query = \"SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id\";\n // Constructs the SearchGoogleAdsStreamRequest.\n SearchGoogleAdsStreamRequest request =\n SearchGoogleAdsStreamRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setQuery(query)\n .build();\n\n // Creates and issues a search Google Ads stream request that will retrieve all campaigns.\n ServerStream<SearchGoogleAdsStreamResponse> stream =\n googleAdsServiceClient.searchStreamCallable().call(request);\n\n // Iterates through and prints all of the results in the stream response.\n for (SearchGoogleAdsStreamResponse response : stream) {\n for (GoogleAdsRow googleAdsRow : response.getResultsList()) {\n System.out.printf(\n \"Campaign with ID %d and name '%s' was found.%n\",\n googleAdsRow.getCampaign().getId(), googleAdsRow.getCampaign().getName());\n }\n }\n }\n}GetCampaigns.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId)\n{\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsService = client.GetService(\n Services.V25.GoogleAdsService);\n\n // Create a query that will retrieve all campaigns.\n string query = @\"SELECT\n campaign.id,\n campaign.name,\n campaign.network_settings.target_content_network\n FROM campaign\n ORDER BY campaign.id\";\n\n try\n {\n // Issue a search request.\n googleAdsService.SearchStream(customerId.ToString(), query,\n delegate (SearchGoogleAdsStreamResponse resp)\n {\n foreach (GoogleAdsRow googleAdsRow in resp.Results)\n {\n Console.WriteLine(\"Campaign with ID {0} and name '{1}' was found.\",\n googleAdsRow.Campaign.Id, googleAdsRow.Campaign.Name);\n }\n }\n );\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}GetCampaigns.cs\n```\n\nExample:\n```text\npublic static function runExample(GoogleAdsClient $googleAdsClient, int $customerId)\n{\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n // Creates a query that retrieves all campaigns.\n $query = 'SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id';\n // Issues a search stream request.\n /** @var GoogleAdsServerStreamDecorator $stream */\n $stream = $googleAdsServiceClient->searchStream(\n SearchGoogleAdsStreamRequest::build($customerId, $query)\n );\n\n // Iterates over all rows in all messages and prints the requested field values for\n // the campaign in each row.\n foreach ($stream->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n printf(\n \"Campaign with ID %d and name '%s' was found.%s\",\n $googleAdsRow->getCampaign()->getId(),\n $googleAdsRow->getCampaign()->getName(),\n PHP_EOL\n );\n }\n}GetCampaigns.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient, customer_id: str) -> None:\n ga_service: GoogleAdsServiceClient = client.get_service(\"GoogleAdsService\")\n\n query: str = \"\"\"\n SELECT\n campaign.id,\n campaign.name\n FROM campaign\n ORDER BY campaign.id\"\"\"\n\n # Issues a search request using streaming.\n stream: Iterator[SearchGoogleAdsStreamResponse] = ga_service.search_stream(\n customer_id=customer_id, query=query\n )\n\n for batch in stream:\n rows: List[GoogleAdsRow] = batch.results\n for row in rows:\n print(\n f\"Campaign with ID {row.campaign.id} and name \"\n f'\"{row.campaign.name}\" was found.'\n )get_campaigns.py\n```\n\nExample:\n```text\ndef get_campaigns(customer_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n responses = client.service.google_ads.search_stream(\n customer_id: customer_id,\n query: 'SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id',\n )\n\n responses.each do |response|\n response.results.each do |row|\n puts \"Campaign with ID #{row.campaign.id} and name '#{row.campaign.name}' was found.\"\n end\n end\nendget_campaigns.rb\n```\n\nExample:\n```text\nsub get_campaigns {\n my ($api_client, $customer_id) = @_;\n\n # Create a search Google Ads stream request that will retrieve all campaigns.\n my $search_stream_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsStreamRequest\n ->new({\n customerId => $customer_id,\n query =>\n \"SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id\"\n });\n\n # Get the GoogleAdsService.\n my $google_ads_service = $api_client->GoogleAdsService();\n\n my $search_stream_handler =\n Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({\n service => $google_ads_service,\n request => $search_stream_request\n });\n\n # Issue a search request and process the stream response to print the requested\n # field values for the campaign in each row.\n $search_stream_handler->process_contents(\n sub {\n my $google_ads_row = shift;\n printf \"Campaign with ID %d and name '%s' was found.\\n\",\n $google_ads_row->{campaign}{id}, $google_ads_row->{campaign}{name};\n });\n\n return 1;\n}get_campaigns.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.150Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":201,"estimatedTokens":1599}}51{"id":"doc-get_started_with_google_sign_in_for_ios_and_maco-476b23ee","source":"documentation","title":"Get started with Google Sign-In for iOS and macOS | Sign in with Google for iOS | Google for Developers","url":"https://developers.google.com/identity/sign-in/ios/start-integrating","text":"Example:\n```text\npod init\n```\n\nExample:\n```text\npod 'GoogleSignIn'\n```\n\nExample:\n```text\npod 'GoogleSignInSwiftSupport'\n```\n\nExample:\n```text\npod install\n```\n\nExample:\n```text\n<key>GIDClientID</key>\n<string>YOUR_IOS_CLIENT_ID</string>\n<key>CFBundleURLTypes</key>\n<array>\n <dict>\n <key>CFBundleURLSchemes</key>\n <array>\n <string>YOUR_DOT_REVERSED_IOS_CLIENT_ID</string>\n </array>\n </dict>\n</array>\n```\n\nExample:\n```text\n<key>GIDServerClientID</key>\n<string>YOUR_SERVER_CLIENT_ID</string>\n```\n\nExample:\n```text\n<key>GIDHostedDomain</key>\n<string>YOUR_HOSTED_DOMAIN</string>\n```\n\nExample:\n```text\n<key>GIDOpenIDRealm</key>\n<string>YOUR_OPENID_REALM</string>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.158Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":54,"estimatedTokens":172}}52{"id":"doc-set_up_google_mobile_ads_unity_plugin_google_for-55ee2207","source":"documentation","title":"Set up Google Mobile Ads Unity Plugin | Google for Developers","url":"https://developers.google.com/admob/unity/quick-start","text":"Example:\n```text\nopenupm add com.google.ads.mobile\n```\n\nExample:\n```text\nName: OpenUPM\nURL: https://package.openupm.com\nScopes: com.google\n```\n\nExample:\n```text\ncurl -sS https://api.github.com/repos/googleads/googleads-mobile-unity/releases/latest | jq -r '.tag_name'\n```\n\nExample:\n```text\n(Invoke-RestMethod -Uri \"https://api.github.com/repos/googleads/googleads-mobile-unity/releases/latest\").tag_name\n```\n\nExample:\n```text\n{\n \"scopedRegistries\": [\n {\n \"name\": \"google\",\n \"url\": \"https://package.openupm.com\",\n \"scopes\": [\n \"com.google\"\n ]\n }\n ],\n \"dependencies\": {\n \"com.google.ads.mobile\": \"11.4.0\"\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.159Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":41,"estimatedTokens":167}}53{"id":"doc-set_up_google_mobile_ads_sdk_ios_google_for_deve-631ecc80","source":"documentation","title":"Set up Google Mobile Ads SDK | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/quick-start","text":"Example:\n```text\nhttps://github.com/googleads/swift-package-manager-google-mobile-ads.git\n```\n\nExample:\n```text\npod 'Google-Mobile-Ads-SDK'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\n<key>GADApplicationIdentifier</key>\n<!-- Sample AdMob app ID: ca-app-pub-3940256099942544~1458002511 -->\n<string>SAMPLE_APP_ID</string>\n<key>SKAdNetworkItems</key>\n<array>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>cstr6suwn9.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>4fzdc2evr5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>2fnua5tdw4.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>ydx93a7ass.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>p78axxw29g.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>v72qych5uu.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>ludvb6z3bs.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>cp8zw746q7.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3sh42y64q3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>c6k4g5qg8m.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>s39g8k73mm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>wg4vff78zm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3qy4746246.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>f38h382jlk.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>hs6bdukanm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>mlmmfzh3r3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>v4nxqhlyqp.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>wzmmz9fp6w.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>su67r6k2v3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>yclnxrl5pm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>t38b2kh725.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>7ug5zh24hu.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>gta9lk7p23.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>vutu7akeur.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>y5ghdn5j9k.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>v9wttpbfk9.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>n38lu8286q.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>47vhws6wlr.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>kbd757ywx3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>9t245vhmpl.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>a2p9lx4jpn.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>22mmun2rn5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>44jx6755aq.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>k674qkevps.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>4468km3ulz.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>2u9pt9hc89.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>8s468mfl3y.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>klf5c3l5u5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>ppxm28t8ap.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>kbmxgpxpgc.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>uw77j35x4d.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>578prtvx9j.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>4dzt52r2t5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>tl55sbb4fm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>c3frkrj4fj.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>e5fvkxwrpn.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>8c4e2ghe7u.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3rd42ekr43.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>97r2b46745.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3qcr597p9d.skadnetwork</string>\n </dict>\n</array>\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads SDK.\nMobileAds.shared.start()ViewController.swift\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads SDK.\nMobileAds.shared.start()GoogleMobileAdsConsentManager.swift\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads SDK.\n[GADMobileAds.sharedInstance startWithCompletionHandler:nil];ViewController.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.161Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":244,"estimatedTokens":1445}}54{"id":"doc-set_up_google_mobile_ads_flutter_plugin_google_f-76522fa6","source":"documentation","title":"Set up Google Mobile Ads Flutter Plugin | Google for Developers","url":"https://developers.google.com/admob/flutter/quick-start","text":"Example:\n```text\n<manifest>\n <application>\n <!-- Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713 -->\n <meta-data\n android:name=\"com.google.android.gms.ads.APPLICATION_ID\"\n android:value=\"ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy\"/>\n <application>\n<manifest>\n```\n\nExample:\n```text\n<key>GADApplicationIdentifier</key>\n<string>ca-app-pub-################~##########</string>\n```\n\nExample:\n```text\n// Initialize the Mobile Ads SDK.\nMobileAds.instance.initialize();main.dart\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.163Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":25,"estimatedTokens":135}}55{"id":"doc-set_up_google_mobile_ads_sdk_legacy_android_goog-21d2ad0a","source":"documentation","title":"Set up Google Mobile Ads SDK (Legacy) | Android | Google for Developers","url":"https://developers.google.com/admob/android/quick-start","text":"Example:\n```devsite-click-to-copy\npluginManagement {\n repositories {\n google()\n mavenCentral()\n gradlePluginPortal()\n }\n}\n\ndependencyResolutionManagement {\n repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n repositories {\n google()\n mavenCentral()\n }\n}\n\nrootProject.name = \"My Application\"\ninclude(\":app\")\n```\n\nExample:\n```devsite-click-to-copy\npluginManagement {\n repositories {\n google()\n mavenCentral()\n gradlePluginPortal()\n }\n}\n\ndependencyResolutionManagement {\n repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n repositories {\n google()\n mavenCentral()\n }\n}\n\nrootProject.name = \"My Application\"\ninclude ':app'\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n}\n```\n\nExample:\n```text\n<manifest>\n <application>\n <!-- Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713 -->\n <meta-data\n android:name=\"com.google.android.gms.ads.APPLICATION_ID\"\n android:value=\"SAMPLE_APP_ID\"/>\n </application>\n</manifest>\n```\n\nExample:\n```text\nMissing application ID.\n```\n\nExample:\n```devsite-click-to-copy\n<manifest>\n <application>\n <meta-data\n android:name=\"com.google.android.gms.ads.APPLICATION_ID\"\n android:value=\"ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy\"/>\n\n <!-- For apps targeting Android 13 or higher & GMA SDK version 20.3.0 or lower -->\n <uses-permission android:name=\"com.google.android.gms.permission.AD_ID\"/>\n\n </application>\n</manifest>\n```\n\nExample:\n```text\nnew Thread(\n () -> {\n // Initialize the Google Mobile Ads SDK on a background thread.\n MobileAds.initialize(this, initializationStatus -> {});\n })\n .start();MyActivity.java\n```\n\nExample:\n```text\nCoroutineScope(Dispatchers.IO).launch {\n // Initialize the Google Mobile Ads SDK on a background thread.\n MobileAds.initialize(this@MainActivity) {}\n}MainActivity.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.163Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":109,"estimatedTokens":521}}56{"id":"doc-set_up_gma_next_gen_sdk_android_google_for_devel-7417a37f","source":"documentation","title":"Set up GMA Next-Gen SDK | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/quick-start","text":"Example:\n```devsite-click-to-copy\npluginManagement {\n repositories {\n google()\n mavenCentral()\n gradlePluginPortal()\n }\n}\n\ndependencyResolutionManagement {\n repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n repositories {\n google()\n mavenCentral()\n }\n}\n\nrootProject.name = \"My Application\"\ninclude(\":app\")\n```\n\nExample:\n```devsite-click-to-copy\npluginManagement {\n repositories {\n google()\n mavenCentral()\n gradlePluginPortal()\n }\n}\n\ndependencyResolutionManagement {\n repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n repositories {\n google()\n mavenCentral()\n }\n}\n\nrootProject.name = \"My Application\"\ninclude ':app'\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n}\n```\n\nExample:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds\nimport com.google.android.libraries.ads.mobile.sdk.initialization.InitializationConfig\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\n\nclass MainActivity : AppCompatActivity() {\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n\n val backgroundScope = CoroutineScope(Dispatchers.IO)\n backgroundScope.launch {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this@MainActivity,\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n InitializationConfig.Builder(\"SAMPLE_APP_ID\").build()\n ) {\n // Adapter initialization is complete.\n }\n // SDK initialization is complete. If you don't want to wait for bidding adapters to finish\n // initializing, start loading ads now.\n }\n }\n}\n```\n\nExample:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds;\nimport com.google.android.libraries.ads.mobile.sdk.initialization.InitializationConfig;\n\npublic class MainActivity extends AppCompatActivity {\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n\n new Thread(\n () -> {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this,\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n new InitializationConfig.Builder(\"SAMPLE_APP_ID\")\n .build(),\n initializationStatus -> {\n // Adapter initialization is complete.\n });\n // SDK initialization is complete. If you don't want to wait for bidding adapters to\n // finish initializing, start loading ads now.\n })\n .start();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.164Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":118,"estimatedTokens":759}}57{"id":"doc-samples_lead_form_webhook_google_for_developers-f71670ba","source":"documentation","title":"Samples | Lead Form Webhook | Google for Developers","url":"https://developers.google.com/google-ads/webhook/docs/samples","text":"Example:\n```text\n{\n \"lead_id\":\"Cj0KCQjwit_8BRCoARIsAIx3Rj7g-AeL6z35IWb6VYiZUygtTfwD3hDlgSGmY-XTTlK3lfV1wcuIwIAaAmMxEALw_wcB\",\n \"campaign_id\":123456,\n \"gcl_id\":\"Cj0KCQjwit_8BRCoARIsAIx3Rj7g-AeL6z35IWb6VYiZUygtTfwD3hDlgSGmY-XTTlK3lfV1wcuIwIAaAmMxEALw_wcB\",\n \"user_column_data\": [\n {\n \"column_name\":\"Full Name\",\n \"string_value\":\"John Doe\",\n \"column_id\": \"FULL_NAME\"\n },\n {\n \"column_name\": \"User Phone\",\n \"string_value\":\"+11234567890\",\n \"column_id\":\"PHONE_NUMBER\"\n }\n ],\n \"api_version\":\"1.0\",\n \"form_id\":1234,\n \"google_key\":\"xfdgdgsgfchgvhgfchg\",\n}\n```\n\nExample:\n```text\n{\n \"lead_id\":\"Cj0KCQjwit_8BRCoARIsAIx3Rj7g-AeL6z35IWb6VYiZUygtTfwD3hDlgSGmY-XTTlK3lfV1wcuIwIAaAmMxEALw_wcB\",\n \"campaign_id\":123456,\n \"gcl_id\":\"Cj0KCQjwit_8BRCoARIsAIx3Rj7g-AeL6z35IWb6VYiZUygtTfwD3hDlgSGmY-XTTlK3lfV1wcuIwIAaAmMxEALw_wcB\",\n \"user_column_data\": [\n {\n \"column_name\":\"Full Name\",\n \"string_value\":\"John Doe\",\n \"column_id\": \"FULL_NAME\"\n },\n {\n \"column_name\": \"User Phone\",\n \"string_value\":\"+11234567890\",\n \"column_id\":\"PHONE_NUMBER\"\n }\n ],\n \"api_version\":\"1.0\",\n \"form_id\":1234,\n \"Google_key\":\"xfdgdgsgfchgvhgfchg\",\n \"is_test\":true\n}\n```\n\nExample:\n```text\n{\n \"lead_id\":\"Cj0KCQjwit_8BRCoARIsAIx3Rj7g-AeL6z35IWb6VYiZUygtTfwD3hDlgSGmY-XTTlK3lfV1wcuIwIAaAmMxEALw_wcB\",\n \"campaign_id\":123456,\n \"gcl_id\":\"Cj0KCQjwit_8BRCoARIsAIx3Rj7g-AeL6z35IWb6VYiZUygtTfwD3hDlgSGmY-XTTlK3lfV1wcuIwIAaAmMxEALw_wcB\",\n \"user_column_data\": [\n {\n \"column_name\":\"Full Name\",\n \"string_value\":\"John Doe\",\n \"column_id\": \"FULL_NAME\"\n },\n {\n \"column_name\": \"User Email\",\n \"string_value\":\"abc@xyz.com\",\n \"column_id\":\"EMAIL\"\n },\n {\n \"column_name\": \"User Phone\",\n \"string_value\":\"+11234567890\",\n \"column_id\":\"PHONE_NUMBER\"\n },\n {\n \"column_name\": \"Postal Code\",\n \"string_value\":\"94043\",\n \"column_id\":\"POSTAL_CODE\"\n }\n ],\n \"api_version\":\"1.0\",\n \"form_id\":1234,\n \"Google_key\":\"xfdgdgsgfchgvhgfchg\",\n \"is_test\":true\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.165Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":85,"estimatedTokens":517}}58{"id":"doc-protect_user_accounts_with_cross_account_protect-8133f638","source":"documentation","title":"Protect user accounts with Cross-Account Protection | Cross-Account Protection (RISC) | Google for Developers","url":"https://developers.google.com/identity/protocols/risc","text":"Example:\n```text\npublic DecodedJWT validateSecurityEventToken(String token) {\n DecodedJWT jwt = null;\n try {\n // In a real implementation, get these values from\n // https://accounts.google.com/.well-known/risc-configuration\n String issuer = \"accounts.google.com\";\n String jwksUri = \"https://www.googleapis.com/oauth2/v3/certs\";\n\n // Get the ID of the key used to sign the token.\n DecodedJWT unverifiedJwt = JWT.decode(token);\n String keyId = unverifiedJwt.getKeyId();\n\n // Get the public key from Google.\n JwkProvider googleCerts = new UrlJwkProvider(new URL(jwksUri), null, null);\n PublicKey publicKey = googleCerts.get(keyId).getPublicKey();\n\n // Verify and decode the token.\n Algorithm rsa = Algorithm.RSA256((RSAPublicKey) publicKey, null);\n JWTVerifier verifier = JWT.require(rsa)\n .withIssuer(issuer)\n // Get your apps' client IDs from the API console:\n // https://console.developers.google.com/apis/credentials?project=_\n .withAudience(\"123456789-abcedfgh.apps.googleusercontent.com\",\n \"123456789-ijklmnop.apps.googleusercontent.com\",\n \"123456789-qrstuvwx.apps.googleusercontent.com\")\n .acceptLeeway(Long.MAX_VALUE) // Don't check for expiration.\n .build();\n jwt = verifier.verify(token);\n } catch (JwkException e) {\n // Key not found. Return HTTP 400.\n } catch (InvalidClaimException e) {\n\n } catch (JWTDecodeException exception) {\n // Malformed token. Return HTTP 400.\n } catch (MalformedURLException e) {\n // Invalid JWKS URI.\n }\n return jwt;\n}\n```\n\nExample:\n```text\nimport json\nimport jwt # pip install pyjwt\nimport requests # pip install requests\n\ndef validate_security_token(token, client_ids):\n # Get Google's RISC configuration.\n risc_config_uri = 'https://accounts.google.com/.well-known/risc-configuration'\n risc_config = requests.get(risc_config_uri).json()\n\n # Get the public key used to sign the token.\n google_certs = requests.get(risc_config['jwks_uri']).json()\n jwt_header = jwt.get_unverified_header(token)\n key_id = jwt_header['kid']\n public_key = None\n for key in google_certs['keys']:\n if key['kid'] == key_id:\n public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(key))\n if not public_key:\n raise Exception('Public key certificate not found.')\n # In this situation, return HTTP 400\n\n # Decode the token, validating its signature, audience, and issuer.\n try:\n token_data = jwt.decode(token, public_key, algorithms='RS256',\n options={'verify_exp': False},\n audience=client_ids, issuer=risc_config['issuer'])\n except:\n raise\n # Validation failed. Return HTTP 400.\n return token_data\n\n# Get your apps' client IDs from the API console:\n# https://console.developers.google.com/apis/credentials?project=_\nclient_ids = ['123456789-abcedfgh.apps.googleusercontent.com',\n '123456789-ijklmnop.apps.googleusercontent.com',\n '123456789-qrstuvwx.apps.googleusercontent.com']\ntoken_data = validate_security_token(token, client_ids)\n```\n\nExample:\n```text\n{\n \"iss\": \"https://accounts.google.com/\",\n \"aud\": \"123456789-abcedfgh.apps.googleusercontent.com\",\n \"iat\": 1508184845,\n \"jti\": \"756E69717565206964656E746966696572\",\n \"events\": {\n \"https://schemas.openid.net/secevent/risc/event-type/account-disabled\": {\n \"subject\": {\n \"subject_type\": \"iss-sub\",\n \"iss\": \"https://accounts.google.com/\",\n \"sub\": \"7375626A656374\"\n },\n \"reason\": \"hijacking\"\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"iss\": SERVICE_ACCOUNT_EMAIL,\n \"sub\": SERVICE_ACCOUNT_EMAIL,\n \"aud\": \"https://risc.googleapis.com/google.identity.risc.v1beta.RiscManagementService\",\n \"iat\": CURRENT_TIME,\n \"exp\": CURRENT_TIME + 3600\n}\n```\n\nExample:\n```text\npublic static String makeBearerToken() {\n String token = null;\n try {\n // Get signing key and client email address.\n FileInputStream is = new FileInputStream(\"your-service-account-credentials.json\");\n ServiceAccountCredentials credentials =\n (ServiceAccountCredentials) GoogleCredentials.fromStream(is);\n PrivateKey privateKey = credentials.getPrivateKey();\n String keyId = credentials.getPrivateKeyId();\n String clientEmail = credentials.getClientEmail();\n\n // Token must expire in exactly one hour.\n Date issuedAt = new Date();\n Date expiresAt = new Date(issuedAt.getTime() + 3600000);\n\n // Create signed token.\n Algorithm rsaKey = Algorithm.RSA256(null, (RSAPrivateKey) privateKey);\n token = JWT.create()\n .withIssuer(clientEmail)\n .withSubject(clientEmail)\n .withAudience(\"https://risc.googleapis.com/google.identity.risc.v1beta.RiscManagementService\")\n .withIssuedAt(issuedAt)\n .withExpiresAt(expiresAt)\n .withKeyId(keyId)\n .sign(rsaKey);\n } catch (ClassCastException e) {\n // Credentials file doesn't contain a service account key.\n } catch (IOException e) {\n // Credentials file couldn't be loaded.\n }\n return token;\n}\n```\n\nExample:\n```text\nimport json\nimport time\n\nimport jwt # pip install pyjwt\n\ndef make_bearer_token(credentials_file):\n with open(credentials_file) as service_json:\n service_account = json.load(service_json)\n issuer = service_account['client_email']\n subject = service_account['client_email']\n private_key_id = service_account['private_key_id']\n private_key = service_account['private_key']\n issued_at = int(time.time())\n expires_at = issued_at + 3600\n payload = {'iss': issuer,\n 'sub': subject,\n 'aud': 'https://risc.googleapis.com/google.identity.risc.v1beta.RiscManagementService',\n 'iat': issued_at,\n 'exp': expires_at}\n encoded = jwt.encode(payload, private_key, algorithm='RS256',\n headers={'kid': private_key_id})\n return encoded\n\nauth_token = make_bearer_token('your-service-account-credentials.json')\n```\n\nExample:\n```text\nPOST /v1beta/stream:update HTTP/1.1\nHost: risc.googleapis.com\nAuthorization: Bearer AUTH_TOKEN\n\n{\n \"delivery\": {\n \"delivery_method\":\n \"https://schemas.openid.net/secevent/risc/delivery-method/push\",\n \"url\": RECEIVER_ENDPOINT\n },\n \"events_requested\": [\n SECURITY_EVENT_TYPES\n ]\n}\n```\n\nExample:\n```text\npublic static void configureEventStream(final String receiverEndpoint,\n final List<String> eventsRequested,\n String authToken) throws IOException {\n ObjectMapper jsonMapper = new ObjectMapper();\n String streamConfig = jsonMapper.writeValueAsString(new Object() {\n public Object delivery = new Object() {\n public String delivery_method =\n \"https://schemas.openid.net/secevent/risc/delivery-method/push\";\n public String url = receiverEndpoint;\n };\n public List<String> events_requested = eventsRequested;\n });\n\n HttpPost updateRequest = new HttpPost(\"https://risc.googleapis.com/v1beta/stream:update\");\n updateRequest.addHeader(\"Content-Type\", \"application/json\");\n updateRequest.addHeader(\"Authorization\", \"Bearer \" + authToken);\n updateRequest.setEntity(new StringEntity(streamConfig));\n\n HttpResponse updateResponse = new DefaultHttpClient().execute(updateRequest);\n Header[] responseContentTypeHeaders = updateResponse.getHeaders(\"Content-Type\");\n StatusLine responseStatus = updateResponse.getStatusLine();\n int statusCode = responseStatus.getStatusCode();\n HttpEntity entity = updateResponse.getEntity();\n // Now handle response\n}\n\n// ...\n\nconfigureEventStream(\n \"https://your-service.example.com/security-event-receiver\",\n Arrays.asList(\n \"https://schemas.openid.net/secevent/risc/event-type/account-credential-change-required\",\n \"https://schemas.openid.net/secevent/risc/event-type/account-disabled\"),\n authToken);\n```\n\nExample:\n```text\nimport requests\n\ndef configure_event_stream(auth_token, receiver_endpoint, events_requested):\n stream_update_endpoint = 'https://risc.googleapis.com/v1beta/stream:update'\n headers = {'Authorization': 'Bearer {}'.format(auth_token)}\n stream_cfg = {'delivery': {'delivery_method': 'https://schemas.openid.net/secevent/risc/delivery-method/push',\n 'url': receiver_endpoint},\n 'events_requested': events_requested}\n response = requests.post(stream_update_endpoint, json=stream_cfg, headers=headers)\n response.raise_for_status() # Raise exception for unsuccessful requests\n\nconfigure_event_stream(auth_token, 'https://your-service.example.com/security-event-receiver',\n ['https://schemas.openid.net/secevent/risc/event-type/account-credential-change-required',\n 'https://schemas.openid.net/secevent/risc/event-type/account-disabled'])\n```\n\nExample:\n```text\n{\n \"state\": \"ANYTHING\"\n}\n```\n\nExample:\n```text\npublic static void testEventStream(final String stateString,\n String authToken) throws IOException {\n ObjectMapper jsonMapper = new ObjectMapper();\n String json = jsonMapper.writeValueAsString(new Object() {\n public String state = stateString;\n });\n\n HttpPost updateRequest = new HttpPost(\"https://risc.googleapis.com/v1beta/stream:verify\");\n updateRequest.addHeader(\"Content-Type\", \"application/json\");\n updateRequest.addHeader(\"Authorization\", \"Bearer \" + authToken);\n updateRequest.setEntity(new StringEntity(json));\n\n HttpResponse updateResponse = new DefaultHttpClient().execute(updateRequest);\n Header[] responseContentTypeHeaders = updateResponse.getHeaders(\"Content-Type\");\n StatusLine responseStatus = updateResponse.getStatusLine();\n int statusCode = responseStatus.getStatusCode();\n HttpEntity entity = updateResponse.getEntity();\n // Now handle response\n}\n\n// ...\n\ntestEventStream(\"Test token requested at \" + new Date().toString(), authToken);\n```\n\nExample:\n```text\nimport requests\nimport time\n\ndef test_event_stream(auth_token, nonce):\n stream_verify_endpoint = 'https://risc.googleapis.com/v1beta/stream:verify'\n headers = {'Authorization': 'Bearer {}'.format(auth_token)}\n state = {'state': nonce}\n response = requests.post(stream_verify_endpoint, json=state, headers=headers)\n response.raise_for_status() # Raise exception for unsuccessful requests\n\ntest_event_stream(auth_token, 'Test token requested at {}'.format(time.ctime()))\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.168Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":303,"estimatedTokens":2736}}59{"id":"doc-implementation_lead_form_webhook_google_for_deve-25abb1a7","source":"documentation","title":"Implementation | Lead Form Webhook | Google for Developers","url":"https://developers.google.com/google-ads/webhook/docs/implementation","text":"Example:\n```text\n// Represent user lead data for single column\nmessage UserLeadColumnData {\n // Human-readable text of the field type (e.g.: Full Name, What is your\n // preferred dealership?). This field might not always be populated.\n optional string column_name = 1;\n\n // Column value based on column type\n oneof column_value {\n string string_value = 2;\n }\n // Column ID. Populated for all types of fields. (e.g.: FULL_NAME)\n optional string column_id = 3;\n}\n\n// Message to construct webhook JSON payload\nmessage WebhookLead {\n // Unique ID to represent lead\n optional string lead_id = 1;\n // User inputted data per column\n repeated UserLeadColumnData user_column_data = 2;\n // API version\n optional string api_version = 3;\n // Form ID to which lead belonged to.\n optional int64 form_id = 4;\n // Campaign ID that the lead form is associated with\n optional int64 campaign_id = 5;\n // Key to be used by advertiser to verify the request\n // is from Google.\n optional string google_key = 6;\n // Denotes if the lead is a test lead.\n optional bool is_test = 7;\n // Click ID for the lead submission.\n optional string gcl_id = 8;\n // Adgroup ID which generated the lead.\n optional int64 adgroup_id = 9;\n // Creative ID which generated the lead.\n optional int64 creative_id = 10;\n // Asset group ID represents the container for holding assets, associated\n // URLs, hints and criteria that will be used to select assets and for\n // optimization. This field is only populated for Performance Max campaigns.\n int64 asset_group_id = 11;\n // Lead stage at the time of delivery.\n string lead_stage = 12 [(datapol.semantic_type) = ST_NOT_REQUIRED];\n // Lead submit time in ISO-8601 format. Ex- 2024-09-26T12:30:00Z\n string lead_submit_time = 13 [(datapol.semantic_type) = ST_NOT_REQUIRED];\n // The source of the lead submission.\n // Possible values: \"LEAD_FORM\" or \"CONVERSATIONAL_AGENT\".\n string lead_source = 14 [(datapol.semantic_type) = ST_NOT_REQUIRED];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.168Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":54,"estimatedTokens":502}}60{"id":"doc-testing_lead_form_webhook_google_for_developers-601ef7d8","source":"documentation","title":"Testing | Lead Form Webhook | Google for Developers","url":"https://developers.google.com/google-ads/webhook/docs/testing","text":"Example:\n```text\n{\n \"lead_id\":\"Cj0KCQjwit_8BRCoARIsAIx3Rj7g-AeL6z35IWb6VYiZUygtTfwD3hDlgSGmY-XTTlK3lfV1wcuIwIAaAmMxEALw_wcB\",\n \"campaign_id\":123456,\n \"adgroup_id\":0,\n \"creative_id\":0,\n \"gcl_id\":\"Cj0KCQjwit_8BRCoARIsAIx3Rj7g-AeL6z35IWb6VYiZUygtTfwD3hDlgSGmY-XTTlK3lfV1wcuIwIAaAmMxEALw_wcB\",\n \"user_column_data\": [\n {\n \"column_name\": \"Full Name\",\n \"string_value\":\"FirstName LastName\",\n \"column_id\": \"FULL_NAME\"\n },\n {\n \"column_name\":\"User Phone\",\n \"string_value\":\"1-650-555-0123\",\n \"column_id\":\"PHONE_NUMBER\"\n },\n {\n \"column_name\":\"User Email\",\n \"string_value\":\"test@example.com\",\n \"column_id\":\"EMAIL\"\n }],\n \"api_version\":\"1.0\",\n \"form_id\":123456789,\n \"google_key\":\"testkey\",\n \"is_test\":true\n}\n```\n\nExample:\n```text\n$ curl -v -X POST --header \"Content-Type:application/json\" -d @request.txt https://webhook_url\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.168Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":37,"estimatedTokens":225}}61{"id":"doc-get_started_c_google_for_developers-f65cae6d","source":"documentation","title":"Get Started | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/quick-start","text":"Example:\n```text\nsystemProp.firebase_cpp_sdk.dir=FULL_PATH_TO_SDK\n```\n\nExample:\n```text\ndef firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')\n\ngradle.ext.firebase_cpp_sdk_dir = \"$firebase_cpp_sdk_dir\"\nincludeBuild \"$firebase_cpp_sdk_dir\"\n```\n\nExample:\n```text\nandroid.defaultConfig.externalNativeBuild.cmake {\n arguments \"-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir\"\n}\n\n# Add the dependency for the Google Mobile Ads C++ SDK\napply from: \"$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle\"\nfirebaseCpp.dependencies {\n gma\n}\n```\n\nExample:\n```text\n# Add Firebase libraries to the target using the function from the SDK.\nadd_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL)\n\n# Add the Google Mobile Ads C++ SDK.\n\n# The Firebase C++ library `firebase_app` is required,\n# and it must always be listed last.\n\nset(firebase_libs\n firebase_gma\n firebase_app\n)\n\ntarget_link_libraries(${target_name} \"${firebase_libs}\")\n```\n\nExample:\n```text\nsudo gem install cocoapods --pre\n```\n\nExample:\n```text\ncd APP_DIRECTORYpod init\n```\n\nExample:\n```text\npod 'Firebase/CoreOnly'\npod 'Google-Mobile-Ads-SDK'\npod 'GoogleUserMessagingPlatform'\n```\n\nExample:\n```text\npod installopen APP.xcworkspace\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads library\nfirebase::InitResult result;\nFuture<AdapterInitializationStatus> future =\n firebase::gma::Initialize(jni_env, j_activity, &result);\n\nif (result != kInitResultSuccess) {\n // Initialization immediately failed, most likely due to a missing\n // dependency. Check the device logs for more information.\n return;\n}\n\n// Monitor the status of the future.\n// See \"Use a Future to monitor the completion status of a method call\" below.\nif (future.status() == firebase::kFutureStatusComplete &&\n future.error() == firebase::gma::kAdErrorCodeNone) {\n // Initialization completed.\n} else {\n // Initialization on-going, or an error has occurred.\n}\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads library.\nfirebase::InitResult result;\nFuture<AdapterInitializationStatus> future =\n firebase::gma::Initialize(&result);\n\nif (result != kInitResultSuccess) {\n // Initialization immediately failed, most likely due to a missing\n // dependency. Check the device logs for more information.\n return;\n}\n\n// Monitor the status of the future.\n// See \"Use a Future to monitor the completion status of a method call\" below.\nif (future.status() == firebase::kFutureStatusComplete &&\n future.error() == firebase::gma::kAdErrorCodeNone) {\n // Initialization completed.\n} else {\n // Initialization on-going, or an error has occurred.\n}\n```\n\nExample:\n```text\n// Registers the OnCompletion callback. user_data is a pointer that is passed verbatim\n// to the callback as a void*. This allows you to pass any custom data to the callback\n// handler. In this case, the app has no data, so you must pass nullptr.\nfirebase::gma::InitializeLastResult().OnCompletion(OnCompletionCallback,\n /*user_data=*/nullptr);\n\n// The OnCompletion callback function.\nstatic void OnCompletionCallback(\n const firebase::Future<AdapterInitializationStatus>& future, void* user_data) {\n // Called when the Future is completed for the last call to firebase::gma::Initialize().\n // If the error code is firebase::gma::kAdErrorCodeNone,\n // then the SDK has been successfully initialized.\n if (future.error() == firebase::gma::kAdErrorCodeNone) {\n // success!\n } else {\n // failure.\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.169Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":135,"estimatedTokens":869}}62{"id":"doc-data_manager_api_google_for_developers-4777df0a","source":"documentation","title":"Data Manager API | Google for Developers","url":"https://developers.google.com/data-manager/api","text":"Example:\n```text\nPOST https://datamanager.googleapis.com/v1/audiencemembers:ingest\n\n{\n \"destinations\": [\n {\n object (Destination)\n }\n ],\n \"audienceMembers\": [\n {\n object (IngestedAudienceMember)\n }\n ],\n \"consent\": {\n object (Consent)\n },\n \"validateOnly\": boolean,\n \"encoding\": enum (Encoding)\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.170Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":24,"estimatedTokens":87}}63{"id":"doc-set_up_google_mobile_ads_sdk_ios_google_for_deve-20f9ed27","source":"documentation","title":"Set up Google Mobile Ads SDK | iOS | Google for Developers","url":"https://developers.google.com/admob/ios","text":"Example:\n```text\nhttps://github.com/googleads/swift-package-manager-google-mobile-ads.git\n```\n\nExample:\n```text\npod 'Google-Mobile-Ads-SDK'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\n<key>GADApplicationIdentifier</key>\n<!-- Sample AdMob app ID: ca-app-pub-3940256099942544~1458002511 -->\n<string>SAMPLE_APP_ID</string>\n<key>SKAdNetworkItems</key>\n<array>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>cstr6suwn9.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>4fzdc2evr5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>2fnua5tdw4.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>ydx93a7ass.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>p78axxw29g.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>v72qych5uu.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>ludvb6z3bs.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>cp8zw746q7.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3sh42y64q3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>c6k4g5qg8m.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>s39g8k73mm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>wg4vff78zm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3qy4746246.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>f38h382jlk.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>hs6bdukanm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>mlmmfzh3r3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>v4nxqhlyqp.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>wzmmz9fp6w.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>su67r6k2v3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>yclnxrl5pm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>t38b2kh725.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>7ug5zh24hu.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>gta9lk7p23.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>vutu7akeur.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>y5ghdn5j9k.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>v9wttpbfk9.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>n38lu8286q.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>47vhws6wlr.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>kbd757ywx3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>9t245vhmpl.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>a2p9lx4jpn.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>22mmun2rn5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>44jx6755aq.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>k674qkevps.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>4468km3ulz.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>2u9pt9hc89.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>8s468mfl3y.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>klf5c3l5u5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>ppxm28t8ap.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>kbmxgpxpgc.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>uw77j35x4d.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>578prtvx9j.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>4dzt52r2t5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>tl55sbb4fm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>c3frkrj4fj.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>e5fvkxwrpn.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>8c4e2ghe7u.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3rd42ekr43.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>97r2b46745.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3qcr597p9d.skadnetwork</string>\n </dict>\n</array>\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads SDK.\nMobileAds.shared.start()ViewController.swift\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads SDK.\nMobileAds.shared.start()GoogleMobileAdsConsentManager.swift\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads SDK.\n[GADMobileAds.sharedInstance startWithCompletionHandler:nil];ViewController.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.175Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":244,"estimatedTokens":1445}}64{"id":"doc-set_up_google_mobile_ads_sdk_mobile_ads_sdk_for_-241f7e78","source":"documentation","title":"Set up Google Mobile Ads SDK | Mobile Ads SDK for iOS | Google for Developers","url":"https://developers.google.com/ad-manager/mobile-ads-sdk/ios","text":"Example:\n```text\nhttps://github.com/googleads/swift-package-manager-google-mobile-ads.git\n```\n\nExample:\n```text\npod 'Google-Mobile-Ads-SDK'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\n<key>GADApplicationIdentifier</key>\n<!-- Sample Ad Manager app ID: ca-app-pub-3940256099942544~1458002511 -->\n<string>SAMPLE_APP_ID</string>\n<key>SKAdNetworkItems</key>\n<array>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>cstr6suwn9.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>4fzdc2evr5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>2fnua5tdw4.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>ydx93a7ass.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>p78axxw29g.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>v72qych5uu.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>ludvb6z3bs.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>cp8zw746q7.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3sh42y64q3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>c6k4g5qg8m.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>s39g8k73mm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>wg4vff78zm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3qy4746246.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>f38h382jlk.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>hs6bdukanm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>mlmmfzh3r3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>v4nxqhlyqp.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>wzmmz9fp6w.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>su67r6k2v3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>yclnxrl5pm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>t38b2kh725.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>7ug5zh24hu.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>gta9lk7p23.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>vutu7akeur.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>y5ghdn5j9k.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>v9wttpbfk9.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>n38lu8286q.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>47vhws6wlr.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>kbd757ywx3.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>9t245vhmpl.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>a2p9lx4jpn.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>22mmun2rn5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>44jx6755aq.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>k674qkevps.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>4468km3ulz.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>2u9pt9hc89.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>8s468mfl3y.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>klf5c3l5u5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>ppxm28t8ap.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>kbmxgpxpgc.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>uw77j35x4d.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>578prtvx9j.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>4dzt52r2t5.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>tl55sbb4fm.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>c3frkrj4fj.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>e5fvkxwrpn.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>8c4e2ghe7u.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3rd42ekr43.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>97r2b46745.skadnetwork</string>\n </dict>\n <dict>\n <key>SKAdNetworkIdentifier</key>\n <string>3qcr597p9d.skadnetwork</string>\n </dict>\n</array>\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads SDK.\nMobileAds.shared.start()ViewController.swift\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads SDK.\nMobileAds.shared.start()GoogleMobileAdsConsentManager.swift\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads SDK.\n[GADMobileAds.sharedInstance startWithCompletionHandler:nil];ViewController.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.176Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":244,"estimatedTokens":1447}}65{"id":"doc-set_up_google_mobile_ads_unity_plugin_google_for-a6d463db","source":"documentation","title":"Set up Google Mobile Ads Unity Plugin | Google for Developers","url":"https://developers.google.com/admob/unity","text":"Example:\n```text\nopenupm add com.google.ads.mobile\n```\n\nExample:\n```text\nName: OpenUPM\nURL: https://package.openupm.com\nScopes: com.google\n```\n\nExample:\n```text\ncurl -sS https://api.github.com/repos/googleads/googleads-mobile-unity/releases/latest | jq -r '.tag_name'\n```\n\nExample:\n```text\n(Invoke-RestMethod -Uri \"https://api.github.com/repos/googleads/googleads-mobile-unity/releases/latest\").tag_name\n```\n\nExample:\n```text\n{\n \"scopedRegistries\": [\n {\n \"name\": \"google\",\n \"url\": \"https://package.openupm.com\",\n \"scopes\": [\n \"com.google\"\n ]\n }\n ],\n \"dependencies\": {\n \"com.google.ads.mobile\": \"11.4.0\"\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.177Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":41,"estimatedTokens":167}}66{"id":"doc-get_started_with_google_publisher_tag_google_for-97d4096b","source":"documentation","title":"Get Started with Google Publisher Tag | Google for Developers","url":"https://developers.google.com/publisher-tag/guides/get-started","text":"Example:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <meta name=\"description\" content=\"Display a fixed-sized test ad.\" />\n <title>Display a test ad</title>\n <style></style>\n </head>\n <body>\n </body>\n</html>\n```\n\nExample:\n```devsite-click-to-copy\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <meta name=\"description\" content=\"Display a fixed-sized test ad.\" />\n <title>Display a test ad</title>\n <script\n async\n src=\"https://securepubads.g.doubleclick.net/tag/js/gpt.js\"\n crossorigin=\"anonymous\"\n ></script>\n <style></style>\n</head>\n```\n\nExample:\n```devsite-click-to-copy\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <meta name=\"description\" content=\"Display a fixed-sized test ad.\" />\n <title>Display a test ad</title>\n <script\n async\n src=\"https://securepubads.g.doubleclick.net/tag/js/gpt.js\"\n crossorigin=\"anonymous\"\n ></script>\n <script>\n window.googletag = window.googletag || { cmd: [] };\n\n googletag.cmd.push(() => {\n // Define an ad slot for div with id \"banner-ad\".\n googletag\n .defineSlot(\"/6355419/Travel/Europe/France/Paris\", [300, 250], \"banner-ad\")\n .addService(googletag.pubads());\n\n // Enable the PubAdsService.\n googletag.enableServices();\n });\n </script>\n <style></style>\n</head>\n```\n\nExample:\n```devsite-click-to-copy\n<body>\n <div id=\"banner-ad\" style=\"width: 300px; height: 250px\"></div>\n <script>\n googletag.cmd.push(() => {\n // Request and render an ad for the \"banner-ad\" slot.\n googletag.display(\"banner-ad\");\n });\n </script>\n</body>\n```\n\nExample:\n```text\n<head>\n <meta charset=\"utf-8\">\n <title>Hello GPT</title>\n <script src=\"https://securepubads.g.doubleclick.net/tag/js/gpt.js\" crossorigin=\"anonymous\" async></script>\n <script>\n window.googletag = window.googletag || {cmd: []};\n googletag.cmd.push(function() {\n googletag\n .defineSlot(\"ad-unit-path\", [width, height], \"div-id\")\n .addService(googletag.pubads());\n googletag.enableServices();\n });\n </script>\n</head>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.177Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":93,"estimatedTokens":571}}67{"id":"doc-set_up_google_mobile_ads_flutter_plugin_google_f-a353be26","source":"documentation","title":"Set up Google Mobile Ads Flutter Plugin | Google for Developers","url":"https://developers.google.com/ad-manager/mobile-ads-sdk/flutter","text":"Example:\n```text\n<manifest>\n <application>\n <!-- Sample Ad Manager app ID: ca-app-pub-3940256099942544~3347511713 -->\n <meta-data\n android:name=\"com.google.android.gms.ads.APPLICATION_ID\"\n android:value=\"ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy\"/>\n <application>\n<manifest>\n```\n\nExample:\n```text\n<key>GADApplicationIdentifier</key>\n<string>ca-app-pub-################~##########</string>\n```\n\nExample:\n```text\n// Initialize the Mobile Ads SDK.\nMobileAds.instance.initialize();main.dart\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.178Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":25,"estimatedTokens":136}}68{"id":"doc-set_up_gma_next_gen_sdk_android_google_for_devel-275cfa4e","source":"documentation","title":"Set up GMA Next-Gen SDK | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen","text":"Example:\n```devsite-click-to-copy\npluginManagement {\n repositories {\n google()\n mavenCentral()\n gradlePluginPortal()\n }\n}\n\ndependencyResolutionManagement {\n repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n repositories {\n google()\n mavenCentral()\n }\n}\n\nrootProject.name = \"My Application\"\ninclude(\":app\")\n```\n\nExample:\n```devsite-click-to-copy\npluginManagement {\n repositories {\n google()\n mavenCentral()\n gradlePluginPortal()\n }\n}\n\ndependencyResolutionManagement {\n repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n repositories {\n google()\n mavenCentral()\n }\n}\n\nrootProject.name = \"My Application\"\ninclude ':app'\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n}\n```\n\nExample:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds\nimport com.google.android.libraries.ads.mobile.sdk.initialization.InitializationConfig\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\n\nclass MainActivity : AppCompatActivity() {\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n\n val backgroundScope = CoroutineScope(Dispatchers.IO)\n backgroundScope.launch {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this@MainActivity,\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n InitializationConfig.Builder(\"SAMPLE_APP_ID\").build()\n ) {\n // Adapter initialization is complete.\n }\n // SDK initialization is complete. If you don't want to wait for bidding adapters to finish\n // initializing, start loading ads now.\n }\n }\n}\n```\n\nExample:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds;\nimport com.google.android.libraries.ads.mobile.sdk.initialization.InitializationConfig;\n\npublic class MainActivity extends AppCompatActivity {\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n\n new Thread(\n () -> {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this,\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n new InitializationConfig.Builder(\"SAMPLE_APP_ID\")\n .build(),\n initializationStatus -> {\n // Adapter initialization is complete.\n });\n // SDK initialization is complete. If you don't want to wait for bidding adapters to\n // finish initializing, start loading ads now.\n })\n .start();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.178Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":118,"estimatedTokens":759}}69{"id":"doc-set_up_google_mobile_ads_unity_plugin_google_for-c5b3a6d0","source":"documentation","title":"Set up Google Mobile Ads Unity Plugin | Google for Developers","url":"https://developers.google.com/ad-manager/mobile-ads-sdk/unity","text":"Example:\n```text\nopenupm add com.google.ads.mobile\n```\n\nExample:\n```text\nName: OpenUPM\nURL: https://package.openupm.com\nScopes: com.google\n```\n\nExample:\n```text\ncurl -sS https://api.github.com/repos/googleads/googleads-mobile-unity/releases/latest | jq -r '.tag_name'\n```\n\nExample:\n```text\n(Invoke-RestMethod -Uri \"https://api.github.com/repos/googleads/googleads-mobile-unity/releases/latest\").tag_name\n```\n\nExample:\n```text\n{\n \"scopedRegistries\": [\n {\n \"name\": \"google\",\n \"url\": \"https://package.openupm.com\",\n \"scopes\": [\n \"com.google\"\n ]\n }\n ],\n \"dependencies\": {\n \"com.google.ads.mobile\": \"11.4.0\"\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.179Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":41,"estimatedTokens":167}}70{"id":"doc-set_up_gma_next_gen_sdk_android_google_for_devel-0c14ef31","source":"documentation","title":"Set up GMA Next-Gen SDK | Android | Google for Developers","url":"https://developers.google.com/ad-manager/mobile-ads-sdk/android/next-gen","text":"Example:\n```devsite-click-to-copy\npluginManagement {\n repositories {\n google()\n mavenCentral()\n gradlePluginPortal()\n }\n}\n\ndependencyResolutionManagement {\n repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n repositories {\n google()\n mavenCentral()\n }\n}\n\nrootProject.name = \"My Application\"\ninclude(\":app\")\n```\n\nExample:\n```devsite-click-to-copy\npluginManagement {\n repositories {\n google()\n mavenCentral()\n gradlePluginPortal()\n }\n}\n\ndependencyResolutionManagement {\n repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n repositories {\n google()\n mavenCentral()\n }\n}\n\nrootProject.name = \"My Application\"\ninclude ':app'\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n}\n```\n\nExample:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds\nimport com.google.android.libraries.ads.mobile.sdk.initialization.InitializationConfig\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\n\nclass MainActivity : AppCompatActivity() {\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n\n val backgroundScope = CoroutineScope(Dispatchers.IO)\n backgroundScope.launch {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this@MainActivity,\n // Sample Ad Manager app ID: ca-app-pub-3940256099942544~3347511713\n InitializationConfig.Builder(\"SAMPLE_APP_ID\").build()\n ) {\n // Adapter initialization is complete.\n }\n // SDK initialization is complete. If you don't want to wait for bidding adapters to finish\n // initializing, start loading ads now.\n }\n }\n}\n```\n\nExample:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds;\nimport com.google.android.libraries.ads.mobile.sdk.initialization.InitializationConfig;\n\npublic class MainActivity extends AppCompatActivity {\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n\n new Thread(\n () -> {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this,\n // Sample Ad Manager app ID: ca-app-pub-3940256099942544~3347511713\n new InitializationConfig.Builder(\"SAMPLE_APP_ID\")\n .build(),\n initializationStatus -> {\n // Adapter initialization is complete.\n });\n // SDK initialization is complete. If you don't want to wait for bidding adapters to\n // finish initializing, start loading ads now.\n })\n .start();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.180Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":118,"estimatedTokens":762}}71{"id":"doc-getting_started_ad_manager_api_beta_google_for_d-0a20eb86","source":"documentation","title":"Getting Started | Ad Manager API (Beta) | Google for Developers","url":"https://developers.google.com/ad-manager/api/beta","text":"Example:\n```text\nexport GOOGLE_APPLICATION_CREDENTIALS=KEY_FILE_PATH\n```\n\nExample:\n```text\nset GOOGLE_APPLICATION_CREDENTIALS=KEY_FILE_PATH\n```\n\nExample:\n```text\ngcloud auth application-default login --scopes=\"https://www.googleapis.com/auth/admanager\"\n# End user credentials must specify the cloud project where the API is enabled.\ngcloud auth application-default set-quota-project PROJECT_ID\n```\n\nExample:\n```text\n<!-- pom.xml -->\n<dependency>\n <groupId>com.google.api-ads</groupId>\n <artifactId>ad-manager</artifactId>\n <version>0.1.0</version>\n</dependency>\n```\n\nExample:\n```text\nimplementation 'com.google.api-ads:ad-manager:0.1.0'\n```\n\nExample:\n```text\npip install google-ads-admanager\n```\n\nExample:\n```text\ndotnet add package Google.Ads.AdManager.V1 --version 1.0.0-beta01\n```\n\nExample:\n```text\n<PackageReference Include=\"Google.Ads.AdManager.V1\" Version=\"1.0.0-beta01\" />\n```\n\nExample:\n```text\ncomposer require googleads/ad-manager\n```\n\nExample:\n```text\ngem 'google-ads-ad_manager', '~> 0.2.0'\n```\n\nExample:\n```text\ngem install google-ads-ad_manager\n```\n\nExample:\n```text\nnpm install @google-ads/admanager\n```\n\nExample:\n```text\n// package.json\n\"dependencies\": {\n \"@google-ads/admanager\": \"^0.1.0\"\n}\n```\n\nExample:\n```text\nimport com.google.ads.admanager.v1.GetNetworkRequest;\nimport com.google.ads.admanager.v1.Network;\nimport com.google.ads.admanager.v1.NetworkName;\nimport com.google.ads.admanager.v1.NetworkServiceClient;\n\npublic class SyncGetNetwork {\n\n public static void main(String[] args) throws Exception {\n syncGetNetwork();\n }\n\n public static void syncGetNetwork() throws Exception {\n try (NetworkServiceClient networkServiceClient = NetworkServiceClient.create()) {\n GetNetworkRequest request =\n GetNetworkRequest.newBuilder()\n .setName(NetworkName.of(\"NETWORK_CODE\").toString())\n .build();\n Network response = networkServiceClient.getNetwork(request);\n }\n }\n}SyncGetNetwork.java\n```\n\nExample:\n```text\nfrom google.ads import admanager_v1\n\n\ndef sample_get_network():\n # Create a client\n client = admanager_v1.NetworkServiceClient()\n\n # Initialize request argument(s)\n request = admanager_v1.GetNetworkRequest(\n name=\"networks/NETWORK_CODE\",\n )\n\n # Make the request\n response = client.get_network(request=request)\n\n # Handle the response\n print(response)\nadmanager_v1_generated_network_service_get_network_sync.py\n```\n\nExample:\n```text\nusing Google.Ads.AdManager.V1;\n\npublic sealed partial class GeneratedNetworkServiceClientSnippets\n{\n public void GetNetwork()\n {\n // Create client\n NetworkServiceClient networkServiceClient = NetworkServiceClient.Create();\n // Initialize request argument(s)\n string name = \"networks/NETWORK_CODE\";\n // Make the request\n Network response = networkServiceClient.GetNetwork(name);\n }\n}NetworkServiceClient.GetNetworkSnippet.g.cs\n```\n\nExample:\n```text\n<?phpuse Google\\Ads\\AdManager\\V1\\Client\\NetworkServiceClient;\nuse Google\\Ads\\AdManager\\V1\\GetNetworkRequest;\nuse Google\\Ads\\AdManager\\V1\\Network;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * Retrieves a `Network` object.\n *\n * @param string $formattedName Resource name of Network.\n * Format: networks/{network_code}\n * Please see {@see NetworkServiceClient::networkName()} for help formatting this field.\n */\nfunction get_network_sample(string $formattedName): void\n{\n // Create a client.\n $networkServiceClient = new NetworkServiceClient();\n\n // Prepare the request message.\n $request = (new GetNetworkRequest())\n ->setName($formattedName);\n\n // Call the API and handle any network failures.\n try {\n /** @var Network $response */\n $response = $networkServiceClient->getNetwork($request);\n printf('Response data: %s' . PHP_EOL, $response->serializeToJsonString());\n } catch (ApiException $ex) {\n printf('Call failed with message: %s' . PHP_EOL, $ex->getMessage());\n }\n}\n\n/**\n * Helper to execute the sample.\n *\n */\nfunction callSample(): void\n{\n $formattedName = NetworkServiceClient::networkName('NETWORK_CODE');\n\n get_network_sample($formattedName);\n}get_network.php\n```\n\nExample:\n```text\nrequire \"google/ads/ad_manager/v1\"\n\ndef get_network\n # Create a client object. The client can be reused for multiple calls.\n client = Google::Ads::AdManager::V1::NetworkService::Rest::Client.new\n\n # Create a request. To set request fields, pass in keyword arguments.\n request = Google::Ads::AdManager::V1::GetNetworkRequest.new(:name => 'networks/NETWORK_CODE)'\n\n # Call the get_network method.\n result = client.get_network request\n\n # The returned object is of type Google::Ads::AdManager::V1::Network.\n p result\nendget_network.rb\n```\n\nExample:\n```text\n// Resource name of the Network\nconst name = 'networks/NETWORK_CODE'\n\n// Imports the Admanager library\nconst {NetworkServiceClient} = require('@google-ads/admanager').v1;\n\n// Instantiates a client\nconst admanagerClient = new NetworkServiceClient();\n\nasync function callGetNetwork() {\n // Construct request\n const request = {\n name,\n };\n\n // Run request\n const response = await admanagerClient.getNetwork(request);\n console.log(response);\n}\n\ncallGetNetwork();network_service.get_network.js\n```\n\nExample:\n```text\ncurl -H \"Authorization: Bearer $(gcloud auth application-default print-access-token)\" \\\n https://admanager.googleapis.com/v1/networks/NETWORK_CODE\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.180Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":20,"totalLines":236,"estimatedTokens":1377}}72{"id":"doc-set_up_google_mobile_ads_flutter_plugin_google_f-099b7804","source":"documentation","title":"Set up Google Mobile Ads Flutter Plugin | Google for Developers","url":"https://developers.google.com/admob/flutter","text":"Example:\n```text\n<manifest>\n <application>\n <!-- Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713 -->\n <meta-data\n android:name=\"com.google.android.gms.ads.APPLICATION_ID\"\n android:value=\"ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy\"/>\n <application>\n<manifest>\n```\n\nExample:\n```text\n<key>GADApplicationIdentifier</key>\n<string>ca-app-pub-################~##########</string>\n```\n\nExample:\n```text\n// Initialize the Mobile Ads SDK.\nMobileAds.instance.initialize();main.dart\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.181Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":25,"estimatedTokens":135}}73{"id":"doc-get_started_ad_manager_soap_api_google_for_devel-01803b0c","source":"documentation","title":"Get started | Ad Manager SOAP API | Google for Developers","url":"https://developers.google.com/ad-manager/api","text":"Example:\n```text\ncurl https://raw.githubusercontent.com/googleads/googleads-java-lib/main/examples/admanager_axis/src/main/resources/ads.properties -o ~/ads.properties\n```\n\nExample:\n```text\n[...]\napi.admanager.applicationName=INSERT_APPLICATION_NAME_HERE\napi.admanager.jsonKeyFilePath=INSERT_PATH_TO_JSON_KEY_FILE_HERE\napi.admanager.networkCode=INSERT_NETWORK_CODE_HERE\n[...]\n```\n\nExample:\n```text\n<dependency>\n <groupId>com.google.api-ads</groupId>\n <artifactId>ads-lib</artifactId>\n <version>RELEASE</version>\n</dependency>\n<dependency>\n <groupId>com.google.api-ads</groupId>\n <artifactId>dfp-axis</artifactId>\n <version>RELEASE</version>\n</dependency>\n```\n\nExample:\n```text\nimport com.google.api.ads.common.lib.auth.OfflineCredentials;\nimport com.google.api.ads.common.lib.auth.OfflineCredentials.Api;\nimport com.google.api.ads.admanager.axis.factory.AdManagerServices;\nimport com.google.api.ads.admanager.axis.v202602.Network;\nimport com.google.api.ads.admanager.axis.v202602.NetworkServiceInterface;\nimport com.google.api.ads.admanager.lib.client.AdManagerSession;\nimport com.google.api.client.auth.oauth2.Credential;\n\npublic class App {\n public static void main(String[] args) throws Exception {\n Credential oAuth2Credential = new OfflineCredentials.Builder()\n .forApi(Api.AD_MANAGER)\n .fromFile()\n .build()\n .generateCredential();\n\n // Construct an AdManagerSession.\n AdManagerSession session = new AdManagerSession.Builder()\n .fromFile()\n .withOAuth2Credential(oAuth2Credential)\n .build();\n\n // Construct a Google Ad Manager service factory, which can only be used once per\n // thread, but should be reused as much as possible.\n AdManagerServices adManagerServices = new AdManagerServices();\n\n // Retrieve the appropriate service\n NetworkServiceInterface networkService = adManagerServices.get(session,\n NetworkServiceInterface.class);\n\n // Make a request\n Network network = networkService.getCurrentNetwork();\n\n System.out.printf(\"Current network has network code '%s' and display\" +\n \" name '%s'.%n\", network.getNetworkCode(), network.getDisplayName());\n }\n}\n```\n\nExample:\n```text\npython3 -m pip install googleadscurl https://raw.githubusercontent.com/googleads/googleads-python-lib/main/googleads.yaml \\\n -o ~/googleads.yaml\n```\n\nExample:\n```text\nad_manager:\n application_name: INSERT_APPLICATION_NAME_HERE\n network_code: INSERT_NETWORK_CODE_HERE\n path_to_private_key_file: INSERT_PATH_TO_FILE_HERE\n```\n\nExample:\n```text\n# Import the library.\nfrom googleads import ad_manager\n\n# Initialize a client object, by default uses the credentials in ~/googleads.yaml.\nclient = ad_manager.AdManagerClient.LoadFromStorage()\n\n# Initialize a service.\nnetwork_service = client.GetService('NetworkService', version='v202602')\n\n# Make a request.\ncurrent_network = network_service.getCurrentNetwork()\n\nprint(\"Current network has network code '%s' and display name '%s'.\" %\n (current_network['networkCode'], current_network['displayName']))\n```\n\nExample:\n```text\ncomposer require googleads/googleads-php-libcurl https://raw.githubusercontent.com/googleads/googleads-php-lib/main/examples/AdManager/adsapi_php.ini -o ~/adsapi_php.ini\n```\n\nExample:\n```text\n[AD_MANAGER]\nnetworkCode = \"INSERT_NETWORK_CODE_HERE\"\napplicationName = \"INSERT_APPLICATION_NAME_HERE\"\n\n[OAUTH2]\njsonKeyFilePath = \"INSERT_ABSOLUTE_PATH_TO_OAUTH2_JSON_KEY_FILE_HERE\"\nscopes = \"https://www.googleapis.com/auth/dfp\"\n```\n\nExample:\n```text\n<?php\nrequire 'vendor/autoload.php';\nuse Google\\AdsApi\\AdManager\\AdManagerSession;\nuse Google\\AdsApi\\AdManager\\AdManagerSessionBuilder;\nuse Google\\AdsApi\\AdManager\\v202602\\ApiException;\nuse Google\\AdsApi\\AdManager\\v202602\\ServiceFactory;\nuse Google\\AdsApi\\Common\\OAuth2TokenBuilder;\n\n// Generate a refreshable OAuth2 credential for authentication.\n$oAuth2Credential = (new OAuth2TokenBuilder())\n ->fromFile()\n ->build();\n// Construct an API session configured from a properties file and the OAuth2\n// credentials above.\n$session = (new AdManagerSessionBuilder())\n ->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n// Get a service.\n$serviceFactory = new ServiceFactory();\n$networkService = $serviceFactory->createNetworkService($session);\n\n// Make a request\n$network = $networkService->getCurrentNetwork();\nprintf(\n \"Network with code %d and display name '%s' was found.\\n\",\n $network->getNetworkCode(),\n $network->getDisplayName()\n);\n```\n\nExample:\n```text\n<add key=\"ApplicationName\" value=\"INSERT_YOUR_APPLICATION_NAME_HERE\" />\n<add key=\"NetworkCode\" value=\"INSERT_YOUR_NETWORK_CODE_HERE\" />\n<add key=\"OAuth2Mode\" value=\"SERVICE_ACCOUNT\" />\n<add key=\"OAuth2SecretsJsonPath\" value=\"INSERT_OAUTH2_SECRETS_JSON_FILE_PATH_HERE\" />\n```\n\nExample:\n```text\nAdManagerUser user = new AdManagerUser();\n using (InventoryService inventoryService = user.GetService<InventoryService>())\n {\n // Create a statement to select ad units.\n int pageSize = StatementBuilder.SUGGESTED_PAGE_LIMIT;\n StatementBuilder statementBuilder =\n new StatementBuilder().OrderBy(\"id ASC\").Limit(pageSize);\n\n // Retrieve a small amount of ad units at a time, paging through until all\n // ad units have been retrieved.\n int totalResultSetSize = 0;\n do\n {\n AdUnitPage page =\n inventoryService.getAdUnitsByStatement(statementBuilder.ToStatement());\n\n // Print out some information for each ad unit.\n if (page.results != null)\n {\n totalResultSetSize = page.totalResultSetSize;\n int i = page.startIndex;\n foreach (AdUnit adUnit in page.results)\n {\n Console.WriteLine(\n \"{0}) Ad unit with ID \\\"{1}\\\" and name \\\"{2}\\\" was found.\", i++,\n adUnit.id, adUnit.name);\n }\n }\n\n statementBuilder.IncreaseOffsetBy(pageSize);\n } while (statementBuilder.GetOffset() < totalResultSetSize);\n\n Console.WriteLine(\"Number of results found: {0}\", totalResultSetSize);\n }\n```\n\nExample:\n```text\ngem install google-dfp-apicurl https://raw.githubusercontent.com/googleads/google-api-ads-ruby/main/ad_manager_api/ad_manager_api.yml -o ~/ad_manager_api.yml\n```\n\nExample:\n```text\n:authentication:\n :oauth2_keyfile: INSERT_PATH_TO_JSON_KEY_FILE_HERE\n :application_name: INSERT_APPLICATION_NAME_HERE\n :network_code: INSERT_NETWORK_CODE_HERE\n```\n\nExample:\n```text\n# Import the library.\nrequire 'ad_manager_api'\n\n# Initialize an Ad Manager client instance (uses credentials in ~/ad_manager_api.yml by default).\nad_manager = AdManagerApi::Api.new\n\n# Get a service instance.\nnetwork_service = ad_manager.service(:NetworkService, :v202602)\n\n# Make a request.\nnetwork = network_service.get_current_network()\n\nputs \"The current network is %s (%d).\" %\n [network[:display_name], network[:network_code]]\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.183Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":229,"estimatedTokens":1813}}74{"id":"doc-set_up_the_ima_sdk_ima_sdk_for_ios_google_for_de-a9425240","source":"documentation","title":"Set up the IMA SDK | IMA SDK for iOS | Google for Developers","url":"https://developers.google.com/interactive-media-ads/docs/sdks/ios/client-side","text":"Example:\n```text\nplatform :ios, '15'\n\ntarget \"BasicExample\" do\n pod 'GoogleAds-IMA-iOS-SDK', '~> 3.32.0'\nend\nPodfile\n```\n\nExample:\n```text\n#import \"ViewController.h\"\n\n@import AVFoundation;ViewController.m\n```\n\nExample:\n```text\n@interface ViewController () <IMAAdsLoaderDelegate, IMAAdsManagerDelegate>\n\n/// Content video player.\n@property(nonatomic, strong) AVPlayer *contentPlayer;\n\n/// Play button.\n@property(nonatomic, weak) IBOutlet UIButton *playButton;\n\n/// UIView in which we will render our AVPlayer for content.\n@property(nonatomic, weak) IBOutlet UIView *videoView;ViewController.m\n```\n\nExample:\n```text\n@implementation ViewController\n\n// The content URL to play.\nNSString *const kTestAppContentUrl_MP4 =\n @\"https://storage.googleapis.com/gvabox/media/samples/stock.mp4\";\n\n// Ad tag\nNSString *const kTestAppAdTagUrl = @\"https://pubads.g.doubleclick.net/gampad/ads?\"\n @\"iu=/21775744923/external/single_ad_samples&sz=640x480&cust_params=sample_ct%3Dlinear&\"\n @\"ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&\"\n @\"correlator=\";\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n\n self.playButton.layer.zPosition = MAXFLOAT;\n\n [self setupAdsLoader];\n [self setUpContentPlayer];\n}\n\n#pragma mark Content Player Setup\n\n- (void)setUpContentPlayer {\n // Load AVPlayer with path to our content.\n NSURL *contentURL = [NSURL URLWithString:kTestAppContentUrl_MP4];\n self.contentPlayer = [AVPlayer playerWithURL:contentURL];\n\n // Create a player layer for the player.\n AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.contentPlayer];\n\n // Size, position, and display the AVPlayer.\n playerLayer.frame = self.videoView.layer.bounds;\n [self.videoView.layer addSublayer:playerLayer];\n\n // Set up our content playhead and contentComplete callback.\n self.contentPlayhead = [[IMAAVPlayerContentPlayhead alloc] initWithAVPlayer:self.contentPlayer];\n [[NSNotificationCenter defaultCenter] addObserver:self\n selector:@selector(contentDidFinishPlaying:)\n name:AVPlayerItemDidPlayToEndTimeNotification\n object:self.contentPlayer.currentItem];\n}\n\n- (IBAction)onPlayButtonTouch:(id)sender {\n [self requestAds];\n self.playButton.hidden = YES;\n}ViewController.m\n```\n\nExample:\n```text\nimport AVFoundationPlayerContainerViewController.swift\n```\n\nExample:\n```text\nclass PlayerContainerViewController: UIViewController, IMAAdsLoaderDelegate, IMAAdsManagerDelegate {\n static let contentURL = URL(\n string: \"https://storage.googleapis.com/gvabox/media/samples/stock.mp4\")!\n\n private var contentPlayer = AVPlayer(url: PlayerContainerViewController.contentURL)\n\n private lazy var playerLayer: AVPlayerLayer = {\n AVPlayerLayer(player: contentPlayer)\n }()PlayerContainerViewController.swift\n```\n\nExample:\n```text\nprivate lazy var videoView: UIView = {\n let videoView = UIView()\n videoView.translatesAutoresizingMaskIntoConstraints = false\n view.addSubview(videoView)\n\n NSLayoutConstraint.activate([\n videoView.bottomAnchor.constraint(\n equalTo: view.safeAreaLayoutGuide.bottomAnchor),\n videoView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),\n videoView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),\n videoView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),\n ])\n return videoView\n}()\n\n// MARK: - View controller lifecycle methods\n\noverride func viewDidLoad() {\n super.viewDidLoad()\n\n videoView.layer.addSublayer(playerLayer)\n adsLoader.delegate = self\n\n NotificationCenter.default.addObserver(\n self,\n selector: #selector(contentDidFinishPlaying(_:)),\n name: .AVPlayerItemDidPlayToEndTime,\n object: contentPlayer.currentItem)\n}\n\noverride func viewDidAppear(_ animated: Bool) {\n super.viewDidAppear(animated)\n playerLayer.frame = videoView.layer.bounds\n}\n\noverride func viewWillTransition(\n to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator\n) {\n coordinator.animate { _ in\n // do nothing\n } completion: { _ in\n self.playerLayer.frame = self.videoView.layer.bounds\n }\n}\n\n// MARK: - Public methods\n\nfunc playButtonPressed() {\n requestAds()\n}PlayerContainerViewController.swift\n```\n\nExample:\n```text\n@import GoogleInteractiveMediaAds;ViewController.m\n```\n\nExample:\n```text\n// SDK\n/// Entry point for the SDK. Used to make ad requests.\n@property(nonatomic, strong) IMAAdsLoader *adsLoader;\n\n/// Playhead used by the SDK to track content video progress and insert mid-rolls.\n@property(nonatomic, strong) IMAAVPlayerContentPlayhead *contentPlayhead;\n\n/// Main point of interaction with the SDK. Created by the SDK as the result of an ad request.\n@property(nonatomic, strong) IMAAdsManager *adsManager;ViewController.m\n```\n\nExample:\n```text\nimport GoogleInteractiveMediaAds\nPlayerContainerViewController.swift\n```\n\nExample:\n```text\nstatic let adTagURLString =\n \"https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/\"\n + \"single_ad_samples&sz=640x480&cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&\"\n + \"gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&correlator=\"\n\nprivate let adsLoader = IMAAdsLoader()\nprivate var adsManager: IMAAdsManager?\n\nprivate lazy var contentPlayhead: IMAAVPlayerContentPlayhead = {\n IMAAVPlayerContentPlayhead(avPlayer: contentPlayer)\n}()PlayerContainerViewController.swift\n```\n\nExample:\n```text\n// Set up our content playhead and contentComplete callback.\nself.contentPlayhead = [[IMAAVPlayerContentPlayhead alloc] initWithAVPlayer:self.contentPlayer];\n[[NSNotificationCenter defaultCenter] addObserver:self\n selector:@selector(contentDidFinishPlaying:)\n name:AVPlayerItemDidPlayToEndTimeNotification\n object:self.contentPlayer.currentItem];ViewController.m\n```\n\nExample:\n```text\n- (void)contentDidFinishPlaying:(NSNotification *)notification {\n // Make sure we don't call contentComplete as a result of an ad completing.\n if (notification.object == self.contentPlayer.currentItem) {\n [self.adsLoader contentComplete];\n }\n}ViewController.m\n```\n\nExample:\n```text\nNotificationCenter.default.addObserver(\n self,\n selector: #selector(contentDidFinishPlaying(_:)),\n name: .AVPlayerItemDidPlayToEndTime,\n object: contentPlayer.currentItem)PlayerContainerViewController.swift\n```\n\nExample:\n```text\n@objc func contentDidFinishPlaying(_ notification: Notification) {\n // Make sure we don't call contentComplete as a result of an ad completing.\n if notification.object as? AVPlayerItem == contentPlayer.currentItem {\n adsLoader.contentComplete()\n }\n}PlayerContainerViewController.swift\n```\n\nExample:\n```text\n- (void)setupAdsLoader {\n self.adsLoader = [[IMAAdsLoader alloc] initWithSettings:nil];\n self.adsLoader.delegate = self;\n}\n\n- (void)requestAds {\n // Create an ad display container for ad rendering.\n IMAAdDisplayContainer *adDisplayContainer =\n [[IMAAdDisplayContainer alloc] initWithAdContainer:self.videoView\n viewController:self\n companionSlots:nil];\n // Create an ad request with our ad tag, display container, and optional user context.\n IMAAdsRequest *request = [[IMAAdsRequest alloc] initWithAdTagUrl:kTestAppAdTagUrl\n adDisplayContainer:adDisplayContainer\n contentPlayhead:self.contentPlayhead\n userContext:nil];\n [self.adsLoader requestAdsWithRequest:request];\n}ViewController.m\n```\n\nExample:\n```text\nprivate func requestAds() {\n // Create ad display container for ad rendering.\n let adDisplayContainer = IMAAdDisplayContainer(\n adContainer: videoView, viewController: self, companionSlots: nil)\n // Create an ad request with our ad tag, display container, and optional user context.\n let request = IMAAdsRequest(\n adTagUrl: PlayerContainerViewController.adTagURLString,\n adDisplayContainer: adDisplayContainer,\n contentPlayhead: contentPlayhead,\n userContext: nil)\n\n adsLoader.requestAds(with: request)\n}PlayerContainerViewController.swift\n```\n\nExample:\n```text\n- (void)adsLoader:(IMAAdsLoader *)loader adsLoadedWithData:(IMAAdsLoadedData *)adsLoadedData {\n // Grab the instance of the IMAAdsManager and set ourselves as the delegate.\n self.adsManager = adsLoadedData.adsManager;\n self.adsManager.delegate = self;\n // Create ads rendering settings to tell the SDK to use the in-app browser.\n IMAAdsRenderingSettings *adsRenderingSettings = [[IMAAdsRenderingSettings alloc] init];\n adsRenderingSettings.linkOpenerPresentingController = self;\n // Initialize the ads manager.\n [self.adsManager initializeWithAdsRenderingSettings:adsRenderingSettings];\n}\n\n- (void)adsLoader:(IMAAdsLoader *)loader failedWithErrorData:(IMAAdLoadingErrorData *)adErrorData {\n // Something went wrong loading ads. Log the error and play the content.\n NSLog(@\"Error loading ads: %@\", adErrorData.adError.message);\n [self.contentPlayer play];\n}ViewController.m\n```\n\nExample:\n```text\nfunc adsLoader(_ loader: IMAAdsLoader, adsLoadedWith adsLoadedData: IMAAdsLoadedData) {\n // Grab the instance of the IMAAdsManager and set ourselves as the delegate.\n adsManager = adsLoadedData.adsManager\n adsManager?.delegate = self\n\n // Create ads rendering settings and tell the SDK to use the in-app browser.\n let adsRenderingSettings = IMAAdsRenderingSettings()\n adsRenderingSettings.linkOpenerPresentingController = self\n\n // Initialize the ads manager.\n adsManager?.initialize(with: adsRenderingSettings)\n}\n\nfunc adsLoader(_ loader: IMAAdsLoader, failedWith adErrorData: IMAAdLoadingErrorData) {\n if let message = adErrorData.adError.message {\n print(\"Error loading ads: \\(message)\")\n }\n contentPlayer.play()\n}PlayerContainerViewController.swift\n```\n\nExample:\n```text\n- (void)adsManager:(IMAAdsManager *)adsManager didReceiveAdEvent:(IMAAdEvent *)event {\n // When the SDK notified us that ads have been loaded, play them.\n if (event.type == kIMAAdEvent_LOADED) {\n [adsManager start];\n }\n}ViewController.m\n```\n\nExample:\n```text\nfunc adsManager(_ adsManager: IMAAdsManager, didReceive event: IMAAdEvent) {\n // When the SDK notifies us the ads have been loaded, play them.\n if event.type == IMAAdEventType.LOADED {\n adsManager.start()\n }\n}PlayerContainerViewController.swift\n```\n\nExample:\n```text\n- (void)adsManager:(IMAAdsManager *)adsManager didReceiveAdError:(IMAAdError *)error {\n // Something went wrong with the ads manager after ads were loaded. Log the error and play the\n // content.\n NSLog(@\"AdsManager error: %@\", error.message);\n [self.contentPlayer play];\n}ViewController.m\n```\n\nExample:\n```text\nfunc adsManager(_ adsManager: IMAAdsManager, didReceive error: IMAAdError) {\n // Something went wrong with the ads manager after ads were loaded.\n // Log the error and play the content.\n if let message = error.message {\n print(\"AdsManager error: \\(message)\")\n }\n contentPlayer.play()\n}PlayerContainerViewController.swift\n```\n\nExample:\n```text\n- (void)adsManagerDidRequestContentPause:(IMAAdsManager *)adsManager {\n // The SDK is going to play ads, so pause the content.\n [self.contentPlayer pause];\n}\n\n- (void)adsManagerDidRequestContentResume:(IMAAdsManager *)adsManager {\n // The SDK is done playing ads (at least for now), so resume the content.\n [self.contentPlayer play];\n}ViewController.m\n```\n\nExample:\n```text\nfunc adsManagerDidRequestContentPause(_ adsManager: IMAAdsManager) {\n // The SDK is going to play ads, so pause the content.\n contentPlayer.pause()\n}\n\nfunc adsManagerDidRequestContentResume(_ adsManager: IMAAdsManager) {\n // The SDK is done playing ads (at least for now), so resume the content.\n contentPlayer.play()\n}PlayerContainerViewController.swift\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.185Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":25,"totalLines":383,"estimatedTokens":3007}}75{"id":"doc-set_up_ima_sdk_ima_client_side_sdk_for_tvos_goog-19d58309","source":"documentation","title":"Set up IMA SDK | IMA Client-Side SDK for tvOS | Google for Developers","url":"https://developers.google.com/interactive-media-ads/docs/sdks/tvos/client-side","text":"Example:\n```text\nhttps://github.com/googleads/swift-package-manager-google-interactive-media-ads-tvos\n```\n\nExample:\n```text\nsource 'https://github.com/CocoaPods/Specs.git'\n\nplatform :tvos, '15'\n\ntarget \"BasicExample\" do\n pod 'GoogleAds-IMA-tvOS-SDK', '~> 4.17.0'\nend\nPodfile\n```\n\nExample:\n```text\n#import \"ViewController.h\"\n#import <AVKit/AVKit.h>\n\n@import GoogleInteractiveMediaAds;ViewController.m\n```\n\nExample:\n```text\nimport AVFoundation\nimport GoogleInteractiveMediaAds\nimport UIKit\nViewController.swift\n```\n\nExample:\n```text\nNSString *const kContentURLString =\n @\"https://storage.googleapis.com/interactive-media-ads/media/stock.mp4\";\nNSString *const kAdTagURLString =\n @\"https://pubads.g.doubleclick.net/gampad/ads?\"\n @\"iu=/21775744923/external/vmap_ad_samples&sz=640x480&\"\n @\"cust_params=sample_ar%3Dpremidpostlongpod&ciu_szs=300x250&gdfp_req=1&ad_rule=1&\"\n @\"output=vmap&unviewed_position_start=1&env=vp&cmsid=496&vid=short_onecue&correlator=\";\n\n@interface ViewController () <IMAAdsLoaderDelegate, IMAAdsManagerDelegate>\n@property(nonatomic) IMAAdsLoader *adsLoader;\n@property(nonatomic) IMAAdDisplayContainer *adDisplayContainer;\n@property(nonatomic) IMAAdsManager *adsManager;\n@property(nonatomic) IMAAVPlayerContentPlayhead *contentPlayhead;\n@property(nonatomic) AVPlayerViewController *contentPlayerViewController;\n@property(nonatomic, getter=isAdBreakActive) BOOL adBreakActive;\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n self.view.backgroundColor = [UIColor blackColor];\n [self setupAdsLoader];\n [self setupContentPlayer];\n}\n\n- (void)viewDidAppear:(BOOL)animated {\n [super viewDidAppear:animated];\n [self requestAds];\n}\n\n// Add the content video player as a child view controller.\n- (void)showContentPlayer {\n [self addChildViewController:self.contentPlayerViewController];\n self.contentPlayerViewController.view.frame = self.view.bounds;\n [self.view insertSubview:self.contentPlayerViewController.view atIndex:0];\n [self.contentPlayerViewController didMoveToParentViewController:self];\n}\n\n// Remove and detach the content video player.\n- (void)hideContentPlayer {\n // The whole controller needs to be detached so that it doesn't capture resume events from the\n // remote and play content underneath the ad.\n [self.contentPlayerViewController willMoveToParentViewController:nil];\n [self.contentPlayerViewController.view removeFromSuperview];\n [self.contentPlayerViewController removeFromParentViewController];\n}ViewController.m\n```\n\nExample:\n```text\nclass ViewController: UIViewController, IMAAdsLoaderDelegate, IMAAdsManagerDelegate {\n static let contentURLString =\n \"https://devstreaming-cdn.apple.com/videos/streaming/examples/\"\n + \"img_bipbop_adv_example_fmp4/master.m3u8\"\n static let adTagURLString =\n \"https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/single_ad_samples&\"\n + \"sz=640x480&cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast&\"\n + \"unviewed_position_start=1&env=vp&correlator=\"\n\n var adsLoader: IMAAdsLoader!\n var adDisplayContainer: IMAAdDisplayContainer!\n var adsManager: IMAAdsManager!\n var contentPlayhead: IMAAVPlayerContentPlayhead?\n var playerViewController: AVPlayerViewController!\n var adBreakActive = false\n\n deinit {\n NotificationCenter.default.removeObserver(self)\n }\n\n override func viewDidLoad() {\n super.viewDidLoad()\n self.view.backgroundColor = UIColor.black\n setUpContentPlayer()\n setUpAdsLoader()\n }\n\n override func viewDidAppear(_ animated: Bool) {\n super.viewDidAppear(animated)\n requestAds()\n }ViewController.swift\n```\n\nExample:\n```text\n- (void)setupContentPlayer {\n // Create a content video player. Create a playhead to track content progress so the SDK knows\n // when to play ads in a VMAP playlist.\n NSURL *contentURL = [NSURL URLWithString:kContentURLString];\n AVPlayer *player = [AVPlayer playerWithURL:contentURL];\n self.contentPlayerViewController = [[AVPlayerViewController alloc] init];\n self.contentPlayerViewController.player = player;\n self.contentPlayerViewController.view.frame = self.view.bounds;\n self.contentPlayhead =\n [[IMAAVPlayerContentPlayhead alloc] initWithAVPlayer:self.contentPlayerViewController.player];\n\n // Track end of content.\n AVPlayerItem *contentPlayerItem = self.contentPlayerViewController.player.currentItem;\n [[NSNotificationCenter defaultCenter] addObserver:self\n selector:@selector(contentDidFinishPlaying:)\n name:AVPlayerItemDidPlayToEndTimeNotification\n object:contentPlayerItem];\n\n // Attach content video player to view hierarchy.\n [self showContentPlayer];\n}ViewController.m\n```\n\nExample:\n```text\nfunc setUpContentPlayer() {\n // Load AVPlayer with path to our content.\n let contentURL = URL(string: ViewController.contentURLString)!\n let player = AVPlayer(url: contentURL)\n playerViewController = AVPlayerViewController()\n playerViewController.player = player\n\n // Set up our content playhead and contentComplete callback.\n contentPlayhead = IMAAVPlayerContentPlayhead(avPlayer: player)\n NotificationCenter.default.addObserver(\n self,\n selector: #selector(ViewController.contentDidFinishPlaying(_:)),\n name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,\n object: player.currentItem)\n\n showContentPlayer()\n}ViewController.swift\n```\n\nExample:\n```text\n- (void)contentDidFinishPlaying:(NSNotification *)notification {\n // Notify the SDK that the postrolls should be played.\n [self.adsLoader contentComplete];\n}\n\n- (void)dealloc {\n [[NSNotificationCenter defaultCenter] removeObserver:self];\n}ViewController.m\n```\n\nExample:\n```text\n@objc func contentDidFinishPlaying(_ notification: Notification) {\n adsLoader.contentComplete()\n}ViewController.swift\n```\n\nExample:\n```text\n- (void)setupAdsLoader {\n self.adsLoader = [[IMAAdsLoader alloc] init];\n self.adsLoader.delegate = self;\n}\n\n- (void)requestAds {\n // Pass the main view as the container for ad display.\n self.adDisplayContainer = [[IMAAdDisplayContainer alloc] initWithAdContainer:self.view\n viewController:self];\n IMAAdsRequest *request = [[IMAAdsRequest alloc] initWithAdTagUrl:kAdTagURLString\n adDisplayContainer:self.adDisplayContainer\n contentPlayhead:self.contentPlayhead\n userContext:nil];\n [self.adsLoader requestAdsWithRequest:request];\n}ViewController.m\n```\n\nExample:\n```text\nfunc setUpAdsLoader() {\n adsLoader = IMAAdsLoader(settings: nil)\n adsLoader.delegate = self\n}\n\nfunc requestAds() {\n // Create ad display container for ad rendering.\n adDisplayContainer = IMAAdDisplayContainer(adContainer: self.view, viewController: self)\n // Create an ad request with our ad tag, display container, and optional user context.\n let request = IMAAdsRequest(\n adTagUrl: ViewController.adTagURLString,\n adDisplayContainer: adDisplayContainer,\n contentPlayhead: contentPlayhead,\n userContext: nil)\n\n adsLoader.requestAds(with: request)\n}ViewController.swift\n```\n\nExample:\n```text\n#pragma mark - IMAAdsLoaderDelegate\n\n- (void)adsLoader:(IMAAdsLoader *)loader adsLoadedWithData:(IMAAdsLoadedData *)adsLoadedData {\n // Initialize and listen to the ads manager loaded for this request.\n self.adsManager = adsLoadedData.adsManager;\n self.adsManager.delegate = self;\n [self.adsManager initializeWithAdsRenderingSettings:nil];\n}\n\n- (void)adsLoader:(IMAAdsLoader *)loader failedWithErrorData:(IMAAdLoadingErrorData *)adErrorData {\n // Fall back to playing content.\n NSLog(@\"Error loading ads: %@\", adErrorData.adError.message);\n [self.contentPlayerViewController.player play];\n}ViewController.m\n```\n\nExample:\n```text\nfunc adsLoader(_ loader: IMAAdsLoader, adsLoadedWith adsLoadedData: IMAAdsLoadedData) {\n // Grab the instance of the IMAAdsManager and set ourselves as the delegate.\n adsManager = adsLoadedData.adsManager\n adsManager.delegate = self\n adsManager.initialize(with: nil)\n}\n\nfunc adsLoader(_ loader: IMAAdsLoader, failedWith adErrorData: IMAAdLoadingErrorData) {\n print(\"Error loading ads: \\(adErrorData.adError.message ?? \"No error message available.\")\")\n showContentPlayer()\n playerViewController.player?.play()\n}ViewController.swift\n```\n\nExample:\n```text\n#pragma mark - IMAAdsManagerDelegate\n\n- (void)adsManager:(IMAAdsManager *)adsManager didReceiveAdEvent:(IMAAdEvent *)event {\n switch (event.type) {\n case kIMAAdEvent_LOADED: {\n // Play each ad once it has loaded.\n [adsManager start];\n break;\n }\n case kIMAAdEvent_ICON_FALLBACK_IMAGE_CLOSED: {\n // Resume ad after user has closed dialog.\n [adsManager resume];\n break;\n }\n default:\n break;\n }\n}ViewController.m\n```\n\nExample:\n```text\nfunc adsManager(_ adsManager: IMAAdsManager, didReceive event: IMAAdEvent) {\n switch event.type {\n case IMAAdEventType.LOADED:\n // Play each ad once it has been loaded.\n adsManager.start()\n case IMAAdEventType.ICON_FALLBACK_IMAGE_CLOSED:\n // Resume playback after the user has closed the dialog.\n adsManager.resume()\n default:\n break\n }\n}ViewController.swift\n```\n\nExample:\n```text\n- (void)adsManager:(IMAAdsManager *)adsManager didReceiveAdError:(IMAAdError *)error {\n // Fall back to playing content.\n NSLog(@\"AdsManager error: %@\", error.message);\n [self showContentPlayer];\n [self.contentPlayerViewController.player play];\n}ViewController.m\n```\n\nExample:\n```text\nfunc adsManager(_ adsManager: IMAAdsManager, didReceive error: IMAAdError) {\n // Fall back to playing content\n print(\"AdsManager error: \\(error.message ?? \"No error message available.\")\")\n showContentPlayer()\n playerViewController.player?.play()\n}ViewController.swift\n```\n\nExample:\n```text\n- (void)adsManagerDidRequestContentPause:(IMAAdsManager *)adsManager {\n // Pause the content for the SDK to play ads.\n [self.contentPlayerViewController.player pause];\n [self hideContentPlayer];\n // Trigger an update to send focus to the ad display container.\n self.adBreakActive = YES;\n [self setNeedsFocusUpdate];\n}\n\n- (void)adsManagerDidRequestContentResume:(IMAAdsManager *)adsManager {\n // Resume the content since the SDK is done playing ads (at least for now).\n [self showContentPlayer];\n [self.contentPlayerViewController.player play];\n // Trigger an update to send focus to the content player.\n self.adBreakActive = NO;\n [self setNeedsFocusUpdate];\n}ViewController.m\n```\n\nExample:\n```text\nfunc adsManagerDidRequestContentPause(_ adsManager: IMAAdsManager) {\n // Pause the content for the SDK to play ads.\n playerViewController.player?.pause()\n hideContentPlayer()\n // Trigger an update to send focus to the ad display container.\n adBreakActive = true\n setNeedsFocusUpdate()\n}\n\nfunc adsManagerDidRequestContentResume(_ adsManager: IMAAdsManager) {\n // Resume the content since the SDK is done playing ads (at least for now).\n showContentPlayer()\n playerViewController.player?.play()\n // Trigger an update to send focus to the content player.\n adBreakActive = false\n setNeedsFocusUpdate()\n}ViewController.swift\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.187Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":20,"totalLines":359,"estimatedTokens":2835}}76{"id":"doc-set_up_the_ima_sdk_ima_sdk_for_html5_google_for_-9fc567f5","source":"documentation","title":"Set up the IMA SDK | IMA SDK for HTML5 | Google for Developers","url":"https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side","text":"Example:\n```text\npython -m http.server 8000\n```\n\nExample:\n```text\n<html>\n <head>\n <title>IMA HTML5 Simple Demo</title>\n <link rel=\"stylesheet\" href=\"style.css\">\n </head>\n\n <body>\n <div id=\"mainContainer\">\n <div id=\"content\">\n <video id=\"contentElement\">\n <source src=\"https://storage.googleapis.com/gvabox/media/samples/stock.mp4\"></source>\n </video>\n </div>\n <div id=\"adContainer\"></div>\n </div>\n <button id=\"playButton\">Play</button>\n <script src=\"//imasdk.googleapis.com/js/sdkloader/ima3.js\"></script>\n <script src=\"ads.js\"></script>\n </body>\n</html>\nindex.html\n```\n\nExample:\n```text\n#mainContainer {\n position: relative;\n width: 640px;\n height: 360px;\n}\n\n#content {\n position: absolute;\n top: 0;\n left: 0;\n width: 640px;\n height: 360px;\n}\n\n#contentElement {\n width: 640px;\n height: 360px;\n overflow: hidden;\n}\n\n#playButton {\n margin-top:10px;\n vertical-align: top;\n width: 350px;\n height: 60px;\n padding: 0;\n font-size: 22px;\n color: white;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);\n background: #2c3e50;\n border: 0;\n border-bottom: 2px solid #22303f;\n cursor: pointer;\n -webkit-box-shadow: inset 0 -2px #22303f;\n box-shadow: inset 0 -2px #22303f;\n}style.css\n```\n\nExample:\n```text\nlet adsManager;\nlet adsLoader;\nlet adDisplayContainer;\nlet isAdPlaying;\nlet isContentFinished;\nlet playButton;\nlet videoContent;\nlet adContainer;\n\n// On window load, attach an event to the play button click\n// that triggers playback of the video element.\nwindow.addEventListener('load', function(event) {\n videoContent = document.getElementById('contentElement');\n adContainer = document.getElementById('adContainer');\n adContainer.addEventListener('click', adContainerClick);\n playButton = document.getElementById('playButton');\n playButton.addEventListener('click', playAds);\n setUpIMA();\n});ads.js\n```\n\nExample:\n```text\n<script src=\"//imasdk.googleapis.com/js/sdkloader/ima3.js\"></script>index.html\n```\n\nExample:\n```text\n<div id=\"adContainer\"></div>index.html\n```\n\nExample:\n```text\n#adContainer {\n position: absolute;\n top: 0;\n left: 0;\n width: 640px;\n height: 360px;\n}style.css\n```\n\nExample:\n```text\n/**\n * Sets the 'adContainer' div as the IMA ad display container.\n */\nfunction createAdDisplayContainer() {\n adDisplayContainer = new google.ima.AdDisplayContainer(\n document.getElementById('adContainer'), videoContent);\n}ads.js\n```\n\nExample:\n```text\n/**\n * Sets up IMA ad display container, ads loader, and makes an ad request.\n */\nfunction setUpIMA() {\n // Create the ad display container.\n createAdDisplayContainer();\n // Create ads loader.\n adsLoader = new google.ima.AdsLoader(adDisplayContainer);\n // Listen and respond to ads loaded and error events.\n adsLoader.addEventListener(\n google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,\n onAdsManagerLoaded);\n adsLoader.addEventListener(\n google.ima.AdErrorEvent.Type.AD_ERROR, onAdError);\n\n // An event listener to tell the SDK that our content video\n // is completed so the SDK can play any post-roll ads.\n const contentEndedListener = function() {\n // An ad might have been playing in the content element, in which case the\n // content has not actually ended.\n if (isAdPlaying) return;\n isContentFinished = true;\n adsLoader.contentComplete();\n };\n videoContent.onended = contentEndedListener;\n\n // Request video ads.\n const adsRequest = new google.ima.AdsRequest();\n adsRequest.adTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?' +\n 'iu=/21775744923/external/single_ad_samples&sz=640x480&' +\n 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&gdfp_req=1&' +\n 'output=vast&unviewed_position_start=1&env=vp&correlator=';\n\n // Specify the linear and nonlinear slot sizes. This helps the SDK to\n // select the correct creative if multiple are returned.\n adsRequest.linearAdSlotWidth = 640;\n adsRequest.linearAdSlotHeight = 400;\n\n adsRequest.nonLinearAdSlotWidth = 640;\n adsRequest.nonLinearAdSlotHeight = 150;\n\n adsLoader.requestAds(adsRequest);\n}ads.js\n```\n\nExample:\n```text\n/**\n * Handles the ad manager loading and sets ad event listeners.\n * @param {!google.ima.AdsManagerLoadedEvent} adsManagerLoadedEvent\n */\nfunction onAdsManagerLoaded(adsManagerLoadedEvent) {\n // Get the ads manager.\n const adsRenderingSettings = new google.ima.AdsRenderingSettings();\n adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;\n // videoContent should be set to the content video element.\n adsManager =\n adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings);\n\n // Add listeners to the required events.\n adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError);\n adsManager.addEventListener(\n google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested);\n adsManager.addEventListener(\n google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,\n onContentResumeRequested);\n adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdLoaded);\n}\n\n/**\n * Handles ad errors.\n * @param {!google.ima.AdErrorEvent} adErrorEvent\n */\nfunction onAdError(adErrorEvent) {\n // Handle the error logging.\n console.log(adErrorEvent.getError());\n adsManager.destroy();\n}ads.js\n```\n\nExample:\n```text\nadsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError);ads.js\n```\n\nExample:\n```text\nadsManager.addEventListener(\n google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested);\nadsManager.addEventListener(\n google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,\n onContentResumeRequested);ads.js\n```\n\nExample:\n```text\n/**\n * Pauses video content and sets up ad UI.\n */\nfunction onContentPauseRequested() {\n isAdPlaying = true;\n videoContent.pause();\n // This function is where you should setup UI for showing ads (for example,\n // display ad timer countdown, disable seeking and more.)\n // setupUIForAds();\n}\n\n/**\n * Resumes video content and removes ad UI.\n */\nfunction onContentResumeRequested() {\n isAdPlaying = false;\n if (!isContentFinished) {\n videoContent.play();\n }\n // This function is where you should ensure that your UI is ready\n // to play content. It is the responsibility of the Publisher to\n // implement this function when necessary.\n // setupUIForContent();\n}ads.js\n```\n\nExample:\n```text\nadsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdLoaded);ads.js\n```\n\nExample:\n```text\n/**\n * Handles ad loaded event to support non-linear ads. Continues content playback\n * if the ad is not linear.\n * @param {!google.ima.AdEvent} adEvent\n */\nfunction onAdLoaded(adEvent) {\n let ad = adEvent.getAd();\n if (!ad.isLinear()) {\n videoContent.play();\n }\n}ads.js\n```\n\nExample:\n```text\n/**\n * Handles clicks on the ad container to support expected play and pause\n * behavior on mobile devices.\n * @param {!Event} event\n */\nfunction adContainerClick(event) {\n console.log(\"ad container clicked\");\n if(videoContent.paused) {\n videoContent.play();\n } else {\n videoContent.pause();\n }\n}ads.js\n```\n\nExample:\n```text\n/**\n * Loads the video content and initializes IMA ad playback.\n */\nfunction playAds() {\n // Initialize the container. Must be done through a user action on mobile\n // devices.\n videoContent.load();\n adDisplayContainer.initialize();\n\n try {\n // Initialize the ads manager. This call starts ad playback for VMAP ads.\n adsManager.init(640, 360);\n // Call play to start showing the ad. Single video and overlay ads will\n // start at this time; the call will be ignored for VMAP ads.\n adsManager.start();\n } catch (adError) {\n // An error may be thrown if there was a problem with the VAST response.\n videoContent.play();\n }\n}ads.js\n```\n\nExample:\n```text\nwindow.addEventListener('resize', function(event) {\n console.log(\"window resized\");\n if(adsManager) {\n let width = videoContent.clientWidth;\n let height = videoContent.clientHeight;\n adsManager.resize(width, height, google.ima.ViewMode.NORMAL);\n }\n});ads.js\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.188Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":324,"estimatedTokens":2023}}77{"id":"doc-set_up_the_ima_sdk_ima_sdk_for_android_google_fo-a1ebbb42","source":"documentation","title":"Set up the IMA SDK | IMA SDK for Android | Google for Developers","url":"https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side","text":"Example:\n```text\napply plugin: 'com.android.application'\n\nandroid {\n namespace = 'com.google.ads.interactivemedia.v3.samples.videoplayerapp'\n compileSdk = 36\n\n // Java 17 required by Gradle 8+\n compileOptions {\n // Required by IMA SDK v3.37.0+\n coreLibraryDesugaringEnabled = true\n\n // Java 17 required by Gradle 8+\n sourceCompatibility = JavaVersion.VERSION_17\n targetCompatibility = JavaVersion.VERSION_17\n }\n\n defaultConfig {\n applicationId = \"com.google.ads.interactivemedia.v3.samples.videoplayerapp\"\n minSdkVersion(23)\n targetSdkVersion(36)\n versionCode = 1\n versionName = \"1.0\"\n }\n buildTypes {\n release {\n minifyEnabled = true\n proguardFiles(getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro')\n }\n }\n}\n\nrepositories {\n google()\n mavenCentral()\n}\n\ndependencies {\n coreLibraryDesugaring('com.android.tools:desugar_jdk_libs:2.1.5')\n implementation(platform('org.jetbrains.kotlin:kotlin-bom:2.3.0'))\n implementation('androidx.appcompat:appcompat:1.7.1')\n implementation('androidx.browser:browser:1.9.0')\n implementation('androidx.media:media:1.7.1')\n implementation('com.google.ads.interactivemedia.v3:interactivemedia:3.39.0')\n}\nbuild.gradle\n```\n\nExample:\n```text\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tools=\"http://schemas.android.com/tools\"\n android:id=\"@+id/container\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\"\n android:orientation=\"vertical\"\n tools:context=\".MyActivity\"\n tools:ignore=\"MergeRootFrame\">\n\n <RelativeLayout\n android:background=\"#000000\"\n android:fitsSystemWindows=\"true\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"0dp\"\n android:layout_weight=\"0.4\"\n android:orientation=\"vertical\"\n android:id=\"@+id/videoPlayerContainer\" >\n\n <VideoView\n android:id=\"@+id/videoView\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\" />\n\n <ImageButton\n android:id=\"@+id/playButton\"\n android:contentDescription=\"@string/play_description\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\"\n android:src=\"@drawable/ic_action_play_over_video\"\n android:background=\"@null\" />\n\n </RelativeLayout>\n\n <FrameLayout\n android:layout_width=\"match_parent\"\n android:layout_height=\"0dp\"\n android:layout_weight=\"0.6\"\n android:id=\"@+id/videoDescription\" >\n\n <TextView\n android:id=\"@+id/playerDescription\"\n android:text=\"@string/app_name\"\n android:textAlignment=\"center\"\n android:gravity=\"center_horizontal\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\"\n android:paddingVertical=\"@dimen/font_size\"\n android:textSize=\"@dimen/font_size\" />\n </FrameLayout>\n\n</LinearLayout>\nactivity_my.xml\n```\n\nExample:\n```text\nimport android.content.Context;\nimport android.content.res.Configuration;\nimport android.media.AudioManager;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.MediaController;\nimport android.widget.VideoView;\nimport androidx.appcompat.app.AppCompatActivity;\nimport com.google.ads.interactivemedia.v3.api.AdDisplayContainer;\nimport com.google.ads.interactivemedia.v3.api.AdErrorEvent;\nimport com.google.ads.interactivemedia.v3.api.AdEvent;\nimport com.google.ads.interactivemedia.v3.api.AdsLoader;\nimport com.google.ads.interactivemedia.v3.api.AdsManager;\nimport com.google.ads.interactivemedia.v3.api.AdsRenderingSettings;\nimport com.google.ads.interactivemedia.v3.api.AdsRequest;\nimport com.google.ads.interactivemedia.v3.api.ImaSdkFactory;\nimport com.google.ads.interactivemedia.v3.api.ImaSdkSettings;\nimport com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;\nimport java.util.Arrays;\nMyActivity.java\n```\n\nExample:\n```text\n/** Main activity. */\npublic class MyActivity extends AppCompatActivity {\n\n private static final String LOGTAG = \"IMABasicSample\";\n private static final String SAMPLE_VIDEO_URL =\n \"https://storage.googleapis.com/gvabox/media/samples/stock.mp4\";\n\n /**\n * IMA sample tag for a single skippable inline video ad. See more IMA sample tags at\n * https://developers.google.com/interactive-media-ads/docs/sdks/html5/client-side/tags\n */\n private static final String SAMPLE_VAST_TAG_URL =\n \"https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/\"\n + \"single_preroll_skippable&sz=640x480&ciu_szs=300x250%2C728x90&gdfp_req=1&output=vast\"\n + \"&unviewed_position_start=1&env=vp&correlator=\";\n\n // Factory class for creating SDK objects.\n private ImaSdkFactory sdkFactory;\n\n // The AdsLoader instance exposes the requestAds method.\n private AdsLoader adsLoader;\n\n // AdsManager exposes methods to control ad playback and listen to ad events.\n private AdsManager adsManager;\n\n // The saved content position, used to resumed content following an ad break.\n private int savedPosition = 0;\n\n // This sample uses a VideoView for content and ad playback. For production\n // apps, Android's Exoplayer offers a more fully featured player compared to\n // the VideoView.\n private VideoView videoPlayer;\n private MediaController mediaController;\n private VideoAdPlayerAdapter videoAdPlayerAdapter;\n private ImaSdkSettings imaSdkSettings;\nMyActivity.java\n```\n\nExample:\n```text\nimport android.media.AudioManager;\nimport android.media.MediaPlayer;\nimport android.net.Uri;\nimport android.util.Log;\nimport android.widget.VideoView;\nimport com.google.ads.interactivemedia.v3.api.AdPodInfo;\nimport com.google.ads.interactivemedia.v3.api.player.AdMediaInfo;\nimport com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;\nimport com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Timer;\nimport java.util.TimerTask;\n\n/** Example implementation of IMA's VideoAdPlayer interface. */\npublic class VideoAdPlayerAdapter implements VideoAdPlayer {\n\n private static final String LOGTAG = \"IMABasicSample\";\n private static final long POLLING_TIME_MS = 250;\n private static final long INITIAL_DELAY_MS = 250;\n private final VideoView videoPlayer;\n private final AudioManager audioManager;\n private final List<VideoAdPlayerCallback> videoAdPlayerCallbacks = new ArrayList<>();\n private Timer timer;\n private int adDuration;\n\n // The saved ad position, used to resumed ad playback following an ad click-through.\n private int savedAdPosition;\n private AdMediaInfo loadedAdMediaInfo;\n\n public VideoAdPlayerAdapter(VideoView videoPlayer, AudioManager audioManager) {\n this.videoPlayer = videoPlayer;\n this.videoPlayer.setOnCompletionListener(\n (MediaPlayer mediaPlayer) -> notifyImaOnContentCompleted());\n this.audioManager = audioManager;\n }\nVideoAdPlayerAdapter.java\n```\n\nExample:\n```text\n@Override\npublic void addCallback(VideoAdPlayerCallback videoAdPlayerCallback) {\n videoAdPlayerCallbacks.add(videoAdPlayerCallback);\n}\n\n@Override\npublic void loadAd(AdMediaInfo adMediaInfo, AdPodInfo adPodInfo) {\n // This simple ad loading logic works because preloading is disabled. To support\n // preloading ads your app must maintain state for the currently playing ad\n // while handling upcoming ad downloading and buffering at the same time.\n // See the IMA Android preloading guide for more info:\n // https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/preload\n loadedAdMediaInfo = adMediaInfo;\n}\n\n@Override\npublic void pauseAd(AdMediaInfo adMediaInfo) {\n Log.i(LOGTAG, \"pauseAd\");\n savedAdPosition = videoPlayer.getCurrentPosition();\n stopAdTracking();\n}\n\n@Override\npublic void playAd(AdMediaInfo adMediaInfo) {\n videoPlayer.setVideoURI(Uri.parse(adMediaInfo.getUrl()));\n\n videoPlayer.setOnPreparedListener(\n mediaPlayer -> {\n adDuration = mediaPlayer.getDuration();\n if (savedAdPosition > 0) {\n mediaPlayer.seekTo(savedAdPosition);\n }\n mediaPlayer.start();\n startAdTracking();\n });\n videoPlayer.setOnErrorListener(\n (mediaPlayer, errorType, extra) -> notifyImaSdkAboutAdError(errorType));\n videoPlayer.setOnCompletionListener(\n mediaPlayer -> {\n savedAdPosition = 0;\n notifyImaSdkAboutAdEnded();\n });\n}\n\n@Override\npublic void release() {\n // any clean up that needs to be done.\n}\n\n@Override\npublic void removeCallback(VideoAdPlayerCallback videoAdPlayerCallback) {\n videoAdPlayerCallbacks.remove(videoAdPlayerCallback);\n}\n\n@Override\npublic void stopAd(AdMediaInfo adMediaInfo) {\n Log.i(LOGTAG, \"stopAd\");\n stopAdTracking();\n}\n\n/** Returns current volume as a percent of max volume. */\n@Override\npublic int getVolume() {\n return audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)\n / audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);\n}\nVideoAdPlayerAdapter.java\n```\n\nExample:\n```text\nprivate void startAdTracking() {\n Log.i(LOGTAG, \"startAdTracking\");\n if (timer != null) {\n return;\n }\n timer = new Timer();\n TimerTask updateTimerTask =\n new TimerTask() {\n @Override\n public void run() {\n VideoProgressUpdate progressUpdate = getAdProgress();\n notifyImaSdkAboutAdProgress(progressUpdate);\n }\n };\n timer.schedule(updateTimerTask, POLLING_TIME_MS, INITIAL_DELAY_MS);\n}\n\nprivate void notifyImaSdkAboutAdEnded() {\n Log.i(LOGTAG, \"notifyImaSdkAboutAdEnded\");\n savedAdPosition = 0;\n for (VideoAdPlayer.VideoAdPlayerCallback callback : videoAdPlayerCallbacks) {\n callback.onEnded(loadedAdMediaInfo);\n }\n}\n\nprivate void notifyImaSdkAboutAdProgress(VideoProgressUpdate adProgress) {\n for (VideoAdPlayer.VideoAdPlayerCallback callback : videoAdPlayerCallbacks) {\n callback.onAdProgress(loadedAdMediaInfo, adProgress);\n }\n}\n\n/**\n * @param errorType Media player's error type as defined at\n * https://cs.android.com/android/platform/superproject/+/master:frameworks/base/media/java/android/media/MediaPlayer.java;l=4335\n * @return True to stop the current ad playback.\n */\nprivate boolean notifyImaSdkAboutAdError(int errorType) {\n Log.i(LOGTAG, \"notifyImaSdkAboutAdError\");\n\n switch (errorType) {\n case MediaPlayer.MEDIA_ERROR_UNSUPPORTED ->\n Log.e(LOGTAG, \"notifyImaSdkAboutAdError: MEDIA_ERROR_UNSUPPORTED\");\n case MediaPlayer.MEDIA_ERROR_TIMED_OUT ->\n Log.e(LOGTAG, \"notifyImaSdkAboutAdError: MEDIA_ERROR_TIMED_OUT\");\n default -> {}\n }\n for (VideoAdPlayer.VideoAdPlayerCallback callback : videoAdPlayerCallbacks) {\n callback.onError(loadedAdMediaInfo);\n }\n return true;\n}\n\npublic void notifyImaOnContentCompleted() {\n Log.i(LOGTAG, \"notifyImaOnContentCompleted\");\n for (VideoAdPlayer.VideoAdPlayerCallback callback : videoAdPlayerCallbacks) {\n callback.onContentComplete();\n }\n}\n\nprivate void stopAdTracking() {\n Log.i(LOGTAG, \"stopAdTracking\");\n if (timer != null) {\n timer.cancel();\n timer = null;\n }\n}\n\n@Override\npublic VideoProgressUpdate getAdProgress() {\n long adPosition = videoPlayer.getCurrentPosition();\n return new VideoProgressUpdate(adPosition, adDuration);\n}VideoAdPlayerAdapter.java\n```\n\nExample:\n```text\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_my);\n\n // Initialize the IMA SDK as early as possible when the app starts. If your app already\n // overrides Application.onCreate(), call this method inside the onCreate() method.\n // https://developer.android.com/topic/performance/vitals/launch-time#app-creation\n sdkFactory = ImaSdkFactory.getInstance();\n sdkFactory.initialize(this, getImaSdkSettings());\n\n // Create the UI for controlling the video view.\n mediaController = new MediaController(this);\n videoPlayer = findViewById(R.id.videoView);\n mediaController.setAnchorView(videoPlayer);\n videoPlayer.setMediaController(mediaController);\n\n // Create an ad display container that uses a ViewGroup to listen to taps.\n AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);\n videoAdPlayerAdapter = new VideoAdPlayerAdapter(videoPlayer, audioManager);\n\n AdDisplayContainer adDisplayContainer =\n ImaSdkFactory.createAdDisplayContainer(\n findViewById(R.id.videoPlayerContainer), videoAdPlayerAdapter);\n\n // Create an AdsLoader.\n adsLoader = sdkFactory.createAdsLoader(this, getImaSdkSettings(), adDisplayContainer);MyActivity.java\n```\n\nExample:\n```text\n// When the play button is clicked, request ads and hide the button.\nView playButton = findViewById(R.id.playButton);\nplayButton.setOnClickListener(\n view -> {\n videoPlayer.setVideoPath(SAMPLE_VIDEO_URL);\n requestAds(SAMPLE_VAST_TAG_URL);\n view.setVisibility(View.GONE);\n });MyActivity.java\n```\n\nExample:\n```text\nprivate ImaSdkSettings getImaSdkSettings() {\n if (imaSdkSettings == null) {\n imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();\n // Set any IMA SDK settings here.\n }\n return imaSdkSettings;\n}MyActivity.java\n```\n\nExample:\n```text\n// Add listeners for when ads are loaded and for errors.\nadsLoader.addAdErrorListener(\n new AdErrorEvent.AdErrorListener() {\n /** An event raised when there is an error loading or playing ads. */\n @Override\n public void onAdError(AdErrorEvent adErrorEvent) {\n Log.i(LOGTAG, \"Ad Error: \" + adErrorEvent.getError().getMessage());\n resumeContent();\n }\n });\nadsLoader.addAdsLoadedListener(\n adsManagerLoadedEvent -> {\n // Ads were successfully loaded, so get the AdsManager instance. AdsManager has\n // events for ad playback and errors.\n adsManager = adsManagerLoadedEvent.getAdsManager();\n\n // Attach event and error event listeners.\n adsManager.addAdErrorListener(\n new AdErrorEvent.AdErrorListener() {\n /** An event raised when there is an error loading or playing ads. */\n @Override\n public void onAdError(AdErrorEvent adErrorEvent) {\n Log.e(LOGTAG, \"Ad Error: \" + adErrorEvent.getError().getMessage());\n String universalAdIds =\n Arrays.toString(adsManager.getCurrentAd().getUniversalAdIds());\n Log.i(\n LOGTAG,\n \"Discarding the current ad break with universal \"\n + \"ad Ids: \"\n + universalAdIds);\n adsManager.discardAdBreak();\n }\n });MyActivity.java\n```\n\nExample:\n```text\nadsManager.addAdEventListener(\n new AdEvent.AdEventListener() {\n /** Responds to AdEvents. */\n @Override\n public void onAdEvent(AdEvent adEvent) {\n if (adEvent.getType() != AdEvent.AdEventType.AD_PROGRESS) {\n Log.i(LOGTAG, \"Event: \" + adEvent.getType());\n }\n // These are the suggested event types to handle. For full list of\n // all ad event types, see AdEvent.AdEventType documentation.\n switch (adEvent.getType()) {\n case LOADED ->\n // AdEventType.LOADED is fired when ads are ready to play.\n // This sample app uses the sample tag\n // single_preroll_skippable_ad_tag_url that requires calling\n // AdsManager.start() to start ad playback.\n // If you use a different ad tag URL that returns a VMAP or\n // an ad rules playlist, the adsManager.init() function will\n // trigger ad playback automatically and the IMA SDK will\n // ignore the adsManager.start().\n // It is safe to always call adsManager.start() in the\n // LOADED event.\n adsManager.start();\n case CONTENT_PAUSE_REQUESTED ->\n // AdEventType.CONTENT_PAUSE_REQUESTED is fired when you\n // should pause your content and start playing an ad.\n pauseContentForAds();\n case CONTENT_RESUME_REQUESTED ->\n // AdEventType.CONTENT_RESUME_REQUESTED is fired when the ad\n // you should play your content.\n resumeContent();\n case ALL_ADS_COMPLETED -> {\n // Calling adsManager.destroy() triggers the function\n // VideoAdPlayer.release().\n adsManager.destroy();\n adsManager = null;\n }\n case CLICKED -> {\n // When the user clicks on the Learn More button, the IMA SDK fires\n // this event, pauses the ad, and opens the ad's click-through URL.\n // When the user returns to the app, the IMA SDK calls the\n // VideoAdPlayer.playAd() function automatically.\n }\n default -> {}\n }\n }\n });\n AdsRenderingSettings adsRenderingSettings =\n ImaSdkFactory.getInstance().createAdsRenderingSettings();\n // Add any ads rendering settings here.\n // This init() only loads the UI rendering settings locally.\n adsManager.init(adsRenderingSettings);\n});MyActivity.java\n```\n\nExample:\n```text\nprivate void pauseContentForAds() {\n Log.i(LOGTAG, \"pauseContentForAds\");\n savedPosition = videoPlayer.getCurrentPosition();\n videoPlayer.stopPlayback();\n // Hide the buttons and seek bar controlling the video view.\n videoPlayer.setMediaController(null);\n}\n\nprivate void resumeContent() {\n Log.i(LOGTAG, \"resumeContent\");\n\n // Show the buttons and seek bar controlling the video view.\n videoPlayer.setVideoPath(SAMPLE_VIDEO_URL);\n videoPlayer.setMediaController(mediaController);\n videoPlayer.setOnPreparedListener(\n mediaPlayer -> {\n if (savedPosition > 0) {\n mediaPlayer.seekTo(savedPosition);\n }\n mediaPlayer.start();\n });\n videoPlayer.setOnCompletionListener(\n mediaPlayer -> videoAdPlayerAdapter.notifyImaOnContentCompleted());\n}\nMyActivity.java\n```\n\nExample:\n```text\nprivate void requestAds(String adTagUrl) {\n // Create the ads request.\n AdsRequest request = sdkFactory.createAdsRequest();\n request.setAdTagUrl(adTagUrl);\n request.setContentProgressProvider(\n () -> {\n if (videoPlayer.getDuration() <= 0) {\n return VideoProgressUpdate.VIDEO_TIME_NOT_READY;\n }\n return new VideoProgressUpdate(\n videoPlayer.getCurrentPosition(), videoPlayer.getDuration());\n });\n\n // Request the ad. After the ad is loaded, onAdsManagerLoaded() will be called.\n adsLoader.requestAds(request);\n}\nMyActivity.java\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.190Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":557,"estimatedTokens":4704}}78{"id":"doc-versioning_google_ads_api_google_for_developers-a155297c","source":"documentation","title":"Versioning | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/concepts/versioning","text":"Example:\n```text\nhttps://googleads.googleapis.com/vX\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.199Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":18}}79{"id":"doc-understand_the_google_ads_access_model_google_ad-d749cce1","source":"documentation","title":"Understand the Google Ads Access Model | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/oauth/access-model","text":"Example:\n```text\napi.googleads.loginCustomerId=INSERT_LOGIN_CUSTOMER_ID_HERE\n```\n\nExample:\n```text\nGoogleAdsConfig config = new GoogleAdsConfig()\n{\n ...\n LoginCustomerId = ******\n};\nGoogleAdsClient client = new GoogleAdsClient(config);\n```\n\nExample:\n```text\n[GOOGLE_ADS]\nloginCustomerId = \"INSERT_LOGIN_CUSTOMER_ID_HERE\"\n```\n\nExample:\n```text\nlogin_customer_id: INSERT_LOGIN_CUSTOMER_ID_HERE\n```\n\nExample:\n```text\nGoogle::Ads::GoogleAds::Config.new do |c|\n c.login_customer_id = 'INSERT_LOGIN_CUSTOMER_ID_HERE'\nend\n```\n\nExample:\n```text\nclient = Google::Ads::GoogleAds::GoogleAdsClient.new('path/to/google_ads_config.rb')\n```\n\nExample:\n```text\nloginCustomerId=INSERT_LOGIN_CUSTOMER_ID_HERE\n```\n\nExample:\n```text\n-H \"login-customer-id: LOGIN_CUSTOMER_ID\"\n```\n\nExample:\n```text\nprivate void runExample(GoogleAdsClient client) {\n // Optional: Change credentials to use a different refresh token, to retrieve customers\n // available for a specific user.\n //\n // UserCredentials credentials =\n // UserCredentials.newBuilder()\n // .setClientId(\"INSERT_OAUTH_CLIENT_ID\")\n // .setClientSecret(\"INSERT_OAUTH_CLIENT_SECRET\")\n // .setRefreshToken(\"INSERT_REFRESH_TOKEN\")\n // .build();\n //\n // client = client.toBuilder().setCredentials(credentials).build();\n\n try (CustomerServiceClient customerService =\n client.getLatestVersion().createCustomerServiceClient()) {\n ListAccessibleCustomersResponse response =\n customerService.listAccessibleCustomers(\n ListAccessibleCustomersRequest.newBuilder().build());\n\n System.out.printf(\"Total results: %d%n\", response.getResourceNamesCount());\n\n for (String customerResourceName : response.getResourceNamesList()) {\n System.out.printf(\"Customer resource name: %s%n\", customerResourceName);\n }\n }\n}ListAccessibleCustomers.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client)\n{\n // Get the CustomerService.\n CustomerServiceClient customerService = client.GetService(Services.V25.CustomerService);\n\n try\n {\n // Retrieve the list of customer resources.\n string[] customerResourceNames = customerService.ListAccessibleCustomers();\n\n // Display the result.\n foreach (string customerResourceName in customerResourceNames)\n {\n Console.WriteLine(\n $\"Found customer with resource name = '{customerResourceName}'.\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}ListAccessibleCustomers.cs\n```\n\nExample:\n```text\npublic static function runExample(GoogleAdsClient $googleAdsClient)\n{\n $customerServiceClient = $googleAdsClient->getCustomerServiceClient();\n\n // Issues a request for listing all accessible customers.\n $accessibleCustomers =\n $customerServiceClient->listAccessibleCustomers(new ListAccessibleCustomersRequest());\n print 'Total results: ' . count($accessibleCustomers->getResourceNames()) . PHP_EOL;\n\n // Iterates over all accessible customers' resource names and prints them.\n foreach ($accessibleCustomers->getResourceNames() as $resourceName) {\n /** @var string $resourceName */\n printf(\"Customer resource name: '%s'%s\", $resourceName, PHP_EOL);\n }\n}ListAccessibleCustomers.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient) -> None:\n customer_service: CustomerServiceClient = client.get_service(\n \"CustomerService\"\n )\n\n accessible_customers: ListAccessibleCustomersResponse = (\n customer_service.list_accessible_customers()\n )\n result_total: int = len(accessible_customers.resource_names)\n print(f\"Total results: {result_total}\")\n\n resource_names: List[str] = accessible_customers.resource_names\n for resource_name in resource_names: # resource_name is implicitly str\n print(f'Customer resource name: \"{resource_name}\"')list_accessible_customers.py\n```\n\nExample:\n```text\ndef list_accessible_customers()\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n accessible_customers = client.service.customer.list_accessible_customers().resource_names\n\n accessible_customers.each do |resource_name|\n puts \"Customer resource name: #{resource_name}\"\n end\nendlist_accessible_customers.rb\n```\n\nExample:\n```text\nsub list_accessible_customers {\n my ($api_client) = @_;\n\n my $list_accessible_customers_response =\n $api_client->CustomerService()->list_accessible_customers();\n\n printf \"Total results: %d.\\n\",\n scalar @{$list_accessible_customers_response->{resourceNames}};\n\n foreach\n my $resource_name (@{$list_accessible_customers_response->{resourceNames}})\n {\n printf \"Customer resource name: '%s'.\\n\", $resource_name;\n }\n\n return 1;\n}list_accessible_customers.pl\n```\n\nExample:\n```text\n# Returns the resource names of customers directly accessible by the user\n# authenticating the call.\n#\n# Variables:\n# API_VERSION,\n# DEVELOPER_TOKEN,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n#\ncurl -f --request GET \\\n\"https://googleads.googleapis.com/v${API_VERSION}/customers:listAccessibleCustomers\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\list_accessible_customers.sh\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.200Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":201,"estimatedTokens":1417}}80{"id":"doc-single_user_authentication_workflow_google_ads_a-a3a40526","source":"documentation","title":"Single User Authentication Workflow | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/oauth/single-user-authentication","text":"Example:\n```text\n:~$ gcloud version\nGoogle Cloud SDK 492.0.0\nalpha 2024.09.06\nbeta 2024.09.06\nbq 2.1.8\nbundled-python3-unix 3.11.9\ncore 2024.09.06\nenterprise-certificate-proxy 0.3.2\ngcloud-crc32c 1.0.0\ngsutil 5.30\n```\n\nExample:\n```text\ngcloud auth application-default \n login --scopes=https://www.googleapis.com/auth/adwords,https://www.googleapis.com/auth/cloud-platform \n --client-id-file=<path_to_credentials.json>\n```\n\nExample:\n```text\n{\n \"account\": \"\",\n \"client_id\": \"******.apps.googleusercontent.com\",\n \"client_secret\": \"******\",\n \"refresh_token\": \"******\",\n \"type\": \"authorized_user\",\n \"universe_domain\": \"googleapis.com\"\n}\n```\n\nExample:\n```text\napi.googleads.clientId=INSERT_CLIENT_ID_HERE\napi.googleads.clientSecret=INSERT_CLIENT_SECRET_HERE\napi.googleads.refreshToken=INSERT_REFRESH_TOKEN_HERE\napi.googleads.loginCustomerId=INSERT_LOGIN_CUSTOMER_ID_HERE\n```\n\nExample:\n```text\nGoogleAdsConfig config = new GoogleAdsConfig()\n{\n OAuth2Mode = OAuth2Flow.APPLICATION,\n OAuth2ClientId = \"INSERT_OAUTH2_CLIENT_ID\",\n OAuth2ClientSecret = \"INSERT_OAUTH2_CLIENT_SECRET\",\n OAuth2RefreshToken = \"INSERT_OAUTH2_REFRESH_TOKEN\",\n ...\n};\nGoogleAdsClient client = new GoogleAdsClient(config);\n```\n\nExample:\n```text\nclient_id: INSERT_OAUTH2_CLIENT_ID_HERE\nclient_secret: INSERT_OAUTH2_CLIENT_SECRET_HERE\nrefresh_token: INSERT_REFRESH_TOKEN_HERE\nlogin_customer_id: INSERT_LOGIN_CUSTOMER_ID_HERE\n```\n\nExample:\n```text\n[GOOGLE_ADS]\nloginCustomerId = \"INSERT_LOGIN_CUSTOMER_ID_HERE\"\n\n[OAUTH2]\nclientId = \"INSERT_OAUTH2_CLIENT_ID_HERE\"\nclientSecret = \"INSERT_OAUTH2_CLIENT_SECRET_HERE\"\nrefreshToken = \"INSERT_OAUTH2_REFRESH_TOKEN_HERE\"\n```\n\nExample:\n```text\nGoogle::Ads::GoogleAds::Config.new do |c|\n c.client_id = 'INSERT_CLIENT_ID_HERE'\n c.client_secret = 'INSERT_CLIENT_SECRET_HERE'\n c.refresh_token = 'INSERT_REFRESH_TOKEN_HERE'\n c.developer_token = 'INSERT_DEVELOPER_TOKEN_HERE'\n c.login_customer_id = 'INSERT_LOGIN_CUSTOMER_ID_HERE'\nend\n```\n\nExample:\n```text\nclientId=INSERT_OAUTH2_CLIENT_ID_HERE\nclientSecret=INSERT_OAUTH2_CLIENT_SECRET_HERE\nrefreshToken=INSERT_OAUTH2_REFRESH_TOKEN_HERE\nloginCustomerId=INSERT_LOGIN_CUSTOMER_ID_HERE\n```\n\nExample:\n```text\ncurl \\\n--data \"grant_type=refresh_token\" \\\n--data \"client_id=CLIENT_ID\" \\\n--data \"client_secret=CLIENT_SECRET\" \\\n--data \"refresh_token=REFRESH_TOKEN\" \\\nhttps://www.googleapis.com/oauth2/v3/token\n```\n\nExample:\n```text\ncurl -i -X POST https://googleads.googleapis.com/v25/customers/CUSTOMER_ID/googleAds:searchStream \\\n-H \"Content-Type: application/json\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\" \\\n-H \"developer-token: DEVELOPER_TOKEN\" \\\n-H \"login-customer-id: LOGIN_CUSTOMER_ID\" \\\n--data-binary \"@query.json\"\n```\n\nExample:\n```text\n{\n \"query\": \"SELECT campaign.id, campaign.name, campaign.network_settings.target_content_network FROM campaign ORDER BY campaign.id\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.202Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":120,"estimatedTokens":722}}81{"id":"doc-multi_party_approvals_mpa_google_ads_api_google_-6bbdd5a0","source":"documentation","title":"Multi-party approvals (MPA) | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/oauth/multi-party-approvals","text":"Example:\n```text\nSELECT\n multi_party_auth_review.resource_name,\n multi_party_auth_review.multi_party_auth_review_id,\n multi_party_auth_review.creation_date_time,\n multi_party_auth_review.request_user_email,\n multi_party_auth_review.operation_type,\n multi_party_auth_review.justification,\n multi_party_auth_review.target_resource,\n multi_party_auth_review.customer_user_access_review.old_customer_user_access,\n multi_party_auth_review.customer_user_access_review.new_customer_user_access,\n multi_party_auth_review.customer_user_access_invitation_review.new_customer_user_access_invitation\nFROM multi_party_auth_review\nWHERE multi_party_auth_review.review_status = 'PENDING'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.203Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":18,"estimatedTokens":175}}82{"id":"doc-retrieving_objects_google_ads_api_google_for_dev-122f92b5","source":"documentation","title":"Retrieving objects | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/concepts/retrieving-objects","text":"Example:\n```text\nSELECT campaign.status, metrics.impressions\nFROM campaign\nWHERE segments.date DURING LAST_14_DAYS\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.204Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":8,"estimatedTokens":33}}83{"id":"doc-api_structure_google_ads_api_google_for_develope-8beea5f6","source":"documentation","title":"API Structure | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/concepts/api-structure","text":"Example:\n```text\ncustomers/customer_id/campaigns/campaign_id\n```\n\nExample:\n```text\ncustomers/1234567/campaigns/987654\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.206Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":34}}84{"id":"doc-oauth_2_0_internals_for_google_ads_api_google_fo-01fbc02f","source":"documentation","title":"OAuth 2.0 Internals for Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/oauth/internals","text":"Example:\n```text\nhttps://www.googleapis.com/auth/adwords\n```\n\nExample:\n```text\n# Returns the resource names of customers directly accessible by the user\n# authenticating the call.\n#\n# Variables:\n# API_VERSION,\n# DEVELOPER_TOKEN,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n#\ncurl -f --request GET \\\n\"https://googleads.googleapis.com/v${API_VERSION}/customers:listAccessibleCustomers\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.207Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":25,"estimatedTokens":160}}85{"id":"doc-api_call_structure_google_ads_api_google_for_dev-239fca02","source":"documentation","title":"API Call Structure | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/concepts/call-structure","text":"Example:\n```text\nhttps://googleads.googleapis.com/v25/customers/1234567890/campaignBudgets:mutate\n```\n\nExample:\n```text\nAuthorization: Bearer YOUR_ACCESS_TOKEN\ndeveloper-token: YOUR_DEVELOPER_TOKEN\nlogin-customer-id: 2222222222\nlinked-customer-id: 1111111111\n```\n\nExample:\n```text\nAuthorization: Bearer YOUR_ACCESS_TOKEN\ndeveloper-token: YOUR_DEVELOPER_TOKEN\nlogin-customer-id: 2222222222\nlinked-customer-id: 3333333333\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.208Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":22,"estimatedTokens":110}}86{"id":"doc-service_account_workflow_google_ads_api_google_f-463e33a8","source":"documentation","title":"Service Account Workflow | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/oauth/service-accounts","text":"Example:\n```text\napi.googleads.serviceAccountSecretsPath=JSON_KEY_FILE_PATH\n```\n\nExample:\n```text\nGoogleAdsConfig config = new GoogleAdsConfig()\n{\n OAuth2Mode = OAuth2Flow.SERVICE_ACCOUNT,\n OAuth2SecretsJsonPath = \"PATH_TO_JSON_SECRETS_PATH\",\n ...\n};\nGoogleAdsClient client = new GoogleAdsClient(config);\n```\n\nExample:\n```text\njson_key_file_path: JSON_KEY_FILE_PATH\n```\n\nExample:\n```text\nexport GOOGLE_ADS_JSON_KEY_FILE_PATH=JSON_KEY_FILE_PATH\n```\n\nExample:\n```text\n; For service account flow.\njsonKeyFilePath = \"JSON_KEY_FILE_PATH\"\nscopes = \"https://www.googleapis.com/auth/adwords\"\n```\n\nExample:\n```text\nc.keyfile = 'JSON_KEY_FILE_PATH'\n```\n\nExample:\n```text\njsonKeyFilePath=JSON_KEY_FILE_PATH\n```\n\nExample:\n```text\ngcloud auth login --cred-file=PATH_TO_CREDENTIALS_JSON\n```\n\nExample:\n```text\ngcloud auth \\\n print-access-token \\\n --scopes='https://www.googleapis.com/auth/adwords'\n```\n\nExample:\n```text\ncurl -i -X POST https://googleads.googleapis.com/v25/customers/CUSTOMER_ID/googleAds:searchStream \\\n -H \"Content-Type: application/json\" \\\n -H \"Authorization: Bearer ACCESS_TOKEN\" \\\n -H \"developer-token: DEVELOPER_TOKEN\" \\\n -H \"login-customer-id: LOGIN_CUSTOMER_ID\" \\\n --data-binary \"@query.json\"\n```\n\nExample:\n```text\n{\n \"query\": \"SELECT campaign.id, campaign.name, campaign.network_settings.target_content_network FROM campaign ORDER BY campaign.id\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.209Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":73,"estimatedTokens":350}}87{"id":"doc-multi_user_authentication_workflow_google_ads_ap-4c5fb304","source":"documentation","title":"Multi-user authentication workflow | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/oauth/multi-user-authentication","text":"Example:\n```text\nhttps://www.googleapis.com/auth/adwords\n```\n\nExample:\n```text\nUserCredentials credentials =\n UserCredentials.newBuilder()\n .setClientId(OAUTH_CLIENT_ID)\n .setClientSecret(OAUTH_CLIENT_SECRET)\n .setRefreshToken(REFRESH_TOKEN)\n .build();\n\n// Creates a GoogleAdsClient with the provided credentials.\nGoogleAdsClient client =\n GoogleAdsClient.newBuilder()\n // Sets the developer token which enables API access.\n .setDeveloperToken(DEVELOPER_TOKEN)\n // Sets the OAuth credentials which provide Google Ads account access.\n .setCredentials(credentials)\n // Optional: sets the login customer ID.\n .setLoginCustomerId(Long.valueOf(LOGIN_CUSTOMER_ID))\n .build();\n```\n\nExample:\n```text\nGoogleAdsConfig googleAdsConfig = new GoogleAdsConfig()\n{\n DeveloperToken = DEVELOPER_TOKEN,\n LoginCustomerId = LOGIN_CUSTOMER_ID,\n OAuth2ClientId = OAUTH_CLIENT_ID,\n OAuth2ClientSecret = OAUTH_CLIENT_SECRET,\n OAuth2RefreshToken = REFRESH_TOKEN,\n};\n\nGoogleAdsClient googleAdsClient = new GoogleAdsClient(googleAdsConfig);\n```\n\nExample:\n```text\nfrom google.ads.googleads.client import GoogleAdsClient\n\ncredentials = {\n \"developer_token\": \"INSERT_DEVELOPER_TOKEN_HERE\",\n \"login_customer_id\": \"INSERT_LOGIN_CUSTOMER_ID_HERE\",\n \"refresh_token\": \"REFRESH_TOKEN\",\n \"client_id\": \"OAUTH_CLIENT_ID\",\n \"client_secret\": \"OAUTH_CLIENT_SECRET\"}\n\nclient = GoogleAdsClient.load_from_dict(credentials)\n```\n\nExample:\n```text\n$oAuth2Credential = (new OAuth2TokenBuilder())\n ->withClientId('INSERT_CLIENT_ID_HERE')\n ->withClientSecret('INSERT_CLIENT_SECRET_HERE')\n ->withRefreshToken('INSERT_REFRESH_TOKEN_HERE')\n ->build();\n\n$googleAdsClient = (new GoogleAdsClientBuilder())\n ->withOAuth2Credential($oAuth2Credential)\n ->withDeveloperToken('INSERT_DEVELOPER_TOKEN_HERE')\n ->withLoginCustomerId('INSERT_LOGIN_CUSTOMER_ID_HERE')\n ->build();\n```\n\nExample:\n```text\nclient = Google::Ads::GoogleAds::GoogleAdsClient.new do |config|\n config.client_id = 'INSERT_CLIENT_ID_HERE'\n config.client_secret = 'INSERT_CLIENT_SECRET_HERE'\n config.refresh_token = 'INSERT_REFRESH_TOKEN_HERE'\n config.login_customer_id = 'INSERT_LOGIN_CUSTOMER_ID_HERE'\n config.developer_token = 'INSERT_DEVELOPER_TOKEN_HERE'\nend\n```\n\nExample:\n```text\nmy $api_client = Google::Ads::GoogleAds::Client->new({\n developer_token => \"INSERT_DEVELOPER_TOKEN_HERE\",\n login_customer_id => \"INSERT_LOGIN_CUSTOMER_ID_HERE\"\n});\n\nmy $oauth2_applications_handler = $api_client->get_oauth2_applications_handler();\n$oauth2_applications_handler->set_client_id(\"INSERT_CLIENT_ID\");\n$oauth2_applications_handler->set_client_secret(\"INSERT_CLIENT_SECRET\");\n$oauth2_applications_handler->set_refresh_token(\"INSERT_REFRESH_TOKEN\");\n```\n\nExample:\n```text\ncurl \\\n --data \"grant_type=refresh_token\" \\\n --data \"client_id=CLIENT_ID\" \\\n --data \"client_secret=CLIENT_SECRET\" \\\n --data \"refresh_token=REFRESH_TOKEN\" \\\n https://www.googleapis.com/oauth2/v3/token\n```\n\nExample:\n```text\ncurl -i -X POST https://googleads.googleapis.com/v25/customers/CUSTOMER_ID/googleAds:searchStream \\\n -H \"Content-Type: application/json\" \\\n -H \"Authorization: Bearer ACCESS_TOKEN\" \\\n -H \"developer-token: DEVELOPER_TOKEN\" \\\n -H \"login-customer-id: LOGIN_CUSTOMER_ID\" \\\n --data-binary \"@query.json\"\n```\n\nExample:\n```text\n{\n \"query\": \"SELECT campaign.id, campaign.name, campaign.network_settings.target_content_network FROM campaign ORDER BY campaign.id\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.210Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":121,"estimatedTokens":882}}88{"id":"doc-bulk_mutates_google_ads_api_google_for_developer-1fc02bb4","source":"documentation","title":"Bulk Mutates | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/mutating/bulk-mutate","text":"Example:\n```text\nmutate_operation1 = client.operation(:Mutate)\nmutate_operation2 = client.operation(:Mutate)\n\ncampaign_operation = client.operation(:Campaign)\nad_group_operation = client.operation(:AdGroup)\n\n# Do some setup here to get campaign_operation and ad_group_operation into the\n# state you would want them for a regular mutate call to their respective\n# services.\n\nmutate_operation1.campaign_operation = campaign_operation\nmutate_operation2.ad_group_operation = ad_group_operation\n\ngoogle_ads_service.mutate(customer_id, [mutate_operation1, mutate_operation2])\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.211Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":19,"estimatedTokens":147}}89{"id":"doc-resource_metadata_google_ads_api_google_for_deve-951f0f8a","source":"documentation","title":"Resource Metadata | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/concepts/field-service","text":"Example:\n```text\nhttps://googleads.googleapis.com/v25/googleAdsFields/{resource_or_field}\n```\n\nExample:\n```text\nhttps://googleads.googleapis.com/v25/googleAdsFields/ad_group\n```\n\nExample:\n```text\n{\n \"resourceName\": \"googleAdsFields/ad_group\",\n \"name\": \"ad_group\",\n \"category\": \"RESOURCE\",\n \"selectable\": false,\n \"filterable\": false,\n \"sortable\": false,\n \"selectableWith\": [\n \"campaign\",\n \"customer\",\n \"metrics.average_cpc\",\n \"segments.device\",\n ...\n ],\n \"attributeResources\": [\n \"customer\",\n \"campaign\"\n ],\n\n \"metrics\": [\n \"metrics.conversions\",\n \"metrics.search_budget_lost_impression_share\",\n \"metrics.average_cost\",\n \"metrics.clicks\",\n ...\n ],\n \"segments\": [\n \"segments.date\",\n \"segments.ad_network_type\",\n \"segments.device\",\n ...\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.211Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":48,"estimatedTokens":205}}90{"id":"doc-cloud_managed_access_levels_google_ads_api_googl-3ded940e","source":"documentation","title":"Cloud-managed access levels | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/concepts/no-developer-token","text":"Example:\n```text\n// Create a client.\nGoogleAdsClient client = new GoogleAdsClient();\n\n// Opt into the pilot.\nclient.Config.UseCloudOrgForApiAccess = true;\n\n// Make the API calls.\n...\n```\n\nExample:\n```text\ncurl -i -X POST https://googleads.googleapis.com/v25/customers/CUSTOMER_ID/googleAds:searchStream \\\n -H \"Content-Type: application/json\" \\\n -H \"Authorization: Bearer ACCESS_TOKEN\" \\\n -H \"login-customer-id: LOGIN_CUSTOMER_ID\" \\\n --data-binary \"@query.json\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.213Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":22,"estimatedTokens":121}}91{"id":"doc-mutate_best_practices_google_ads_api_google_for_-e7ebe255","source":"documentation","title":"Mutate Best Practices | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/mutating/best-practices","text":"Example:\n```text\nmutate_operations: [\n {\n campaign_operation: {\n create: {\n resource_name: \"customers/<YOUR_CUSTOMER_ID>/campaigns/-1\",\n ...\n }\n }\n },\n {\n ad_group_operation: {\n create: {\n resource_name: \"customers/<YOUR_CUSTOMER_ID>/adGroups/-2\",\n campaign: \"customers/<YOUR_CUSTOMER_ID>/campaigns/-1\"\n ...\n }\n }\n },\n {\n ad_group_ad_operation: {\n create: {\n ad_group: \"customers/<YOUR_CUSTOMER_ID>/adGroups/-2\"\n ...\n }\n }\n },\n]\n```\n\nExample:\n```text\nprivate String createExperimentArms(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId, String experiment) {\n List<ExperimentArmOperation> operations = new ArrayList<>();\n operations.add(\n ExperimentArmOperation.newBuilder()\n .setCreate(\n // The \"control\" arm references an already-existing campaign.\n ExperimentArm.newBuilder()\n .setControl(true)\n .addCampaigns(ResourceNames.campaign(customerId, campaignId))\n .setExperiment(experiment)\n .setName(\"control arm\")\n .setTrafficSplit(40)\n .build())\n .build());\n operations.add(\n ExperimentArmOperation.newBuilder()\n .setCreate(\n // In standard campaign experiments, creating the treatment arm automatically\n // generates a draft campaign that you can modify before starting the experiment.\n ExperimentArm.newBuilder()\n .setControl(false)\n .setExperiment(experiment)\n .setName(\"experiment arm\")\n .setTrafficSplit(60)\n .build())\n .build());\n\n try (ExperimentArmServiceClient experimentArmServiceClient =\n googleAdsClient.getLatestVersion().createExperimentArmServiceClient()) {\n // Constructs the mutate request.\n MutateExperimentArmsRequest mutateRequest =\n MutateExperimentArmsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .addAllOperations(operations)\n // We want to fetch the draft campaign IDs from the treatment arm, so the easiest way\n // to do that is to have the response return the newly created entities.\n .setResponseContentType(ResponseContentType.MUTABLE_RESOURCE)\n .build();\n\n // Sends the mutate request.\n MutateExperimentArmsResponse response =\n experimentArmServiceClient.mutateExperimentArms(mutateRequest);\n\n // Results always return in the order that you specify them in the request. Since we created\n // the treatment arm last, it will be the last result. If you don't remember which arm is the\n // treatment arm, you can always filter the query in the next section with\n // `experiment_arm.control = false`.\n MutateExperimentArmResult controlArmResult = response.getResults(0);\n MutateExperimentArmResult treatmentArmResult =\n response.getResults(response.getResultsCount() - 1);\n\n System.out.printf(\n \"Created control arm with resource name '%s'%n\", controlArmResult.getResourceName());\n System.out.printf(\n \"Created treatment arm with resource name '%s'%n\", treatmentArmResult.getResourceName());\n\n return treatmentArmResult.getExperimentArm().getInDesignCampaigns(0);\n }\n}\nCreateSearchCustomExperiment.java\n```\n\nExample:\n```text\nprivate static (MutateExperimentArmResult, MutateExperimentArmResult)\n CreateExperimentArms(GoogleAdsClient client, long customerId, long baseCampaignId,\n string experimentResourceName)\n{\n // Get the ExperimentArmService.\n ExperimentArmServiceClient experimentService = client.GetService(\n Services.V25.ExperimentArmService);\n\n // Create the control arm. The control arm references an already-existing campaign.\n ExperimentArmOperation controlArmOperation = new ExperimentArmOperation()\n {\n Create = new ExperimentArm()\n {\n Control = true,\n Campaigns = {\n ResourceNames.Campaign(customerId, baseCampaignId)\n },\n Experiment = experimentResourceName,\n Name = \"Control Arm\",\n TrafficSplit = 40\n }\n };\n\n // Create the non-control arm.\n // In standard campaign experiments, creating the treatment arm automatically\n // generates a draft campaign that you can modify before starting the experiment.\n ExperimentArmOperation treatmentArmOperation = new ExperimentArmOperation()\n {\n Create = new ExperimentArm()\n {\n Control = false,\n Experiment = experimentResourceName,\n Name = \"Experiment Arm\",\n TrafficSplit = 60\n }\n };\n\n // We want to fetch the draft campaign IDs from the treatment arm, so the\n // easiest way to do that is to have the response return the newly created\n // entities.\n MutateExperimentArmsRequest request = new MutateExperimentArmsRequest\n {\n CustomerId = customerId.ToString(),\n Operations = { controlArmOperation, treatmentArmOperation },\n ResponseContentType = ResponseContentType.MutableResource\n };\n\n MutateExperimentArmsResponse response = experimentService.MutateExperimentArms(\n request\n );\n\n // Results always return in the order that you specify them in the request.\n // Since we created the treatment arm last, it will be the last result.\n MutateExperimentArmResult controlArm = response.Results.First();\n MutateExperimentArmResult treatmentArm = response.Results.Last();\n\n Console.WriteLine($\"Created control arm with resource name \" +\n $\"'{controlArm.ResourceName}'.\");\n Console.WriteLine($\"Created treatment arm with resource name\" +\n $\" '{treatmentArm.ResourceName}'.\");\n return (controlArm, treatmentArm);\n}CreateSearchCustomExperiment.cs\n```\n\nExample:\n```text\nprivate static function createExperimentArms(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId,\n string $experimentResourceName\n): string {\n $operations = [];\n $experimentArm1 = new ExperimentArm([\n // The \"control\" arm references an already-existing campaign.\n 'control' => true,\n 'campaigns' => [ResourceNames::forCampaign($customerId, $campaignId)],\n 'experiment' => $experimentResourceName,\n 'name' => 'control arm',\n 'traffic_split' => 40\n ]);\n $operations[] = new ExperimentArmOperation(['create' => $experimentArm1]);\n $experimentArm2 = new ExperimentArm([\n // The non-\"control\" arm, also called a \"treatment\" arm, will automatically\n // generate draft campaigns that you can modify before starting the\n // experiment.\n 'control' => false,\n 'experiment' => $experimentResourceName,\n 'name' => 'experiment arm',\n 'traffic_split' => 60\n ]);\n $operations[] = new ExperimentArmOperation(['create' => $experimentArm2]);\n\n // Issues a request to create the experiment arms.\n $experimentArmServiceClient = $googleAdsClient->getExperimentArmServiceClient();\n $response = $experimentArmServiceClient->mutateExperimentArms(\n MutateExperimentArmsRequest::build($customerId, $operations)\n // We want to fetch the draft campaign IDs from the treatment arm, so the easiest\n // way to do that is to have the response return the newly created entities.\n ->setResponseContentType(ResponseContentType::MUTABLE_RESOURCE)\n );\n // Results always return in the order that you specify them in the request.\n // Since we created the treatment arm last, it will be the last result.\n $controlArmResourceName = $response->getResults()[0]->getResourceName();\n $treatmentArm = $response->getResults()[count($operations) - 1];\n print \"Created control arm with resource name '$controlArmResourceName'\" . PHP_EOL;\n print \"Created treatment arm with resource name '{$treatmentArm->getResourceName()}'\"\n . PHP_EOL;\n\n return $treatmentArm->getExperimentArm()->getInDesignCampaigns()[0];\n}CreateExperiment.php\n```\n\nExample:\n```text\ndef create_experiment_arms(\n client: GoogleAdsClient,\n customer_id: str,\n base_campaign_id: str,\n experiment: str,\n) -> str:\n \"\"\"Creates a control and treatment experiment arms.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n base_campaign_id: the campaign ID to associate with the control arm of\n the experiment.\n experiment: the resource name for an experiment.\n\n Returns:\n the resource name for the new treatment experiment arm.\n \"\"\"\n operations: List[ExperimentArmOperation] = []\n\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n # The \"control\" arm references an already-existing campaign.\n operation_1: ExperimentArmOperation = client.get_type(\n \"ExperimentArmOperation\"\n )\n exa_1: ExperimentArm = operation_1.create\n exa_1.control = True\n exa_1.campaigns.append(\n campaign_service.campaign_path(customer_id, base_campaign_id)\n )\n exa_1.experiment = experiment\n exa_1.name = \"control arm\"\n exa_1.traffic_split = 40\n operations.append(operation_1)\n\n # In standard campaign experiments, creating the treatment arm automatically\n # generates a draft campaign that you can modify before starting the experiment.\n operation_2: ExperimentArmOperation = client.get_type(\n \"ExperimentArmOperation\"\n )\n exa_2: ExperimentArm = operation_2.create\n exa_2.control = False\n exa_2.experiment = experiment\n exa_2.name = \"experiment arm\"\n exa_2.traffic_split = 60\n operations.append(operation_2)\n\n experiment_arm_service: ExperimentArmServiceClient = client.get_service(\n \"ExperimentArmService\"\n )\n request: MutateExperimentArmsRequest = client.get_type(\n \"MutateExperimentArmsRequest\"\n )\n request.customer_id = customer_id\n request.operations = operations\n # We want to fetch the draft campaign IDs from the treatment arm, so the\n # easiest way to do that is to have the response return the newly created\n # entities.\n request.response_content_type = (\n client.enums.ResponseContentTypeEnum.MUTABLE_RESOURCE\n )\n response: MutateExperimentArmsResponse = (\n experiment_arm_service.mutate_experiment_arms(request=request)\n )\n\n # Results always return in the order that you specify them in the request.\n # Since we created the treatment arm second, it will be the second result.\n control_arm_result: Any = response.results[0]\n treatment_arm_result: Any = response.results[1]\n\n print(\n f\"Created control arm with resource name {control_arm_result.resource_name}\"\n )\n print(\n f\"Created treatment arm with resource name {treatment_arm_result.resource_name}\"\n )\n\n return treatment_arm_result.experiment_arm.in_design_campaigns[0]create_search_custom_experiment.py\n```\n\nExample:\n```text\ndef create_experiment_arms(client, customer_id, base_campaign_id, experiment)\n operations = []\n operations << client.operation.create_resource.experiment_arm do |ea|\n # The \"control\" arm references an already-existing campaign.\n ea.control = true\n ea.campaigns << client.path.campaign(customer_id, base_campaign_id)\n ea.experiment = experiment\n ea.name = 'control arm'\n ea.traffic_split = 40\n end\n operations << client.operation.create_resource.experiment_arm do |ea|\n # The non-\"control\" arm, also called a \"treatment\" arm, will automatically\n # generate draft campaigns that you can modify before starting the\n # experiment.\n ea.control = false\n ea.experiment = experiment\n ea.name = 'experiment arm'\n ea.traffic_split = 60\n end\n\n response = client.service.experiment_arm.mutate_experiment_arms(\n customer_id: customer_id,\n operations: operations,\n # We want to fetch the draft campaign IDs from the treatment arm, so the\n # easiest way to do that is to have the response return the newly created\n # entities.\n response_content_type: :MUTABLE_RESOURCE,\n )\n\n # Results always return in the order that you specify them in the request.\n # Since we created the treatment arm last, it will be the last result.\n control_arm_result = response.results.first\n treatment_arm_result = response.results.last\n\n puts \"Created control arm with resource name #{control_arm_result.resource_name}.\"\n puts \"Created treatment arm with resource name #{treatment_arm_result.resource_name}.\"\n\n treatment_arm_result.experiment_arm.in_design_campaigns.first\nendcreate_experiment.rb\n```\n\nExample:\n```text\nsub create_experiment_arms {\n my ($api_client, $customer_id, $base_campaign_id, $experiment) = @_;\n\n my $operations = [];\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::ExperimentArmService::ExperimentArmOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::ExperimentArm->new({\n # The \"control\" arm references an already-existing campaign.\n control => \"true\",\n campaigns => [\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $base_campaign_id\n )\n ],\n experiment => $experiment,\n name => \"control arm\",\n trafficSplit => 40\n })});\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::ExperimentArmService::ExperimentArmOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::ExperimentArm->new({\n # The non-\"control\" arm, also called a \"treatment\" arm, will automatically\n # generate draft campaigns that you can modify before starting the\n # experiment.\n control => \"false\",\n experiment => $experiment,\n name => \"experiment arm\",\n trafficSplit => 60\n })});\n\n my $response = $api_client->ExperimentArmService()->mutate({\n customerId => $customer_id,\n operations => $operations,\n # We want to fetch the draft campaign IDs from the treatment arm, so the\n # easiest way to do that is to have the response return the newly created\n # entities.\n responseContentType => MUTABLE_RESOURCE\n });\n\n # Results always return in the order that you specify them in the request.\n # Since we created the treatment arm last, it will be the last result.\n my $control_arm_result = $response->{results}[0];\n my $treatment_arm_result = $response->{results}[1];\n\n printf \"Created control arm with resource name '%s'.\\n\",\n $control_arm_result->{resourceName};\n printf \"Created treatment arm with resource name '%s'.\\n\",\n $treatment_arm_result->{resourceName};\n return $treatment_arm_result->{experimentArm}{inDesignCampaigns}[0];\n}create_experiment.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.216Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":397,"estimatedTokens":3733}}92{"id":"doc-reports_google_ads_api_google_for_developers-42950918","source":"documentation","title":"Reports | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/ads/upgraded-urls/reports","text":"Example:\n```text\nfinal_urls {\n value: \"http://www.example.com/locations/mars/\"\n }\n final_urls {\n value: \"http://www.example.com/cruise/space/\"\n }\n```\n\nExample:\n```text\nSELECT\n ad_group_ad.ad.name,\n ad_group_ad.ad.final_urls\nFROM ad_group_ad\nWHERE ad_group_ad.ad.type = RESPONSIVE_SEARCH_AD\n```\n\nExample:\n```text\nurl_custom_parameters {\n key {\n value: \"promocode\"\n }\n value {\n value: \"NYC123\"\n }\n }\n url_custom_parameters {\n key {\n value: \"season\"\n }\n value {\n value: \"spring\"\n }\n }\n```\n\nExample:\n```text\nSELECT\n ad_group.name,\n ad_group.url_custom_parameters\nFROM ad_group\nWHERE ad_group.type = SEARCH_STANDARD\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.217Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":49,"estimatedTokens":182}}93{"id":"doc-change_status_google_ads_api_google_for_develope-000cafbb","source":"documentation","title":"Change Status | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/change-status","text":"Example:\n```text\ncustomers/{customer_id}/changeStatus/{timestamp}-{resource_type_id}-{additional_ids}\n```\n\nExample:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient, long customerId) {\n String query =\n \"SELECT change_status.resource_name, \"\n + \"change_status.last_change_date_time, \"\n + \"change_status.resource_status, \"\n + \"change_status.resource_type, \"\n + \"change_status.ad_group, \"\n + \"change_status.ad_group_ad, \"\n + \"change_status.ad_group_bid_modifier, \"\n + \"change_status.ad_group_criterion, \"\n + \"change_status.campaign, \"\n + \"change_status.campaign_criterion, \"\n + \"FROM change_status \"\n + \"WHERE change_status.last_change_date_time DURING LAST_14_DAYS \"\n + \"ORDER BY change_status.last_change_date_time \"\n + \"LIMIT 10000\";\n\n try (GoogleAdsServiceClient client =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n SearchPagedResponse response = client.search(String.valueOf(customerId), query);\n\n for (GoogleAdsRow row : response.iterateAll()) {\n Optional<String> resourceNameOfChangedEntity =\n getResourceNameForResourceType(row.getChangeStatus());\n\n System.out.printf(\n \"On '%s', change status '%s' shows a resource type of '%s' \"\n + \"with resource name '%s' was '%s'.%n\",\n row.getChangeStatus().getLastChangeDateTime(),\n row.getChangeStatus().getResourceName(),\n row.getChangeStatus().getResourceType().name(),\n resourceNameOfChangedEntity.orElse(\"\"),\n row.getChangeStatus().getResourceStatus().name());\n }\n }\n}GetChangeSummary.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId)\n{\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsService = client.GetService(\n Services.V25.GoogleAdsService);\n\n string searchQuery = @\"\n SELECT\n change_status.resource_name,\n change_status.last_change_date_time,\n change_status.resource_type,\n change_status.campaign,\n change_status.ad_group,\n change_status.resource_status,\n change_status.ad_group_ad,\n change_status.ad_group_criterion,\n change_status.campaign_criterion\n FROM change_status\n WHERE\n change_status.last_change_date_time DURING LAST_14_DAYS\n ORDER BY change_status.last_change_date_time\n LIMIT 10000\";\n\n // Create a request that will retrieve all changes.\n SearchGoogleAdsRequest request = new SearchGoogleAdsRequest()\n {\n Query = searchQuery,\n CustomerId = customerId.ToString()\n };\n\n try\n {\n // Issue the search request.\n PagedEnumerable<SearchGoogleAdsResponse, GoogleAdsRow> searchPagedResponse =\n googleAdsService.Search(request);\n\n // Iterate over all rows in all pages and prints the requested field values for the\n // campaign in each row.\n foreach (GoogleAdsRow googleAdsRow in searchPagedResponse)\n {\n Console.WriteLine(\"Last change: {0}, Resource type: {1}, \" +\n \"Resource name: {2}, Resource status: {3}, Specific resource name: {4}\",\n googleAdsRow.ChangeStatus.LastChangeDateTime,\n googleAdsRow.ChangeStatus.ResourceType,\n googleAdsRow.ChangeStatus.ResourceName,\n googleAdsRow.ChangeStatus.ResourceStatus,\n SpecificResourceName(googleAdsRow.ChangeStatus.ResourceType,\n googleAdsRow));\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}\n\n/// <summary>\n/// Return the name of the most specific resource that changed.\n/// </summary>\n/// <param name=\"resourceType\">Type of the resource.</param>\n/// <param name=\"row\">Each returned row contains all possible changed fields</param>\n/// <returns>The resource name of the changed field based on the resource type.\n/// The changed field's parent is also populated, but is not used.</returns>\nprivate string SpecificResourceName(ChangeStatusResourceType resourceType, GoogleAdsRow row)\n{\n string resourceName;\n switch (resourceType)\n {\n case ChangeStatusResourceType.AdGroup:\n resourceName = row.ChangeStatus.AdGroup;\n break;\n\n case ChangeStatusResourceType.AdGroupAd:\n resourceName = row.ChangeStatus.AdGroupAd;\n break;\n\n case ChangeStatusResourceType.AdGroupCriterion:\n resourceName = row.ChangeStatus.AdGroupCriterion;\n break;\n\n case ChangeStatusResourceType.Campaign:\n resourceName = row.ChangeStatus.Campaign;\n break;\n\n case ChangeStatusResourceType.CampaignCriterion:\n resourceName = row.ChangeStatus.CampaignCriterion;\n break;\n\n case ChangeStatusResourceType.Unknown:\n case ChangeStatusResourceType.Unspecified:\n default:\n resourceName = \"\";\n break;\n }\n return resourceName;\n}GetChangeSummary.cs\n```\n\nExample:\n```text\npublic static function runExample(GoogleAdsClient $googleAdsClient, int $customerId)\n{\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n // Creates a query to find information about changed resources in your account.\n $query = 'SELECT change_status.resource_name, '\n . 'change_status.last_change_date_time, '\n . 'change_status.resource_status, '\n . 'change_status.resource_type, '\n . 'change_status.ad_group, '\n . 'change_status.ad_group_ad, '\n . 'change_status.ad_group_bid_modifier, '\n . 'change_status.ad_group_criterion, '\n . 'change_status.ad_group_feed, '\n . 'change_status.campaign, '\n . 'change_status.campaign_criterion, '\n . 'change_status.campaign_feed, '\n . 'change_status.feed, '\n . 'change_status.feed_item '\n . 'FROM change_status '\n . 'WHERE change_status.last_change_date_time DURING LAST_14_DAYS '\n . 'ORDER BY change_status.last_change_date_time '\n . 'LIMIT 10000';\n\n // Issues a search request.\n $response = $googleAdsServiceClient->search(\n SearchGoogleAdsRequest::build($customerId, $query)\n );\n\n // Iterates over all rows in all pages and prints the requested field values for\n // the change status in each row.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n printf(\n \"On %s, change status '%s' shows resource '%s' with type '%s' and status '%s'.%s\",\n $googleAdsRow->getChangeStatus()->getLastChangeDateTime(),\n $googleAdsRow->getChangeStatus()->getResourceName(),\n self::getResourceNameForResourceType($googleAdsRow->getChangeStatus()),\n ChangeStatusResourceType::name(\n $googleAdsRow->getChangeStatus()->getResourceType()\n ),\n ChangeStatusOperation::name($googleAdsRow->getChangeStatus()->getResourceStatus()),\n PHP_EOL\n );\n }\n}\n\n/**\n * Gets the resource name for the resource type of the change status object.\n *\n * Each returned row contains all possible changed resources, only one of which is populated\n * with the name of the changed resource. This function returns the resource name of the\n * changed resource based on the resource type.\n *\n * @param ChangeStatus $changeStatus the change status object for getting changed resource\n * @return string the name of the resource that changed\n */\nprivate static function getResourceNameForResourceType(\n ChangeStatus $changeStatus\n) {\n $resourceType = $changeStatus->getResourceType();\n $resourceName = ''; // Default value for UNSPECIFIED or UNKNOWN resource type.\n switch ($resourceType) {\n case ChangeStatusResourceType::AD_GROUP:\n $resourceName = $changeStatus->getAdGroup();\n break;\n case ChangeStatusResourceType::AD_GROUP_AD:\n $resourceName = $changeStatus->getAdGroupAd();\n break;\n case ChangeStatusResourceType::AD_GROUP_BID_MODIFIER:\n $resourceName = $changeStatus->getAdGroupBidModifier();\n break;\n case ChangeStatusResourceType::AD_GROUP_CRITERION:\n $resourceName = $changeStatus->getAdGroupCriterion();\n break;\n case ChangeStatusResourceType::AD_GROUP_FEED:\n $resourceName = $changeStatus->getAdGroupFeed();\n break;\n case ChangeStatusResourceType::CAMPAIGN:\n $resourceName = $changeStatus->getCampaign();\n break;\n case ChangeStatusResourceType::CAMPAIGN_CRITERION:\n $resourceName = $changeStatus->getCampaignCriterion();\n break;\n case ChangeStatusResourceType::CAMPAIGN_FEED:\n $resourceName = $changeStatus->getCampaignFeed();\n break;\n case ChangeStatusResourceType::FEED:\n $resourceName = $changeStatus->getFeed();\n break;\n case ChangeStatusResourceType::FEED_ITEM:\n $resourceName = $changeStatus->getFeedItem();\n break;\n }\n\n return $resourceName;\n}GetChangeSummary.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient, customer_id: str) -> None:\n ads_service: GoogleAdsServiceClient = client.get_service(\"GoogleAdsService\")\n\n # Construct a query to find information about changed resources in your\n # account.\n query = \"\"\"\n SELECT\n change_status.resource_name,\n change_status.last_change_date_time,\n change_status.resource_type,\n change_status.campaign,\n change_status.ad_group,\n change_status.resource_status,\n change_status.ad_group_ad,\n change_status.ad_group_criterion,\n change_status.campaign_criterion\n FROM change_status\n WHERE change_status.last_change_date_time DURING LAST_14_DAYS\n ORDER BY change_status.last_change_date_time\n LIMIT 10000\"\"\"\n\n search_request: SearchGoogleAdsRequest = client.get_type(\n \"SearchGoogleAdsRequest\"\n )\n search_request.customer_id = customer_id\n search_request.query = query\n\n response: SearchPagedResponse = ads_service.search(request=search_request)\n\n row: GoogleAdsRow\n for row in response:\n cs: ChangeStatus = row.change_status\n resource_type: str = cs.resource_type.name\n resource_name: str\n if resource_type == \"AD_GROUP\":\n resource_name = cs.ad_group\n elif resource_type == \"AD_GROUP_AD\":\n resource_name = cs.ad_group_ad\n elif resource_type == \"AD_GROUP_CRITERION\":\n resource_name = cs.ad_group_criterion\n elif resource_type == \"CAMPAIGN\":\n resource_name = cs.campaign\n elif resource_type == \"CAMPAIGN_CRITERION\":\n resource_name = cs.campaign_criterion\n else:\n resource_name = \"UNKNOWN\"\n\n resource_status: str = cs.resource_status.name\n print(\n f\"On '{cs.last_change_date_time}', change status \"\n f\"'{cs.resource_name}' shows that a resource type of \"\n f\"'{resource_type}' with resource name '{resource_name}' was \"\n f\"{resource_status}\"\n )get_change_summary.py\n```\n\nExample:\n```text\ndef get_change_summary(customer_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Construct a query to find information about changed resources in your\n # account.\n query = <<~QUERY\n SELECT\n change_status.resource_name,\n change_status.last_change_date_time,\n change_status.resource_type,\n change_status.campaign,\n change_status.ad_group,\n change_status.resource_status,\n change_status.ad_group_ad,\n change_status.ad_group_criterion,\n change_status.campaign_criterion\n FROM\n change_status\n WHERE change_status.last_change_date_time DURING LAST_14_DAYS\n ORDER BY\n change_status.last_change_date_time\n LIMIT 10000\n QUERY\n\n # Execute the query.\n response = client.service.google_ads.search(\n customer_id: customer_id,\n query: query,\n )\n\n # Process the results.\n response.each do |row|\n cs = row.change_status\n resource_name = case cs.resource_type\n when :AD_GROUP\n cs.ad_group\n when :AD_GROUP_AD\n cs.ad_group_ad\n when :AD_GROUP_CRITERION\n cs.ad_group_criterion\n when :CAMPAIGN\n cs.campaign\n when :CAMPAIGN_CRITERION\n cs.campaign_criterion\n else\n \"UNKNOWN\"\n end\n puts \"On #{cs.last_change_date_time}, change status #{cs.resource_name} \" \\\n \"shows a resource type of #{cs.resource_type} \" \\\n \"with resource name #{resource_name} was #{cs.resource_status}.\"\n end\nendget_change_summary.rb\n```\n\nExample:\n```text\nsub get_change_summary {\n my ($api_client, $customer_id) = @_;\n\n # Construct a search query to find information about changed resources in your\n # account.\n my $search_query =\n \"SELECT change_status.resource_name, change_status.last_change_date_time, \"\n . \"change_status.resource_status, \"\n . \"change_status.resource_type, \"\n . \"change_status.ad_group, \"\n . \"change_status.ad_group_ad, \"\n . \"change_status.ad_group_bid_modifier, \"\n . \"change_status.ad_group_criterion, \"\n . \"change_status.ad_group_feed, \"\n . \"change_status.campaign, \"\n . \"change_status.campaign_criterion, \"\n . \"change_status.campaign_feed, \"\n . \"change_status.feed, \"\n . \"change_status.feed_item \"\n . \"FROM change_status \"\n . \"WHERE change_status.last_change_date_time DURING LAST_14_DAYS \"\n . \"ORDER BY change_status.last_change_date_time \"\n . \"LIMIT 10000\";\n\n # Create a search Google Ads request that will retrieve all change statuses using\n # pages of the specified page size.\n my $search_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsRequest\n ->new({\n customerId => $customer_id,\n query => $search_query\n });\n\n # Get the GoogleAdsService.\n my $google_ads_service = $api_client->GoogleAdsService();\n\n my $iterator = Google::Ads::GoogleAds::Utils::SearchGoogleAdsIterator->new({\n service => $google_ads_service,\n request => $search_request\n });\n\n # Iterate over all rows in all pages and print the requested field values for\n # the change status in each row.\n while ($iterator->has_next) {\n my $google_ads_row = $iterator->next;\n\n my $change_status = $google_ads_row->{changeStatus};\n\n printf \"On %s, change status '%s' shows a resource type of '%s' \" .\n \"with resource name '%s' was '%s'.\\n\",\n $change_status->{lastChangeDateTime},\n $change_status->{resourceName}, $change_status->{resourceType},\n __get_resource_name_for_resource_type($change_status),\n $change_status->{resourceStatus};\n }\n\n return 1;\n}\n\n# This method returns the resource name of the changed field based on the\n# resource type. The changed field's parent is also populated but is not used.\nsub __get_resource_name_for_resource_type {\n my $change_status = shift;\n my $resource_type = $change_status->{resourceType};\n if ($resource_type eq AD_GROUP) {\n return $change_status->{adGroup};\n } elsif ($resource_type eq AD_GROUP_AD) {\n return $change_status->{adGroupAd};\n } elsif ($resource_type eq AD_GROUP_BID_MODIFIER) {\n return $change_status->{adGroupBidModifier};\n } elsif ($resource_type eq AD_GROUP_CRITERION) {\n return $change_status->{adGroupCriterion};\n } elsif ($resource_type eq AD_GROUP_FEED) {\n return $change_status->{adGroupFeed};\n } elsif ($resource_type eq CAMPAIGN) {\n return $change_status->{campaign};\n } elsif ($resource_type eq CAMPAIGN_CRITERION) {\n return $change_status->{campaignCriterion};\n } elsif ($resource_type eq CAMPAIGN_FEED) {\n return $change_status->{campaignFeed};\n } elsif ($resource_type eq FEED) {\n return $change_status->{feed};\n } elsif ($resource_type eq FEED_ITEM) {\n return $change_status->{feedItem};\n } else {\n return \"\";\n }\n}get_change_summary.pl\n```\n\nExample:\n```text\nWHERE change_status.last_change_date_time DURING LAST_7_DAYS\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.220Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":463,"estimatedTokens":4133}}94{"id":"doc-fields_google_ads_api_google_for_developers-c0c48206","source":"documentation","title":"Fields | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/ads/upgraded-urls/fields","text":"Example:\n```text\n{ifmobile:{ifsearch:{keyword:cp={_customP}}}}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.222Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":20}}95{"id":"doc-serving_url_expansion_rules_google_ads_api_googl-6c44db12","source":"documentation","title":"Serving URL Expansion Rules | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/ads/upgraded-urls/serving-url-rules","text":"Example:\n```text\nCustomer\n Campaign\n Ad Group\n Ad\n Ad Group Criterion\n FeedItem (including sitelinks)\n```\n\nExample:\n```text\nAccount\n Campaign 1\n Ad Group 1\n Ad 1\n Keyword 1 (shoes)\n Keyword 2 (hats)\n Ad Group 2\n Ad 2\n Ad 3\n Keyword 3 (watches)\n FeedItem 1 (sitelink 1)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.224Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":26,"estimatedTokens":107}}96{"id":"doc-create_a_things_to_do_campaign_google_ads_api_go-6d732480","source":"documentation","title":"Create a Things to do campaign | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/things-to-do-ads/create-campaign","text":"Example:\n```text\nprivate String addThingsToDoCampaign(\n GoogleAdsClient googleAdsClient,\n long customerId,\n String budgetResourceName,\n long thingsToDoCenterAccountId) {\n // Creates the campaign.\n Campaign campaign =\n Campaign.newBuilder()\n .setName(\"Interplanetary Cruise #\" + getPrintableDateTime())\n // Configures settings related to Things to do campaigns including advertising channel\n // type, advertising channel sub type and travel campaign settings.\n .setAdvertisingChannelType(AdvertisingChannelType.TRAVEL)\n .setAdvertisingChannelSubType(AdvertisingChannelSubType.TRAVEL_ACTIVITIES)\n .setTravelCampaignSettings(\n TravelCampaignSettings.newBuilder().setTravelAccountId(thingsToDoCenterAccountId))\n // Recommendation: Sets the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve\n .setStatus(CampaignStatus.PAUSED)\n // Sets the bidding strategy to MaximizeConversionValue. Only this type can be used\n // for Things to do campaigns.\n .setMaximizeConversionValue(MaximizeConversionValue.newBuilder())\n // Sets the budget.\n .setCampaignBudget(budgetResourceName)\n // Configures the campaign network options. Only Google Search is allowed for\n // Things to do campaigns.\n .setNetworkSettings(NetworkSettings.newBuilder().setTargetGoogleSearch(true))\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n .build();\n\n // Creates a campaign operation.\n CampaignOperation operation = CampaignOperation.newBuilder().setCreate(campaign).build();\n\n // Issues a mutate request to add the campaign.\n try (CampaignServiceClient campaignServiceClient =\n googleAdsClient.getLatestVersion().createCampaignServiceClient()) {\n MutateCampaignsResponse response =\n campaignServiceClient.mutateCampaigns(\n Long.toString(customerId), Collections.singletonList(operation));\n MutateCampaignResult result = response.getResults(0);\n System.out.printf(\n \"Added a Things to do campaign with resource name: '%s'%n\", result.getResourceName());\n return result.getResourceName();\n }\n}AddThingsToDoAd.java\n```\n\nExample:\n```text\n// Creates a campaign.\nCampaign campaign = new Campaign()\n{\n Name = \"Interplanetary Cruise #\" + ExampleUtilities.GetRandomString(),\n AdvertisingChannelType = AdvertisingChannelType.Travel,\n AdvertisingChannelSubType = AdvertisingChannelSubType.TravelActivities,\n\n TravelCampaignSettings = new TravelCampaignSettings()\n {\n TravelAccountId = thingsToDoCenterAccountId\n },\n\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve\n Status = CampaignStatus.Paused,\n\n // Set the bidding strategy and budget.\n MaximizeConversionValue = new MaximizeConversionValue(),\n CampaignBudget = budget,\n\n // Set the campaign network options.\n NetworkSettings = new NetworkSettings\n {\n TargetGoogleSearch = true\n },\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n};AddThingsToDoAd.cs\n```\n\nExample:\n```text\nprivate static function addThingsToDoCampaign(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $budgetResourceName,\n int $thingsToDoCenterAccountId\n) {\n // Creates a campaign.\n $campaign = new Campaign([\n 'name' => 'Interplanetary Cruise Campaign #' . Helper::getPrintableDatetime(),\n // Configures settings related to Things to do campaigns including advertising channel\n // type, advertising channel sub type and travel campaign settings.\n 'advertising_channel_type' => AdvertisingChannelType::TRAVEL,\n 'advertising_channel_sub_type' => AdvertisingChannelSubType::TRAVEL_ACTIVITIES,\n 'travel_campaign_settings'\n => new TravelCampaignSettings(['travel_account_id' => $thingsToDoCenterAccountId]),\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve.\n 'status' => CampaignStatus::PAUSED,\n // Sets the bidding strategy to MaximizeConversionValue. Only this type can be used\n // for Things to do campaigns.\n 'maximize_conversion_value' => new MaximizeConversionValue(),\n // Sets the budget.\n 'campaign_budget' => $budgetResourceName,\n // Configures the campaign network options. Only Google Search is allowed for\n // Things to do campaigns.\n 'network_settings' => new NetworkSettings(['target_google_search' => true]),\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n ]);\n\n // Creates a campaign operation.\n $campaignOperation = new CampaignOperation();\n $campaignOperation->setCreate($campaign);\n\n // Issues a mutate request to add campaigns.\n $campaignServiceClient = $googleAdsClient->getCampaignServiceClient();\n $response = $campaignServiceClient->mutateCampaigns(\n MutateCampaignsRequest::build($customerId, [$campaignOperation])\n );\n\n /** @var Campaign $addedCampaign */\n $addedCampaign = $response->getResults()[0];\n printf(\n \"Added a Things to do campaign with resource name '%s'.%s\",\n $addedCampaign->getResourceName(),\n PHP_EOL\n );\n\n return $addedCampaign->getResourceName();\n}AddThingsToDoAd.php\n```\n\nExample:\n```text\ndef add_things_to_do_campaign(\n client: GoogleAdsClient,\n customer_id: str,\n budget_resource_name: str,\n things_to_do_center_account_id: int,\n) -> str:\n \"\"\"Creates a new Things to do campaign in the specified customer account.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n budget_resource_name: the resource name of a budget for a new campaign.\n things_to_do_center_account_id: the Things to Do Center account ID.\n\n Returns:\n The resource name of the newly created campaign.\n \"\"\"\n # Creates a campaign operation.\n operation: CampaignOperation = client.get_type(\"CampaignOperation\")\n # Creates a campaign.\n campaign: Campaign = operation.create\n campaign.name = (\n f\"Interplanetary Cruise Campaign #{get_printable_datetime()}\"\n )\n # Configures settings related to Things to do campaigns including\n # advertising channel type, advertising channel sub type and travel\n # campaign settings.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.TRAVEL\n )\n campaign.advertising_channel_sub_type = (\n client.enums.AdvertisingChannelSubTypeEnum.TRAVEL_ACTIVITIES\n )\n campaign.travel_campaign_settings.travel_account_id = (\n things_to_do_center_account_id\n )\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n # Sets the bidding strategy to MaximizeConversionValue. Only this type can\n # be used for Things to do campaigns.\n campaign.maximize_conversion_value = client.get_type(\n \"MaximizeConversionValue\"\n )\n # Sets the budget.\n campaign.campaign_budget = budget_resource_name\n # Configures the campaign network options. Only Google Search is allowed for\n # Things to do campaigns.\n campaign.network_settings.target_google_search = True\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n # Issues a mutate request to add campaigns.\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n response: MutateCampaignsResponse = campaign_service.mutate_campaigns(\n customer_id=customer_id, operations=[operation]\n )\n\n resource_name: str = response.results[0].resource_name\n print(\n f\"Added a Things to do campaign with resource name: '{resource_name}'.\"\n )\n return resource_nameadd_things_to_do_ad.py\n```\n\nExample:\n```text\ndef add_things_to_do_campaign(client, customer_id, budget_resource,\n things_to_do_center_account_id)\n\n # Create a campaign.\n campaign_operation = client.operation.create_resource.campaign do |c|\n c.name = generate_random_name_field(\"Interplanetary Cruise Campaign\")\n\n # Configures settings related to Things to Do campaigns including\n # advertising channel type, advertising channel sub type and\n # travel campaign settings.\n c.advertising_channel_type = :TRAVEL\n c.advertising_channel_sub_type = :TRAVEL_ACTIVITIES\n\n c.travel_campaign_settings = client.resource.travel_campaign_settings do |tcs|\n tcs.travel_account_id = things_to_do_center_account_id\n end\n\n # Recommendation: Set the campaign to PAUSED when creating it to prevent the\n # ads from immediately serving. Set to ENABLED once you've added targeting\n # and the ads are ready to serve.\n c.status = :PAUSED\n\n # Sets the bidding strategy to MaximizeConversionValue. Only this type can\n # be used for Things to Do campaigns.\n c.maximize_conversion_value = client.resource.maximize_conversion_value\n\n # Set the budget.\n c.campaign_budget = budget_resource\n\n # Configures the campaign network options. Only Google Search is allowed for\n # Things to Do campaigns.\n c.network_settings = client.resource.network_settings do |ns|\n ns.target_google_search = true\n end\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n end\n\n # Issue a mutate request to add the campaign.\n campaign_service = client.service.campaign\n response = campaign_service.mutate_campaigns(\n customer_id: customer_id,\n operations: [campaign_operation],\n )\n\n # Fetch the new campaign's resource name.\n campaign_resource = response.results.first.resource_name\n\n puts \"Added Things To Do campaign with resource name '#{campaign_resource}'.\"\n\n campaign_resource\nendadd_things_to_do_ad.rb\n```\n\nExample:\n```text\nsub add_things_to_do_campaign {\n my ($api_client, $customer_id, $budget_resource_name,\n $things_to_do_center_account_id)\n = @_;\n\n # Create a campaign.\n my $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n name => \"Interplanetary Cruise Campaign #\" . uniqid(),\n # Configure settings related to Things to do campaigns including\n # advertising channel type, advertising channel sub type and travel\n # campaign settings.\n advertisingChannelType => TRAVEL,\n advertisingChannelSubType => TRAVEL_ACTIVITIES,\n travelCampaignSettings =>\n Google::Ads::GoogleAds::V25::Resources::TravelCampaignSettings->new({\n travelAccountId => $things_to_do_center_account_id\n }\n ),\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n status => Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n # Set the bidding strategy to MaximizeConversionValue. Only this type can be\n # used for Things to do campaigns.\n maximizeConversionValue =>\n Google::Ads::GoogleAds::V25::Common::MaximizeConversionValue->new(),\n # Set the budget.\n campaignBudget => $budget_resource_name,\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n # Configure the campaign network options. Only Google Search is allowed for\n # Things to do campaigns.\n networkSettings =>\n Google::Ads::GoogleAds::V25::Resources::NetworkSettings->new({\n targetGoogleSearch => \"true\"\n })});\n\n # Create a campaign operation.\n my $campaign_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation->\n new({create => $campaign});\n\n # Add the campaign.\n my $campaign_resource_name = $api_client->CampaignService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_operation]})->{results}[0]{resourceName};\n\n printf \"Added a Things to do campaign with resource name: '%s'.\\n\",\n $campaign_resource_name;\n\n return $campaign_resource_name;\n}add_things_to_do_ad.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.227Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":334,"estimatedTokens":3406}}97{"id":"doc-change_event_google_ads_api_google_for_developer-25ce45e9","source":"documentation","title":"Change Event | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/change-event","text":"Example:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient, long customerId) {\n // Defines a GAQL query to retrieve change_event instances from the last 14 days.\n String query =\n String.format(\n \"SELECT\"\n + \" change_event.resource_name,\"\n + \" change_event.change_date_time,\"\n + \" change_event.change_resource_name,\"\n + \" change_event.user_email,\"\n + \" change_event.client_type,\"\n + \" change_event.change_resource_type,\"\n + \" change_event.old_resource,\"\n + \" change_event.new_resource,\"\n + \" change_event.resource_change_operation,\"\n + \" change_event.changed_fields \"\n + \"FROM \"\n + \" change_event \"\n + \"WHERE \"\n + \" change_event.change_date_time <= '%s' \"\n + \" AND change_event.change_date_time >= '%s' \"\n + \"ORDER BY\"\n + \" change_event.change_date_time DESC \"\n + \"LIMIT 5\",\n LocalDate.now().toString(\"YYYY-MM-dd\"),\n LocalDate.now().minusDays(14).toString(\"YYYY-MM-dd\"));\n\n // Creates a GoogleAdsServiceClient instance.\n try (GoogleAdsServiceClient client =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Issues the search query.\n SearchPagedResponse response = client.search(String.valueOf(customerId), query);\n\n // Processes the rows of the response.\n for (GoogleAdsRow row : response.iterateAll()) {\n ChangeEvent event = row.getChangeEvent();\n\n // Prints some general information about the change event.\n System.out.printf(\n \"On '%s', user '%s' used interface '%s' to perform a(n) '%s' operation on a '%s' with\"\n + \" resource name '%s'.%n\",\n event.getChangeDateTime(),\n event.getUserEmail(),\n event.getClientType(),\n event.getResourceChangeOperation(),\n event.getChangeResourceType(),\n event.getResourceName());\n\n // Prints some detailed information about update and create operations.\n if (event.getResourceChangeOperation() == ResourceChangeOperation.UPDATE\n || event.getResourceChangeOperation() == ResourceChangeOperation.CREATE) {\n // Retrieves the entity that was changed.\n Optional<Message> oldResource =\n getResourceByType(event.getOldResource(), event.getChangeResourceType());\n Optional<Message> newResource =\n getResourceByType(event.getNewResource(), event.getChangeResourceType());\n\n // Prints the old and new values for each field that was updated/created.\n for (String changedPath : row.getChangeEvent().getChangedFields().getPathsList()) {\n // Uses the FieldMasks utility to retrieve a value from a . delimited path.\n List<? extends Object> oldValue =\n oldResource.isPresent()\n ? FieldMasks.getFieldValue(changedPath, oldResource.get())\n : Collections.emptyList();\n List<? extends Object> newValue =\n newResource.isPresent()\n ? FieldMasks.getFieldValue(changedPath, newResource.get())\n : Collections.emptyList();\n // Prints different messages for UPDATE and CREATE cases.\n if (event.getResourceChangeOperation() == ResourceChangeOperation.UPDATE) {\n System.out.printf(\"\\t %s changed from %s to %s.%n\", changedPath, oldValue, newValue);\n } else if (event.getResourceChangeOperation() == ResourceChangeOperation.CREATE) {\n System.out.printf(\"\\t %s set to %s.%n\", changedPath, newValue);\n }\n }\n }\n }\n }\n}GetChangeDetails.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId)\n{\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsService = client.GetService(\n Services.V25.GoogleAdsService);\n\n // Construct a query to find details for recent changes in your account.\n // The LIMIT clause is required for the change_event resource.\n // The maximum size is 10000, but a low limit was set here for demonstrative\n // purposes.\n // The WHERE clause on change_date_time is also required. It must specify a\n // window of at most 30 days within the past 30 days.\n\n string startDate = DateTime.Today.Subtract(TimeSpan.FromDays(25)).ToString(\"yyyyMMdd\");\n string endDate = DateTime.Today.Add(TimeSpan.FromDays(1)).ToString(\"yyyyMMdd\");\n string searchQuery = $@\"\n SELECT\n change_event.resource_name,\n change_event.change_date_time,\n change_event.change_resource_name,\n change_event.user_email,\n change_event.client_type,\n change_event.change_resource_type,\n change_event.old_resource,\n change_event.new_resource,\n change_event.resource_change_operation,\n change_event.changed_fields\n FROM\n change_event\n WHERE\n change_event.change_date_time >= '{startDate}' AND\n change_event.change_date_time <= '{endDate}'\n ORDER BY\n change_event.change_date_time DESC\n LIMIT 5\";\n\n try\n {\n // Issue a search request.\n googleAdsService.SearchStream(customerId.ToString(), searchQuery,\n delegate (SearchGoogleAdsStreamResponse resp)\n {\n // Display the results.\n foreach (GoogleAdsRow googleAdsRow in resp.Results)\n {\n ChangeEvent changeEvent = googleAdsRow.ChangeEvent;\n ChangedResource oldResource = changeEvent.OldResource;\n ChangedResource newResource = changeEvent.NewResource;\n\n bool knownResourceType = true;\n IMessage oldResourceEntity = null;\n IMessage newResourceEntity = null;\n switch (changeEvent.ChangeResourceType)\n {\n case ChangeEventResourceType.Ad:\n oldResourceEntity = oldResource.Ad;\n newResourceEntity = newResource.Ad;\n break;\n\n case ChangeEventResourceType.AdGroup:\n oldResourceEntity = oldResource.AdGroup;\n newResourceEntity = newResource.AdGroup;\n break;\n\n case ChangeEventResourceType.AdGroupAd:\n oldResourceEntity = oldResource.AdGroupAd;\n newResourceEntity = newResource.AdGroupAd;\n break;\n\n case ChangeEventResourceType.AdGroupAsset:\n oldResourceEntity = oldResource.AdGroupAsset;\n newResourceEntity = newResource.AdGroupAsset;\n break;\n\n case ChangeEventResourceType.AdGroupBidModifier:\n oldResourceEntity = oldResource.AdGroupBidModifier;\n newResourceEntity = newResource.AdGroupBidModifier;\n break;\n\n case ChangeEventResourceType.AdGroupCriterion:\n oldResourceEntity = oldResource.AdGroupCriterion;\n newResourceEntity = newResource.AdGroupCriterion;\n break;\n\n case ChangeEventResourceType.Asset:\n oldResourceEntity = oldResource.Asset;\n newResourceEntity = newResource.Asset;\n break;\n\n case ChangeEventResourceType.AssetSet:\n oldResourceEntity = oldResource.AssetSet;\n newResourceEntity = newResource.AssetSet;\n break;\n\n case ChangeEventResourceType.AssetSetAsset:\n oldResourceEntity = oldResource.AssetSetAsset;\n newResourceEntity = newResource.AssetSetAsset;\n break;\n\n case ChangeEventResourceType.Campaign:\n oldResourceEntity = oldResource.Campaign;\n newResourceEntity = newResource.Campaign;\n break;\n\n case ChangeEventResourceType.CampaignAsset:\n oldResourceEntity = oldResource.CampaignAsset;\n newResourceEntity = newResource.CampaignAsset;\n break;\n\n case ChangeEventResourceType.CampaignAssetSet:\n oldResourceEntity = oldResource.CampaignAssetSet;\n newResourceEntity = newResource.CampaignAssetSet;\n break;\n\n case ChangeEventResourceType.CampaignBudget:\n oldResourceEntity = oldResource.CampaignBudget;\n newResourceEntity = newResource.CampaignBudget;\n break;\n\n case ChangeEventResourceType.CampaignCriterion:\n oldResourceEntity = oldResource.CampaignCriterion;\n newResourceEntity = newResource.CampaignCriterion;\n break;\n\n case ChangeEventResourceType.CustomerAsset:\n oldResourceEntity = oldResource.CustomerAsset;\n newResourceEntity = newResource.CustomerAsset;\n break;\n\n default:\n knownResourceType = false;\n break;\n }\n\n if (!knownResourceType)\n {\n Console.WriteLine($\"Unknown change_resource_type \" +\n $\"'{changeEvent.ChangeResourceType}'.\");\n continue;\n }\n\n Console.WriteLine($\"On #{changeEvent.ChangeDateTime}, user \" +\n $\"{changeEvent.UserEmail} used interface {changeEvent.ClientType} \" +\n $\"to perform a(n) '{changeEvent.ResourceChangeOperation}' \" +\n $\"operation on a '{changeEvent.ChangeResourceType}' with \" +\n $\"resource name {changeEvent.ChangeResourceName}.\");\n\n foreach (string fieldMaskPath in changeEvent.ChangedFields.Paths)\n {\n if (changeEvent.ResourceChangeOperation ==\n ResourceChangeOperation.Create)\n {\n object newValue = FieldMasks.GetFieldValue(\n fieldMaskPath, newResourceEntity);\n Console.WriteLine($\"\\t{fieldMaskPath} set to '{newValue}'.\");\n }\n else if (changeEvent.ResourceChangeOperation ==\n ResourceChangeOperation.Update)\n {\n object oldValue = FieldMasks.GetFieldValue(fieldMaskPath,\n oldResourceEntity);\n object newValue = FieldMasks.GetFieldValue(fieldMaskPath,\n newResourceEntity);\n\n Console.WriteLine($\"\\t{fieldMaskPath} changed from \" +\n $\"'{oldValue}' to '{newValue}'.\");\n }\n }\n }\n });\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}GetChangeDetails.cs\n```\n\nExample:\n```text\npublic static function runExample(GoogleAdsClient $googleAdsClient, int $customerId)\n{\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n\n // Constructs a query to find details for recent changes in your account.\n // The LIMIT clause is required for the change_event resource.\n // The maximum size is 10000, but a low limit was set here for demonstrative\n // purposes.\n // The WHERE clause on change_date_time is also required. It must specify a\n // window of at most 30 days within the past 30 days.\n $query = 'SELECT change_event.resource_name, '\n . 'change_event.change_date_time, '\n . 'change_event.change_resource_name, '\n . 'change_event.user_email, '\n . 'change_event.client_type, '\n . 'change_event.change_resource_type, '\n . 'change_event.old_resource, '\n . 'change_event.new_resource, '\n . 'change_event.resource_change_operation, '\n . 'change_event.changed_fields '\n . 'FROM change_event '\n . sprintf(\n 'WHERE change_event.change_date_time <= %s ',\n date_format(new DateTime('+1 day'), 'Ymd')\n ) . sprintf(\n 'AND change_event.change_date_time >= %s ',\n date_format(new DateTime('-14 days'), 'Ymd')\n ) . 'ORDER BY change_event.change_date_time DESC '\n . 'LIMIT 5';\n // Issues a search request.\n $response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n // Iterates over all rows in all pages and prints the requested field values for\n // the change event in each row.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $changeEvent = $googleAdsRow->getChangeEvent();\n $oldResource = $changeEvent->getOldResource();\n $newResource = $changeEvent->getNewResource();\n\n $isResourceTypeKnown = true;\n $oldResourceEntity = null;\n $newResourceEntity = null;\n switch ($changeEvent->getChangeResourceType()) {\n case ChangeEventResourceType::AD:\n $oldResourceEntity = $oldResource->getAd();\n $newResourceEntity = $newResource->getAd();\n break;\n case ChangeEventResourceType::AD_GROUP:\n $oldResourceEntity = $oldResource->getAdGroup();\n $newResourceEntity = $newResource->getAdGroup();\n break;\n case ChangeEventResourceType::AD_GROUP_AD:\n $oldResourceEntity = $oldResource->getAdGroupAd();\n $newResourceEntity = $newResource->getAdGroupAd();\n break;\n case ChangeEventResourceType::AD_GROUP_ASSET:\n $oldResourceEntity = $oldResource->getAdGroupAsset();\n $newResourceEntity = $newResource->getAdGroupAsset();\n break;\n case ChangeEventResourceType::AD_GROUP_CRITERION:\n $oldResourceEntity = $oldResource->getAdGroupCriterion();\n $newResourceEntity = $newResource->getAdGroupCriterion();\n break;\n case ChangeEventResourceType::AD_GROUP_BID_MODIFIER:\n $oldResourceEntity = $oldResource->getAdGroupBidModifier();\n $newResourceEntity = $newResource->getAdGroupBidModifier();\n break;\n case ChangeEventResourceType::ASSET:\n $oldResourceEntity = $oldResource->getAsset();\n $newResourceEntity = $newResource->getAsset();\n break;\n case ChangeEventResourceType::ASSET_SET:\n $oldResourceEntity = $oldResource->getAssetSet();\n $newResourceEntity = $newResource->getAssetSet();\n break;\n case ChangeEventResourceType::ASSET_SET_ASSET:\n $oldResourceEntity = $oldResource->getAssetSetAsset();\n $newResourceEntity = $newResource->getAssetSetAsset();\n break;\n case ChangeEventResourceType::CAMPAIGN:\n $oldResourceEntity = $oldResource->getCampaign();\n $newResourceEntity = $newResource->getCampaign();\n break;\n case ChangeEventResourceType::CAMPAIGN_ASSET:\n $oldResourceEntity = $oldResource->getCampaignAsset();\n $newResourceEntity = $newResource->getCampaignAsset();\n break;\n case ChangeEventResourceType::CAMPAIGN_ASSET_SET:\n $oldResourceEntity = $oldResource->getCampaignAssetSet();\n $newResourceEntity = $newResource->getCampaignAssetSet();\n break;\n case ChangeEventResourceType::CAMPAIGN_BUDGET:\n $oldResourceEntity = $oldResource->getCampaignBudget();\n $newResourceEntity = $newResource->getCampaignBudget();\n break;\n case ChangeEventResourceType::CAMPAIGN_CRITERION:\n $oldResourceEntity = $oldResource->getCampaignCriterion();\n $newResourceEntity = $newResource->getCampaignCriterion();\n break;\n case ChangeEventResourceType::CUSTOMER_ASSET:\n $oldResourceEntity = $oldResource->getCustomerAsset();\n $newResourceEntity = $newResource->getCustomerAsset();\n break;\n default:\n $isResourceTypeKnown = false;\n break;\n }\n if (!$isResourceTypeKnown) {\n printf(\n \"Unknown change_resource_type %s.%s\",\n ChangeEventResourceType::name($changeEvent->getChangeResourceType()),\n PHP_EOL\n );\n }\n $resourceChangeOperation = $changeEvent->getResourceChangeOperation();\n printf(\n \"On %s, user '%s' used interface '%s' to perform a(n) '%s' operation on a '%s' \"\n . \"with resource name '%s'.%s\",\n $changeEvent->getChangeDateTime(),\n $changeEvent->getUserEmail(),\n ChangeClientType::name($changeEvent->getClientType()),\n ResourceChangeOperation::name($resourceChangeOperation),\n ChangeEventResourceType::name($changeEvent->getChangeResourceType()),\n $changeEvent->getChangeResourceName(),\n PHP_EOL\n );\n\n if (\n $resourceChangeOperation !== ResourceChangeOperation::CREATE\n && $resourceChangeOperation !== ResourceChangeOperation::UPDATE\n ) {\n continue;\n }\n foreach ($changeEvent->getChangedFields()->getPaths() as $path) {\n $newValueStr = self::convertToString(\n FieldMasks::getFieldValue($path, $newResourceEntity, true)\n );\n if ($resourceChangeOperation === ResourceChangeOperation::CREATE) {\n printf(\"\\t'$path' set to '%s'.%s\", $newValueStr, PHP_EOL);\n } elseif ($resourceChangeOperation === ResourceChangeOperation::UPDATE) {\n printf(\n \"\\t'$path' changed from '%s' to '%s'.%s\",\n self::convertToString(\n FieldMasks::getFieldValue($path, $oldResourceEntity, true)\n ),\n $newValueStr,\n PHP_EOL\n );\n }\n }\n }\n}\n\n/**\n * Converts the specified value to string.\n *\n * @param mixed $value the value to be converted to string\n * @return string the value in string\n */\nprivate static function convertToString($value)\n{\n if (is_null($value)) {\n return 'no value';\n }\n if (gettype($value) === 'boolean') {\n return $value ? 'true' : 'false';\n } elseif (gettype($value) === 'object') {\n if (get_class($value) === RepeatedField::class) {\n $strValues = [];\n foreach (iterator_to_array($value->getIterator()) as $element) {\n /** @type Message $element */\n $strValues[] = $element->serializeToJsonString();\n }\n return '[' . implode(',', $strValues) . ']';\n }\n return json_encode($value);\n } else {\n return strval($value);\n }\n}GetChangeDetails.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient, customer_id: str) -> None:\n \"\"\"Gets specific details about the most recent changes in the given account.\n\n Args:\n client: The Google Ads client.\n customer_id: The Google Ads customer ID.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n # Construct a query to find details for recent changes in your account.\n # The LIMIT clause is required for the change_event resource.\n # The maximum size is 10000, but a low limit was set here for demonstrative\n # purposes. For more information see:\n # https://developers.google.com/google-ads/api/docs/change-event#getting_changes\n # The WHERE clause on change_date_time is also required. It must specify a\n # window within the past 30 days.\n tomorrow: str = (datetime.now() + timedelta(1)).strftime(\"%Y-%m-%d\")\n two_weeks_ago: str = (datetime.now() + timedelta(-14)).strftime(\"%Y-%m-%d\")\n query: str = f\"\"\"\n SELECT\n change_event.resource_name,\n change_event.change_date_time,\n change_event.change_resource_name,\n change_event.user_email,\n change_event.client_type,\n change_event.change_resource_type,\n change_event.old_resource,\n change_event.new_resource,\n change_event.resource_change_operation,\n change_event.changed_fields\n FROM change_event\n WHERE change_event.change_date_time <= '{tomorrow}'\n AND change_event.change_date_time >= '{two_weeks_ago}'\n ORDER BY change_event.change_date_time DESC\n LIMIT 5\"\"\"\n\n search_request: SearchGoogleAdsRequest = client.get_type(\n \"SearchGoogleAdsRequest\"\n )\n search_request.customer_id = customer_id\n search_request.query = query\n\n results: SearchPagedResponse = googleads_service.search(\n request=search_request\n )\n\n row: GoogleAdsRow\n for row in results:\n event: ChangeEvent = row.change_event\n resource_type: str = event.change_resource_type.name\n old_resource: Any\n new_resource: Any\n if resource_type == \"AD\":\n old_resource = event.old_resource.ad\n new_resource = event.new_resource.ad\n elif resource_type == \"AD_GROUP\":\n old_resource = event.old_resource.ad_group\n new_resource = event.new_resource.ad_group\n elif resource_type == \"AD_GROUP_AD\":\n old_resource = event.old_resource.ad_group_ad\n new_resource = event.new_resource.ad_group_ad\n elif resource_type == \"AD_GROUP_ASSET\":\n old_resource = event.old_resource.ad_group_asset\n new_resource = event.new_resource.ad_group_asset\n elif resource_type == \"AD_GROUP_CRITERION\":\n old_resource = event.old_resource.ad_group_criterion\n new_resource = event.new_resource.ad_group_criterion\n elif resource_type == \"AD_GROUP_BID_MODIFIER\":\n old_resource = event.old_resource.ad_group_bid_modifier\n new_resource = event.new_resource.ad_group_bid_modifier\n elif resource_type == \"AD_GROUP_FEED\":\n old_resource = event.old_resource.ad_group_feed\n new_resource = event.new_resource.ad_group_feed\n elif resource_type == \"ASSET\":\n old_resource = event.old_resource.asset\n new_resource = event.new_resource.asset\n elif resource_type == \"ASSET_SET\":\n old_resource = event.old_resource.asset_set\n new_resource = event.new_resource.asset_set\n elif resource_type == \"ASSET_SET_ASSET\":\n old_resource = event.old_resource.asset_set_asset\n new_resource = event.new_resource.asset_set_asset\n elif resource_type == \"CAMPAIGN\":\n old_resource = event.old_resource.campaign\n new_resource = event.new_resource.campaign\n elif resource_type == \"CAMPAIGN_ASSET\":\n old_resource = event.old_resource.campaign_asset\n new_resource = event.new_resource.campaign_asset\n elif resource_type == \"CAMPAIGN_ASSET_SET\":\n old_resource = event.old_resource.campaign_asset_set\n new_resource = event.new_resource.campaign_asset_set\n elif resource_type == \"CAMPAIGN_BUDGET\":\n old_resource = event.old_resource.campaign_budget\n new_resource = event.new_resource.campaign_budget\n elif resource_type == \"CAMPAIGN_CRITERION\":\n old_resource = event.old_resource.campaign_criterion\n new_resource = event.new_resource.campaign_criterion\n elif resource_type == \"CAMPAIGN_FEED\":\n old_resource = event.old_resource.campaign_feed\n new_resource = event.new_resource.campaign_feed\n elif resource_type == \"CUSTOMER_ASSET\":\n old_resource = event.old_resource.customer_asset\n new_resource = event.new_resource.customer_asset\n elif resource_type == \"FEED\":\n old_resource = event.old_resource.feed\n new_resource = event.new_resource.feed\n elif resource_type == \"FEED_ITEM\":\n old_resource = event.old_resource.feed_item\n new_resource = event.new_resource.feed_item\n else:\n print(\n \"Unknown change_resource_type: '{event.change_resource_type}'\"\n )\n # If the resource type is unrecognized then we continue to\n # the next row.\n continue\n\n print(\n f\"On {event.change_date_time}, user {event.user_email} \"\n f\"used interface {event.client_type.name} to perform a(n) \"\n f\"{event.resource_change_operation.name} operation on a \"\n f\"{event.change_resource_type.name} with resource name \"\n f\"'{event.change_resource_name}'\"\n )\n\n operation_type: str = event.resource_change_operation.name\n\n if operation_type in (\"UPDATE\", \"CREATE\"):\n for changed_field_path in event.changed_fields.paths:\n changed_field: str = changed_field_path\n # Change field name from \"type\" to \"type_\" so that it doesn't\n # raise an exception when accessed on the protobuf object, see:\n # https://developers.google.com/google-ads/api/docs/client-libs/python/library-version-10#field_names_that_are_reserved_words\n if changed_field == \"type\":\n changed_field = \"type_\"\n\n new_value: Any = get_nested_attr(new_resource, changed_field)\n # If the field value is an Enum get the human readable name\n # so that it is printed instead of the field ID integer.\n if isinstance(type(new_value), ProtoEnumMeta):\n new_value = new_value.name\n\n if operation_type == \"CREATE\":\n print(f\"\\t{changed_field} set to {new_value}\")\n else:\n old_value: Any = get_nested_attr(\n old_resource, changed_field\n )\n # If the field value is an Enum get the human readable name\n # so that it is printed instead of the field ID integer.\n if isinstance(type(old_value), ProtoEnumMeta):\n old_value = old_value.name\n\n print(\n f\"\\t{changed_field} changed from {old_value} to {new_value}\"\n )get_change_details.py\n```\n\nExample:\n```text\ndef get_change_details(customer_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Construct a query to find details for recent changes in your account.\n # The LIMIT clause is required for the change_event resource.\n # The maximum size is 10000, but a low limit was set here for demonstrative\n # purposes.\n # The WHERE clause on change_date_time is also required. It must specify a\n # window of at most 30 days within the past 30 days.\n query = <<~QUERY\n SELECT\n change_event.resource_name,\n change_event.change_date_time,\n change_event.change_resource_name,\n change_event.user_email,\n change_event.client_type,\n change_event.change_resource_type,\n change_event.old_resource,\n change_event.new_resource,\n change_event.resource_change_operation,\n change_event.changed_fields\n FROM\n change_event\n WHERE\n change_event.change_date_time <= '#{(Date.today + 1).to_s}'\n AND change_event.change_date_time >= '#{(Date.today - 14).to_s}'\n ORDER BY\n change_event.change_date_time DESC\n LIMIT 5\n QUERY\n\n # Execute the query to fetch results from the API.\n response = client.service.google_ads.search(\n customer_id: customer_id,\n query: query,\n )\n\n # Process the results and output changes.\n response.each do |row|\n event = row.change_event\n old_resource, new_resource = case event.change_resource_type\n when :AD\n [event.old_resource.ad, event.new_resource.ad]\n when :AD_GROUP\n [event.old_resource.ad_group, event.new_resource.ad_group]\n when :AD_GROUP_AD\n [event.old_resource.ad_group_ad, event.new_resource.ad_group_ad]\n when :AD_GROUP_ASSET\n [event.old_resource.ad_group_asset, event.new_resource.ad_group_asset]\n when :AD_GROUP_CRITERION\n [event.old_resource.ad_group_criterion, event.new_resource.ad_group_criterion]\n when :AD_GROUP_BID_MODIFIER\n [event.old_resource.ad_group_bid_modifier, event.new_resource.ad_group_bid_modifier]\n when :ASSET\n [event.old_resource.asset, event.new_resource.asset]\n when :ASSET_SET\n [event.old_resource.asset_set, event.new_resource.asset_set]\n when :ASSET_SET_ASSET\n [event.old_resource.asset_set_asset, event.new_resource.asset_set_asset]\n when :CAMPAIGN\n [event.old_resource.campaign, event.new_resource.campaign]\n when :CAMPAIGN_ASSET\n [event.old_resource.campaign_asset, event.new_resource.campaign_asset]\n when :CAMPAIGN_ASSET_SET\n [event.old_resource.campaign_asset_set, event.new_resource.campaign_asset_set]\n when :CAMPAIGN_BUDGET\n [event.old_resource.campaign_budget, event.new_resource.campaign_budget]\n when :CAMPAIGN_CRITERION\n [event.old_resource.campaign_criterion, event.new_resource.campaign_criterion]\n when :ASSET\n [event.old_resource.asset, event.new_resource.asset]\n when :CUSTOMER_ASSET\n [event.old_resource.customer_asset, event.new_resource.customer_asset]\n else\n puts \"Unknown change_resource_type #{event.change_resource_type}.\"\n next\n end\n puts \"On #{event.change_date_time}, user #{event.user_email} used interface \" \\\n \"#{event.client_type} to perform a(n) #{event.resource_change_operation} \" \\\n \"operation on a #{event.change_resource_type} with resource name \" \\\n \"#{event.change_resource_name}.\"\n if [:UPDATE, :CREATE].include? event.resource_change_operation\n event.changed_fields.paths.each do |changed_field|\n new_value = get_value_from_path(changed_field, new_resource)\n if :CREATE == event.resource_change_operation\n puts \"\\t#{changed_field} set to '#{new_value}'.\"\n else\n old_value = get_value_from_path(changed_field, old_resource)\n puts \"\\t#{changed_field} changed from '#{old_value}' to '#{new_value}'.\"\n end\n end\n end\n end\nend\n\n# Given the string value of a path from the response, look up the value of the\n# field located at that path on the given object.\ndef get_value_from_path(path, object)\n path.split(\".\").inject(object) {|obj, key| obj.send(key)}\nendget_change_details.rb\n```\n\nExample:\n```text\nsub get_change_details {\n my ($api_client, $customer_id) = @_;\n\n # Construct a query to find details for recent changes in your account.\n # The LIMIT clause is required for the change_event resource.\n # The maximum size is 10000, but a low limit was set here for demonstrative\n # purposes.\n # The WHERE clause on change_date_time is also required. It must specify a\n # window of at most 30 days within the past 30 days.\n my $search_query =\n \"SELECT change_event.resource_name, change_event.change_date_time, \" .\n \"change_event.change_resource_name, change_event.user_email, \" .\n \"change_event.client_type, change_event.change_resource_type, \" .\n \"change_event.old_resource, change_event.new_resource, \" .\n \"change_event.resource_change_operation, change_event.changed_fields \" .\n \"FROM change_event \" .\n \"WHERE change_event.change_date_time DURING LAST_14_DAYS \" .\n \"ORDER BY change_event.change_date_time DESC LIMIT 5\";\n\n # Create a search Google Ads request that will retrieve all change events using\n # pages of the specified page size.\n my $search_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsRequest\n ->new({\n customerId => $customer_id,\n query => $search_query\n });\n\n # Get the GoogleAdsService.\n my $google_ads_service = $api_client->GoogleAdsService();\n\n my $iterator = Google::Ads::GoogleAds::Utils::SearchGoogleAdsIterator->new({\n service => $google_ads_service,\n request => $search_request\n });\n\n # Iterate over all rows in all pages and print the requested field values for\n # the change event in each row.\n while ($iterator->has_next) {\n my $google_ads_row = $iterator->next;\n\n my $change_event = $google_ads_row->{changeEvent};\n printf \"On %s, user %s used interface %s to perform a(n) %s operation \" .\n \"on a %s with resource name '%s'.\\n\", $change_event->{changeDateTime},\n $change_event->{userEmail}, $change_event->{clientType},\n $change_event->{resourceChangeOperation},\n $change_event->{changeResourceType}, $change_event->{changeResourceName};\n\n if (grep /$change_event->{resourceChangeOperation}/, (CREATE, UPDATE)) {\n my ($old_resource, $new_resource) =\n _get_changed_resources_for_resource_type($change_event);\n\n foreach my $changed_field (split /,/, $change_event->{changedFields}) {\n my $new_value =\n _convert_to_string(get_field_value($new_resource, $changed_field))\n || \"\";\n if ($change_event->{resourceChangeOperation} eq CREATE) {\n print \"\\t$changed_field set to '$new_value'.\\n\";\n } else {\n my $old_value =\n _convert_to_string(get_field_value($old_resource, $changed_field))\n || \"\";\n print \"\\t$changed_field changed from '$old_value' to '$new_value'.\\n\";\n }\n }\n }\n }\n\n return 1;\n}\n\n# This method converts the specified value to a string.\nsub _convert_to_string {\n my $value = shift;\n my $string_value = \"\";\n\n if (ref($value) eq \"ARRAY\") {\n $string_value .= \"[\";\n foreach my $item (@$value) {\n if (is_hash_ref($item)) {\n $string_value .= (JSON::XS->new->utf8->encode($item) . \",\");\n } else {\n $string_value .= ($item . \",\");\n }\n }\n $string_value .= \"]\";\n } elsif (is_hash_ref($value)) {\n $string_value .= JSON::XS->new->utf8->encode($value);\n } else {\n $string_value = $value;\n }\n return $string_value;\n}\n\n# This method returns the old resource and new resource based on the change\n# resource type of a change event.\nsub _get_changed_resources_for_resource_type {\n my $change_event = shift;\n my $resource_type = $change_event->{changeResourceType};\n if ($resource_type eq AD) {\n return $change_event->{oldResource}{ad}, $change_event->{newResource}{ad};\n } elsif ($resource_type eq AD_GROUP) {\n return $change_event->{oldResource}{adGroup},\n $change_event->{newResource}{adGroup};\n } elsif ($resource_type eq AD_GROUP_AD) {\n return $change_event->{oldResource}{adGroupAd},\n $change_event->{newResource}{adGroupAd};\n } elsif ($resource_type eq AD_GROUP_ASSET) {\n return $change_event->{oldResource}{adGroupAsset},\n $change_event->{newResource}{adGroupAsset};\n } elsif ($resource_type eq AD_GROUP_CRITERION) {\n return $change_event->{oldResource}{adGroupCriterion},\n $change_event->{newResource}{adGroupCriterion};\n } elsif ($resource_type eq AD_GROUP_BID_MODIFIER) {\n return $change_event->{oldResource}{adGroupBidModifier},\n $change_event->{newResource}{adGroupBidModifier};\n } elsif ($resource_type eq ASSET) {\n return $change_event->{oldResource}{asset},\n $change_event->{newResource}{asset};\n } elsif ($resource_type eq ASSET_SET) {\n return $change_event->{oldResource}{assetSet},\n $change_event->{newResource}{assetSet};\n } elsif ($resource_type eq ASSET_SET_ASSET) {\n return $change_event->{oldResource}{assetSetAsset},\n $change_event->{newResource}{assetSetAsset};\n } elsif ($resource_type eq CAMPAIGN) {\n return $change_event->{oldResource}{campaign},\n $change_event->{newResource}{campaign};\n } elsif ($resource_type eq CAMPAIGN_ASSET) {\n return $change_event->{oldResource}{campaignAsset},\n $change_event->{newResource}{campaignAsset};\n } elsif ($resource_type eq CAMPAIGN_ASSET_SET) {\n return $change_event->{oldResource}{campaignAssetSet},\n $change_event->{newResource}{campaignAssetSet};\n } elsif ($resource_type eq CAMPAIGN_BUDGET) {\n return $change_event->{oldResource}{campaignBudget},\n $change_event->{newResource}{campaignBudget};\n } elsif ($resource_type eq CAMPAIGN_CRITERION) {\n return $change_event->{oldResource}{campaignCriterion},\n $change_event->{newResource}{campaignCriterion};\n } elsif ($resource_type eq CUSTOMER_ASSET) {\n return $change_event->{oldResource}{customerAsset},\n $change_event->{newResource}{customerAsset};\n } else {\n print \"Unknown change_resource_type $resource_type.\\n\";\n }\n}get_change_details.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.230Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":872,"estimatedTokens":9529}}98{"id":"doc-travel_feeds_in_search_ads_google_ads_api_google-c477b2c2","source":"documentation","title":"Travel Feeds in Search Ads | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/hotel-ads/travel-feeds","text":"Example:\n```text\nSELECT asset_set.resource_name, asset_set.name FROM customer_asset_set WHERE asset_set.type = 'TRAVEL_FEED'\n```\n\nExample:\n```text\nSELECT campaign.excluded_parent_asset_set_types FROM campaign WHERE campaign.id = 'INSERT_YOUR_CAMPAIGN_ID'\n```\n\nExample:\n```text\nSELECT asset_set.resource_name, asset_set.name FROM campaign_asset_set\nWHERE campaign_asset_set.campaign = 'INSERT_YOUR_CAMPAIGN_RESOURCE_NAME' AND asset_set.type = 'TRAVEL_FEED'\n```\n\nExample:\n```text\nSELECT campaign.name, segments.click_type, metrics.impressions, metrics.clicks FROM campaign WHERE segments.click_type = 'TRAVEL_ASSETS'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.231Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":22,"estimatedTokens":158}}99{"id":"doc-reporting_google_ads_api_google_for_developers-ff23fc47","source":"documentation","title":"Reporting | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/dynamic-search-ads/reporting","text":"Example:\n```text\nSELECT\n dynamic_search_ads_search_term_view.search_term,\n metrics.clicks,\n metrics.impressions,\n segments.date,\n metrics.cost_micros,\n dynamic_search_ads_search_term_view.landing_page\nFROM dynamic_search_ads_search_term_view\nWHERE segments.date DURING LAST_MONTH\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.232Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":14,"estimatedTokens":76}}100{"id":"doc-creating_a_hotel_ad_group_ad_google_ads_api_goog-77aa0a3b","source":"documentation","title":"Creating a Hotel Ad Group Ad | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/hotel-ads/create-ad-group-ad","text":"Example:\n```text\nprivate String addHotelAdGroupAd(\n GoogleAdsClient googleAdsClient, long customerId, String adGroupResourceName) {\n // Creates a new hotel ad.\n Ad ad = Ad.newBuilder().setHotelAd(HotelAdInfo.newBuilder().build()).build();\n // Creates a new ad group ad and sets the hotel ad to it.\n AdGroupAd adGroupAd =\n AdGroupAd.newBuilder()\n // Sets the ad to the ad created above.\n .setAd(ad)\n // Set the ad group ad to enabled. Setting this to paused will cause an error\n // for hotel campaigns. For hotels pausing should happen at either the ad group or\n // campaign level.\n .setStatus(AdGroupAdStatus.ENABLED)\n // Sets the ad group.\n .setAdGroup(adGroupResourceName)\n .build();\n\n // Creates an ad group ad operation.\n AdGroupAdOperation operation = AdGroupAdOperation.newBuilder().setCreate(adGroupAd).build();\n\n // Issues a mutate request to add an ad group ad.\n try (AdGroupAdServiceClient adGroupAdServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupAdServiceClient()) {\n MutateAdGroupAdResult mutateAdGroupAdResult =\n adGroupAdServiceClient\n .mutateAdGroupAds(Long.toString(customerId), Collections.singletonList(operation))\n .getResults(0);\n System.out.printf(\n \"Added a hotel ad group ad with resource name: '%s'%n\",\n mutateAdGroupAdResult.getResourceName());\n return mutateAdGroupAdResult.getResourceName();\n }\n}AddHotelAd.java\n```\n\nExample:\n```text\nprivate static void AddHotelAdGroupAd(GoogleAdsClient client, long customerId,\n string adGroupResourceName)\n{\n // Get the AdGroupAdService.\n AdGroupAdServiceClient service = client.GetService(Services.V25.AdGroupAdService);\n\n // Create a new ad group ad and sets the hotel ad to it.\n AdGroupAd adGroupAd = new AdGroupAd()\n {\n // Create a new hotel ad.\n Ad = new Ad()\n {\n HotelAd = new HotelAdInfo(),\n },\n // Set the ad group.\n AdGroup = adGroupResourceName,\n // Set the ad group ad to enabled. Setting this to paused will cause an error\n // for hotel campaigns. For hotels pausing should happen at either the ad group or\n // campaign level.\n Status = AdGroupAdStatus.Enabled\n };\n\n // Create an ad group ad operation.\n AdGroupAdOperation adGroupAdOperation = new AdGroupAdOperation()\n {\n Create = adGroupAd\n };\n\n // Issue a mutate request to add an ad group ad.\n MutateAdGroupAdsResponse response = service.MutateAdGroupAds(customerId.ToString(),\n new AdGroupAdOperation[] { adGroupAdOperation });\n\n MutateAdGroupAdResult addedAdGroupAd = response.Results[0];\n Console.WriteLine($\"Added a hotel ad group ad with resource name \" +\n $\"{addedAdGroupAd.ResourceName}.\");\n}AddHotelAd.cs\n```\n\nExample:\n```text\nprivate static function addHotelAdGroupAd(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $adGroupResourceName\n) {\n // Creates a new hotel ad.\n $ad = new Ad([\n 'hotel_ad' => new HotelAdInfo(),\n ]);\n\n // Creates a new ad group ad and sets the hotel ad to it.\n $adGroupAd = new AdGroupAd([\n 'ad' => $ad,\n // Set the ad group ad to enabled. Setting this to paused will cause an error\n // for hotel campaigns. For hotels pausing should happen at either the ad group or\n // campaign level.\n 'status' => AdGroupAdStatus::ENABLED,\n // Sets the ad group.\n 'ad_group' => $adGroupResourceName\n ]);\n\n // Creates an ad group ad operation.\n $adGroupAdOperation = new AdGroupAdOperation();\n $adGroupAdOperation->setCreate($adGroupAd);\n\n // Issues a mutate request to add an ad group ad.\n $adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();\n $response = $adGroupAdServiceClient->mutateAdGroupAds(\n MutateAdGroupAdsRequest::build($customerId, [$adGroupAdOperation])\n );\n\n /** @var AdGroupAd $addedAdGroupAd */\n $addedAdGroupAd = $response->getResults()[0];\n printf(\n \"Added a hotel ad group ad with resource name '%s'.%s\",\n $addedAdGroupAd->getResourceName(),\n PHP_EOL\n );\n}AddHotelAd.php\n```\n\nExample:\n```text\ndef add_hotel_ad(\n client: GoogleAdsClient, customer_id: str, ad_group_resource_name: str\n) -> str:\n ad_group_ad_service: AdGroupAdServiceClient = client.get_service(\n \"AdGroupAdService\"\n )\n\n # Creates a new ad group ad and sets the hotel ad to it.\n ad_group_ad_operation: AdGroupAdOperation = client.get_type(\n \"AdGroupAdOperation\"\n )\n ad_group_ad: AdGroupAd = ad_group_ad_operation.create\n ad_group_ad.ad_group = ad_group_resource_name\n # Set the ad group ad to enabled. Setting this to paused will cause an error\n # for hotel campaigns. For hotels pausing should happen at either the ad group or\n # campaign level.\n ad_group_ad.status = client.enums.AdGroupAdStatusEnum.ENABLED\n client.copy_from(ad_group_ad.ad.hotel_ad, client.get_type(\"HotelAdInfo\"))\n\n # Add the ad group ad.\n ad_group_ad_response: MutateAdGroupAdsResponse = (\n ad_group_ad_service.mutate_ad_group_ads(\n customer_id=customer_id, operations=[ad_group_ad_operation]\n )\n )\n\n ad_group_ad_resource_name: str = ad_group_ad_response.results[\n 0\n ].resource_name\n\n print(f\"Created hotel ad with resource name '{ad_group_ad_resource_name}'.\")\n\n return ad_group_resource_nameadd_hotel_ad.py\n```\n\nExample:\n```text\ndef add_hotel_ad_group_ad(client, customer_id, ad_group_resource)\n # Create a new hotel ad.\n ad_group_ad_operation = client.operation.create_resource.ad_group_ad do |aga|\n # Create a new ad group ad and sets the hotel ad to it.\n aga.ad = client.resource.ad do |ad|\n ad.hotel_ad = client.resource.hotel_ad_info\n end\n # Set the ad group ad to enabled. Setting this to paused will cause an error\n # for hotel campaigns. For hotels pausing should happen at either the ad group or\n # campaign level.\n aga.status = :ENABLED\n\n # Set the ad group.\n aga.ad_group = ad_group_resource\n end\n\n # Issue a mutate request to add the ad group ad.\n ad_group_ad_service = client.service.ad_group_ad\n response = ad_group_ad_service.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [ad_group_ad_operation],\n )\n\n # Fetch the new ad group ad's resource name.\n ad_group_ad_resource = response.results.first.resource_name\n\n puts \"Added hotel ad group ad with resource name '#{ad_group_ad_resource}'.\"\nendadd_hotel_ad.rb\n```\n\nExample:\n```text\nsub add_hotel_ad_group_ad {\n my ($api_client, $customer_id, $ad_group_resource_name) = @_;\n\n # Create an ad group ad and set a hotel ad to it.\n my $ad_group_ad = Google::Ads::GoogleAds::V25::Resources::AdGroupAd->new({\n # Set the ad group.\n adGroup => $ad_group_resource_name,\n # Set the ad to a new shopping product ad.\n ad => Google::Ads::GoogleAds::V25::Resources::Ad->new({\n hotelAd => Google::Ads::GoogleAds::V25::Common::HotelAdInfo->new()}\n ),\n status => Google::Ads::GoogleAds::V25::Enums::AdGroupAdStatusEnum::ENABLED\n });\n\n # Create an ad group ad operation.\n my $ad_group_ad_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation\n ->new({create => $ad_group_ad});\n\n # Add the ad group ad.\n my $ad_group_ad_resource_name = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]})->{results}[0]{resourceName};\n\n printf \"Added a hotel ad group ad with resource name: '%s'.\\n\",\n $ad_group_ad_resource_name;\n\n return $ad_group_ad_resource_name;\n}add_hotel_ad.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.233Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":223,"estimatedTokens":1943}}101{"id":"doc-creating_hotel_listing_groups_google_ads_api_goo-7c2394e8","source":"documentation","title":"Creating Hotel Listing Groups | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/hotel-ads/create-listing-groups","text":"Example:\n```text\nprivate static String addLevel1Nodes(\n long customerId,\n long adGroupId,\n String rootResourceName,\n List<AdGroupCriterionOperation> operations,\n long percentCpcBidMicroAmount) {\n // Creates hotel class info and dimension info for 5-star hotels.\n ListingDimensionInfo fiveStarredDimensionInfo =\n ListingDimensionInfo.newBuilder()\n .setHotelClass(HotelClassInfo.newBuilder().setValue(5).build())\n .build();\n // Creates listing group info for 5-star hotels as a UNIT node.\n ListingGroupInfo fiveStarredUnit =\n ListingGroupInfo.newBuilder()\n .setType(ListingGroupType.UNIT)\n .setParentAdGroupCriterion(rootResourceName)\n .setCaseValue(fiveStarredDimensionInfo)\n .build();\n // Creates an ad group criterion for 5-star hotels.\n AdGroupCriterion fiveStarredAdGroupCriterion =\n createAdGroupCriterion(customerId, adGroupId, fiveStarredUnit, percentCpcBidMicroAmount);\n // Decrements the temp ID for the next ad group criterion.\n AdGroupCriterionOperation operation = generateCreateOperation(fiveStarredAdGroupCriterion);\n operations.add(operation);\n\n // You can also create more UNIT nodes for other hotel classes by copying the above code in\n // this method and modifying the value passed to HotelClassInfo() to the value you want.\n // For instance, passing 4 instead of 5 in the above code will create a UNIT node of 4-star\n // hotels instead.\n\n // Creates hotel class info and dimension info for other hotel classes by not specifying\n // any attributes on those object.\n ListingDimensionInfo otherHotelsDimensionInfo =\n ListingDimensionInfo.newBuilder()\n .setHotelClass(HotelClassInfo.newBuilder().build())\n .build();\n // Creates listing group info for other hotel classes as a SUBDIVISION node, which will be\n // used as a parent node for children nodes of the next level.\n ListingGroupInfo otherHotelsSubdivision =\n createListingGroupInfo(\n ListingGroupType.SUBDIVISION, rootResourceName, otherHotelsDimensionInfo);\n // Creates an ad group criterion for other hotel classes.\n AdGroupCriterion otherHotelsAdGroupCriterion =\n createAdGroupCriterion(\n customerId, adGroupId, otherHotelsSubdivision, percentCpcBidMicroAmount);\n operation = generateCreateOperation(otherHotelsAdGroupCriterion);\n operations.add(operation);\n\n return otherHotelsAdGroupCriterion.getResourceName();\n}AddHotelListingGroupTree.java\n```\n\nExample:\n```text\nprivate string AddLevel1Nodes(long customerId, long adGroupId, string rootResourceName,\n List<AdGroupCriterionOperation> operations, long percentCpcBidMicroAmount)\n{\n // Create listing dimension info for 5-star class hotels.\n ListingDimensionInfo fiveStarredListingDimensionInfo = new ListingDimensionInfo\n {\n HotelClass = new HotelClassInfo\n {\n Value = 5\n }\n };\n\n // Create a listing group info for 5-star hotels as a UNIT node.\n ListingGroupInfo fiveStarredUnit = CreateListingGroupInfo(ListingGroupType.Unit,\n rootResourceName, fiveStarredListingDimensionInfo);\n\n // Create an ad group criterion for 5-star hotels.\n AdGroupCriterion fiveStarredAdGroupCriterion = CreateAdGroupCriterion(customerId,\n adGroupId, fiveStarredUnit, percentCpcBidMicroAmount);\n\n // Create an operation and add it to the list of operations.\n operations.Add(new AdGroupCriterionOperation\n {\n Create = fiveStarredAdGroupCriterion\n });\n\n // Decrement the temp ID for the next ad group criterion.\n nextTempId--;\n\n // You can also create more UNIT nodes for other hotel classes by copying the above code\n // in this method and modifying the value passed to HotelClassInfo().\n // For instance, passing 4 instead of 5 in the above code will instead create a UNIT\n // node of 4-star hotels.\n\n // Create hotel class info and dimension info for other hotel classes by *not*\n // specifying any attributes on those object.\n ListingDimensionInfo otherHotelsListingDimensionInfo = new ListingDimensionInfo\n {\n HotelClass = new HotelClassInfo()\n };\n\n // Create listing group info for other hotel classes as a SUBDIVISION node, which will\n // be used as a parent node for children nodes of the next level.\n ListingGroupInfo otherHotelsSubdivisionListingGroupInfo = CreateListingGroupInfo\n (ListingGroupType.Subdivision, rootResourceName, otherHotelsListingDimensionInfo);\n\n // Create an ad group criterion for other hotel classes.\n AdGroupCriterion otherHotelsAdGroupCriterion = CreateAdGroupCriterion(customerId,\n adGroupId, otherHotelsSubdivisionListingGroupInfo, percentCpcBidMicroAmount);\n\n // Create an operation and add it to the list of operations.\n operations.Add(new AdGroupCriterionOperation\n {\n Create = otherHotelsAdGroupCriterion\n });\n\n // Decrement the temp ID for the next ad group criterion.\n nextTempId--;\n\n return otherHotelsAdGroupCriterion.ResourceName;\n}AddHotelListingGroupTree.cs\n```\n\nExample:\n```text\nprivate static function addLevel1Nodes(\n int $customerId,\n int $adGroupId,\n string $rootResourceName,\n array &$operations,\n int $percentCpcBidMicroAmount\n) {\n // Creates hotel class info and dimension info for 5-star hotels.\n $fiveStarredDimensionInfo = new ListingDimensionInfo([\n 'hotel_class' => new HotelClassInfo(['value' => 5])\n ]);\n // Creates listing group info for 5-star hotels as a UNIT node.\n $fiveStarredUnit = self::createListingGroupInfo(\n ListingGroupType::UNIT,\n $rootResourceName,\n $fiveStarredDimensionInfo\n );\n // Creates an ad group criterion for 5-star hotels.\n $fiveStarredAdGroupCriterion = self::createAdGroupCriterion(\n $customerId,\n $adGroupId,\n $fiveStarredUnit,\n $percentCpcBidMicroAmount\n );\n // Decrements the temp ID for the next ad group criterion.\n self::$nextTempId--;\n $operation = self::generateCreateOperation($fiveStarredAdGroupCriterion);\n $operations[] = $operation;\n\n // You can also create more UNIT nodes for other hotel classes by copying the above code in\n // this method and modifying the value passed to HotelClassInfo() to the value you want.\n // For instance, passing 4 instead of 5 in the above code will create a UNIT node of 4-star\n // hotels instead.\n\n // Creates hotel class info and dimension info for other hotel classes by *not* specifying\n // any attributes on those object.\n $othersHotelsDimensionInfo = new ListingDimensionInfo([\n 'hotel_class' => new HotelClassInfo()\n ]);\n // Creates listing group info for other hotel classes as a SUBDIVISION node, which will be\n // used as a parent node for children nodes of the next level.\n $otherHotelsSubDivision = self::createListingGroupInfo(\n ListingGroupType::SUBDIVISION,\n $rootResourceName,\n $othersHotelsDimensionInfo\n );\n // Creates an ad group criterion for other hotel classes.\n $otherHotelsAdGroupCriterion = self::createAdGroupCriterion(\n $customerId,\n $adGroupId,\n $otherHotelsSubDivision,\n $percentCpcBidMicroAmount\n );\n $operation = self::generateCreateOperation($otherHotelsAdGroupCriterion);\n $operations[] = $operation;\n\n self::$nextTempId--;\n return $otherHotelsAdGroupCriterion->getResourceName();\n}AddHotelListingGroupTree.php\n```\n\nExample:\n```text\ndef add_level1_nodes(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n root_resource_name: str,\n operations: List[AdGroupCriterionOperation],\n percent_cpc_bid_micro_amount: int,\n) -> str:\n \"\"\"Creates child nodes on level 1, partitioned by the hotel class info.\n\n Args:\n client: The Google Ads API client.\n customer_id: The Google Ads customer ID.\n ad_group_id: The ad group ID to which the hotel listing group will be\n added.\n root_resource_name: The string resource name of the listing group's root\n node.\n operations: A list of AdGroupCriterionOperations.\n percent_cpc_bid_micro_amount: The CPC bid micro amount to be set on\n created ad group criteria.\n\n Returns:\n The string resource name of the \"other hotel classes\" node, which serves\n as the parent node for the next level of the listing tree.\n \"\"\"\n global next_temp_id\n\n # Create listing dimension info for 5-star class hotels.\n five_starred_listing_dimension_info: ListingDimensionInfo = client.get_type(\n \"ListingDimensionInfo\"\n )\n five_starred_listing_dimension_info.hotel_class.value = 5\n\n # Create a listing group info for 5-star hotels as a UNIT node.\n five_starred_unit: ListingGroupInfo = create_listing_group_info(\n client,\n client.enums.ListingGroupTypeEnum.UNIT,\n root_resource_name,\n five_starred_listing_dimension_info,\n )\n\n # Create an ad group criterion for 5-star hotels.\n five_starred_ad_group_criterion: AdGroupCriterion = (\n create_ad_group_criterion(\n client,\n customer_id,\n ad_group_id,\n five_starred_unit,\n percent_cpc_bid_micro_amount,\n )\n )\n\n # Create an operation and add it to the list of operations.\n five_starred_ad_group_criterion_operation: AdGroupCriterionOperation = (\n client.get_type(\"AdGroupCriterionOperation\")\n )\n client.copy_from(\n five_starred_ad_group_criterion_operation.create,\n five_starred_ad_group_criterion,\n )\n operations.append(five_starred_ad_group_criterion_operation)\n\n # Decrement the temp ID for the next ad group criterion.\n next_temp_id -= 1\n\n # You can also create more UNIT nodes for other hotel classes by copying the\n # above code in this method and modifying the hotel class value.\n # For instance, passing 4 instead of 5 in the above code will instead create\n # a UNIT node of 4-star hotels.\n\n # Create hotel class info and dimension info without any specifying\n # attributes. This node will then represent hotel classes other than those\n # already covered by UNIT nodes at this level.\n other_hotels_listing_dimension_info: ListingDimensionInfo = client.get_type(\n \"ListingDimensionInfo\"\n )\n # Set \"hotel_class\" as the oneof field on the ListingDimensionInfo object\n # without specifying the optional hotel_class field.\n client.copy_from(\n other_hotels_listing_dimension_info.hotel_class,\n client.get_type(\"HotelClassInfo\"),\n )\n\n # Create listing group info for other hotel classes as a SUBDIVISION node,\n # which will be used as a parent node for children nodes of the next level.\n other_hotels_subdivision_listing_group_info: ListingGroupInfo = (\n create_listing_group_info(\n client,\n client.enums.ListingGroupTypeEnum.SUBDIVISION,\n root_resource_name,\n other_hotels_listing_dimension_info,\n )\n )\n\n # Create an ad group criterion for other hotel classes.\n other_hotels_ad_group_criterion: AdGroupCriterion = (\n create_ad_group_criterion(\n client,\n customer_id,\n ad_group_id,\n other_hotels_subdivision_listing_group_info,\n percent_cpc_bid_micro_amount,\n )\n )\n\n # Create an operation and add it to the list of operations.\n other_hotels_ad_group_criterion_operation: AdGroupCriterionOperation = (\n client.get_type(\"AdGroupCriterionOperation\")\n )\n client.copy_from(\n other_hotels_ad_group_criterion_operation.create,\n other_hotels_ad_group_criterion,\n )\n operations.append(other_hotels_ad_group_criterion_operation)\n\n # Decrement the temp ID for the next ad group criterion.\n next_temp_id -= 1\n\n return other_hotels_ad_group_criterion.resource_nameadd_hotel_listing_group_tree.py\n```\n\nExample:\n```text\ndef add_level1_nodes(\n client,\n customer_id,\n ad_group_id,\n root_resource_name,\n operations,\n percent_cpc_bid_micro_amount)\n # Creates hotel class info and dimension info for 5-star hotels.\n five_starred_dimension_info = client.resource.listing_dimension_info do |d|\n d.hotel_class = client.resource.hotel_class_info do |c|\n c.value = 5\n end\n end\n\n # Creates listing group info for 5-star hotels as a UNIT node.\n five_starred_unit = create_listing_group_info(\n client,\n :UNIT,\n root_resource_name,\n five_starred_dimension_info,\n )\n\n # Creates an ad group criterion for 5-star hotels.\n five_starred_ad_group_criterion = create_ad_group_criterion(\n client,\n customer_id,\n ad_group_id,\n five_starred_unit,\n percent_cpc_bid_micro_amount,\n )\n\n operations << generate_create_operation(\n client,\n five_starred_ad_group_criterion,\n )\n\n # You can also create more UNIT nodes for other hotel classes by copying the\n # above code in this method and modifying the value passed to HotelClassInfo()\n # to the value you want.\n # For instance, passing 4 instead of 5 in the above code will create a UNIT\n # node of 4-star hotels instead.\n\n # Creates hotel class info and dimension info for other hotel classes\n # by *not* specifying any attributes on those object.\n other_hotels_dimention_info = client.resource.listing_dimension_info do |d|\n d.hotel_class = client.resource.hotel_class_info\n end\n\n # Creates listing group info for other hotel classes as a SUBDIVISION node,\n # which will be used as a parent node for children nodes of the next level.\n other_hotels_subdivision = create_listing_group_info(\n client,\n :SUBDIVISION,\n root_resource_name,\n other_hotels_dimention_info,\n )\n\n # Creates an ad group criterion for other hotel classes.\n other_hotels_ad_group_criterion = create_ad_group_criterion(\n client,\n customer_id,\n ad_group_id,\n other_hotels_subdivision,\n percent_cpc_bid_micro_amount,\n )\n\n operations << generate_create_operation(\n client,\n other_hotels_ad_group_criterion,\n )\n\n other_hotels_ad_group_criterion.resource_name\nendadd_hotel_listing_group_tree.rb\n```\n\nExample:\n```text\nsub add_level_1_nodes {\n my ($customer_id, $ad_group_id, $root_resource_name, $operations,\n $percent_cpc_bid_micro_amount)\n = @_;\n\n # Create hotel class info and dimension info for 5-star hotels.\n my $five_starred_dimension_info =\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n hotelClass => Google::Ads::GoogleAds::V25::Common::HotelClassInfo->new({\n value => 5\n })});\n\n # Create listing group info for 5-star hotels as a UNIT node.\n my $five_starred_unit = create_listing_group_info(UNIT, $root_resource_name,\n $five_starred_dimension_info);\n\n # Create an ad group criterion for 5-star hotels.\n my $five_starred_ad_group_criterion =\n create_ad_group_criterion($customer_id, $ad_group_id, $five_starred_unit,\n $percent_cpc_bid_micro_amount);\n\n my $operation = generate_create_operation($five_starred_ad_group_criterion);\n push @$operations, $operation;\n\n # You can also create more UNIT nodes for other hotel classes by copying the\n # above code in this method and modifying the value passed to HotelClassInfo\n # to the value you want. For instance, passing 4 instead of 5 in the above code\n # will create a UNIT node of 4-star hotels instead.\n\n # Create hotel class info and dimension info for other hotel classes by *not*\n # specifying any attributes on those object.\n my $others_hotels_dimension_info =\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n hotelClass => Google::Ads::GoogleAds::V25::Common::HotelClassInfo->new()}\n );\n\n # Create listing group info for other hotel classes as a SUBDIVISION node, which\n # will be used as a parent node for children nodes of the next level.\n my $other_hotels_subdivision =\n create_listing_group_info(SUBDIVISION, $root_resource_name,\n $others_hotels_dimension_info);\n\n # Create an ad group criterion for other hotel classes.\n my $other_hotels_ad_group_criterion =\n create_ad_group_criterion($customer_id, $ad_group_id,\n $other_hotels_subdivision, $percent_cpc_bid_micro_amount);\n\n $operation = generate_create_operation($other_hotels_ad_group_criterion);\n push @$operations, $operation;\n\n return $other_hotels_ad_group_criterion->{resourceName};\n}add_hotel_listing_group_tree.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.234Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":439,"estimatedTokens":4108}}102{"id":"doc-create_campaigns_google_ads_api_google_for_devel-99fb3bfd","source":"documentation","title":"Create Campaigns | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/create-campaigns","text":"Example:\n```text\n// Copyright 2018 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.basicoperations;\n\nimport static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime;\nimport static com.google.ads.googleads.v25.enums.EuPoliticalAdvertisingStatusEnum.EuPoliticalAdvertisingStatus.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.ManualCpc;\nimport com.google.ads.googleads.v25.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType;\nimport com.google.ads.googleads.v25.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod;\nimport com.google.ads.googleads.v25.enums.CampaignStatusEnum.CampaignStatus;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.Campaign;\nimport com.google.ads.googleads.v25.resources.Campaign.NetworkSettings;\nimport com.google.ads.googleads.v25.resources.CampaignBudget;\nimport com.google.ads.googleads.v25.services.CampaignBudgetOperation;\nimport com.google.ads.googleads.v25.services.CampaignBudgetServiceClient;\nimport com.google.ads.googleads.v25.services.CampaignOperation;\nimport com.google.ads.googleads.v25.services.CampaignServiceClient;\nimport com.google.ads.googleads.v25.services.MutateCampaignBudgetsResponse;\nimport com.google.ads.googleads.v25.services.MutateCampaignResult;\nimport com.google.ads.googleads.v25.services.MutateCampaignsResponse;\nimport com.google.common.collect.ImmutableList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.joda.time.DateTime;\n\n/** Adds new campaigns to a client account. */\npublic class AddCampaigns {\n\n /** The number of campaigns this example will add. */\n private static final int NUMBER_OF_CAMPAIGNS_TO_ADD = 2;\n\n private static class AddCampaignsParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n }\n\n public static void main(String[] args) {\n AddCampaignsParams params = new AddCampaignsParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new AddCampaigns().runExample(googleAdsClient, params.customerId);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Creates a new CampaignBudget in the specified client account.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @return resource name of the newly created budget.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private static String addCampaignBudget(GoogleAdsClient googleAdsClient, long customerId) {\n CampaignBudget budget =\n CampaignBudget.newBuilder()\n .setName(\"Interplanetary Cruise Budget #\" + getPrintableDateTime())\n .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)\n .setAmountMicros(500_000)\n .build();\n\n CampaignBudgetOperation op = CampaignBudgetOperation.newBuilder().setCreate(budget).build();\n\n try (CampaignBudgetServiceClient campaignBudgetServiceClient =\n googleAdsClient.getLatestVersion().createCampaignBudgetServiceClient()) {\n MutateCampaignBudgetsResponse response =\n campaignBudgetServiceClient.mutateCampaignBudgets(\n Long.toString(customerId), ImmutableList.of(op));\n String budgetResourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Added budget: %s%n\", budgetResourceName);\n return budgetResourceName;\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void runExample(GoogleAdsClient googleAdsClient, long customerId) {\n\n // Creates a single shared budget to be used by the campaigns added below.\n String budgetResourceName = addCampaignBudget(googleAdsClient, customerId);\n\n List<CampaignOperation> operations = new ArrayList<>(NUMBER_OF_CAMPAIGNS_TO_ADD);\n\n for (int i = 0; i < NUMBER_OF_CAMPAIGNS_TO_ADD; i++) {\n // Configures the campaign network options\n NetworkSettings networkSettings =\n NetworkSettings.newBuilder()\n .setTargetGoogleSearch(true)\n .setTargetSearchNetwork(true)\n // Enables Display Expansion on Search campaigns. See\n // https://support.google.com/google-ads/answer/7193800 to learn more.\n .setTargetContentNetwork(true)\n .setTargetPartnerSearchNetwork(false)\n .build();\n\n // Creates the campaign.\n Campaign campaign =\n Campaign.newBuilder()\n .setName(\"Interplanetary Cruise #\" + getPrintableDateTime())\n .setAdvertisingChannelType(AdvertisingChannelType.SEARCH)\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve\n .setStatus(CampaignStatus.PAUSED)\n // Sets the bidding strategy and budget.\n .setManualCpc(ManualCpc.newBuilder().build())\n .setCampaignBudget(budgetResourceName)\n // Adds the networkSettings configured above.\n .setNetworkSettings(networkSettings)\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n // Optional: Sets the start & end dates.\n .setStartDateTime(new DateTime().plusDays(1).toString(\"yyyy-MM-dd 00:00:00\"))\n .setEndDateTime(new DateTime().plusDays(30).toString(\"yyyy-MM-dd 23:59:59\"))\n .build();\n\n CampaignOperation op = CampaignOperation.newBuilder().setCreate(campaign).build();\n operations.add(op);\n }\n\n try (CampaignServiceClient campaignServiceClient =\n googleAdsClient.getLatestVersion().createCampaignServiceClient()) {\n MutateCampaignsResponse response =\n campaignServiceClient.mutateCampaigns(Long.toString(customerId), operations);\n System.out.printf(\"Added %d campaigns:%n\", response.getResultsCount());\n for (MutateCampaignResult result : response.getResultsList()) {\n System.out.println(result.getResourceName());\n }\n }\n }\n}\nAddCampaigns.java\n```\n\nExample:\n```text\n// Copyright 2019 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Config;\nusing Google.Ads.GoogleAds.Extensions.Config;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System;\nusing System.Collections.Generic;\nusing System.Configuration;\nusing static Google.Ads.GoogleAds.V25.Enums.AdvertisingChannelTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.BudgetDeliveryMethodEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.CampaignStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.EuPoliticalAdvertisingStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Resources.Campaign.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example adds campaigns.\n /// </summary>\n public class AddCampaigns : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"AddCampaigns\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n AddCampaigns codeExample = new AddCampaigns();\n Console.WriteLine(codeExample.Description); \n codeExample.Run(new GoogleAdsClient(),\n options.CustomerId);\n }\n\n /// <summary>\n /// Number of campaigns to create.\n /// </summary>\n private const int NUM_CAMPAIGNS_TO_CREATE = 5;\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description => \"This code example adds campaigns. To get \" +\n \"campaigns, run GetCampaign.cs.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n public void Run(GoogleAdsClient client, long customerId)\n {\n // Get the CampaignService.\n CampaignServiceClient campaignService = client.GetService(Services.V25.CampaignService);\n\n // Create a budget to be used for the campaign.\n string budget = CreateBudget(client, customerId);\n\n List<CampaignOperation> operations = new List<CampaignOperation>();\n\n for (int i = 0; i < NUM_CAMPAIGNS_TO_CREATE; i++)\n {\n // Create the campaign.\n Campaign campaign = new Campaign()\n {\n Name = \"Interplanetary Cruise #\" + ExampleUtilities.GetRandomString(),\n AdvertisingChannelType = AdvertisingChannelType.Search,\n\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve\n Status = CampaignStatus.Paused,\n\n // Set the bidding strategy and budget.\n ManualCpc = new ManualCpc(),\n CampaignBudget = budget,\n\n // Set the campaign network options.\n NetworkSettings = new NetworkSettings\n {\n TargetGoogleSearch = true,\n TargetSearchNetwork = true,\n // Enable Display Expansion on Search campaigns. See\n // https://support.google.com/google-ads/answer/7193800 to learn more.\n TargetContentNetwork = true,\n TargetPartnerSearchNetwork = false\n },\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n\n // Optional: Set the start date.\n StartDateTime = DateTime.Now.AddDays(1).ToString(\"yyyyMMdd 00:00:00\"),\n\n // Optional: Set the end date.\n EndDateTime = DateTime.Now.AddYears(1).ToString(\"yyyyMMdd 23:59:59\"),\n };\n\n // Create the operation.\n operations.Add(new CampaignOperation() { Create = campaign });\n }\n try\n {\n // Add the campaigns.\n MutateCampaignsResponse retVal = campaignService.MutateCampaigns(\n customerId.ToString(), operations);\n\n // Display the results.\n if (retVal.Results.Count > 0)\n {\n foreach (MutateCampaignResult newCampaign in retVal.Results)\n {\n Console.WriteLine(\"Campaign with resource ID = '{0}' was added.\",\n newCampaign.ResourceName);\n }\n }\n else\n {\n Console.WriteLine(\"No campaigns were added.\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Creates the budget for the campaign.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <returns>The resource name of the newly created campaign budget.</returns>\n private static string CreateBudget(GoogleAdsClient client, long customerId)\n {\n // Get the BudgetService.\n CampaignBudgetServiceClient budgetService = client.GetService(\n Services.V25.CampaignBudgetService);\n\n // Create the campaign budget.\n CampaignBudget budget = new CampaignBudget()\n {\n Name = \"Interplanetary Cruise Budget #\" + ExampleUtilities.GetRandomString(),\n DeliveryMethod = BudgetDeliveryMethod.Standard,\n AmountMicros = 500000\n };\n\n // Create the operation.\n CampaignBudgetOperation budgetOperation = new CampaignBudgetOperation()\n {\n Create = budget\n };\n\n // Create the campaign budget.\n MutateCampaignBudgetsResponse response = budgetService.MutateCampaignBudgets(\n customerId.ToString(), new CampaignBudgetOperation[] { budgetOperation });\n return response.Results[0].ResourceName;\n }\n }\n}\nAddCampaigns.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2018 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\BasicOperations;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ManualCpc;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdvertisingChannelTypeEnum\\AdvertisingChannelType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\BudgetDeliveryMethodEnum\\BudgetDeliveryMethod;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\CampaignStatusEnum\\CampaignStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\EuPoliticalAdvertisingStatusEnum\\EuPoliticalAdvertisingStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Campaign;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Campaign\\NetworkSettings;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CampaignBudget;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignBudgetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateCampaignsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateCampaignBudgetsRequest;\nuse Google\\ApiCore\\ApiException;\n\n/** This example adds new campaigns to an account. */\nclass AddCampaigns\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const NUMBER_OF_CAMPAIGNS_TO_ADD = 2;\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())\n ->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n */\n public static function runExample(GoogleAdsClient $googleAdsClient, int $customerId)\n {\n // Creates a single shared budget to be used by the campaigns added below.\n $budgetResourceName = self::addCampaignBudget($googleAdsClient, $customerId);\n\n // Configures the campaign network options.\n $networkSettings = new NetworkSettings([\n 'target_google_search' => true,\n 'target_search_network' => true,\n // Enables Display Expansion on Search campaigns. See\n // https://support.google.com/google-ads/answer/7193800 to learn more.\n 'target_content_network' => true,\n 'target_partner_search_network' => false\n ]);\n\n $campaignOperations = [];\n for ($i = 0; $i < self::NUMBER_OF_CAMPAIGNS_TO_ADD; $i++) {\n // Creates a campaign.\n $campaign = new Campaign([\n 'name' => 'Interplanetary Cruise #' . Helper::getPrintableDatetime(),\n 'advertising_channel_type' => AdvertisingChannelType::SEARCH,\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve.\n 'status' => CampaignStatus::PAUSED,\n // Sets the bidding strategy and budget.\n 'manual_cpc' => new ManualCpc(),\n 'campaign_budget' => $budgetResourceName,\n // Adds the network settings configured above.\n 'network_settings' => $networkSettings,\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n // Optional: Sets the start and end dates.\n 'start_date_time' => date('Y-m-d 00:00:00', strtotime('+1 day')),\n 'end_date_time' => date('Y-m-d 23:59:59', strtotime('+1 month'))\n ]);\n\n // Creates a campaign operation.\n $campaignOperation = new CampaignOperation();\n $campaignOperation->setCreate($campaign);\n $campaignOperations[] = $campaignOperation;\n }\n\n // Issues a mutate request to add campaigns.\n $campaignServiceClient = $googleAdsClient->getCampaignServiceClient();\n $response = $campaignServiceClient->mutateCampaigns(\n MutateCampaignsRequest::build($customerId, $campaignOperations)\n );\n\n printf(\"Added %d campaigns:%s\", $response->getResults()->count(), PHP_EOL);\n\n foreach ($response->getResults() as $addedCampaign) {\n /** @var Campaign $addedCampaign */\n print \"{$addedCampaign->getResourceName()}\" . PHP_EOL;\n }\n }\n\n /**\n * Creates a new campaign budget in the specified client account.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @return string the resource name of the newly created budget\n */\n private static function addCampaignBudget(GoogleAdsClient $googleAdsClient, int $customerId)\n {\n // Creates a campaign budget.\n $budget = new CampaignBudget([\n 'name' => 'Interplanetary Cruise Budget #' . Helper::getPrintableDatetime(),\n 'delivery_method' => BudgetDeliveryMethod::STANDARD,\n 'amount_micros' => 500000\n ]);\n\n // Creates a campaign budget operation.\n $campaignBudgetOperation = new CampaignBudgetOperation();\n $campaignBudgetOperation->setCreate($budget);\n\n // Issues a mutate request.\n $campaignBudgetServiceClient = $googleAdsClient->getCampaignBudgetServiceClient();\n $response = $campaignBudgetServiceClient->mutateCampaignBudgets(\n MutateCampaignBudgetsRequest::build($customerId, [$campaignBudgetOperation])\n );\n\n /** @var CampaignBudget $addedBudget */\n $addedBudget = $response->getResults()[0];\n printf(\"Added budget named '%s'%s\", $addedBudget->getResourceName(), PHP_EOL);\n\n return $addedBudget->getResourceName();\n }\n}\n\nAddCampaigns::main();\nAddCampaigns.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2018 Google LLC\n#\n# 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.\n\"\"\"This example illustrates how to add a campaign.\n\nTo get campaigns, run get_campaigns.py.\n\"\"\"\n\nimport argparse\nimport datetime\nimport logging\nimport sys\nfrom typing import List\nimport uuid\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.services.services.campaign_budget_service import (\n CampaignBudgetServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.campaign_budget_service import (\n CampaignBudgetOperation,\n MutateCampaignBudgetsResponse,\n)\nfrom google.ads.googleads.v24.services.services.campaign_service import (\n CampaignServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.campaign_service import (\n CampaignOperation,\n MutateCampaignsResponse,\n)\nfrom google.ads.googleads.v24.resources.types.campaign_budget import (\n CampaignBudget,\n)\nfrom google.ads.googleads.v24.resources.types.campaign import Campaign\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\n_START_DATE_FORMAT: str = \"%Y%m%d 00:00:00\"\n_END_DATE_FORMAT: str = \"%Y%m%d 23:59:59\"\n\n\ndef main(client: GoogleAdsClient, customer_id: str) -> None:\n campaign_budget_service: CampaignBudgetServiceClient = client.get_service(\n \"CampaignBudgetService\"\n )\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n # Create a budget, which can be shared by multiple campaigns.\n campaign_budget_operation: CampaignBudgetOperation = client.get_type(\n \"CampaignBudgetOperation\"\n )\n campaign_budget: CampaignBudget = campaign_budget_operation.create\n campaign_budget.name = f\"Interplanetary Budget {uuid.uuid4()}\"\n campaign_budget.delivery_method = (\n client.enums.BudgetDeliveryMethodEnum.STANDARD\n )\n campaign_budget.amount_micros = 500000\n\n # Add budget.\n campaign_budget_response: MutateCampaignBudgetsResponse\n try:\n budget_operations: List[CampaignBudgetOperation] = [\n campaign_budget_operation\n ]\n campaign_budget_response = (\n campaign_budget_service.mutate_campaign_budgets(\n customer_id=customer_id,\n operations=budget_operations,\n )\n )\n except GoogleAdsException as ex:\n handle_googleads_exception(ex)\n # We are exiting in handle_googleads_exception so this return is not\n # strictly necessary, but it makes static analysis happier.\n return\n\n # Create campaign.\n campaign_operation: CampaignOperation = client.get_type(\"CampaignOperation\")\n campaign: Campaign = campaign_operation.create\n campaign.name = f\"Interplanetary Cruise {uuid.uuid4()}\"\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.SEARCH\n )\n\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n\n # Set the bidding strategy and budget.\n campaign.manual_cpc = client.get_type(\"ManualCpc\")\n campaign.campaign_budget = campaign_budget_response.results[0].resource_name\n\n # Set the campaign network options.\n campaign.network_settings.target_google_search = True\n campaign.network_settings.target_search_network = True\n campaign.network_settings.target_partner_search_network = False\n # Enable Display Expansion on Search campaigns. For more details see:\n # https://support.google.com/google-ads/answer/7193800\n campaign.network_settings.target_content_network = True\n\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n # Optional: Set the start date.\n start_time: datetime.date = datetime.date.today() + datetime.timedelta(\n days=1\n )\n campaign.start_date_time = datetime.date.strftime(\n start_time, _START_DATE_FORMAT\n )\n\n # Optional: Set the end date.\n end_time: datetime.date = start_time + datetime.timedelta(weeks=4)\n campaign.end_date_time = datetime.date.strftime(end_time, _END_DATE_FORMAT)\n\n # Add the campaign.\n campaign_response: MutateCampaignsResponse\n try:\n campaign_operations: List[CampaignOperation] = [campaign_operation]\n campaign_response = campaign_service.mutate_campaigns(\n customer_id=customer_id, operations=campaign_operations\n )\n print(f\"Created campaign {campaign_response.results[0].resource_name}.\")\n except GoogleAdsException as ex:\n handle_googleads_exception(ex)\n\n\ndef handle_googleads_exception(exception: GoogleAdsException) -> None:\n print(\n f'Request with ID \"{exception.request_id}\" failed with status '\n f'\"{exception.error.code().name}\" and includes the following errors:'\n )\n for error in exception.failure.errors:\n print(f'\\tError with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\n\n\nif __name__ == \"__main__\":\n parser = argparse.ArgumentParser(\n description=\"Adds a campaign for specified customer.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n args: argparse.Namespace = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n main(googleads_client, args.customer_id)\nadd_campaigns.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2018 Google LLC\n#\n# 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.\n#\n# This example adds a campaign. To get campaigns, run get_campaigns.rb.\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\nrequire 'date'\n\ndef add_campaigns(customer_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Create a budget, which can be shared by multiple campaigns.\n campaign_budget = client.resource.campaign_budget do |cb|\n cb.name = \"Interplanetary Budget #{(Time.new.to_f * 1000).to_i}\"\n cb.delivery_method = :STANDARD\n cb.amount_micros = 500000\n end\n\n operation = client.operation.create_resource.campaign_budget(campaign_budget)\n\n # Add budget.\n return_budget = client.service.campaign_budget.mutate_campaign_budgets(\n customer_id: customer_id,\n operations: [operation],\n )\n\n # Create campaign.\n campaign = client.resource.campaign do |c|\n c.name = \"Interplanetary Cruise #{(Time.new.to_f * 1000).to_i}\"\n c.advertising_channel_type = :SEARCH\n\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n c.status = :PAUSED\n\n # Set the bidding strategy and budget.\n c.manual_cpc = client.resource.manual_cpc\n c.campaign_budget = return_budget.results.first.resource_name\n\n # Set the campaign network options.\n c.network_settings = client.resource.network_settings do |ns|\n ns.target_google_search = true\n ns.target_search_network = true\n # Enable Display Expansion on Search campaigns. See\n # https://support.google.com/google-ads/answer/7193800 to learn more.\n ns.target_content_network = true\n ns.target_partner_search_network = false\n end\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n # Optional: Set the start date.\n c.start_date_time = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d %H:%M:%S')\n\n # Optional: Set the end date.\n c.end_date_time = DateTime.parse((Date.today.next_year).to_s).strftime('%Y%m%d %H:%M:%S')\n end\n\n # Create the operation.\n campaign_operation = client.operation.create_resource.campaign(campaign)\n\n # Add the campaign.\n response = client.service.campaign.mutate_campaigns(\n customer_id: customer_id,\n operations: [campaign_operation],\n )\n\n puts \"Created campaign #{response.results.first.resource_name}.\"\nend\n\nif __FILE__ == $0\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: add_campaigns.rb [options]')\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n add_campaigns(options.fetch(:customer_id).tr(\"-\", \"\"))\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\nadd_campaigns.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2019, Google LLC\n#\n# 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# http://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.\n#\n# This example adds a campaign. To get campaigns, run get_campaigns.pl.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignBudget;\nuse Google::Ads::GoogleAds::V25::Resources::Campaign;\nuse Google::Ads::GoogleAds::V25::Resources::NetworkSettings;\nuse Google::Ads::GoogleAds::V25::Common::ManualCpc;\nuse Google::Ads::GoogleAds::V25::Enums::BudgetDeliveryMethodEnum qw(STANDARD);\nuse Google::Ads::GoogleAds::V25::Enums::AdvertisingChannelTypeEnum qw(SEARCH);\nuse Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum qw(PAUSED);\nuse Google::Ads::GoogleAds::V25::Enums::EuPoliticalAdvertisingStatusEnum\n qw(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING);\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation;\nuse Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\nuse POSIX qw(strftime);\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\n\nsub add_campaigns {\n my ($api_client, $customer_id) = @_;\n\n # Create a campaign budget, which can be shared by multiple campaigns.\n my $campaign_budget =\n Google::Ads::GoogleAds::V25::Resources::CampaignBudget->new({\n name => \"Interplanetary budget #\" . uniqid(),\n deliveryMethod => STANDARD,\n amountMicros => 500000\n });\n\n # Create a campaign budget operation.\n my $campaign_budget_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation\n ->new({create => $campaign_budget});\n\n # Add the campaign budget.\n my $campaign_budgets_response = $api_client->CampaignBudgetService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_budget_operation]});\n\n # Create a campaign.\n my $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n name => \"Interplanetary Cruise #\" . uniqid(),\n advertisingChannelType => SEARCH,\n # Recommendation: Set the campaign to PAUSED when creating it to stop\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n status => PAUSED,\n # Set the bidding strategy and budget.\n manualCpc => Google::Ads::GoogleAds::V25::Common::ManualCpc->new(),\n campaignBudget => $campaign_budgets_response->{results}[0]{resourceName},\n # Set the campaign network options.\n networkSettings =>\n Google::Ads::GoogleAds::V25::Resources::NetworkSettings->new({\n targetGoogleSearch => \"true\",\n targetSearchNetwork => \"true\",\n # Enable Display Expansion on Search campaigns. See\n # https://support.google.com/google-ads/answer/7193800 to learn more.\n targetContentNetwork => \"true\",\n targetPartnerSearchNetwork => \"false\"\n }\n ),\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n # Optional: Set the start datetime. The campaign starts tomorrow.\n startDateTime =>\n strftime(\"%Y%m%d 00:00:00\", localtime(time + 60 * 60 * 24)),\n # Optional: Set the end datetime. The campaign runs for 30 days.\n endDateTime =>\n strftime(\"%Y%m%d 23:59:59\", localtime(time + 60 * 60 * 24 * 30)),\n });\n\n # Create a campaign operation.\n my $campaign_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation->\n new({create => $campaign});\n\n # Add the campaign.\n my $campaigns_response = $api_client->CampaignService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_operation]});\n\n printf \"Created campaign '%s'.\\n\",\n $campaigns_response->{results}[0]{resourceName};\n\n return 1;\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\"customer_id=s\" => \\$customer_id);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id);\n\n# Call the example.\nadd_campaigns($api_client, $customer_id =~ s/-//gr);\n\n=pod\n\n=head1 NAME\n\nadd_campaigns\n\n=head1 DESCRIPTION\n\nThis example adds a campaign. To get campaigns, run get_campaigns.pl.\n\n=head1 SYNOPSIS\n\nadd_campaigns.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n\n=cut\nadd_campaigns.pl\n```\n\nExample:\n```text\n#!/bin/bash\n# Copyright 2025 Google LLC\n\n# 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.\n\n# Creates a campaign budget.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\ncurl -f --request POST \\\n \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/campaignBudgets:mutate\" \\\n --header \"Content-Type: application/json\" \\\n --header \"Developer-Token: ${DEVELOPER_TOKEN}\" \\\n --header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n --header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n --data @- <<EOF\n{\n \"operations\": [\n {\n \"create\": {\n \"name\":\"Interplanetary Cruise Budget #${RANDOM}\",\n \"deliveryMethod\":\"STANDARD\",\n \"amountMicros\":500000\n }\n }\n ]\n}\nEOF\n\n# Creates a campaign.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n# CAMPAIGN_BUDGET_RESOURCE_NAME:\n# The resource of the campaign budget as returned by the previous step.\n\ncurl -f --request POST \\\n \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/campaigns:mutate\" \\\n --header \"Content-Type: application/json\" \\\n --header \"Developer-Token: ${DEVELOPER_TOKEN}\" \\\n --header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n --header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n --data @- <<EOF\n{\n \"operations\": [\n {\n \"create\": {\n \"campaignBudget\": \"${CAMPAIGN_BUDGET_RESOURCE_NAME}\",\n \"name\": \"Interplanetary Cruise Campaign #${RANDOM}\",\n \"advertisingChannelType\": \"SEARCH\",\n \"status\": \"PAUSED\",\n \"manualCpc\": {},\n \"networkSettings\": {\n \"targetGoogleSearch\":true,\n \"targetSearchNetwork\":true,\n \"targetContentNetwork\":true,\n \"targetPartnerSearchNetwork\":false\n }\n }\n }\n ]\n}\nEOF\nadd_campaigns.sh\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.239Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":1194,"estimatedTokens":11429}}103{"id":"doc-create_ad_groups_google_ads_api_google_for_devel-62e0247f","source":"documentation","title":"Create Ad Groups | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/create-ad-groups","text":"Example:\n```text\n// Copyright 2018 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.basicoperations;\n\nimport static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.enums.AdGroupStatusEnum.AdGroupStatus;\nimport com.google.ads.googleads.v25.enums.AdGroupTypeEnum.AdGroupType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.AdGroup;\nimport com.google.ads.googleads.v25.services.AdGroupOperation;\nimport com.google.ads.googleads.v25.services.AdGroupServiceClient;\nimport com.google.ads.googleads.v25.services.MutateAdGroupResult;\nimport com.google.ads.googleads.v25.services.MutateAdGroupsResponse;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/** Adds ad groups to a campaign. */\npublic class AddAdGroups {\n\n private static class AddAdGroupParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(names = ArgumentNames.CAMPAIGN_ID, required = true)\n private Long campaignId;\n }\n\n public static void main(String[] args) throws IOException {\n AddAdGroupParams params = new AddAdGroupParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.campaignId = Long.parseLong(\"INSERT_CAMPAIGN_ID_HERE\");\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new AddAdGroups().runExample(googleAdsClient, params.customerId, params.campaignId);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param campaignId the campaign ID.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void runExample(GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n String campaignResourceName = ResourceNames.campaign(customerId, campaignId);\n\n // Creates an ad group, setting an optional CPC value.\n AdGroup adGroup1 =\n AdGroup.newBuilder()\n .setName(\"Earth to Mars Cruises #\" + getPrintableDateTime())\n .setStatus(AdGroupStatus.ENABLED)\n .setCampaign(campaignResourceName)\n .setType(AdGroupType.SEARCH_STANDARD)\n .setCpcBidMicros(10_000_000L)\n .build();\n\n // You may add as many additional ad groups as you need.\n AdGroup adGroup2 =\n AdGroup.newBuilder()\n .setName(\"Earth to Venus Cruises #\" + getPrintableDateTime())\n .setStatus(AdGroupStatus.ENABLED)\n .setCampaign(campaignResourceName)\n .setType(AdGroupType.SEARCH_STANDARD)\n .setCpcBidMicros(10_000_000L)\n .build();\n\n List<AdGroupOperation> operations = new ArrayList<>();\n operations.add(AdGroupOperation.newBuilder().setCreate(adGroup1).build());\n operations.add(AdGroupOperation.newBuilder().setCreate(adGroup2).build());\n\n try (AdGroupServiceClient adGroupServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupServiceClient()) {\n MutateAdGroupsResponse response =\n adGroupServiceClient.mutateAdGroups(Long.toString(customerId), operations);\n System.out.printf(\"Added %d ad groups:%n\", response.getResultsCount());\n for (MutateAdGroupResult result : response.getResultsList()) {\n System.out.println(result.getResourceName());\n }\n }\n }\n}\nAddAdGroups.java\n```\n\nExample:\n```text\n// Copyright 2019 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Enums;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example illustrates how to create ad groups. To create campaigns, run\n /// AddCampaigns.cs.\n /// </summary>\n public class AddAdGroups : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"AddAdGroups\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// ID of the campaign to which ad groups are added.\n /// </summary>\n [Option(\"campaignId\", Required = true, HelpText =\n \"ID of the campaign to which ad groups are added.\")]\n public long CampaignId { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n AddAdGroups codeExample = new AddAdGroups();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(),\n options.CustomerId,\n options.CampaignId);\n }\n\n /// <summary>\n /// Number of ad groups to create.\n /// </summary>\n private const int NUM_ADGROUPS_TO_CREATE = 5;\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example illustrates how to create ad groups. To create campaigns, run \" +\n \"AddCampaigns.cs\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"campaignId\">ID of the campaign to which ad groups are added.</param>\n public void Run(GoogleAdsClient client, long customerId, long campaignId)\n {\n // Get the AdGroupService.\n AdGroupServiceClient adGroupService = client.GetService(Services.V25.AdGroupService);\n\n List<AdGroupOperation> operations = new List<AdGroupOperation>();\n\n for (int i = 0; i < NUM_ADGROUPS_TO_CREATE; i++)\n {\n // Create the ad group.\n AdGroup adGroup = new AdGroup()\n {\n Name = $\"Earth to Mars Cruises #{ExampleUtilities.GetRandomString()}\",\n Status = AdGroupStatusEnum.Types.AdGroupStatus.Enabled,\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n\n // Set the ad group bids.\n CpcBidMicros = 10000000\n };\n\n // Create the operation.\n AdGroupOperation operation = new AdGroupOperation()\n {\n Create = adGroup\n };\n operations.Add(operation);\n }\n\n try\n {\n // Create the ad groups.\n MutateAdGroupsResponse response = adGroupService.MutateAdGroups(\n customerId.ToString(), operations);\n\n // Display the results.\n foreach (MutateAdGroupResult newAdGroup in response.Results)\n {\n Console.WriteLine(\"Ad group with resource name '{0}' was created.\",\n newAdGroup.ResourceName);\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n }\n}\nAddAdGroups.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2018 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\BasicOperations;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdGroupStatusEnum\\AdGroupStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdGroupTypeEnum\\AdGroupType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AdGroup;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AdGroupOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAdGroupsRequest;\nuse Google\\ApiCore\\ApiException;\n\n/** This example adds ad groups to a campaign. */\nclass AddAdGroups\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const CAMPAIGN_ID = 'INSERT_CAMPAIGN_ID_HERE';\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::CAMPAIGN_ID => GetOpt::REQUIRED_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::CAMPAIGN_ID] ?: self::CAMPAIGN_ID\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $campaignId the campaign ID to add ad groups to\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n ) {\n $campaignResourceName = ResourceNames::forCampaign($customerId, $campaignId);\n\n $operations = [];\n\n // Constructs an ad group and sets an optional CPC value.\n $adGroup1 = new AdGroup([\n 'name' => 'Earth to Mars Cruises #' . Helper::getPrintableDatetime(),\n 'campaign' => $campaignResourceName,\n 'status' => AdGroupStatus::ENABLED,\n 'type' => AdGroupType::SEARCH_STANDARD,\n 'cpc_bid_micros' => 10000000\n ]);\n\n $adGroupOperation1 = new AdGroupOperation();\n $adGroupOperation1->setCreate($adGroup1);\n $operations[] = $adGroupOperation1;\n\n // Constructs another ad group.\n $adGroup2 = new AdGroup([\n 'name' => 'Earth to Venus Cruises #' . Helper::getPrintableDatetime(),\n 'campaign' => $campaignResourceName,\n 'status' => AdGroupStatus::ENABLED,\n 'type' => AdGroupType::SEARCH_STANDARD,\n 'cpc_bid_micros' => 20000000\n ]);\n\n $adGroupOperation2 = new AdGroupOperation();\n $adGroupOperation2->setCreate($adGroup2);\n $operations[] = $adGroupOperation2;\n\n // Issues a mutate request to add the ad groups.\n $adGroupServiceClient = $googleAdsClient->getAdGroupServiceClient();\n $response = $adGroupServiceClient->mutateAdGroups(MutateAdGroupsRequest::build(\n $customerId,\n $operations\n ));\n\n printf(\"Added %d ad groups:%s\", $response->getResults()->count(), PHP_EOL);\n\n foreach ($response->getResults() as $addedAdGroup) {\n /** @var AdGroup $addedAdGroup */\n print $addedAdGroup->getResourceName() . PHP_EOL;\n }\n }\n}\n\nAddAdGroups::main();\nAddAdGroups.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2018 Google LLC\n#\n# 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.\n\"\"\"This example adds an ad group.\n\nTo get ad groups, run get_ad_groups.py.\n\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import List\nimport uuid\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.services.services.ad_group_service import (\n AdGroupServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_service import (\n AdGroupOperation,\n MutateAdGroupsResponse,\n)\nfrom google.ads.googleads.v24.services.services.campaign_service import (\n CampaignServiceClient,\n)\nfrom google.ads.googleads.v24.resources.types.ad_group import AdGroup\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef main(client: GoogleAdsClient, customer_id: str, campaign_id: str) -> None:\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n # Create ad group.\n ad_group_operation: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group: AdGroup = ad_group_operation.create\n ad_group.name = f\"Earth to Mars cruises {uuid.uuid4()}\"\n ad_group.status = client.enums.AdGroupStatusEnum.ENABLED\n ad_group.campaign = campaign_service.campaign_path(customer_id, campaign_id)\n ad_group.type_ = client.enums.AdGroupTypeEnum.SEARCH_STANDARD\n ad_group.cpc_bid_micros = 10000000\n\n operations: List[AdGroupOperation] = [ad_group_operation]\n\n # Add the ad group.\n ad_group_response: MutateAdGroupsResponse = (\n ad_group_service.mutate_ad_groups(\n customer_id=customer_id,\n operations=operations,\n )\n )\n print(f\"Created ad group {ad_group_response.results[0].resource_name}.\")\n\n\nif __name__ == \"__main__\":\n parser = argparse.ArgumentParser(\n description=\"Adds an ad group for specified customer and campaign id.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-i\", \"--campaign_id\", type=str, required=True, help=\"The campaign ID.\"\n )\n args: argparse.Namespace = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(googleads_client, args.customer_id, args.campaign_id)\n except GoogleAdsException as ex:\n print(\n f'Request with ID \"{ex.request_id}\" failed with status '\n f'\"{ex.error.code().name}\" and includes the following errors:'\n )\n for error in ex.failure.errors:\n print(f'\\tError with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nadd_ad_groups.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2018 Google LLC\n#\n# 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.\n#\n# This example adds an ad group. To get ad groups, run get_ad_groups.rb.\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\nrequire 'date'\nrequire_relative '../shared/error_handler.rb'\n\ndef add_ad_groups(customer_id, campaign_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Create an ad group, setting an optional CPC value.\n ad_group = client.resource.ad_group do |ag|\n ag.name = \"Earth to Mars Cruises #{(Time.new.to_f * 1000).to_i}\"\n ag.status = :ENABLED\n ag.campaign = client.path.campaign(customer_id, campaign_id)\n ag.type = :SEARCH_STANDARD\n ag.cpc_bid_micros = 10_000_000\n end\n\n # Create the operation\n ad_group_operation = client.operation.create_resource.ad_group(ad_group)\n\n # Add the ad group.\n response = client.service.ad_group.mutate_ad_groups(\n customer_id: customer_id,\n operations: [ad_group_operation],\n )\n\n puts \"Created ad group #{response.results.first.resource_name}.\"\nend\n\nif __FILE__ == $0\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:campaign_id] = 'INSERT_CAMPAIGN_ID_HERE'\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-c', '--campaign-id CAMPAIGN-ID', String, 'Campaign ID') do |v|\n options[:campaign_id] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n add_ad_groups(options.fetch(:customer_id).tr(\"-\", \"\"), options[:campaign_id])\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n GoogleAdsErrorHandler.handle_google_ads_error(e)\n raise # Re-raise the error to maintain original script behavior.\n end\nend\nadd_ad_groups.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2019, Google LLC\n#\n# 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# http://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.\n#\n# This example adds an ad group. To get ad groups, run get_ad_groups.pl.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroup;\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupStatusEnum qw(ENABLED);\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupTypeEnum qw(SEARCH_STANDARD);\nuse Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\nmy $campaign_id = \"INSERT_CAMPAIGN_ID_HERE\";\n\nsub add_ad_groups {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n # Create an ad group, setting an optional CPC value.\n my $ad_group = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Earth to Mars Cruises #\" . uniqid(),\n status => ENABLED,\n campaign => Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $campaign_id\n ),\n type => SEARCH_STANDARD,\n cpcBidMicros => 10000000\n });\n\n # Create an ad group operation.\n my $ad_group_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group});\n\n # Add the ad group.\n my $ad_groups_response = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_operation]});\n\n printf \"Created ad group '%s'.\\n\",\n $ad_groups_response->{results}[0]{resourceName};\n\n return 1;\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\"customer_id=s\" => \\$customer_id, \"campaign_id=i\" => \\$campaign_id);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id, $campaign_id);\n\n# Call the example.\nadd_ad_groups($api_client, $customer_id =~ s/-//gr, $campaign_id);\n\n=pod\n\n=head1 NAME\n\nadd_ad_groups\n\n=head1 DESCRIPTION\n\nThis example adds an ad group. To get ad groups, run get_ad_groups.pl.\n\n=head1 SYNOPSIS\n\nadd_ad_groups.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -campaign_id The campaign ID.\n\n=cut\nadd_ad_groups.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.241Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":777,"estimatedTokens":6882}}104{"id":"doc-creating_a_hotel_ad_group_google_ads_api_google_-dc6e93b6","source":"documentation","title":"Creating a Hotel Ad Group | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/hotel-ads/create-ad-group","text":"Example:\n```text\nprivate String addHotelAdGroup(\n GoogleAdsClient googleAdsClient, long customerId, String campaignResourceName) {\n // Creates an ad group.\n AdGroup adGroup =\n AdGroup.newBuilder()\n .setName(\"Earth to Mars Cruises #\" + getPrintableDateTime())\n .setCampaign(campaignResourceName)\n // Sets the ad group type to HOTEL_ADS. This cannot be set to other types.\n .setType(AdGroupType.HOTEL_ADS)\n .setCpcBidMicros(1_000_000L)\n .setStatus(AdGroupStatus.ENABLED)\n .build();\n\n // Creates an ad group operation.\n AdGroupOperation operation = AdGroupOperation.newBuilder().setCreate(adGroup).build();\n\n // Issues a mutate request to add an ad group.\n try (AdGroupServiceClient adGroupServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupServiceClient()) {\n MutateAdGroupResult mutateAdGroupResult =\n adGroupServiceClient\n .mutateAdGroups(Long.toString(customerId), Collections.singletonList(operation))\n .getResults(0);\n System.out.printf(\n \"Added a hotel ad group with resource name: '%s'%n\",\n mutateAdGroupResult.getResourceName());\n return mutateAdGroupResult.getResourceName();\n }\n}AddHotelAd.java\n```\n\nExample:\n```text\nprivate static string AddHotelAdGroup(GoogleAdsClient client, long customerId,\n string campaignResourceName)\n{\n // Get the AdGroupService.\n AdGroupServiceClient service = client.GetService(Services.V25.AdGroupService);\n\n // Create an ad group.\n AdGroup adGroup = new AdGroup()\n {\n Name = \"Earth to Mars Cruise #\" + ExampleUtilities.GetRandomString(),\n\n // Sets the campaign.\n Campaign = campaignResourceName,\n\n // Optional: Sets the ad group type to HOTEL_ADS.\n // This cannot be set to other types.\n Type = AdGroupType.HotelAds,\n\n CpcBidMicros = 10000000,\n Status = AdGroupStatus.Enabled\n };\n\n // Create an ad group operation.\n AdGroupOperation adGroupOperation = new AdGroupOperation()\n {\n Create = adGroup\n };\n\n // Issue a mutate request to add an ad group.\n MutateAdGroupsResponse response = service.MutateAdGroups(customerId.ToString(),\n new AdGroupOperation[] { adGroupOperation });\n return response.Results[0].ResourceName;\n}AddHotelAd.cs\n```\n\nExample:\n```text\nprivate static function addHotelAdGroup(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $campaignResourceName\n) {\n // Creates an ad group.\n $adGroup = new AdGroup([\n 'name' => 'Earth to Mars Cruise #' . Helper::getPrintableDatetime(),\n // Sets the campaign.\n 'campaign' => $campaignResourceName,\n // Sets the ad group type to HOTEL_ADS.\n // This cannot be set to other types.\n 'type' => AdGroupType::HOTEL_ADS,\n 'cpc_bid_micros' => 10000000,\n 'status' => AdGroupStatus::ENABLED,\n ]);\n\n // Creates an ad group operation.\n $adGroupOperation = new AdGroupOperation();\n $adGroupOperation->setCreate($adGroup);\n\n // Issues a mutate request to add an ad group.\n $adGroupServiceClient = $googleAdsClient->getAdGroupServiceClient();\n $response = $adGroupServiceClient->mutateAdGroups(\n MutateAdGroupsRequest::build($customerId, [$adGroupOperation])\n );\n\n /** @var AdGroup $addedAdGroup */\n $addedAdGroup = $response->getResults()[0];\n printf(\n \"Added a hotel ad group with resource name '%s'.%s\",\n $addedAdGroup->getResourceName(),\n PHP_EOL\n );\n\n return $addedAdGroup->getResourceName();\n}AddHotelAd.php\n```\n\nExample:\n```text\ndef add_hotel_ad_group(\n client: GoogleAdsClient, customer_id: str, campaign_resource_name: str\n) -> str:\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n\n # Create ad group.\n ad_group_operation: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group: AdGroup = ad_group_operation.create\n ad_group.name = f\"Earth to Mars cruise {uuid.uuid4()}\"\n ad_group.status = client.enums.AdGroupStatusEnum.ENABLED\n ad_group.campaign = campaign_resource_name\n # Sets the ad group type to HOTEL_ADS. This cannot be set to other types.\n ad_group.type_ = client.enums.AdGroupTypeEnum.HOTEL_ADS\n ad_group.cpc_bid_micros = 10000000\n\n # Add the ad group.\n ad_group_response: MutateAdGroupsResponse = (\n ad_group_service.mutate_ad_groups(\n customer_id=customer_id, operations=[ad_group_operation]\n )\n )\n\n ad_group_resource_name: str = ad_group_response.results[0].resource_name\n\n print(\n \"Added a hotel ad group with resource name '{ad_group_resource_name}'.\"\n )\n\n return ad_group_resource_nameadd_hotel_ad.py\n```\n\nExample:\n```text\ndef add_hotel_ad_group(client, customer_id, campaign_resource)\n # Create an ad group.\n ad_group_operation = client.operation.create_resource.ad_group do |ag|\n ag.name = generate_random_name_field(\"Earth to Mars Cruise\")\n\n # Set the campaign.\n ag.campaign = campaign_resource\n\n # Optional: Set the ad group type to HOTEL_ADS.\n # This cannot be set to other types.\n ag.type = :HOTEL_ADS\n ag.cpc_bid_micros = 10_000_000\n ag.status = :ENABLED\n end\n\n # Issue a mutate request to add the ad group.\n ad_group_service = client.service.ad_group\n response = ad_group_service.mutate_ad_groups(\n customer_id: customer_id,\n operations: [ad_group_operation]\n )\n\n # Fetch the new ad group's resource name.\n ad_group_resource = response.results.first.resource_name\n\n puts \"Added hotel ad group with resource name '#{ad_group_resource}'.\"\n\n ad_group_resource\nendadd_hotel_ad.rb\n```\n\nExample:\n```text\nsub add_hotel_ad_group {\n my ($api_client, $customer_id, $campaign_resource_name) = @_;\n\n # Create an ad group.\n my $ad_group = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Earth to Mars Cruise #\" . uniqid(),\n # Set the campaign.\n campaign => $campaign_resource_name,\n # Set the ad group type to HOTEL_ADS.\n # This cannot be set to other types.\n type => HOTEL_ADS,\n cpcBidMicros => 1000000,\n status => Google::Ads::GoogleAds::V25::Enums::AdGroupStatusEnum::ENABLED\n });\n\n # Create an ad group operation.\n my $ad_group_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group});\n\n # Add the ad group.\n my $ad_group_resource_name = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_operation]})->{results}[0]{resourceName};\n\n printf \"Added a hotel ad group with resource name: '%s'.\\n\",\n $ad_group_resource_name;\n\n return $ad_group_resource_name;\n}add_hotel_ad.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.242Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":214,"estimatedTokens":1691}}105{"id":"doc-dynamic_search_ads_page_feeds_google_ads_api_goo-066b4f30","source":"documentation","title":"Dynamic Search Ads Page Feeds | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/dynamic-search-ads/dsa-page-feeds","text":"Example:\n```text\nList<String> urls =\n ImmutableList.of(\n \"http://www.example.com/discounts/rental-cars\",\n \"http://www.example.com/discounts/hotel-deals\",\n \"http://www.example.com/discounts/flight-deals\");\n\n// Creates one operation per URL.\nList<AssetOperation> assetOperations = new ArrayList<>();\nfor (String url : urls) {\n PageFeedAsset pageFeedAsset =\n PageFeedAsset.newBuilder()\n // Sets the URL of the page to include.\n .setPageUrl(url)\n // Recommended: adds labels to the asset. These labels can be used later in ad group\n // targeting to restrict the set of pages that can serve.\n .addLabels(dsaPageUrlLabel)\n .build();\n Asset asset = Asset.newBuilder().setPageFeedAsset(pageFeedAsset).build();\n assetOperations.add(AssetOperation.newBuilder().setCreate(asset).build());\n}\n\n// Creates the service client.\ntry (AssetServiceClient assetServiceClient =\n googleAdsClient.getLatestVersion().createAssetServiceClient()) {\n // Adds the assets.\n MutateAssetsResponse response =\n assetServiceClient.mutateAssets(String.valueOf(customerId), assetOperations);\n // Prints some information about the result.\n List<String> resourceNames =\n response.getResultsList().stream()\n .map(MutateAssetResult::getResourceName)\n .collect(Collectors.toList());\n resourceNames.forEach(r -> System.out.printf(\"Created asset with resource name %s.%n\", r));\n return resourceNames;\n}AddDynamicPageFeedAsset.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates Assets to be used in a DSA page feed.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n/// <param name=\"dsaPageUrlLabel\">The DSA page URL label.</param>\n/// <returns>The list of asset resource names.</returns>\nprivate static List<string> CreateAssets(GoogleAdsClient client, long customerId,\n string dsaPageUrlLabel)\n{\n AssetServiceClient assetService = client.GetService(Services.V25.AssetService);\n\n string[] urls = new[]\n {\n \"http://www.example.com/discounts/rental-cars\",\n \"http://www.example.com/discounts/hotel-deals\",\n \"http://www.example.com/discounts/flight-deals\"\n };\n\n // Creates one operation per URL.\n List<AssetOperation> assetOperations = new List<AssetOperation>();\n foreach (string url in urls)\n {\n PageFeedAsset pageFeedAsset = new PageFeedAsset()\n {\n // Sets the URL of the page to include.\n PageUrl = url,\n\n // Recommended: adds labels to the asset. These labels can be used later in\n // ad group targeting to restrict the set of pages that can serve.\n Labels = { dsaPageUrlLabel }\n };\n\n assetOperations.Add(\n new AssetOperation()\n {\n Create = new Asset()\n {\n PageFeedAsset = pageFeedAsset\n }\n });\n }\n\n // Adds the assets.\n MutateAssetsResponse response =\n assetService.MutateAssets(customerId.ToString(), assetOperations);\n\n // Prints some information about the result.\n List<string> resourceNames = response.Results.Select(\n assetResult => assetResult.ResourceName).ToList();\n foreach (string resourceName in resourceNames)\n {\n Console.Write($\"Created asset with resource name {resourceName}.\");\n }\n return resourceNames;\n}AddDynamicPageFeedAsset.cs\n```\n\nExample:\n```text\n$urls = [\n 'http://www.example.com/discounts/rental-cars',\n 'http://www.example.com/discounts/hotel-deals',\n 'http://www.example.com/discounts/flight-deals'\n];\n$operations = [];\n// Creates one asset per URL.\nforeach ($urls as $url) {\n $pageFeedAsset = new PageFeedAsset([\n 'page_url' => $url,\n // Recommended: adds labels to the asset. These labels can be used later in ad group\n // targeting to restrict the set of pages that can serve.\n 'labels' => [$dsaPageUrlLabel]\n ]);\n\n // Wraps the page feed asset in an asset.\n $asset = new Asset(['page_feed_asset' => $pageFeedAsset]);\n\n // Creates an asset operation and adds it to the list of operations.\n $assetOperation = new AssetOperation();\n $assetOperation->setCreate($asset);\n $operations[] = $assetOperation;\n}\n\n// Issues a mutate request to add the assets and prints its information.\n$assetServiceClient = $googleAdsClient->getAssetServiceClient();\n$response = $assetServiceClient->mutateAssets(MutateAssetsRequest::build(\n $customerId,\n $operations\n));\n$assetResourceNames = [];\nprintf(\"Added %d assets:%s\", $response->getResults()->count(), PHP_EOL);\nforeach ($response->getResults() as $addedAsset) {\n /** @var Asset $addedAsset */\n $assetResourceName = $addedAsset->getResourceName();\n printf(\n \"Created an asset with resource name: '%s'.%s\",\n $assetResourceName,\n PHP_EOL\n );\n $assetResourceNames[] = $assetResourceName;\n}\nreturn $assetResourceNames;AddDynamicPageFeedAsset.php\n```\n\nExample:\n```text\ndef create_assets(\n client: GoogleAdsClient, customer_id: str, dsa_page_url_label: str\n) -> List[str]:\n \"\"\"Creates assets to be used in a DSA page feed.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n dsa_page_url_label: the label for the DSA page URLs.\n\n Returns:\n a list of the created assets' resource names.\n \"\"\"\n urls: List[str] = [\n \"http://www.example.com/discounts/rental-cars\",\n \"http://www.example.com/discounts/hotel-deals\",\n \"http://www.example.com/discounts/flight-deals\",\n ]\n operations: List[AssetOperation] = []\n\n # Creates one asset per URL.\n for url in urls:\n # Creates an asset operation and adds it to the list of operations.\n operation: AssetOperation = client.get_type(\"AssetOperation\")\n asset: Asset = operation.create\n page_feed_asset: PageFeedAsset = asset.page_feed_asset\n page_feed_asset.page_url = url\n # Recommended: adds labels to the asset. These labels can be used later\n # in ad group targeting to restrict the set of pages that can serve.\n page_feed_asset.labels.append(dsa_page_url_label)\n operations.append(operation)\n\n # Issues a mutate request to add the assets and prints its information.\n asset_service: AssetServiceClient = client.get_service(\"AssetService\")\n response: MutateAssetsResponse = asset_service.mutate_assets(\n customer_id=customer_id, operations=operations\n )\n\n print(f\"Added {len(response.results)} assets:\")\n\n resource_names: List[str] = []\n result: MutateAssetResult\n for result in response.results:\n resource_name: str = result.resource_name\n print(f\"\\tCreated an asset with resource name: '{resource_name}'\")\n resource_names.append(resource_name)\n\n return resource_namesadd_dynamic_page_feed_asset.py\n```\n\nExample:\n```text\ndef create_assets(client, dsa_page_url_label, customer_id)\n urls = [\n 'http://www.example.com/discounts/rental-cars',\n 'http://www.example.com/discounts/hotel-deals',\n 'http://www.example.com/discounts/flight-deals',\n ]\n\n operations = urls.map do |url|\n client.operation.create_resource.asset do |asset|\n asset.page_feed_asset = client.resource.page_feed_asset do |pfa|\n # Sets the URL of the page to include.\n pfa.page_url = url\n # Recommended: adds labels to the asset. These labels can be used later\n # in ad group targeting to restrict the set of pages that can serve.\n pfa.labels << dsa_page_url_label\n end\n end\n end\n\n response = client.service.asset.mutate_assets(\n customer_id: customer_id,\n operations: operations,\n )\n\n resource_names = []\n response.results.each do |result|\n resource_name = result.resource_name\n puts \"Created asset with resource name '#{resource_name}'\"\n resource_names << resource_name\n end\n\n resource_names\nendadd_dynamic_page_feed_asset.rb\n```\n\nExample:\n```text\nmy $urls = [\n \"http://www.example.com/discounts/rental-cars\",\n \"http://www.example.com/discounts/hotel-deals\",\n \"http://www.example.com/discounts/flight-deals\"\n];\n\n# Create one operation per URL.\nmy $asset_operations = [];\nforeach my $url (@$urls) {\n my $page_feed_asset =\n Google::Ads::GoogleAds::V25::Common::PageFeedAsset->new({\n # Set the URL of the page to include.\n pageUrl => $url,\n # Recommended: add labels to the asset. These labels can be used later in\n # ad group targeting to restrict the set of pages that can serve.\n labels => [$dsa_page_url_label]});\n my $asset = Google::Ads::GoogleAds::V25::Resources::Asset->new({\n pageFeedAsset => $page_feed_asset\n });\n\n push @$asset_operations,\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->new({\n create => $asset\n });\n}\n\n# Add the assets.\nmy $response = $api_client->AssetService()->mutate({\n customerId => $customer_id,\n operations => $asset_operations\n});\n\n# Print some information about the response.\nmy $resource_names = [];\nforeach my $result (@{$response->{results}}) {\n push @$resource_names, $result->{resourceName};\n printf \"Created asset with resource name '%s'.\\n\", $result->{resourceName};\n}\nreturn $resource_names;add_dynamic_page_feed_asset.pl\n```\n\nExample:\n```text\n// Creates an AssetSet which will be used to link the dynamic page feed assets to a campaign.\nAssetSet assetSet =\n AssetSet.newBuilder()\n .setName(\"My dynamic page feed \" + CodeSampleHelper.getPrintableDateTime())\n .setType(AssetSetType.PAGE_FEED)\n .build();\n// Creates an operation to add the AssetSet.\nAssetSetOperation operation = AssetSetOperation.newBuilder().setCreate(assetSet).build();\ntry (AssetSetServiceClient serviceClient =\n googleAdsClient.getLatestVersion().createAssetSetServiceClient()) {\n // Sends the mutate request.\n MutateAssetSetsResponse response =\n serviceClient.mutateAssetSets(\n String.valueOf(params.customerId), ImmutableList.of(operation));\n // Prints some information about the response.\n String resourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Created asset set with resource name %s.%n\", resourceName);\n return resourceName;\n}AddDynamicPageFeedAsset.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates an AssetSet.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n/// <returns>The resource name of the asset set.</returns>\nprivate string CreateAssetSet(GoogleAdsClient client, long customerId)\n{\n AssetSetServiceClient assetSetService = client.GetService(\n Services.V25.AssetSetService);\n\n // Creates an AssetSet which will be used to link the dynamic page feed assets\n // to a campaign.\n AssetSet assetSet = new AssetSet()\n {\n Name = \"My dynamic page feed \" + ExampleUtilities.GetRandomString(),\n Type = AssetSetType.PageFeed\n };\n\n // Creates an operation to add the AssetSet.\n AssetSetOperation operation = new AssetSetOperation()\n {\n Create = assetSet\n };\n\n // Sends the mutate request.\n MutateAssetSetsResponse response =\n assetSetService.MutateAssetSets(\n customerId.ToString(), new[] { operation });\n // Prints some information about the response.\n string resourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Created asset set with resource name {resourceName}.\");\n return resourceName;\n}AddDynamicPageFeedAsset.cs\n```\n\nExample:\n```text\n// Creates an asset set which will be used to link the dynamic page feed assets to a\n// campaign.\n$assetSet = new AssetSet([\n 'name' => 'My dynamic page feed ' . Helper::getPrintableDatetime(),\n 'type' => AssetSetType::PAGE_FEED\n]);\n\n// Creates an asset set operation.\n$assetSetOperation = new AssetSetOperation();\n$assetSetOperation->setCreate($assetSet);\n\n// Issues a mutate request to add the asset set and prints its information.\n$assetSetServiceClient = $googleAdsClient->getAssetSetServiceClient();\n$response = $assetSetServiceClient->mutateAssetSets(MutateAssetSetsRequest::build(\n $customerId,\n [$assetSetOperation]\n));\n$assetSetResourceName = $response->getResults()[0]->getResourceName();\nprintf(\n \"Created an asset set with resource name: '%s'.%s\",\n $assetSetResourceName,\n PHP_EOL\n);\n\nreturn $assetSetResourceName;AddDynamicPageFeedAsset.php\n```\n\nExample:\n```text\ndef create_asset_set(client: GoogleAdsClient, customer_id: str) -> str:\n \"\"\"Creates an asset set.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n the created asset set's resource name.\n \"\"\"\n operation: AssetSetOperation = client.get_type(\"AssetSetOperation\")\n # Creates an asset set which will be used to link the dynamic page feed\n # assets to a campaign.\n asset_set: AssetSet = operation.create\n asset_set.name = f\"My dynamic page feed {get_printable_datetime()}\"\n asset_set.type_ = client.enums.AssetSetTypeEnum.PAGE_FEED\n\n # Issues a mutate request to add the asset set and prints its information.\n asset_set_service: AssetSetServiceClient = client.get_service(\n \"AssetSetService\"\n )\n response: MutateAssetSetsResponse = asset_set_service.mutate_asset_sets(\n customer_id=customer_id, operations=[operation]\n )\n\n resource_name: str = response.results[0].resource_name\n print(f\"Created an asset set with resource name: '{resource_name}'\")\n return resource_nameadd_dynamic_page_feed_asset.py\n```\n\nExample:\n```text\ndef create_asset_set(client, customer_id)\n # Creates an AssetSet which will be used to link the dynamic page feed assets to a campaign.\n # Creates an operation to add the AssetSet.\n operation = client.operation.create_resource.asset_set do |asset_set|\n asset_set.name = \"My dynamic page feed #{Time.now}\"\n asset_set.type = :PAGE_FEED\n end\n\n # Sends the mutate request.\n response = client.service.asset_set.mutate_asset_sets(\n customer_id: customer_id,\n operations: [operation],\n )\n # Prints some information about the response.\n resource_name = response.results.first.resource_name\n puts \"Created asset set with resource name '#{resource_name}'\"\n\n resource_name\nendadd_dynamic_page_feed_asset.rb\n```\n\nExample:\n```text\n# Create an AssetSet which will be used to link the dynamic page feed assets to\n# a campaign.\nmy $asset_set = Google::Ads::GoogleAds::V25::Resources::AssetSet->new({\n name => \"My dynamic page feed #\" . uniqid(),\n type => PAGE_FEED\n});\n\n# Create an operation to add the AssetSet.\nmy $operation =\n Google::Ads::GoogleAds::V25::Services::AssetSetService::AssetSetOperation->\n new({\n create => $asset_set\n });\n\n# Send the mutate request.\nmy $response = $api_client->AssetSetService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n\n# Print some information about the response.\nmy $resource_name = $response->{results}[0]{resourceName};\nprintf \"Created asset set with resource name '%s'.\\n\", $resource_name;\nreturn $resource_name;add_dynamic_page_feed_asset.pl\n```\n\nExample:\n```text\nList<AssetSetAssetOperation> operations = new ArrayList<>();\nfor (String assetResourceName : assetResourceNames) {\n AssetSetAsset assetSetAsset =\n AssetSetAsset.newBuilder()\n .setAsset(assetResourceName)\n .setAssetSet(assetSetResourceName)\n .build();\n // Creates an operation to add the link.\n AssetSetAssetOperation operation =\n AssetSetAssetOperation.newBuilder().setCreate(assetSetAsset).build();\n operations.add(operation);\n}\ntry (AssetSetAssetServiceClient client =\n googleAdsClient.getLatestVersion().createAssetSetAssetServiceClient()) {\n // Sends the mutate request.\n MutateAssetSetAssetsResponse response =\n client.mutateAssetSetAssets(String.valueOf(params.customerId), operations);\n // Prints some information about the response.\n String resourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Created AssetSetAsset link with resource name %s.%n\", resourceName);\n}AddDynamicPageFeedAsset.java\n```\n\nExample:\n```text\n/// <summary>\n/// Adds an Asset to an AssetSet by creating an AssetSetAsset link.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n/// <param name=\"assetResourceNames\">The asset resource names.</param>\n/// <param name=\"assetSetResourceName\">Resource name of the asset set.</param>\nprivate void AddAssetsToAssetSet(GoogleAdsClient client, long customerId,\n List<string> assetResourceNames, string assetSetResourceName)\n{\n AssetSetAssetServiceClient assetSetAssetService = client.GetService(\n Services.V25.AssetSetAssetService);\n\n List<AssetSetAssetOperation> operations = new List<AssetSetAssetOperation>();\n foreach (string assetResourceName in assetResourceNames)\n {\n AssetSetAsset assetSetAsset = new AssetSetAsset()\n {\n Asset = assetResourceName,\n AssetSet = assetSetResourceName\n };\n\n // Creates an operation to add the link.\n AssetSetAssetOperation operation = new AssetSetAssetOperation()\n {\n Create = assetSetAsset\n };\n\n operations.Add(operation);\n }\n // Sends the mutate request.\n MutateAssetSetAssetsResponse response =\n assetSetAssetService.MutateAssetSetAssets(customerId.ToString(), operations);\n // Prints some information about the response.\n string resourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Created AssetSetAsset link with resource name {resourceName}.\");\n}AddDynamicPageFeedAsset.cs\n```\n\nExample:\n```text\n$operations = [];\nforeach ($assetResourceNames as $assetResourceName) {\n // Creates an asset set asset.\n $assetSetAsset = new AssetSetAsset([\n 'asset' => $assetResourceName,\n 'asset_set' => $assetSetResourceName\n ]);\n\n // Creates an asset set asset operation and adds it to the list of operations.\n $assetSetAssetOperation = new AssetSetAssetOperation();\n $assetSetAssetOperation->setCreate($assetSetAsset);\n $operations[] = $assetSetAssetOperation;\n}\n\n// Issues a mutate request to add the asset set assets and prints its information.\n$assetSetAssetServiceClient = $googleAdsClient->getAssetSetAssetServiceClient();\n$response = $assetSetAssetServiceClient->mutateAssetSetAssets(\n MutateAssetSetAssetsRequest::build($customerId, $operations)\n);\nprintf(\"Added %d asset set assets:%s\", $response->getResults()->count(), PHP_EOL);\nforeach ($response->getResults() as $addedAssetSetAsset) {\n /** @var AssetSetAsset $addedAssetSetAsset */\n printf(\n \"Created an asset set asset link with resource name: '%s'.%s\",\n $addedAssetSetAsset->getResourceName(),\n PHP_EOL\n );\n}AddDynamicPageFeedAsset.php\n```\n\nExample:\n```text\ndef add_assets_to_asset_set(\n client: GoogleAdsClient,\n customer_id: str,\n asset_resource_names: List[str],\n asset_set_resource_name: str,\n) -> None:\n \"\"\"Adds assets to an asset set by creating an asset set asset link.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n asset_resource_names: a list of asset resource names.\n asset_set_resource_name: a resource name for an asset set.\n \"\"\"\n operations: List[AssetSetAssetOperation] = []\n for resource_name in asset_resource_names:\n # Creates an asset set asset operation and adds it to the list of\n # operations.\n operation: AssetSetAssetOperation = client.get_type(\n \"AssetSetAssetOperation\"\n )\n asset_set_asset: AssetSetAsset = operation.create\n asset_set_asset.asset = resource_name\n asset_set_asset.asset_set = asset_set_resource_name\n operations.append(operation)\n\n # Issues a mutate request to add the asset set assets and prints its\n # information.\n asset_set_asset_service: AssetSetAssetServiceClient = client.get_service(\n \"AssetSetAssetService\"\n )\n response: MutateAssetSetAssetsResponse = (\n asset_set_asset_service.mutate_asset_set_assets(\n customer_id=customer_id, operations=operations\n )\n )\n\n print(f\"Added {len(response.results)} asset set assets:\")\n\n result: MutateAssetSetAssetResult\n for result in response.results:\n print(\n \"\\tCreated an asset set asset link with resource name \"\n f\"'{result.resource_name}'\"\n )add_dynamic_page_feed_asset.py\n```\n\nExample:\n```text\ndef add_assets_to_asset_set(client, asset_resource_names, asset_set_resource_name, customer_id)\n operations = asset_resource_names.map do |asset_resource_name|\n client.operation.create_resource.asset_set_asset do |asa|\n asa.asset = asset_resource_name\n asa.asset_set = asset_set_resource_name\n end\n end\n\n response = client.service.asset_set_asset.mutate_asset_set_assets(\n customer_id: customer_id,\n operations: operations,\n )\n resource_name = response.results.first.resource_name\n puts \"Created asset set asset with resource name '#{resource_name}'\"\nendadd_dynamic_page_feed_asset.rb\n```\n\nExample:\n```text\nmy $operations = [];\nforeach my $asset_resource_name (@$asset_resource_names) {\n my $asset_set_asset =\n Google::Ads::GoogleAds::V25::Resources::AssetSetAsset->new({\n asset => $asset_resource_name,\n assetSet => $asset_set_resource_name\n });\n\n # Create an operation to add the link.\n my $operation =\n Google::Ads::GoogleAds::V25::Services::AssetSetAssetService::AssetSetAssetOperation\n ->new({\n create => $asset_set_asset\n });\n push @$operations, $operation;\n}\n\n# Send the mutate request.\nmy $response = $api_client->AssetSetAssetService()->mutate({\n customerId => $customer_id,\n operations => $operations\n});\n\n# Print some information about the response.\nmy $resource_name = $response->{results}[0]{resourceName};\nprintf \"Created AssetSetAsset link with resource name '%s'.\\n\",\n $resource_name;add_dynamic_page_feed_asset.pl\n```\n\nExample:\n```text\n// Creates a CampaignAssetSet representing the link between an AssetSet and a Campaign.\nCampaignAssetSet campaignAssetSet =\n CampaignAssetSet.newBuilder()\n .setCampaign(ResourceNames.campaign(params.customerId, params.campaignId))\n .setAssetSet(assetSetResourceName)\n .build();\n// Creates an operation to add the CampaignAssetSet.\nCampaignAssetSetOperation operation =\n CampaignAssetSetOperation.newBuilder().setCreate(campaignAssetSet).build();\n// Creates the service client.\ntry (CampaignAssetSetServiceClient client =\n googleAdsClient.getLatestVersion().createCampaignAssetSetServiceClient()) {\n // Issues the mutate request.\n MutateCampaignAssetSetsResponse response =\n client.mutateCampaignAssetSets(\n String.valueOf(params.customerId), ImmutableList.of(operation));\n String resourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Created a CampaignAssetSet with resource name %s.%n\", resourceName);\n}AddDynamicPageFeedAsset.java\n```\n\nExample:\n```text\n/// <summary>\n/// Links an AssetSet to a Campaign by creating a CampaignAssetSet.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n/// <param name=\"campaignId\">ID of the campaign to which the asset is linked.</param>\n/// <param name=\"assetSetResourceName\">Resource name of the asset set.</param>\nprivate void LinkAssetSetToCampaign(GoogleAdsClient client, long customerId,\n long campaignId, string assetSetResourceName)\n{\n CampaignAssetSetServiceClient campaignAssetSetService = client.GetService(\n Services.V25.CampaignAssetSetService);\n\n // Creates a CampaignAssetSet representing the link between an AssetSet and a Campaign.\n CampaignAssetSet campaignAssetSet = new CampaignAssetSet()\n {\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n AssetSet = assetSetResourceName,\n };\n\n // Creates an operation to add the CampaignAssetSet.\n CampaignAssetSetOperation operation = new CampaignAssetSetOperation()\n {\n Create = campaignAssetSet\n };\n // Issues the mutate request.\n MutateCampaignAssetSetsResponse response =\n campaignAssetSetService.MutateCampaignAssetSets(\n customerId.ToString(), new[] { operation });\n string resourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Created a CampaignAssetSet with resource name {resourceName}.\");\n}AddDynamicPageFeedAsset.cs\n```\n\nExample:\n```text\n// Creates a campaign asset set representing the link between an asset set and a campaign.\n$campaignAssetSet = new CampaignAssetSet([\n 'asset_set' => $assetSetResourceName,\n 'campaign' => ResourceNames::forCampaign($customerId, $campaignId)\n]);\n\n// Creates a campaign asset set operation.\n$campaignAssetSetOperation = new CampaignAssetSetOperation();\n$campaignAssetSetOperation->setCreate($campaignAssetSet);\n\n// Issues a mutate request to add the campaign asset set and prints its information.\n$campaignAssetSetServiceClient = $googleAdsClient->getCampaignAssetSetServiceClient();\n$response = $campaignAssetSetServiceClient->mutateCampaignAssetSets(\n MutateCampaignAssetSetsRequest::build($customerId, [$campaignAssetSetOperation])\n);\nprintf(\n \"Created a campaign asset set with resource name: '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n);AddDynamicPageFeedAsset.php\n```\n\nExample:\n```text\ndef link_asset_set_to_campaign(\n client: GoogleAdsClient,\n customer_id: str,\n campaign_id: str,\n asset_set_resource_name: str,\n) -> None:\n \"\"\"Links the asset set to the campaign by creating a campaign asset set.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n campaign_id: the ID for a Campaign.\n asset_set_resource_name: a resource name for an asset set.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n # Creates a campaign asset set representing the link between an asset set\n # and a campaign.\n operation: CampaignAssetSetOperation = client.get_type(\n \"CampaignAssetSetOperation\"\n )\n campaign_asset_set: CampaignAssetSet = operation.create\n campaign_asset_set.asset_set = asset_set_resource_name\n campaign_asset_set.campaign = googleads_service.campaign_path(\n customer_id, campaign_id\n )\n\n campaign_asset_set_service: CampaignAssetSetServiceClient = (\n client.get_service(\"CampaignAssetSetService\")\n )\n response: MutateCampaignAssetSetsResponse = (\n campaign_asset_set_service.mutate_campaign_asset_sets(\n customer_id=customer_id, operations=[operation]\n )\n )\n\n resource_name: str = response.results[0].resource_name\n print(f\"Created a campaign asset set with resource name: '{resource_name}'\")add_dynamic_page_feed_asset.py\n```\n\nExample:\n```text\ndef link_asset_set_to_campaign(client, asset_set_resource_name, customer_id, campaign_id)\n # Creates a CampaignAssetSet representing the link between an AssetSet and a Campaign.\n # Creates an operation to add the CampaignAssetSet.\n operation = client.operation.create_resource.campaign_asset_set do |cas|\n cas.campaign = client.path.campaign(customer_id, campaign_id)\n cas.asset_set = asset_set_resource_name\n end\n\n # Issues the mutate request.\n response = client.service.campaign_asset_set.mutate_campaign_asset_sets(\n customer_id: customer_id,\n operations: [operation],\n )\n resource_name = response.results.first.resource_name\n puts \"Created a campaign asset set with resource name '#{resource_name}'\"\nendadd_dynamic_page_feed_asset.rb\n```\n\nExample:\n```text\n# Create a CampaignAssetSet representing the link between an AssetSet and a Campaign.\nmy $campaign_asset_set =\n Google::Ads::GoogleAds::V25::Resources::CampaignAssetSet->new({\n campaign => Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $campaign_id\n ),\n assetSet => $asset_set_resource_name\n });\n\n# Create an operation to add the CampaignAssetSet.\nmy $operation =\n Google::Ads::GoogleAds::V25::Services::CampaignAssetSetService::CampaignAssetSetOperation\n ->new({\n create => $campaign_asset_set\n });\n\n# Issue the mutate request.\nmy $response = $api_client->CampaignAssetSetService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n\n# Print some information about the response.\nmy $resource_name = $response->{results}[0]{resourceName};\nprintf \"Created a CampaignAssetSet with resource name '%s'.\\n\",\n $resource_name;add_dynamic_page_feed_asset.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.244Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":24,"totalLines":823,"estimatedTokens":7250}}106{"id":"doc-creating_a_hotel_campaign_google_ads_api_google_-289f436b","source":"documentation","title":"Creating a Hotel Campaign | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/hotel-ads/create-campaign","text":"Example:\n```text\nprivate void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n long hotelCenterAccountId,\n long cpcBidCeilingMicroAmount) {\n\n // Creates a budget to be used by the campaign that will be created below.\n String budgetResourceName = addCampaignBudget(googleAdsClient, customerId);\n\n // Creates a hotel campaign.\n String campaignResourceName =\n addHotelCampaign(\n googleAdsClient,\n customerId,\n budgetResourceName,\n hotelCenterAccountId,\n cpcBidCeilingMicroAmount);\n\n // Creates a hotel ad group.\n String adGroupResourceName = addHotelAdGroup(googleAdsClient, customerId, campaignResourceName);\n\n // Creates a hotel ad group ad.\n addHotelAdGroupAd(googleAdsClient, customerId, adGroupResourceName);\n}AddHotelAd.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long hotelCenterAccountId,\n long cpcBidCeilingMicroAmount)\n{\n try\n {\n // Create a budget to be used by the campaign that will be created below.\n string budgetResourceName = AddCampaignBudget(client, customerId);\n\n // Create a hotel campaign.\n string campaignResourceName = AddHotelCampaign(client, customerId,\n budgetResourceName, hotelCenterAccountId, cpcBidCeilingMicroAmount);\n\n // Create a hotel ad group.\n string adGroupResourceName = AddHotelAdGroup(client, customerId,\n campaignResourceName);\n\n // Create a hotel ad group ad.\n AddHotelAdGroupAd(client, customerId, adGroupResourceName);\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}AddHotelAd.cs\n```\n\nExample:\n```text\nprivate static function addHotelCampaign(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $budgetResourceName,\n int $hotelCenterAccountId,\n int $cpcBidCeilingMicroAmount\n) {\n // Creates a campaign.\n $campaign = new Campaign([\n 'name' => 'Interplanetary Cruise Campaign #' . Helper::getPrintableDatetime(),\n // Configures settings related to hotel campaigns including advertising channel type\n // and hotel setting info.\n 'advertising_channel_type' => AdvertisingChannelType::HOTEL,\n 'hotel_setting' => new HotelSettingInfo(['hotel_center_id' => $hotelCenterAccountId]),\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve.\n 'status' => CampaignStatus::PAUSED,\n // Sets the bidding strategy to PercentCpc. Only Manual CPC and Percent CPC can be used\n // for hotel campaigns.\n 'percent_cpc' => new PercentCpc([\n 'cpc_bid_ceiling_micros' => $cpcBidCeilingMicroAmount\n ]),\n // Sets the budget.\n 'campaign_budget' => $budgetResourceName,\n // Configures the campaign network options. Only Google Search is allowed for\n // hotel campaigns.\n 'network_settings' => new NetworkSettings([\n 'target_google_search' => true,\n ]),\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n ]);\n\n // Creates a campaign operation.\n $campaignOperation = new CampaignOperation();\n $campaignOperation->setCreate($campaign);\n\n // Issues a mutate request to add campaigns.\n $campaignServiceClient = $googleAdsClient->getCampaignServiceClient();\n $response = $campaignServiceClient->mutateCampaigns(\n MutateCampaignsRequest::build($customerId, [$campaignOperation])\n );\n\n /** @var Campaign $addedCampaign */\n $addedCampaign = $response->getResults()[0];\n printf(\n \"Added a hotel campaign with resource name '%s'.%s\",\n $addedCampaign->getResourceName(),\n PHP_EOL\n );\n\n return $addedCampaign->getResourceName();\n}AddHotelAd.php\n```\n\nExample:\n```text\ndef add_hotel_campaign(\n client: GoogleAdsClient,\n customer_id: str,\n budget_resource_name: str,\n hotel_center_account_id: int,\n cpc_bid_ceiling_micro_amount: int,\n) -> str:\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n # Create campaign.\n campaign_operation: CampaignOperation = client.get_type(\"CampaignOperation\")\n campaign: Campaign = campaign_operation.create\n campaign.name = f\"Interplanetary Cruise Campaign {uuid.uuid4()}\"\n\n # Configures settings related to hotel campaigns including advertising\n # channel type and hotel setting info.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.HOTEL\n )\n campaign.hotel_setting.hotel_center_id = hotel_center_account_id\n\n # Recommendation: Set the campaign to PAUSED when creating it to prevent the\n # ads from immediately serving. Set to ENABLED once you've added targeting\n # and the ads are ready to serve.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n\n # Set the bidding strategy to PercentCpc. Only Manual CPC and Percent CPC\n # can be used for hotel campaigns.\n campaign.percent_cpc.cpc_bid_ceiling_micros = cpc_bid_ceiling_micro_amount\n\n # Sets the budget.\n campaign.campaign_budget = budget_resource_name\n\n # Set the campaign network options. Only Google Search is allowed for hotel\n # campaigns.\n campaign.network_settings.target_google_search = True\n\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n # Add the campaign.\n campaign_response: MutateCampaignsResponse = (\n campaign_service.mutate_campaigns(\n customer_id=customer_id, operations=[campaign_operation]\n )\n )\n\n campaign_resource_name: str = campaign_response.results[0].resource_name\n\n print(\n \"Added a hotel campaign with resource name '{campaign_resource_name}'.\"\n )\n\n return campaign_resource_nameadd_hotel_ad.py\n```\n\nExample:\n```text\ndef add_hotel_campaign(client, customer_id, budget_resource,\n hotel_center_account_id, cpc_bid_ceiling_micro_amount)\n # Create a campaign.\n campaign_operation = client.operation.create_resource.campaign do |c|\n c.name = generate_random_name_field(\"Interplanetary Cruise Campaign\")\n\n # Configure settings related to hotel campaigns.\n c.advertising_channel_type = :HOTEL\n c.hotel_setting = client.resource.hotel_setting_info do |hsi|\n hsi.hotel_center_id = hotel_center_account_id\n end\n\n # Recommendation: Set the campaign to PAUSED when creating it to prevent the\n # ads from immediately serving. Set to ENABLED once you've added targeting and\n # the ads are ready to serve.\n c.status = :PAUSED\n\n # Set the bidding strategy to PercentCpc. Only Manual CPC and Percent CPC can\n # be used for hotel campaigns.\n c.percent_cpc = client.resource.percent_cpc do |pcpc|\n pcpc.cpc_bid_ceiling_micros = cpc_bid_ceiling_micro_amount\n end\n\n # Set the budget.\n c.campaign_budget = budget_resource\n\n # Configures the campaign network options. Only Google Search is allowed for\n # hotel campaigns.\n c.network_settings = client.resource.network_settings do |ns|\n ns.target_google_search = true\n end\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n end\n\n # Issue a mutate request to add the campaign.\n campaign_service = client.service.campaign\n response = campaign_service.mutate_campaigns(\n customer_id: customer_id,\n operations: [campaign_operation],\n )\n\n # Fetch the new campaign's resource name.\n campaign_resource = response.results.first.resource_name\n\n puts \"Added hotel campaign with resource name '#{campaign_resource}'.\"\n\n campaign_resource\nendadd_hotel_ad.rb\n```\n\nExample:\n```text\nsub add_hotel_campaign {\n my ($api_client, $customer_id, $budget_resource_name,\n $hotel_center_account_id, $cpc_bid_ceiling_micro_amount)\n = @_;\n\n # Create a hotel campaign.\n my $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n name => \"Interplanetary Cruise Campaign #\" . uniqid(),\n # Configure settings related to hotel campaigns including advertising\n # channel type and hotel setting info.\n advertisingChannelType => HOTEL,\n hotelSetting =>\n Google::Ads::GoogleAds::V25::Resources::HotelSettingInfo->new({\n hotelCenterId => $hotel_center_account_id\n }\n ),\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n status => Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n # Set the bidding strategy to PercentCpc. Only Manual CPC and Percent CPC\n # can be used for hotel campaigns.\n percentCpc => Google::Ads::GoogleAds::V25::Common::PercentCpc->new(\n {cpcBidCeilingMicros => $cpc_bid_ceiling_micro_amount}\n ),\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n # Set the budget.\n campaignBudget => $budget_resource_name,\n # Configure the campaign network options. Only Google Search is allowed for\n # hotel campaigns.\n networkSettings =>\n Google::Ads::GoogleAds::V25::Resources::NetworkSettings->new({\n targetGoogleSearch => \"true\"\n })});\n\n # Create a campaign operation.\n my $campaign_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation->\n new({create => $campaign});\n\n # Add the campaign.\n my $campaign_resource_name = $api_client->CampaignService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_operation]})->{results}[0]{resourceName};\n\n printf \"Added a hotel campaign with resource name: '%s'.\\n\",\n $campaign_resource_name;\n\n return $campaign_resource_name;\n}add_hotel_ad.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.245Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":299,"estimatedTokens":2727}}107{"id":"doc-assign_or_update_a_bidding_strategy_google_ads_a-57b2116c","source":"documentation","title":"Assign or update a bidding strategy | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/hotel-ads/bidding/assign-strategies","text":"Example:\n```text\n// Creates the campaign.\nCampaign campaign =\n Campaign.newBuilder()\n .setName(\"Interplanetary Cruise #\" + getPrintableDateTime())\n // Configures settings related to hotel campaigns including advertising channel type\n // and hotel setting info.\n .setAdvertisingChannelType(AdvertisingChannelType.HOTEL)\n .setHotelSetting(hotelSettingInfo)\n // Recommendation: Sets the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve\n .setStatus(CampaignStatus.PAUSED)\n // Sets the bidding strategy to Percent CPC. Only Manual CPC and Percent CPC can be used\n // for hotel campaigns.\n .setPercentCpc(\n PercentCpc.newBuilder().setCpcBidCeilingMicros(cpcBidCeilingMicroAmount).build())\n // Sets the budget.\n .setCampaignBudget(budgetResourceName)\n // Adds the networkSettings configured above.\n .setNetworkSettings(networkSettings)\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n .build();AddHotelAd.java\n```\n\nExample:\n```text\n// Create a campaign.\nCampaign campaign = new Campaign()\n{\n Name = \"Interplanetary Cruise Campaign #\" + ExampleUtilities.GetRandomString(),\n\n // Configure settings related to hotel campaigns including advertising channel type\n // and hotel setting info.\n AdvertisingChannelType = AdvertisingChannelType.Hotel,\n HotelSetting = new HotelSettingInfo()\n {\n HotelCenterId = hotelCenterAccountId\n },\n\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve.\n Status = CampaignStatus.Paused,\n\n // Sets the bidding strategy to PercentCpc. Only Manual CPC and Percent CPC can\n // be used for hotel campaigns.\n PercentCpc = new PercentCpc()\n {\n CpcBidCeilingMicros = cpcBidCeilingMicroAmount\n },\n\n // Set the budget.\n CampaignBudget = budgetResourceName,\n\n // Configure the campaign network options. Only Google Search is allowed for\n // hotel campaigns.\n NetworkSettings = new NetworkSettings()\n {\n TargetGoogleSearch = true\n },\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n};AddHotelAd.cs\n```\n\nExample:\n```text\n// Creates a campaign.\n$campaign = new Campaign([\n 'name' => 'Interplanetary Cruise Campaign #' . Helper::getPrintableDatetime(),\n // Configures settings related to hotel campaigns including advertising channel type\n // and hotel setting info.\n 'advertising_channel_type' => AdvertisingChannelType::HOTEL,\n 'hotel_setting' => new HotelSettingInfo(['hotel_center_id' => $hotelCenterAccountId]),\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve.\n 'status' => CampaignStatus::PAUSED,\n // Sets the bidding strategy to PercentCpc. Only Manual CPC and Percent CPC can be used\n // for hotel campaigns.\n 'percent_cpc' => new PercentCpc([\n 'cpc_bid_ceiling_micros' => $cpcBidCeilingMicroAmount\n ]),\n // Sets the budget.\n 'campaign_budget' => $budgetResourceName,\n // Configures the campaign network options. Only Google Search is allowed for\n // hotel campaigns.\n 'network_settings' => new NetworkSettings([\n 'target_google_search' => true,\n ]),\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n]);AddHotelAd.php\n```\n\nExample:\n```text\n# Create campaign.\ncampaign_operation: CampaignOperation = client.get_type(\"CampaignOperation\")\ncampaign: Campaign = campaign_operation.create\ncampaign.name = f\"Interplanetary Cruise Campaign {uuid.uuid4()}\"\n\n# Configures settings related to hotel campaigns including advertising\n# channel type and hotel setting info.\ncampaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.HOTEL\n)\ncampaign.hotel_setting.hotel_center_id = hotel_center_account_id\n\n# Recommendation: Set the campaign to PAUSED when creating it to prevent the\n# ads from immediately serving. Set to ENABLED once you've added targeting\n# and the ads are ready to serve.\ncampaign.status = client.enums.CampaignStatusEnum.PAUSED\n\n# Set the bidding strategy to PercentCpc. Only Manual CPC and Percent CPC\n# can be used for hotel campaigns.\ncampaign.percent_cpc.cpc_bid_ceiling_micros = cpc_bid_ceiling_micro_amount\n\n# Sets the budget.\ncampaign.campaign_budget = budget_resource_name\n\n# Set the campaign network options. Only Google Search is allowed for hotel\n# campaigns.\ncampaign.network_settings.target_google_search = True\n\n# Declare whether or not this campaign serves political ads targeting the\n# EU. Valid values are:\n# CONTAINS_EU_POLITICAL_ADVERTISING\n# DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\ncampaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n)add_hotel_ad.py\n```\n\nExample:\n```text\n# Create a campaign.\ncampaign_operation = client.operation.create_resource.campaign do |c|\n c.name = generate_random_name_field(\"Interplanetary Cruise Campaign\")\n\n # Configure settings related to hotel campaigns.\n c.advertising_channel_type = :HOTEL\n c.hotel_setting = client.resource.hotel_setting_info do |hsi|\n hsi.hotel_center_id = hotel_center_account_id\n end\n\n # Recommendation: Set the campaign to PAUSED when creating it to prevent the\n # ads from immediately serving. Set to ENABLED once you've added targeting and\n # the ads are ready to serve.\n c.status = :PAUSED\n\n # Set the bidding strategy to PercentCpc. Only Manual CPC and Percent CPC can\n # be used for hotel campaigns.\n c.percent_cpc = client.resource.percent_cpc do |pcpc|\n pcpc.cpc_bid_ceiling_micros = cpc_bid_ceiling_micro_amount\n end\n\n # Set the budget.\n c.campaign_budget = budget_resource\n\n # Configures the campaign network options. Only Google Search is allowed for\n # hotel campaigns.\n c.network_settings = client.resource.network_settings do |ns|\n ns.target_google_search = true\n end\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\nendadd_hotel_ad.rb\n```\n\nExample:\n```text\n# Create a hotel campaign.\nmy $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n name => \"Interplanetary Cruise Campaign #\" . uniqid(),\n # Configure settings related to hotel campaigns including advertising\n # channel type and hotel setting info.\n advertisingChannelType => HOTEL,\n hotelSetting =>\n Google::Ads::GoogleAds::V25::Resources::HotelSettingInfo->new({\n hotelCenterId => $hotel_center_account_id\n }\n ),\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n status => Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n # Set the bidding strategy to PercentCpc. Only Manual CPC and Percent CPC\n # can be used for hotel campaigns.\n percentCpc => Google::Ads::GoogleAds::V25::Common::PercentCpc->new(\n {cpcBidCeilingMicros => $cpc_bid_ceiling_micro_amount}\n ),\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n # Set the budget.\n campaignBudget => $budget_resource_name,\n # Configure the campaign network options. Only Google Search is allowed for\n # hotel campaigns.\n networkSettings =>\n Google::Ads::GoogleAds::V25::Resources::NetworkSettings->new({\n targetGoogleSearch => \"true\"\n })});add_hotel_ad.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.247Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":216,"estimatedTokens":2143}}108{"id":"doc-reporting_google_ads_api_google_for_developers-aa006c53","source":"documentation","title":"Reporting | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/hotel-ads/reporting","text":"Example:\n```text\nSELECT metrics.clicks\nFROM hotel_performance_view\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"metrics\": {\n \"clicks\": \"78090\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/1234567890/hotelPerformanceView\"\n }\n }\n ],\n \"totalResultsCount\": \"1\",\n \"fieldMask\": \"metrics.clicks\"\n}\n```\n\nExample:\n```text\nSELECT\n segments.partner_hotel_id,\n metrics.clicks\nFROM hotel_performance_view\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"metrics\": {\n \"clicks\": \"7055\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/1234567890/hotelPerformanceView\"\n },\n \"segments\": {\n \"partnerHotelId\": \"1111\"\n }\n },\n {\n \"metrics\": {\n \"clicks\": \"3047\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/1234567890/hotelPerformanceView\"\n },\n \"segments\": {\n \"partnerHotelId\": \"1112\"\n }\n },\n ...\n ]\n}\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n campaign.status,\n ad_group.name,\n segments.date,\n metrics.impressions,\n metrics.clicks\nFROM ad_group\nWHERE ad_group.type = HOTEL_ADS\n AND segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"campaign\": {\n \"resourceName\": \"customers/123456789/campaigns/23456789\",\n \"name\": \"test campaign\",\n \"status\": \"ENABLED\"\n },\n \"adGroup\": {\n \"resourceName\": \"customers/123456789/adGroups/11111111\",\n \"name\": \"test adgroup\"\n },\n \"metrics\": {\n \"clicks\": \"91\",\n \"impressions\": \"5145\"\n },\n \"segments\": {\n \"date\": \"2020-05-10\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nSELECT\n ad_group.id,\n ad_group.name,\n ad_group.status,\n campaign.name,\n campaign.status,\n ad_group_ad.status\nFROM ad_group_ad\nWHERE ad_group_ad.status = ENABLED\n AND campaign.status = ENABLED\n AND ad_group.status = ENABLED\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"campaign\": {\n \"resourceName\": \"customers/123456789/campaigns/23456789\",\n \"name\": \"test campaign\",\n \"status\": \"ENABLED\"\n },\n \"adGroup\": {\n \"resourceName\": \"customers/123456789/adGroups/111111111111\",\n \"id\": \"106121857411\",\n \"name\": \"test adgroup\",\n \"status\": \"ENABLED\"\n },\n \"adGroupAd\": {\n \"resourceName\": \"customers/123456789/adGroupAds/111111111111~33333333333\",\n \"status\": \"ENABLED\",\n \"ad\": {\n \"resourceName\": \"customers/123456789/ads/77777777777\"\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\nSELECT\n metrics.clicks,\n ad_group_criterion.listing_group.case_value.hotel_id.value\nFROM hotel_group_view\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"metrics\": {\n \"clicks\": \"5\"\n },\n \"adGroupCriterion\": {\n \"resourceName\": \"customers/1234567890/adGroupCriteria/22222222222~111111111111\"\n },\n \"hotelGroupView\": {\n \"resourceName\": \"customers/1234567890/hotelGroupViews/22222222222~111111111111\"\n }\n },\n {\n \"metrics\": {\n \"clicks\": \"0\"\n },\n \"adGroupCriterion\": {\n \"resourceName\": \"customers/1234567890/adGroupCriteria/22222222222~111111111112\"\n \"listingGroup\": {\n \"caseValue\": {\n \"hotelId\": {\n }\n }\n }\n },\n \"hotelGroupView\": {\n \"resourceName\": \"customers/1234567890/hotelGroupViews/22222222222~111111111112\"\n }\n },\n {\n \"metrics\": {\n \"clicks\": \"3\"\n },\n \"adGroupCriterion\": {\n \"resourceName\": \"customers/1234567890/adGroupCriteria/22222222222~111111111113\"\n \"listingGroup\": {\n \"caseValue\": {\n \"hotelId\": {\n \"value\": \"11111111111111111\"\n }\n }\n }\n }\n },\n \"hotelGroupView\": {\n \"resourceName\": \"customers/1234567890/hotelGroupViews/22222222222~111111111113\"\n }\n },\n {\n \"metrics\": {\n \"clicks\": \"2\"\n },\n \"adGroupCriterion\": {\n \"resourceName\": \"customers/1234567890/adGroupCriteria/22222222222~111111111114\"\n \"listingGroup\": {\n \"caseValue\": {\n \"hotelId\": {\n \"value\": \"11111111111111112\"\n }\n }\n }\n }\n },\n \"hotelGroupView\": {\n \"resourceName\": \"customers/1234567890/hotelGroupViews/22222222222~111111111114\"\n }\n },\n ]\n}\n```\n\nExample:\n```text\nSELECT\n ad_group.id,\n campaign.id,\n ad_group_criterion.user_list.user_list,\n segments.device,\n segments.hotel_date_selection_type,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.all_conversions_value\nFROM ad_group_audience_view\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"campaign\": {\n \"resourceName\": \"customers/123456789/campaigns/23456789\",\n \"id\": \"23456789\"\n },\n \"metrics\": {\n \"clicks\": \"0\",\n \"conversions\": \"0\",\n \"costMicros\": \"0\",\n \"impressions\": \"3\",\n \"allConversionsValue\": \"0\"\n },\n \"adGroupCriterion\": {\n \"resourceName\": \"customers/123456789/adGroupCriteria/23456789~789456\",\n \"userList\": {\n \"userList\": \"customers/123456789/userLists/456789\"\n }\n },\n \"adGroupAudienceView\": {\n \"resourceName\": \"customers/8005193609/adGroupAudienceViews/23456789~789456\"\n },\n \"segments\": {\n \"device\": \"TABLET\",\n \"hotelDateSelectionType\": \"USER_SELECTED\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign_criterion.user_list.user_list,\n segments.device,\n segments.hotel_date_selection_type,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.all_conversions_value\nFROM campaign_audience_view\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"campaign\": {\n \"resourceName\": \"customers/123456789/campaigns/23456789\",\n \"id\": \"23456789\"\n },\n \"metrics\": {\n \"clicks\": \"0\",\n \"conversions\": \"0\",\n \"costMicros\": \"0\",\n \"impressions\": \"3\",\n \"allConversionsValue\": \"0\"\n },\n \"campaignCriterion\": {\n \"resourceName\": \"customers/123456789/campaignCriteria/23456789~789456\",\n \"userList\": {\n \"userList\": \"customers/123456789/userLists/456789\"\n }\n },\n \"campaignAudienceView\": {\n \"resourceName\": \"customers/8005193609/campaignAudienceViews/23456789~789456\"\n },\n \"segments\": {\n \"device\": \"TABLET\",\n \"hotelDateSelectionType\": \"USER_SELECTED\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nSELECT\n segments.hotel_center_id,\n segments.device,\n segments.partner_hotel_id,\n segments.hotel_check_in_day_of_week,\n segments.hotel_date_selection_type,\n segments.hotel_length_of_stay,\n segments.hotel_booking_window_days,\n metrics.search_top_impression_share,\n metrics.search_absolute_top_impression_share,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.all_conversions_value,\n metrics.search_impression_share\nFROM hotel_performance_view\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"campaign\": {\n \"resourceName\": \"customers/123456789/campaigns/23456789\"\n },\n \"metrics\": {\n \"clicks\": \"0\",\n \"conversions\": \"0.0\",\n \"costMicros\": \"0\",\n \"impressions\": \"0\",\n \"searchImpressionShare\": \"0.0999\",\n \"searchAbsoluteTopImpressionShare\": \"0.0999\",\n \"searchTopImpressionShare\": \"0.0999\",\n \"allConversionsValue\": \"1\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/123456789/hotelPerformanceView\"\n },\n \"segments\": {\n \"device\": \"DESKTOP\",\n \"hotelBookingWindowDays\": \"3\",\n \"hotelCenterId\": \"1234\",\n \"hotelCheckInDayOfWeek\": \"MONDAY\",\n \"hotelDateSelectionType\": \"USER_SELECTED\",\n \"hotelLengthOfStay\": \"4\",\n \"partnerHotelId\": \"123\"\n }\n },\n {\n \"campaign\": {\n \"resourceName\": \"customers/123456789/campaigns/23456789\"\n },\n \"metrics\": {\n \"clicks\": \"0\",\n \"conversions\": \"0.0\",\n \"costMicros\": \"0\",\n \"impressions\": \"1\",\n \"searchImpressionShare\": \"1.0\",\n \"searchAbsoluteTopImpressionShare\": \"0.0999\",\n \"searchTopImpressionShare\": \"1.0\",\n \"allConversionsValue\": \"1\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/123456789/hotelPerformanceView\"\n },\n \"segments\": {\n \"device\": \"DESKTOP\",\n \"hotelBookingWindowDays\": \"3\",\n \"hotelCenterId\": \"1234\",\n \"hotelCheckInDayOfWeek\": \"MONDAY\",\n \"hotelDateSelectionType\": \"USER_SELECTED\",\n \"hotelLengthOfStay\": \"4\",\n \"partnerHotelId\": \"123\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nSELECT\n segments.click_type,\n segments.hotel_center_id,\n segments.device,\n segments.partner_hotel_id,\n segments.hotel_check_in_day_of_week,\n segments.hotel_date_selection_type,\n segments.hotel_length_of_stay,\n segments.hotel_booking_window_days,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions,\n metrics.all_conversions_value\nFROM hotel_performance_view\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"campaign\": {\n \"resourceName\": \"customers/123456789/campaigns/23456789\"\n },\n \"metrics\": {\n \"clicks\": \"0\",\n \"conversions\": \"0.0\",\n \"costMicros\": \"0\",\n \"impressions\": \"1\",\n \"allConversionsValue\": \"0.0\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/123456789/hotelPerformanceView\"\n },\n \"segments\": {\n \"device\": \"DESKTOP\",\n \"hotelBookingWindowDays\": \"0\",\n \"hotelCenterId\": \"1234\",\n \"hotelCheckInDayOfWeek\": \"TUESDAY\",\n \"hotelDateSelectionType\": \"USER_SELECTED\",\n \"hotelLengthOfStay\": \"4\",\n \"partnerHotelId\": \"123\",\n \"clickType\": \"HOTEL_PRICE\"\n }\n },\n {\n \"campaign\": {\n \"resourceName\": \"customers/123456789/campaigns/23456789\"\n },\n \"metrics\": {\n \"clicks\": \"1\",\n \"conversions\": \"0.0\",\n \"costMicros\": \"0\",\n \"impressions\": \"1\",\n \"allConversionsValue\": \"0.0\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/123456789/hotelPerformanceView\"\n },\n \"segments\": {\n \"device\": \"DESKTOP\",\n \"hotelBookingWindowDays\": \"0\",\n \"hotelCenterId\": \"1234\",\n \"hotelCheckInDayOfWeek\": \"TUESDAY\",\n \"hotelDateSelectionType\": \"USER_SELECTED\",\n \"hotelLengthOfStay\": \"4\",\n \"partnerHotelId\": \"12345\",\n \"clickType\": \"HOTEL_PRICE\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n segments.hotel_center_id,\n segments.hotel_country,\n segments.hotel_rate_rule_id,\n segments.hotel_rate_type,\n segments.device,\n segments.partner_hotel_id,\n metrics.search_top_impression_share,\n metrics.search_absolute_top_impression_share,\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros,\n metrics.conversions\nFROM hotel_performance_view\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"campaign\": {\n \"resourceName\": \"customers/123456789/campaigns/23456789\",\n \"id\": \"23456789\"\n },\n \"metrics\": {\n \"clicks\": \"1\",\n \"conversions\": \"0.0\",\n \"costMicros\": \"0\",\n \"impressions\": \"24\",\n \"searchAbsoluteTopImpressionShare\": \"0.0999\",\n \"searchTopImpressionShare\": \"0.17073170731707318\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/123456789/hotelPerformanceView\"\n },\n \"segments\": {\n \"device\": \"TABLET\",\n \"hotelCenterId\": \"1234\",\n \"partnerHotelId\": \"123\",\n \"hotelRateRuleId\": \"desktop\",\n \"hotelRateType\": \"PUBLIC_RATE\"\n }\n },\n {\n \"campaign\": {\n \"resourceName\": \"customers/123456789/campaigns/23456789\",\n \"id\": \"23456789\"\n },\n \"metrics\": {\n \"clicks\": \"107\",\n \"conversions\": \"0.0\",\n \"costMicros\": \"0\",\n \"impressions\": \"1668\",\n \"searchAbsoluteTopImpressionShare\": \"0.0999\",\n \"searchTopImpressionShare\": \"0.3581201665675193\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/123456789/hotelPerformanceView\"\n },\n \"segments\": {\n \"device\": \"TABLET\",\n \"hotelCenterId\": \"1234\",\n \"partnerHotelId\": \"1235\",\n \"hotelRateRuleId\": \"desktop\",\n \"hotelRateType\": \"PUBLIC_RATE\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nSELECT\n segments.hotel_center_id,\n segments.partner_hotel_id,\n segments.hotel_price_bucket,\n metrics.hotel_average_lead_value_micros,\n metrics.hotel_price_difference_percentage\nFROM hotel_performance_view\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"metrics\": {\n \"hotelAverageLeadValueMicros\": \"96416341.829268292\",\n \"hotelPriceDifferencePercentage\": \"-0.014627310872986811\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/123456789/hotelPerformanceView\"\n },\n \"segments\": {\n \"hotelCenterId\": \"1234\",\n \"partnerHotelId\": \"123\",\n \"hotelPriceBucket\": \"LOWEST_TIED\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nSELECT\n segments.hotel_center_id,\n segments.partner_hotel_id,\n segments.hotel_price_bucket,\n metrics.all_conversions_value,\n metrics.conversions\nFROM hotel_performance_view\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"metrics\": {\n \"allConversionsValue\": \"123.5\",\n \"conversions\": \"1\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/123456789/hotelPerformanceView\"\n },\n \"segments\": {\n \"hotelCenterId\": \"1234\",\n \"partnerHotelId\": \"123\",\n \"hotelPriceBucket\": \"LOWEST_TIED\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nSELECT\n segments.date,\n segments.partner_hotel_id,\n metrics.all_conversions_value,\n metrics.cost_micros,\n metrics.conversions\nFROM hotel_performance_view\nWHERE segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\n{\n \"results\": [\n {\n \"metrics\": {\n \"allConversionsValue\": \"250.0\",\n \"costMicros\": \"15000000\",\n \"conversions\": \"2.0\"\n },\n \"hotelPerformanceView\": {\n \"resourceName\": \"customers/123456789/hotelPerformanceView\"\n },\n \"segments\": {\n \"date\": \"2026-03-24\",\n \"partnerHotelId\": \"123\"\n }\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.248Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":26,"totalLines":663,"estimatedTokens":3608}}109{"id":"doc-set_or_update_bids_google_ads_api_google_for_dev-7841344b","source":"documentation","title":"Set or update bids | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/hotel-ads/bidding/set-bids","text":"Example:\n```text\nprivate String addHotelAdGroup(\n GoogleAdsClient googleAdsClient, long customerId, String campaignResourceName) {\n // Creates an ad group.\n AdGroup adGroup =\n AdGroup.newBuilder()\n .setName(\"Earth to Mars Cruises #\" + getPrintableDateTime())\n .setCampaign(campaignResourceName)\n // Sets the ad group type to HOTEL_ADS. This cannot be set to other types.\n .setType(AdGroupType.HOTEL_ADS)\n .setCpcBidMicros(1_000_000L)\n .setStatus(AdGroupStatus.ENABLED)\n .build();\n\n // Creates an ad group operation.\n AdGroupOperation operation = AdGroupOperation.newBuilder().setCreate(adGroup).build();\n\n // Issues a mutate request to add an ad group.\n try (AdGroupServiceClient adGroupServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupServiceClient()) {\n MutateAdGroupResult mutateAdGroupResult =\n adGroupServiceClient\n .mutateAdGroups(Long.toString(customerId), Collections.singletonList(operation))\n .getResults(0);\n System.out.printf(\n \"Added a hotel ad group with resource name: '%s'%n\",\n mutateAdGroupResult.getResourceName());\n return mutateAdGroupResult.getResourceName();\n }\n}AddHotelAd.java\n```\n\nExample:\n```text\nprivate static string AddHotelAdGroup(GoogleAdsClient client, long customerId,\n string campaignResourceName)\n{\n // Get the AdGroupService.\n AdGroupServiceClient service = client.GetService(Services.V25.AdGroupService);\n\n // Create an ad group.\n AdGroup adGroup = new AdGroup()\n {\n Name = \"Earth to Mars Cruise #\" + ExampleUtilities.GetRandomString(),\n\n // Sets the campaign.\n Campaign = campaignResourceName,\n\n // Optional: Sets the ad group type to HOTEL_ADS.\n // This cannot be set to other types.\n Type = AdGroupType.HotelAds,\n\n CpcBidMicros = 10000000,\n Status = AdGroupStatus.Enabled\n };\n\n // Create an ad group operation.\n AdGroupOperation adGroupOperation = new AdGroupOperation()\n {\n Create = adGroup\n };\n\n // Issue a mutate request to add an ad group.\n MutateAdGroupsResponse response = service.MutateAdGroups(customerId.ToString(),\n new AdGroupOperation[] { adGroupOperation });\n return response.Results[0].ResourceName;\n}AddHotelAd.cs\n```\n\nExample:\n```text\nprivate static function addHotelAdGroup(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $campaignResourceName\n) {\n // Creates an ad group.\n $adGroup = new AdGroup([\n 'name' => 'Earth to Mars Cruise #' . Helper::getPrintableDatetime(),\n // Sets the campaign.\n 'campaign' => $campaignResourceName,\n // Sets the ad group type to HOTEL_ADS.\n // This cannot be set to other types.\n 'type' => AdGroupType::HOTEL_ADS,\n 'cpc_bid_micros' => 10000000,\n 'status' => AdGroupStatus::ENABLED,\n ]);\n\n // Creates an ad group operation.\n $adGroupOperation = new AdGroupOperation();\n $adGroupOperation->setCreate($adGroup);\n\n // Issues a mutate request to add an ad group.\n $adGroupServiceClient = $googleAdsClient->getAdGroupServiceClient();\n $response = $adGroupServiceClient->mutateAdGroups(\n MutateAdGroupsRequest::build($customerId, [$adGroupOperation])\n );\n\n /** @var AdGroup $addedAdGroup */\n $addedAdGroup = $response->getResults()[0];\n printf(\n \"Added a hotel ad group with resource name '%s'.%s\",\n $addedAdGroup->getResourceName(),\n PHP_EOL\n );\n\n return $addedAdGroup->getResourceName();\n}AddHotelAd.php\n```\n\nExample:\n```text\ndef add_hotel_ad_group(\n client: GoogleAdsClient, customer_id: str, campaign_resource_name: str\n) -> str:\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n\n # Create ad group.\n ad_group_operation: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group: AdGroup = ad_group_operation.create\n ad_group.name = f\"Earth to Mars cruise {uuid.uuid4()}\"\n ad_group.status = client.enums.AdGroupStatusEnum.ENABLED\n ad_group.campaign = campaign_resource_name\n # Sets the ad group type to HOTEL_ADS. This cannot be set to other types.\n ad_group.type_ = client.enums.AdGroupTypeEnum.HOTEL_ADS\n ad_group.cpc_bid_micros = 10000000\n\n # Add the ad group.\n ad_group_response: MutateAdGroupsResponse = (\n ad_group_service.mutate_ad_groups(\n customer_id=customer_id, operations=[ad_group_operation]\n )\n )\n\n ad_group_resource_name: str = ad_group_response.results[0].resource_name\n\n print(\n \"Added a hotel ad group with resource name '{ad_group_resource_name}'.\"\n )\n\n return ad_group_resource_nameadd_hotel_ad.py\n```\n\nExample:\n```text\ndef add_hotel_ad_group(client, customer_id, campaign_resource)\n # Create an ad group.\n ad_group_operation = client.operation.create_resource.ad_group do |ag|\n ag.name = generate_random_name_field(\"Earth to Mars Cruise\")\n\n # Set the campaign.\n ag.campaign = campaign_resource\n\n # Optional: Set the ad group type to HOTEL_ADS.\n # This cannot be set to other types.\n ag.type = :HOTEL_ADS\n ag.cpc_bid_micros = 10_000_000\n ag.status = :ENABLED\n end\n\n # Issue a mutate request to add the ad group.\n ad_group_service = client.service.ad_group\n response = ad_group_service.mutate_ad_groups(\n customer_id: customer_id,\n operations: [ad_group_operation]\n )\n\n # Fetch the new ad group's resource name.\n ad_group_resource = response.results.first.resource_name\n\n puts \"Added hotel ad group with resource name '#{ad_group_resource}'.\"\n\n ad_group_resource\nendadd_hotel_ad.rb\n```\n\nExample:\n```text\nsub add_hotel_ad_group {\n my ($api_client, $customer_id, $campaign_resource_name) = @_;\n\n # Create an ad group.\n my $ad_group = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Earth to Mars Cruise #\" . uniqid(),\n # Set the campaign.\n campaign => $campaign_resource_name,\n # Set the ad group type to HOTEL_ADS.\n # This cannot be set to other types.\n type => HOTEL_ADS,\n cpcBidMicros => 1000000,\n status => Google::Ads::GoogleAds::V25::Enums::AdGroupStatusEnum::ENABLED\n });\n\n # Create an ad group operation.\n my $ad_group_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group});\n\n # Add the ad group.\n my $ad_group_resource_name = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_operation]})->{results}[0]{resourceName};\n\n printf \"Added a hotel ad group with resource name: '%s'.\\n\",\n $ad_group_resource_name;\n\n return $ad_group_resource_name;\n}add_hotel_ad.pl\n```\n\nExample:\n```text\nprivate static String addLevel1Nodes(\n long customerId,\n long adGroupId,\n String rootResourceName,\n List<AdGroupCriterionOperation> operations,\n long percentCpcBidMicroAmount) {\n // Creates hotel class info and dimension info for 5-star hotels.\n ListingDimensionInfo fiveStarredDimensionInfo =\n ListingDimensionInfo.newBuilder()\n .setHotelClass(HotelClassInfo.newBuilder().setValue(5).build())\n .build();\n // Creates listing group info for 5-star hotels as a UNIT node.\n ListingGroupInfo fiveStarredUnit =\n ListingGroupInfo.newBuilder()\n .setType(ListingGroupType.UNIT)\n .setParentAdGroupCriterion(rootResourceName)\n .setCaseValue(fiveStarredDimensionInfo)\n .build();\n // Creates an ad group criterion for 5-star hotels.\n AdGroupCriterion fiveStarredAdGroupCriterion =\n createAdGroupCriterion(customerId, adGroupId, fiveStarredUnit, percentCpcBidMicroAmount);\n // Decrements the temp ID for the next ad group criterion.\n AdGroupCriterionOperation operation = generateCreateOperation(fiveStarredAdGroupCriterion);\n operations.add(operation);\n\n // You can also create more UNIT nodes for other hotel classes by copying the above code in\n // this method and modifying the value passed to HotelClassInfo() to the value you want.\n // For instance, passing 4 instead of 5 in the above code will create a UNIT node of 4-star\n // hotels instead.\n\n // Creates hotel class info and dimension info for other hotel classes by not specifying\n // any attributes on those object.\n ListingDimensionInfo otherHotelsDimensionInfo =\n ListingDimensionInfo.newBuilder()\n .setHotelClass(HotelClassInfo.newBuilder().build())\n .build();\n // Creates listing group info for other hotel classes as a SUBDIVISION node, which will be\n // used as a parent node for children nodes of the next level.\n ListingGroupInfo otherHotelsSubdivision =\n createListingGroupInfo(\n ListingGroupType.SUBDIVISION, rootResourceName, otherHotelsDimensionInfo);\n // Creates an ad group criterion for other hotel classes.\n AdGroupCriterion otherHotelsAdGroupCriterion =\n createAdGroupCriterion(\n customerId, adGroupId, otherHotelsSubdivision, percentCpcBidMicroAmount);\n operation = generateCreateOperation(otherHotelsAdGroupCriterion);\n operations.add(operation);\n\n return otherHotelsAdGroupCriterion.getResourceName();\n}AddHotelListingGroupTree.java\n```\n\nExample:\n```text\nprivate string AddLevel1Nodes(long customerId, long adGroupId, string rootResourceName,\n List<AdGroupCriterionOperation> operations, long percentCpcBidMicroAmount)\n{\n // Create listing dimension info for 5-star class hotels.\n ListingDimensionInfo fiveStarredListingDimensionInfo = new ListingDimensionInfo\n {\n HotelClass = new HotelClassInfo\n {\n Value = 5\n }\n };\n\n // Create a listing group info for 5-star hotels as a UNIT node.\n ListingGroupInfo fiveStarredUnit = CreateListingGroupInfo(ListingGroupType.Unit,\n rootResourceName, fiveStarredListingDimensionInfo);\n\n // Create an ad group criterion for 5-star hotels.\n AdGroupCriterion fiveStarredAdGroupCriterion = CreateAdGroupCriterion(customerId,\n adGroupId, fiveStarredUnit, percentCpcBidMicroAmount);\n\n // Create an operation and add it to the list of operations.\n operations.Add(new AdGroupCriterionOperation\n {\n Create = fiveStarredAdGroupCriterion\n });\n\n // Decrement the temp ID for the next ad group criterion.\n nextTempId--;\n\n // You can also create more UNIT nodes for other hotel classes by copying the above code\n // in this method and modifying the value passed to HotelClassInfo().\n // For instance, passing 4 instead of 5 in the above code will instead create a UNIT\n // node of 4-star hotels.\n\n // Create hotel class info and dimension info for other hotel classes by *not*\n // specifying any attributes on those object.\n ListingDimensionInfo otherHotelsListingDimensionInfo = new ListingDimensionInfo\n {\n HotelClass = new HotelClassInfo()\n };\n\n // Create listing group info for other hotel classes as a SUBDIVISION node, which will\n // be used as a parent node for children nodes of the next level.\n ListingGroupInfo otherHotelsSubdivisionListingGroupInfo = CreateListingGroupInfo\n (ListingGroupType.Subdivision, rootResourceName, otherHotelsListingDimensionInfo);\n\n // Create an ad group criterion for other hotel classes.\n AdGroupCriterion otherHotelsAdGroupCriterion = CreateAdGroupCriterion(customerId,\n adGroupId, otherHotelsSubdivisionListingGroupInfo, percentCpcBidMicroAmount);\n\n // Create an operation and add it to the list of operations.\n operations.Add(new AdGroupCriterionOperation\n {\n Create = otherHotelsAdGroupCriterion\n });\n\n // Decrement the temp ID for the next ad group criterion.\n nextTempId--;\n\n return otherHotelsAdGroupCriterion.ResourceName;\n}AddHotelListingGroupTree.cs\n```\n\nExample:\n```text\nprivate static function addLevel1Nodes(\n int $customerId,\n int $adGroupId,\n string $rootResourceName,\n array &$operations,\n int $percentCpcBidMicroAmount\n) {\n // Creates hotel class info and dimension info for 5-star hotels.\n $fiveStarredDimensionInfo = new ListingDimensionInfo([\n 'hotel_class' => new HotelClassInfo(['value' => 5])\n ]);\n // Creates listing group info for 5-star hotels as a UNIT node.\n $fiveStarredUnit = self::createListingGroupInfo(\n ListingGroupType::UNIT,\n $rootResourceName,\n $fiveStarredDimensionInfo\n );\n // Creates an ad group criterion for 5-star hotels.\n $fiveStarredAdGroupCriterion = self::createAdGroupCriterion(\n $customerId,\n $adGroupId,\n $fiveStarredUnit,\n $percentCpcBidMicroAmount\n );\n // Decrements the temp ID for the next ad group criterion.\n self::$nextTempId--;\n $operation = self::generateCreateOperation($fiveStarredAdGroupCriterion);\n $operations[] = $operation;\n\n // You can also create more UNIT nodes for other hotel classes by copying the above code in\n // this method and modifying the value passed to HotelClassInfo() to the value you want.\n // For instance, passing 4 instead of 5 in the above code will create a UNIT node of 4-star\n // hotels instead.\n\n // Creates hotel class info and dimension info for other hotel classes by *not* specifying\n // any attributes on those object.\n $othersHotelsDimensionInfo = new ListingDimensionInfo([\n 'hotel_class' => new HotelClassInfo()\n ]);\n // Creates listing group info for other hotel classes as a SUBDIVISION node, which will be\n // used as a parent node for children nodes of the next level.\n $otherHotelsSubDivision = self::createListingGroupInfo(\n ListingGroupType::SUBDIVISION,\n $rootResourceName,\n $othersHotelsDimensionInfo\n );\n // Creates an ad group criterion for other hotel classes.\n $otherHotelsAdGroupCriterion = self::createAdGroupCriterion(\n $customerId,\n $adGroupId,\n $otherHotelsSubDivision,\n $percentCpcBidMicroAmount\n );\n $operation = self::generateCreateOperation($otherHotelsAdGroupCriterion);\n $operations[] = $operation;\n\n self::$nextTempId--;\n return $otherHotelsAdGroupCriterion->getResourceName();\n}AddHotelListingGroupTree.php\n```\n\nExample:\n```text\ndef add_level1_nodes(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n root_resource_name: str,\n operations: List[AdGroupCriterionOperation],\n percent_cpc_bid_micro_amount: int,\n) -> str:\n \"\"\"Creates child nodes on level 1, partitioned by the hotel class info.\n\n Args:\n client: The Google Ads API client.\n customer_id: The Google Ads customer ID.\n ad_group_id: The ad group ID to which the hotel listing group will be\n added.\n root_resource_name: The string resource name of the listing group's root\n node.\n operations: A list of AdGroupCriterionOperations.\n percent_cpc_bid_micro_amount: The CPC bid micro amount to be set on\n created ad group criteria.\n\n Returns:\n The string resource name of the \"other hotel classes\" node, which serves\n as the parent node for the next level of the listing tree.\n \"\"\"\n global next_temp_id\n\n # Create listing dimension info for 5-star class hotels.\n five_starred_listing_dimension_info: ListingDimensionInfo = client.get_type(\n \"ListingDimensionInfo\"\n )\n five_starred_listing_dimension_info.hotel_class.value = 5\n\n # Create a listing group info for 5-star hotels as a UNIT node.\n five_starred_unit: ListingGroupInfo = create_listing_group_info(\n client,\n client.enums.ListingGroupTypeEnum.UNIT,\n root_resource_name,\n five_starred_listing_dimension_info,\n )\n\n # Create an ad group criterion for 5-star hotels.\n five_starred_ad_group_criterion: AdGroupCriterion = (\n create_ad_group_criterion(\n client,\n customer_id,\n ad_group_id,\n five_starred_unit,\n percent_cpc_bid_micro_amount,\n )\n )\n\n # Create an operation and add it to the list of operations.\n five_starred_ad_group_criterion_operation: AdGroupCriterionOperation = (\n client.get_type(\"AdGroupCriterionOperation\")\n )\n client.copy_from(\n five_starred_ad_group_criterion_operation.create,\n five_starred_ad_group_criterion,\n )\n operations.append(five_starred_ad_group_criterion_operation)\n\n # Decrement the temp ID for the next ad group criterion.\n next_temp_id -= 1\n\n # You can also create more UNIT nodes for other hotel classes by copying the\n # above code in this method and modifying the hotel class value.\n # For instance, passing 4 instead of 5 in the above code will instead create\n # a UNIT node of 4-star hotels.\n\n # Create hotel class info and dimension info without any specifying\n # attributes. This node will then represent hotel classes other than those\n # already covered by UNIT nodes at this level.\n other_hotels_listing_dimension_info: ListingDimensionInfo = client.get_type(\n \"ListingDimensionInfo\"\n )\n # Set \"hotel_class\" as the oneof field on the ListingDimensionInfo object\n # without specifying the optional hotel_class field.\n client.copy_from(\n other_hotels_listing_dimension_info.hotel_class,\n client.get_type(\"HotelClassInfo\"),\n )\n\n # Create listing group info for other hotel classes as a SUBDIVISION node,\n # which will be used as a parent node for children nodes of the next level.\n other_hotels_subdivision_listing_group_info: ListingGroupInfo = (\n create_listing_group_info(\n client,\n client.enums.ListingGroupTypeEnum.SUBDIVISION,\n root_resource_name,\n other_hotels_listing_dimension_info,\n )\n )\n\n # Create an ad group criterion for other hotel classes.\n other_hotels_ad_group_criterion: AdGroupCriterion = (\n create_ad_group_criterion(\n client,\n customer_id,\n ad_group_id,\n other_hotels_subdivision_listing_group_info,\n percent_cpc_bid_micro_amount,\n )\n )\n\n # Create an operation and add it to the list of operations.\n other_hotels_ad_group_criterion_operation: AdGroupCriterionOperation = (\n client.get_type(\"AdGroupCriterionOperation\")\n )\n client.copy_from(\n other_hotels_ad_group_criterion_operation.create,\n other_hotels_ad_group_criterion,\n )\n operations.append(other_hotels_ad_group_criterion_operation)\n\n # Decrement the temp ID for the next ad group criterion.\n next_temp_id -= 1\n\n return other_hotels_ad_group_criterion.resource_nameadd_hotel_listing_group_tree.py\n```\n\nExample:\n```text\ndef add_level1_nodes(\n client,\n customer_id,\n ad_group_id,\n root_resource_name,\n operations,\n percent_cpc_bid_micro_amount)\n # Creates hotel class info and dimension info for 5-star hotels.\n five_starred_dimension_info = client.resource.listing_dimension_info do |d|\n d.hotel_class = client.resource.hotel_class_info do |c|\n c.value = 5\n end\n end\n\n # Creates listing group info for 5-star hotels as a UNIT node.\n five_starred_unit = create_listing_group_info(\n client,\n :UNIT,\n root_resource_name,\n five_starred_dimension_info,\n )\n\n # Creates an ad group criterion for 5-star hotels.\n five_starred_ad_group_criterion = create_ad_group_criterion(\n client,\n customer_id,\n ad_group_id,\n five_starred_unit,\n percent_cpc_bid_micro_amount,\n )\n\n operations << generate_create_operation(\n client,\n five_starred_ad_group_criterion,\n )\n\n # You can also create more UNIT nodes for other hotel classes by copying the\n # above code in this method and modifying the value passed to HotelClassInfo()\n # to the value you want.\n # For instance, passing 4 instead of 5 in the above code will create a UNIT\n # node of 4-star hotels instead.\n\n # Creates hotel class info and dimension info for other hotel classes\n # by *not* specifying any attributes on those object.\n other_hotels_dimention_info = client.resource.listing_dimension_info do |d|\n d.hotel_class = client.resource.hotel_class_info\n end\n\n # Creates listing group info for other hotel classes as a SUBDIVISION node,\n # which will be used as a parent node for children nodes of the next level.\n other_hotels_subdivision = create_listing_group_info(\n client,\n :SUBDIVISION,\n root_resource_name,\n other_hotels_dimention_info,\n )\n\n # Creates an ad group criterion for other hotel classes.\n other_hotels_ad_group_criterion = create_ad_group_criterion(\n client,\n customer_id,\n ad_group_id,\n other_hotels_subdivision,\n percent_cpc_bid_micro_amount,\n )\n\n operations << generate_create_operation(\n client,\n other_hotels_ad_group_criterion,\n )\n\n other_hotels_ad_group_criterion.resource_name\nendadd_hotel_listing_group_tree.rb\n```\n\nExample:\n```text\nsub add_level_1_nodes {\n my ($customer_id, $ad_group_id, $root_resource_name, $operations,\n $percent_cpc_bid_micro_amount)\n = @_;\n\n # Create hotel class info and dimension info for 5-star hotels.\n my $five_starred_dimension_info =\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n hotelClass => Google::Ads::GoogleAds::V25::Common::HotelClassInfo->new({\n value => 5\n })});\n\n # Create listing group info for 5-star hotels as a UNIT node.\n my $five_starred_unit = create_listing_group_info(UNIT, $root_resource_name,\n $five_starred_dimension_info);\n\n # Create an ad group criterion for 5-star hotels.\n my $five_starred_ad_group_criterion =\n create_ad_group_criterion($customer_id, $ad_group_id, $five_starred_unit,\n $percent_cpc_bid_micro_amount);\n\n my $operation = generate_create_operation($five_starred_ad_group_criterion);\n push @$operations, $operation;\n\n # You can also create more UNIT nodes for other hotel classes by copying the\n # above code in this method and modifying the value passed to HotelClassInfo\n # to the value you want. For instance, passing 4 instead of 5 in the above code\n # will create a UNIT node of 4-star hotels instead.\n\n # Create hotel class info and dimension info for other hotel classes by *not*\n # specifying any attributes on those object.\n my $others_hotels_dimension_info =\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n hotelClass => Google::Ads::GoogleAds::V25::Common::HotelClassInfo->new()}\n );\n\n # Create listing group info for other hotel classes as a SUBDIVISION node, which\n # will be used as a parent node for children nodes of the next level.\n my $other_hotels_subdivision =\n create_listing_group_info(SUBDIVISION, $root_resource_name,\n $others_hotels_dimension_info);\n\n # Create an ad group criterion for other hotel classes.\n my $other_hotels_ad_group_criterion =\n create_ad_group_criterion($customer_id, $ad_group_id,\n $other_hotels_subdivision, $percent_cpc_bid_micro_amount);\n\n $operation = generate_create_operation($other_hotels_ad_group_criterion);\n push @$operations, $operation;\n\n return $other_hotels_ad_group_criterion->{resourceName};\n}add_hotel_listing_group_tree.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.251Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":652,"estimatedTokens":5795}}110{"id":"doc-linked_merchant_center_and_google_ads_accounts_g-68993fea","source":"documentation","title":"Linked Merchant Center and Google Ads accounts | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center","text":"Example:\n```text\nSELECT\n product_link_invitation.resource_name,\n product_link_invitation.merchant_center.merchant_center_id,\n product_link_invitation.type\nFROM product_link_invitation\nWHERE product_link_invitation.status = 'PENDING_APPROVAL'\n AND product_link_invitation.type = 'MERCHANT_CENTER'\n```\n\nExample:\n```text\nSELECT\n product_link.merchant_center.merchant_center_id,\n product_link.product_link_id\nFROM product_link\nWHERE product_link.type = 'MERCHANT_CENTER'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.252Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":21,"estimatedTokens":126}}111{"id":"doc-adjust_bids_with_bid_modifiers_google_ads_api_go-8991f9bf","source":"documentation","title":"Adjust Bids with Bid Modifiers | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/hotel-ads/bidding/create-ad-group-bid-modifier","text":"Example:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient, long customerId, long adGroupId) {\n List<AdGroupBidModifierOperation> operations = new ArrayList<>();\n\n // Constructs the ad group resource name to use for each bid modifier.\n String adGroupResourceName = ResourceNames.adGroup(customerId, adGroupId);\n\n // 1) Creates an ad group bid modifier based on the hotel check-in day.\n AdGroupBidModifier checkInDayAdGroupBidModifier =\n AdGroupBidModifier.newBuilder()\n // Sets the resource name to the ad group resource name joined with the criterion ID\n // whose value corresponds to the desired check-in day.\n .setAdGroup(adGroupResourceName)\n .setHotelCheckInDay(HotelCheckInDayInfo.newBuilder().setDayOfWeek(DayOfWeek.MONDAY))\n // Sets the bid modifier value to 150%.\n .setBidModifier(1.5d)\n .build();\n operations.add(\n AdGroupBidModifierOperation.newBuilder().setCreate(checkInDayAdGroupBidModifier).build());\n\n // 2) Creates an ad group bid modifier based on the hotel length of stay.\n AdGroupBidModifier lengthOfStayAdGroupBidModifier =\n AdGroupBidModifier.newBuilder()\n // Sets the ad group.\n .setAdGroup(adGroupResourceName)\n // Creates the hotel length of stay info.\n .setHotelLengthOfStay(\n HotelLengthOfStayInfo.newBuilder().setMinNights(3L).setMaxNights(7L).build())\n // Sets the bid modifier value to 170%.\n .setBidModifier(1.7d)\n .build();\n operations.add(\n AdGroupBidModifierOperation.newBuilder().setCreate(lengthOfStayAdGroupBidModifier).build());\n\n // Issues a mutate request to add the ad group bid modifiers.\n try (AdGroupBidModifierServiceClient adGroupBidModifierServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupBidModifierServiceClient()) {\n MutateAdGroupBidModifiersResponse response =\n adGroupBidModifierServiceClient.mutateAdGroupBidModifiers(\n Long.toString(customerId), operations);\n\n // Prints the resource names of the added ad group bid modifiers.\n System.out.printf(\"Added %d hotel ad group bid modifiers:%n\", response.getResultsCount());\n for (MutateAdGroupBidModifierResult mutateAdGroupBidModifierResult :\n response.getResultsList()) {\n System.out.printf(\" %s%n\", mutateAdGroupBidModifierResult.getResourceName());\n }\n }\n}AddHotelAdGroupBidModifiers.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long adGroupId)\n{\n // Get the AdGroupBidModifierService.\n AdGroupBidModifierServiceClient service = client.GetService(\n Services.V25.AdGroupBidModifierService);\n\n // Constructs the ad group resource name to use for each bid modifier.\n string adGroupResourceName = ResourceNames.AdGroup(customerId, adGroupId);\n\n // 1) Create an ad group bid modifier based on the hotel check-in day.\n AdGroupBidModifier checkInDayAdGroupBidModifier = new AdGroupBidModifier()\n {\n // Sets the resource name to the ad group resource name joined with the criterion\n // ID whose value corresponds to the desired check-in day.\n AdGroup = adGroupResourceName,\n HotelCheckInDay = new HotelCheckInDayInfo()\n {\n DayOfWeek = DayOfWeek.Monday\n },\n\n // Set the bid modifier value to 150%.\n BidModifier = 1.5,\n };\n\n // Creates an ad group bid modifier operation.\n var checkInDayAdGroupBidModifierOperation = new AdGroupBidModifierOperation()\n {\n Create = checkInDayAdGroupBidModifier\n };\n\n // 2) Create an ad group bid modifier based on the hotel length of stay.\n AdGroupBidModifier lengthOfStayAdGroupBidModifier = new AdGroupBidModifier()\n {\n // Set the ad group.\n AdGroup = ResourceNames.AdGroup(customerId, adGroupId),\n\n // Set the hotel length of stay info.\n HotelLengthOfStay = new HotelLengthOfStayInfo()\n {\n MinNights = 3,\n MaxNights = 7\n },\n\n // Set the bid modifier value to 170%.\n BidModifier = 1.7\n };\n\n // Create an ad group bid modifier operation.\n var lengthOfStayAdGroupBidModifierOperation = new AdGroupBidModifierOperation()\n {\n Create = lengthOfStayAdGroupBidModifier\n };\n\n try\n {\n // Issue a mutate request to add an ad group bid modifiers.\n MutateAdGroupBidModifiersResponse response = service.MutateAdGroupBidModifiers(\n customerId.ToString(),\n new AdGroupBidModifierOperation[] {\n checkInDayAdGroupBidModifierOperation,\n lengthOfStayAdGroupBidModifierOperation\n }\n );\n\n // Display the resource names of the added ad group bid modifiers.\n Console.WriteLine($\"Added {response.Results.Count} hotel ad group bid modifiers:\");\n\n foreach (MutateAdGroupBidModifierResult result in response.Results)\n {\n Console.WriteLine($\"- {result.ResourceName}\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}AddHotelAdGroupBidModifiers.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId\n) {\n $operations = [];\n\n // 1) Creates an ad group bid modifier based on the hotel check-in day.\n $checkInDayAdGroupBidModifier = new AdGroupBidModifier([\n // Sets the ad group.\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'hotel_check_in_day' => new HotelCheckInDayInfo([\n 'day_of_week' => DayOfWeek::MONDAY\n ]),\n // Sets the bid modifier value to 150%.\n 'bid_modifier' => 1.5\n ]);\n\n // Creates an ad group bid modifier operation.\n $checkInDayAdGroupBidModifierOperation = new AdGroupBidModifierOperation();\n $checkInDayAdGroupBidModifierOperation->setCreate($checkInDayAdGroupBidModifier);\n $operations[] = $checkInDayAdGroupBidModifierOperation;\n\n // 2) Creates an ad group bid modifier based on the hotel length of stay.\n $lengthOfStayAdGroupBidModifier = new AdGroupBidModifier([\n // Sets the ad group.\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n // Creates the hotel length of stay info.\n 'hotel_length_of_stay' => new HotelLengthOfStayInfo([\n 'min_nights' => 3,\n 'max_nights' => 7,\n ]),\n // Sets the bid modifier value to 170%.\n 'bid_modifier' => 1.7\n ]);\n\n // Creates an ad group bid modifier operation.\n $lengthOfStayAdGroupBidModifierOperation = new AdGroupBidModifierOperation();\n $lengthOfStayAdGroupBidModifierOperation->setCreate(\n $lengthOfStayAdGroupBidModifier\n );\n $operations[] = $lengthOfStayAdGroupBidModifierOperation;\n\n // Issues a mutate request to add an ad group bid modifiers.\n $adGroupBidModifierServiceClient = $googleAdsClient->getAdGroupBidModifierServiceClient();\n $response = $adGroupBidModifierServiceClient->mutateAdGroupBidModifiers(\n MutateAdGroupBidModifiersRequest::build($customerId, $operations)\n );\n\n // Print out resource names of the added ad group bid modifiers.\n printf(\n \"Added %d hotel ad group bid modifiers:%s\",\n $response->getResults()->count(),\n PHP_EOL\n );\n foreach ($response->getResults() as $addedAdGroupBidModifier) {\n /** @var AdGroupBidModifier $addedAdGroupBidModifier */\n print $addedAdGroupBidModifier->getResourceName() . PHP_EOL;\n }\n}AddHotelAdGroupBidModifiers.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient, customer_id: str, ad_group_id: str) -> None:\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n ag_bm_service: AdGroupBidModifierServiceClient = client.get_service(\n \"AdGroupBidModifierService\"\n )\n\n # Create ad group bid modifier based on hotel check-in day.\n check_in_ag_bm_operation: AdGroupBidModifierOperation = client.get_type(\n \"AdGroupBidModifierOperation\"\n )\n check_in_ag_bid_modifier: AdGroupBidModifier = (\n check_in_ag_bm_operation.create\n )\n check_in_ag_bid_modifier.hotel_check_in_day.day_of_week = (\n client.enums.DayOfWeekEnum.MONDAY\n )\n check_in_ag_bid_modifier.ad_group = ad_group_service.ad_group_path(\n customer_id, ad_group_id\n )\n # Sets the bid modifier value to 150%.\n check_in_ag_bid_modifier.bid_modifier = 1.5\n\n # Create ad group bid modifier based on hotel length of stay info.\n los_ag_bm_operation: AdGroupBidModifierOperation = client.get_type(\n \"AdGroupBidModifierOperation\"\n )\n los_ag_bid_modifier: AdGroupBidModifier = los_ag_bm_operation.create\n los_ag_bid_modifier.ad_group = ad_group_service.ad_group_path(\n customer_id, ad_group_id\n )\n # Creates the hotel length of stay info.\n hotel_length_of_stay_info: HotelLengthOfStayInfo = (\n los_ag_bid_modifier.hotel_length_of_stay\n )\n hotel_length_of_stay_info.min_nights = 3\n hotel_length_of_stay_info.max_nights = 7\n # Sets the bid modifier value to 170%.\n los_ag_bid_modifier.bid_modifier = 1.7\n\n # Add the bid modifiers\n ag_bm_response: MutateAdGroupBidModifiersResponse = (\n ag_bm_service.mutate_ad_group_bid_modifiers(\n customer_id=customer_id,\n operations=[check_in_ag_bm_operation, los_ag_bm_operation],\n )\n )\n\n # Print out resource names of the added ad group bid modifiers.\n print(f\"Added {len(ag_bm_response.results)} hotel ad group bid modifiers:\")\n\n result: MutateAdGroupBidModifierResult\n for result in ag_bm_response.results:\n print(result.resource_name)add_hotel_ad_group_bid_modifiers.py\n```\n\nExample:\n```text\ndef add_hotel_ad_group_bid_modifiers(customer_id, ad_group_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n operations = []\n ad_group_resource = client.path.ad_group(customer_id, ad_group_id)\n\n # 1) Creates an ad group bid modifier based on the hotel check-in day.\n operations << client.operation.create_resource.ad_group_bid_modifier do |bm|\n # Sets the ad group.\n bm.ad_group = ad_group_resource\n\n # Sets the check-in day to Monday.\n bm.hotel_check_in_day = client.resource.hotel_check_in_day_info do |info|\n info.day_of_week = :MONDAY\n end\n\n # Sets the bid modifier value to 150%.\n bm.bid_modifier = 1.5\n end\n\n # 2) Creates an ad group bid modifier based on the hotel length of stay.\n operations << client.operation.create_resource.ad_group_bid_modifier do |bm|\n # Sets the ad group.\n bm.ad_group = ad_group_resource\n\n # Creates the hotel length of stay info.\n bm.hotel_length_of_stay = client.resource.hotel_length_of_stay_info do |info|\n info.min_nights = 3\n info.max_nights = 7\n end\n\n # Sets the bid modifier value to 170%.\n bm.bid_modifier = 1.7\n end\n\n # 3) Issues a mutate request to add an ad group bid modifiers.\n ad_group_bid_modifier_service = client.service.ad_group_bid_modifier\n response = ad_group_bid_modifier_service.mutate_ad_group_bid_modifiers(\n customer_id: customer_id,\n operations: operations,\n )\n\n # Print out resource names of the added ad group bid modifiers.\n puts \"Added #{response.results.size} hotel ad group bid modifiers:\"\n response.results.each do |added_ad_group_bid_modifier|\n puts \"\\t#{added_ad_group_bid_modifier.resource_name}\"\n end\nendadd_hotel_ad_group_bid_modifiers.rb\n```\n\nExample:\n```text\nsub add_hotel_ad_group_bid_modifiers {\n my ($api_client, $customer_id, $ad_group_id) = @_;\n\n # 1) Create an ad group bid modifier based on the hotel check-in day.\n my $check_in_day_ad_group_bid_modifier =\n Google::Ads::GoogleAds::V25::Resources::AdGroupBidModifier->new({\n # Set the ad group.\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n hotelCheckInDay =>\n Google::Ads::GoogleAds::V25::Common::HotelCheckInDayInfo->new({\n dayOfWeek => MONDAY\n }\n ),\n # Set the bid modifier value to 150%.\n bidModifier => 1.5\n });\n\n # Create an ad group bid modifier operation.\n my $check_in_day_ad_group_bid_modifier_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupBidModifierService::AdGroupBidModifierOperation\n ->new({\n create => $check_in_day_ad_group_bid_modifier\n });\n\n # 2) Create an ad group bid modifier based on the hotel length of stay.\n my $length_of_stay_ad_group_bid_modifier =\n Google::Ads::GoogleAds::V25::Resources::AdGroupBidModifier->new({\n # Set the ad group.\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n # Create the hotel length of stay info.\n hotelLengthOfStay =>\n Google::Ads::GoogleAds::V25::Common::HotelLengthOfStayInfo->new({\n minNights => 3,\n maxNights => 7\n }\n ),\n # Set the bid modifier value to 170%.\n bidModifier => 1.7\n });\n\n # Create an ad group bid modifier operation.\n my $length_of_stay_ad_group_bid_modifier_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupBidModifierService::AdGroupBidModifierOperation\n ->new({\n create => $length_of_stay_ad_group_bid_modifier\n });\n\n # 3) Add the ad group bid modifiers.\n my $ad_group_bid_modifiers_response =\n $api_client->AdGroupBidModifierService()->mutate({\n customerId => $customer_id,\n operations => [\n $check_in_day_ad_group_bid_modifier_operation,\n $length_of_stay_ad_group_bid_modifier_operation\n ]});\n\n # Print out resource names of the added ad group bid modifiers.\n my $ad_group_bid_modifier_results =\n $ad_group_bid_modifiers_response->{results};\n printf \"Added %d hotel ad group bid modifiers:\\n\",\n scalar @$ad_group_bid_modifier_results;\n\n foreach my $ad_group_bid_modifier_result (@$ad_group_bid_modifier_results) {\n printf \"\\t%s\\n\", $ad_group_bid_modifier_result->{resourceName};\n }\n\n return 1;\n}add_hotel_ad_group_bid_modifiers.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.253Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":390,"estimatedTokens":3606}}112{"id":"doc-retrieving_responsive_display_ads_google_ads_api-b650c95b","source":"documentation","title":"Retrieving Responsive Display Ads | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/responsive-display-ads/get-responsive-display-ads","text":"Example:\n```text\nSELECT\n ad_group.id,\n ad_group_ad.ad.id,\n ad_group_ad.ad.responsive_display_ad.business_name,\n ad_group_ad.ad.responsive_display_ad.descriptions,\n ad_group_ad.ad.responsive_display_ad.headlines,\n ad_group_ad.ad.responsive_display_ad.long_headline,\n ad_group_ad.ad.responsive_display_ad.marketing_images,\n ad_group_ad.ad.responsive_display_ad.square_marketing_images,\n ad_group_ad.status\nFROM ad_group_ad\nWHERE ad_group_ad.ad.type = RESPONSIVE_DISPLAY_AD\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.254Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":17,"estimatedTokens":125}}113{"id":"doc-create_dynamic_search_ads_google_ads_api_google_-06424337","source":"documentation","title":"Create Dynamic Search Ads | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/dynamic-search-ads/create-dynamic-search-ads","text":"Example:\n```text\nprivate static String addCampaign(\n GoogleAdsClient googleAdsClient, long customerId, String budgetResourceName) {\n // Creates the campaign.\n Campaign campaign =\n Campaign.newBuilder()\n .setName(\"Interplanetary Cruise #\" + getPrintableDateTime())\n .setAdvertisingChannelType(AdvertisingChannelType.SEARCH)\n .setStatus(CampaignStatus.PAUSED)\n .setManualCpc(ManualCpc.newBuilder().build())\n .setCampaignBudget(budgetResourceName)\n // Enables the campaign for DSAs.\n .setDynamicSearchAdsSetting(\n DynamicSearchAdsSetting.newBuilder()\n .setDomainName(\"example.com\")\n .setLanguageCode(\"en\")\n .build())\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n .setStartDateTime(new DateTime().plusDays(1).toString(\"yyyy-MM-dd 00:00:00\"))\n .setEndDateTime(new DateTime().plusDays(30).toString(\"yyyy-MM-dd 23:59:59\"))\n .build();\n\n // Creates the operation.\n CampaignOperation operation = CampaignOperation.newBuilder().setCreate(campaign).build();\n\n // Creates the campaign service client.\n try (CampaignServiceClient campaignServiceClient =\n googleAdsClient.getLatestVersion().createCampaignServiceClient()) {\n // Adds the campaign.\n MutateCampaignsResponse response =\n campaignServiceClient.mutateCampaigns(\n Long.toString(customerId), ImmutableList.of(operation));\n\n String campaignResourceName = response.getResults(0).getResourceName();\n // Displays the results.\n System.out.printf(\"Added campaign with resource name '%s'.%n\", campaignResourceName);\n return campaignResourceName;\n }\n}AddDynamicSearchAds.java\n```\n\nExample:\n```text\nprivate static string AddCampaign(GoogleAdsClient client, long customerId,\n string budgetResourceName)\n{\n // Get the CampaignService.\n CampaignServiceClient campaignService = client.GetService(Services.V25.CampaignService);\n\n // Create the campaign.\n Campaign campaign = new Campaign()\n {\n Name = \"Interplanetary Cruise #\" + ExampleUtilities.GetRandomString(),\n AdvertisingChannelType = AdvertisingChannelType.Search,\n Status = CampaignStatus.Paused,\n ManualCpc = new ManualCpc(),\n CampaignBudget = budgetResourceName,\n\n // Enable the campaign for DSAs.\n DynamicSearchAdsSetting = new DynamicSearchAdsSetting()\n {\n DomainName = \"example.com\",\n LanguageCode = \"en\"\n },\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n\n StartDateTime = DateTime.Now.AddDays(1).ToString(\"yyyyMMdd 00:00:00\"),\n EndDateTime = DateTime.Now.AddDays(30).ToString(\"yyyyMMdd 23:59:59\")\n };\n\n // Create the operation.\n CampaignOperation operation = new CampaignOperation()\n {\n Create = campaign\n };\n\n // Add the campaign.\n MutateCampaignsResponse response =\n campaignService.MutateCampaigns(customerId.ToString(),\n new CampaignOperation[] { operation });\n\n // Displays the result.\n string campaignResourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Added campaign with resource name '{campaignResourceName}'.\");\n return campaignResourceName;\n}AddDynamicSearchAds.cs\n```\n\nExample:\n```text\nprivate static function createCampaign(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $campaignBudgetResourceName\n) {\n $campaign = new Campaign([\n 'name' => 'Interplanetary Cruise #' . Helper::getPrintableDatetime(),\n 'advertising_channel_type' => AdvertisingChannelType::SEARCH,\n 'status' => CampaignStatus::PAUSED,\n 'manual_cpc' => new ManualCpc(),\n 'campaign_budget' => $campaignBudgetResourceName,\n // Enables the campaign for DSAs.\n 'dynamic_search_ads_setting' => new DynamicSearchAdsSetting([\n 'domain_name' => 'example.com',\n 'language_code' => 'en'\n ]),\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n // Optional: Sets the start and end dates for the campaign, beginning one day from\n // now and ending a month from now.\n 'start_date_time' => date('Y-m-d 00:00:00', strtotime('+1 day')),\n 'end_date_time' => date('Y-m-d 23:59:59', strtotime('+1 month'))\n ]);\n\n // Creates a campaign operation.\n $campaignOperation = new CampaignOperation();\n $campaignOperation->setCreate($campaign);\n\n // Issues a mutate request to add campaigns.\n $campaignServiceClient = $googleAdsClient->getCampaignServiceClient();\n /** @var MutateCampaignsResponse $campaignResponse */\n $campaignResponse = $campaignServiceClient->mutateCampaigns(\n MutateCampaignsRequest::build($customerId, [$campaignOperation])\n );\n\n $campaignResourceName = $campaignResponse->getResults()[0]->getResourceName();\n printf(\"Added campaign named '%s'.%s\", $campaignResourceName, PHP_EOL);\n\n return $campaignResourceName;\n}AddDynamicSearchAds.php\n```\n\nExample:\n```text\ndef create_campaign(\n client: GoogleAdsClient, customer_id: str, budget_resource_name: str\n) -> str:\n \"\"\"Creates a Dynamic Search Ad Campaign under the given customer ID.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID str.\n budget_resource_name: a resource_name str for a Budget\n\n Returns:\n A resource_name str for the newly created Campaign.\n \"\"\"\n # Retrieve a new campaign operation object.\n campaign_operation: CampaignOperation = client.get_type(\"CampaignOperation\")\n campaign: Campaign = campaign_operation.create\n campaign.name = f\"Interplanetary Cruise #{uuid4()}\"\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.SEARCH\n )\n # Recommendation: Set the campaign to PAUSED when creating it to prevent the\n # ads from immediately serving. Set to ENABLED once you've added targeting\n # and the ads are ready to serve.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n campaign.manual_cpc.enhanced_cpc_enabled = True\n campaign.campaign_budget = budget_resource_name\n # Required: Enable the campaign for DSAs by setting the campaign's dynamic\n # search ads setting domain name and language.\n campaign.dynamic_search_ads_setting.domain_name = \"example.com\"\n campaign.dynamic_search_ads_setting.language_code = \"en\"\n\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n # Optional: Sets the start and end dates for the campaign, beginning one day\n # from now and ending a month from now.\n campaign.start_date_time = (datetime.now() + timedelta(days=1)).strftime(\n \"%Y%m%d 00:00:00\"\n )\n campaign.end_date_time = (datetime.now() + timedelta(days=30)).strftime(\n \"%Y%m%d 23:59:59\"\n )\n\n # Retrieve the campaign service.\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n # Issues a mutate request to add campaign.\n response: MutateCampaignsResponse = campaign_service.mutate_campaigns(\n customer_id=customer_id, operations=[campaign_operation]\n )\n resource_name: str = response.results[0].resource_name\n\n print(f'Created campaign with resource_name: \"{resource_name}\"')add_dynamic_search_ads.py\n```\n\nExample:\n```text\ndef create_campaign(client, customer_id, budget_resource_name)\n campaign = client.resource.campaign do |c|\n c.name = \"Interplanetary Cruise #{(Time.now.to_f * 1000).to_i}\"\n\n c.advertising_channel_type = :SEARCH\n c.status = :PAUSED\n c.manual_cpc = client.resource.manual_cpc\n c.campaign_budget = budget_resource_name\n\n c.dynamic_search_ads_setting = client.resource.dynamic_search_ads_setting do |s|\n s.domain_name = \"example.com\"\n s.language_code = \"en\"\n end\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n c.start_date_time = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d %H:%M:%S')\n c.end_date_time = DateTime.parse(Date.today.next_year.to_s).strftime('%Y%m%d %H:%M:%S')\n end\n\n operation = client.operation.create_resource.campaign(campaign)\n\n response = client.service.campaign.mutate_campaigns(\n customer_id: customer_id,\n operations: [operation],\n )\n puts(\"Created campaign with ID: #{response.results.first.resource_name}\")\n response.results.first.resource_name\nendadd_dynamic_search_ads.rb\n```\n\nExample:\n```text\nsub create_campaign {\n my ($api_client, $customer_id, $campaign_budget_resource_name) = @_;\n\n # Create a campaign.\n my $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n name => \"Interplanetary Cruise #\" . uniqid(),\n advertisingChannelType => SEARCH,\n status => Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n manualCpc => Google::Ads::GoogleAds::V25::Common::ManualCpc->new(),\n campaignBudget => $campaign_budget_resource_name,\n # Enable the campaign for DSAs.\n dynamicSearchAdsSetting =>\n Google::Ads::GoogleAds::V25::Resources::DynamicSearchAdsSetting->new({\n domainName => \"example.com\",\n languageCode => \"en\"\n }\n ),\n # Optional: Set the start and end datetimes for the campaign, beginning one day from\n # now and ending a month from now.\n startDateTime =>\n strftime(\"%Y%m%d 00:00:00\", localtime(time + 60 * 60 * 24)),\n endDateTime =>\n strftime(\"%Y%m%d 23:59:59\", localtime(time + 60 * 60 * 24 * 30)),\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n });\n\n # Create a campaign operation.\n my $campaign_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation->\n new({create => $campaign});\n\n # Add the campaign.\n my $campaigns_response = $api_client->CampaignService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_operation]});\n\n my $campaign_resource_name = $campaigns_response->{results}[0]{resourceName};\n\n printf \"Created campaign '%s'.\\n\", $campaign_resource_name;\n\n return $campaign_resource_name;\n}add_dynamic_search_ads.pl\n```\n\nExample:\n```text\nprivate static String addAdGroup(\n GoogleAdsClient googleAdsClient, long customerId, String campaignResourceName) {\n // Creates the ad group.\n AdGroup adGroup =\n AdGroup.newBuilder()\n .setName(\"Earth to Mars Cruises #\" + getPrintableDateTime())\n .setCampaign(campaignResourceName)\n .setType(AdGroupType.SEARCH_DYNAMIC_ADS)\n .setStatus(AdGroupStatus.PAUSED)\n .setTrackingUrlTemplate(\"http://tracker.examples.com/traveltracker/{escapedlpurl}\")\n .setCpcBidMicros(50_000)\n .build();\n\n // Creates the operation.\n AdGroupOperation operation = AdGroupOperation.newBuilder().setCreate(adGroup).build();\n\n // Creates the ad group service client.\n try (AdGroupServiceClient adGroupServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupServiceClient()) {\n MutateAdGroupsResponse response =\n adGroupServiceClient.mutateAdGroups(\n Long.toString(customerId), ImmutableList.of(operation));\n String adGroupResourceName = response.getResults(0).getResourceName();\n // Displays the results.\n System.out.printf(\"Added ad group with resource name '%s'.%n\", adGroupResourceName);\n return adGroupResourceName;\n }\n}AddDynamicSearchAds.java\n```\n\nExample:\n```text\nprivate static string AddAdGroup(GoogleAdsClient client, long customerId,\n string campaignResourceName)\n{\n // Get the AdGroupService.\n AdGroupServiceClient adGroupService = client.GetService(Services.V25.AdGroupService);\n\n // Create the ad group.\n AdGroup adGroup = new AdGroup()\n {\n Name = \"Earth to Mars Cruises #\" + ExampleUtilities.GetRandomString(),\n Campaign = campaignResourceName,\n Type = AdGroupType.SearchDynamicAds,\n Status = AdGroupStatus.Paused,\n TrackingUrlTemplate = \"http://tracker.examples.com/traveltracker/{escapedlpurl}\",\n CpcBidMicros = 50_000\n };\n\n // Create the operation.\n AdGroupOperation operation = new AdGroupOperation()\n {\n Create = adGroup\n };\n\n // Add the ad group.\n MutateAdGroupsResponse response =\n adGroupService.MutateAdGroups(customerId.ToString(),\n new AdGroupOperation[] { operation });\n\n // Display the results.\n string adGroupResourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Added ad group with resource name '{adGroupResourceName}'.\");\n\n return adGroupResourceName;\n}AddDynamicSearchAds.cs\n```\n\nExample:\n```text\nprivate static function createAdGroup(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $campaignResourceName\n) {\n // Constructs an ad group and sets an optional CPC value.\n $adGroup = new AdGroup([\n 'name' => 'Earth to Mars Cruises #' . Helper::getPrintableDatetime(),\n 'campaign' => $campaignResourceName,\n 'status' => AdGroupStatus::PAUSED,\n 'type' => AdGroupType::SEARCH_DYNAMIC_ADS,\n 'tracking_url_template' => 'http://tracker.examples.com/traveltracker/{escapedlpurl}',\n 'cpc_bid_micros' => 10000000\n ]);\n\n // Creates an ad group operation.\n $adGroupOperation = new AdGroupOperation();\n $adGroupOperation->setCreate($adGroup);\n\n // Issues a mutate request to add the ad groups.\n $adGroupServiceClient = $googleAdsClient->getAdGroupServiceClient();\n /** @var MutateAdGroupsResponse $adGroupResponse */\n $adGroupResponse = $adGroupServiceClient->mutateAdGroups(\n MutateAdGroupsRequest::build($customerId, [$adGroupOperation])\n );\n\n $adGroupResourceName = $adGroupResponse->getResults()[0]->getResourceName();\n printf(\"Added ad group named '%s'.%s\", $adGroupResourceName, PHP_EOL);\n\n return $adGroupResourceName;\n}AddDynamicSearchAds.php\n```\n\nExample:\n```text\ndef create_ad_group(\n client: GoogleAdsClient, customer_id: str, campaign_resource_name: str\n) -> str:\n \"\"\"Creates a Dynamic Search Ad Group under the given Campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID str.\n campaign_resource_name: a resource_name str for a Campaign.\n\n Returns:\n A resource_name str for the newly created Ad Group.\n \"\"\"\n # Retrieve a new ad group operation object.\n ad_group_operation: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n # Create an ad group.\n ad_group: AdGroup = ad_group_operation.create\n # Required: set the ad group's type to Dynamic Search Ads.\n ad_group.type_ = client.enums.AdGroupTypeEnum.SEARCH_DYNAMIC_ADS\n ad_group.name = f\"Earth to Mars Cruises {uuid4()}\"\n ad_group.campaign = campaign_resource_name\n ad_group.status = client.enums.AdGroupStatusEnum.PAUSED\n # Recommended: set a tracking URL template for your ad group if you want to\n # use URL tracking software.\n ad_group.tracking_url_template = (\n \"http://tracker.example.com/traveltracker/{escapedlpurl}\"\n )\n # Optional: Set the ad group bid value.\n ad_group.cpc_bid_micros = 10000000\n\n # Retrieve the ad group service.\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n\n # Issues a mutate request to add the ad group.\n response: MutateAdGroupsResponse = ad_group_service.mutate_ad_groups(\n customer_id=customer_id, operations=[ad_group_operation]\n )\n resource_name: str = response.results[0].resource_name\n\n print(f'Created Ad Group with resource_name: \"{resource_name}\"')add_dynamic_search_ads.py\n```\n\nExample:\n```text\ndef create_ad_group(client, customer_id, campaign_resource_name)\n ad_group = client.resource.ad_group do |ag|\n ag.type = :SEARCH_DYNAMIC_ADS\n ag.name = \"Earth to Mars Cruises #{(Time.now.to_f * 1000).to_i}\"\n\n ag.campaign = campaign_resource_name\n\n ag.status = :PAUSED\n ag.tracking_url_template = \"http://tracker.example.com/traveltracker/{escapedlpurl}\"\n\n ag.cpc_bid_micros = 3_000_000\n end\n\n operation = client.operation.create_resource.ad_group(ad_group)\n\n response = client.service.ad_group.mutate_ad_groups(\n customer_id: customer_id,\n operations: [operation],\n )\n\n puts(\"Created ad group with ID: #{response.results.first.resource_name}\")\n response.results.first.resource_name\nendadd_dynamic_search_ads.rb\n```\n\nExample:\n```text\nsub create_ad_group {\n my ($api_client, $customer_id, $campaign_resource_name) = @_;\n\n # Construct an ad group and set an optional CPC value.\n my $ad_group = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Earth to Mars Cruises #\" . uniqid(),\n campaign => $campaign_resource_name,\n status => Google::Ads::GoogleAds::V25::Enums::AdGroupStatusEnum::PAUSED,\n type => SEARCH_DYNAMIC_ADS,\n trackingUrlTemplate =>\n \"http://tracker.examples.com/traveltracker/{escapedlpurl}\",\n cpcBidMicros => 3000000\n });\n\n # Create an ad group operation.\n my $ad_group_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group});\n\n # Add the ad group.\n my $ad_groups_response = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_operation]});\n\n my $ad_group_resource_name = $ad_groups_response->{results}[0]{resourceName};\n\n printf \"Created ad group '%s'.\\n\", $ad_group_resource_name;\n\n return $ad_group_resource_name;\n}add_dynamic_search_ads.pl\n```\n\nExample:\n```text\nprivate static void addExpandedDSA(\n GoogleAdsClient googleAdsClient, long customerId, String adGroupResourceName) {\n // Creates an ad group ad.\n AdGroupAd adGroupAd =\n AdGroupAd.newBuilder()\n .setAdGroup(adGroupResourceName)\n .setStatus(AdGroupAdStatus.PAUSED)\n // Sets the ad as an expanded dynamic search ad\n .setAd(\n Ad.newBuilder()\n .setExpandedDynamicSearchAd(\n ExpandedDynamicSearchAdInfo.newBuilder()\n .setDescription(\"Buy tickets now!\")\n .build())\n .build())\n .build();\n\n // Creates the operation.\n AdGroupAdOperation operation = AdGroupAdOperation.newBuilder().setCreate(adGroupAd).build();\n\n // Creates the ad group ad service client.\n try (AdGroupAdServiceClient adGroupAdServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupAdServiceClient()) {\n // Adds the dynamic search ad.\n MutateAdGroupAdsResponse response =\n adGroupAdServiceClient.mutateAdGroupAds(\n Long.toString(customerId), ImmutableList.of(operation));\n // Displays the response.\n System.out.printf(\n \"Added ad group ad with resource name '%s'.%n\", response.getResults(0).getResourceName());\n }\n}AddDynamicSearchAds.java\n```\n\nExample:\n```text\nprivate static void AddExpandedDSA(GoogleAdsClient client, long customerId,\n string adGroupResourceName)\n{\n // Get the AdGroupAdService.\n AdGroupAdServiceClient adGroupAdService =\n client.GetService(Services.V25.AdGroupAdService);\n\n // Create an ad group ad.\n AdGroupAd adGroupAd = new AdGroupAd()\n {\n AdGroup = adGroupResourceName,\n Status = AdGroupAdStatus.Paused,\n\n // Set the ad as an expanded dynamic search ad.\n Ad = new Ad()\n {\n ExpandedDynamicSearchAd = new ExpandedDynamicSearchAdInfo()\n {\n Description = \"Buy tickets now!\"\n }\n }\n };\n\n // Create the operation.\n AdGroupAdOperation operation = new AdGroupAdOperation()\n {\n Create = adGroupAd\n };\n\n // Add the dynamic search ad.\n MutateAdGroupAdsResponse response = adGroupAdService.MutateAdGroupAds(\n customerId.ToString(), new AdGroupAdOperation[] { operation });\n\n // Display the response.\n Console.WriteLine($\"Added ad group ad with resource name \" +\n $\"'{response.Results[0].ResourceName}'.\");\n}AddDynamicSearchAds.cs\n```\n\nExample:\n```text\nprivate static function createExpandedDSA(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $adGroupResourceName\n) {\n $adGroupAd = new AdGroupAd([\n 'ad_group' => $adGroupResourceName,\n 'status' => AdGroupAdStatus::PAUSED,\n 'ad' => new Ad([\n 'expanded_dynamic_search_ad' => new ExpandedDynamicSearchAdInfo([\n 'description' => 'Buy tickets now!'\n ])\n ])\n ]);\n\n $adGroupAdOperation = new AdGroupAdOperation();\n $adGroupAdOperation->setCreate($adGroupAd);\n\n // Issues a mutate request to add the ad group ads.\n $adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();\n /** @var MutateAdGroupAdsResponse $adGroupAdResponse */\n $adGroupAdResponse = $adGroupAdServiceClient->mutateAdGroupAds(\n MutateAdGroupAdsRequest::build($customerId, [$adGroupAdOperation])\n );\n\n $adGroupAdResourceName = $adGroupAdResponse->getResults()[0]->getResourceName();\n printf(\"Added ad group ad named '%s'.%s\", $adGroupAdResourceName, PHP_EOL);\n\n return $adGroupAdResourceName;\n}AddDynamicSearchAds.php\n```\n\nExample:\n```text\ndef create_expanded_dsa(\n client: GoogleAdsClient, customer_id: str, ad_group_resource_name: str\n) -> None:\n \"\"\"Creates a dynamic search ad under the given ad group.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID str.\n ad_group_resource_name: a resource_name str for an Ad Group.\n \"\"\"\n # Retrieve a new ad group ad operation object.\n ad_group_ad_operation: AdGroupAdOperation = client.get_type(\n \"AdGroupAdOperation\"\n )\n # Create and expanded dynamic search ad. This ad will have its headline,\n # display URL and final URL auto-generated at serving time according to\n # domain name specific information provided by DynamicSearchAdSetting at\n # the campaign level.\n ad_group_ad: AdGroupAd = ad_group_ad_operation.create\n # Optional: set the ad status.\n ad_group_ad.status = client.enums.AdGroupAdStatusEnum.PAUSED\n # Set the ad description.\n ad_group_ad.ad.expanded_dynamic_search_ad.description = \"Buy tickets now!\"\n ad_group_ad.ad_group = ad_group_resource_name\n\n # Retrieve the ad group ad service.\n ad_group_ad_service: AdGroupAdServiceClient = client.get_service(\n \"AdGroupAdService\"\n )\n\n # Submit the ad group ad operation to add the ad group ad.\n response: MutateAdGroupAdsResponse = (\n ad_group_ad_service.mutate_ad_group_ads(\n customer_id=customer_id, operations=[ad_group_ad_operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n\n print(f'Created Ad Group Ad with resource_name: \"{resource_name}\"')add_dynamic_search_ads.py\n```\n\nExample:\n```text\ndef create_expanded_dsa(client, customer_id, ad_group_resource_name)\n ad_group_ad = client.resource.ad_group_ad do |aga|\n aga.status = :PAUSED\n aga.ad = client.resource.ad do |ad|\n ad.expanded_dynamic_search_ad = client.resource.expanded_dynamic_search_ad_info do |info|\n info.description = \"Buy tickets now!\"\n end\n end\n\n aga.ad_group = ad_group_resource_name\n end\n\n operation = client.operation.create_resource.ad_group_ad(ad_group_ad)\n\n response = client.service.ad_group_ad.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [operation],\n )\n puts(\"Created ad group ad with ID: #{response.results.first.resource_name}\")\nendadd_dynamic_search_ads.rb\n```\n\nExample:\n```text\nsub create_expanded_dsa {\n my ($api_client, $customer_id, $ad_group_resource_name) = @_;\n\n # Create an ad group ad.\n my $ad_group_ad = Google::Ads::GoogleAds::V25::Resources::AdGroupAd->new({\n adGroup => $ad_group_resource_name,\n status => Google::Ads::GoogleAds::V25::Enums::AdGroupAdStatusEnum::PAUSED,\n ad => Google::Ads::GoogleAds::V25::Resources::Ad->new({\n expandedDynamicSearchAd =>\n Google::Ads::GoogleAds::V25::Common::ExpandedDynamicSearchAdInfo->\n new({\n description => \"Buy tickets now!\"\n })})});\n\n # Create an ad group ad operation.\n my $ad_group_ad_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation\n ->new({create => $ad_group_ad});\n\n # Add the ad group ad.\n my $ad_group_ads_response = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]});\n\n my $ad_group_ad_resource_name =\n $ad_group_ads_response->{results}[0]{resourceName};\n\n printf \"Created ad group ad '%s'.\\n\", $ad_group_ad_resource_name;\n\n return $ad_group_ad_resource_name;\n}add_dynamic_search_ads.pl\n```\n\nExample:\n```text\nhttp://tracking.com/redir.php?tracking=xyz&url={lpurl}\n```\n\nExample:\n```text\ndsa.setTrackingUrlTemplate(\n StringValue.of(\"http://example.com/traveltracker/{escapedlpurl}\"));\n```\n\nExample:\n```text\nSELECT\n domain_category.category,\n domain_category.language_code,\n domain_category.recommended_cpc_bid_micros\nFROM domain_category\nWHERE domain_category.domain = 'example.com'\n AND campaign.id = campaign_id\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.257Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":21,"totalLines":735,"estimatedTokens":6586}}114{"id":"doc-responsive_search_ad_rsa_customization_google_ad-065a6220","source":"documentation","title":"Responsive Search Ad (RSA) customization | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/ads/customize-responsive-search-ads","text":"Example:\n```text\n{CUSTOMIZER.CUSTOMIZER_ATTRIBUTE_NAME:DEFAULT_VALUE}\n```\n\nExample:\n```text\n{\n \"ad\": {\n \"responsiveSearchAd\": {\n \"headlines\": [\n { \"text\": \"Great Deals on {Keyword:Shoes}\" },\n { \"text\": \"Free Shipping in {LOCATION(City):Your City}\" },\n { \"text\": \"Offer Ends: {COUNTDOWN(2026-01-31 23:59:59,5)}\" }\n ],\n \"descriptions\": [\n { \"text\": \"Find the best {Keyword:footwear} for your needs.\" },\n { \"text\": \"Limited time offer, don't miss out!\" }\n ]\n },\n \"finalUrls\": [\"https://www.example.com\"]\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.259Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":26,"estimatedTokens":148}}115{"id":"doc-retrieving_responsive_search_ads_google_ads_api_-b065fd51","source":"documentation","title":"Retrieving Responsive Search Ads | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/responsive-search-ads/get-responsive-search-ads","text":"Example:\n```text\nSELECT\n ad_group.id,\n ad_group_ad.ad.id,\n ad_group_ad.ad.responsive_search_ad.headlines,\n ad_group_ad.ad.responsive_search_ad.descriptions,\n ad_group_ad.status\nFROM ad_group_ad\nWHERE ad_group_ad.ad.type = RESPONSIVE_SEARCH_AD\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.261Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":67}}116{"id":"doc-creating_a_things_to_do_ad_group_ad_google_ads_a-7ef7a3b0","source":"documentation","title":"Creating a Things to do ad group ad | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/things-to-do-ads/create-ad-group-ad","text":"Example:\n```text\nprivate String addAddGroupAd(\n GoogleAdsClient googleAdsClient, long customerId, String adGroupResourceName) {\n // Creates a new travel ad.\n Ad ad = Ad.newBuilder().setTravelAd(TravelAdInfo.newBuilder()).build();\n // Creates a new ad group ad and sets its ad to the travel ad.\n AdGroupAd adGroupAd =\n AdGroupAd.newBuilder()\n // Sets the ad to the ad created above.\n .setAd(ad)\n // Set the ad group ad to enabled. Setting this to paused will cause an error for\n // Things to do campaigns. Pausing should happen at either the ad group or campaign\n // level.\n .setStatus(AdGroupAdStatus.ENABLED)\n // Sets the ad group.\n .setAdGroup(adGroupResourceName)\n .build();\n\n // Creates an ad group ad operation.\n AdGroupAdOperation operation = AdGroupAdOperation.newBuilder().setCreate(adGroupAd).build();\n\n // Issues a mutate request to add an ad group ad.\n try (AdGroupAdServiceClient adGroupAdServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupAdServiceClient()) {\n MutateAdGroupAdResult mutateAdGroupAdResult =\n adGroupAdServiceClient\n .mutateAdGroupAds(Long.toString(customerId), Collections.singletonList(operation))\n .getResults(0);\n System.out.printf(\n \"Added an ad group ad with resource name: '%s'%n\",\n mutateAdGroupAdResult.getResourceName());\n return mutateAdGroupAdResult.getResourceName();\n }\n}AddThingsToDoAd.java\n```\n\nExample:\n```text\nprivate static void CreateAdGroupAd(GoogleAdsClient client, long customerId,\n string adGroup)\n{\n\n // Get the AdGroupAdService.\n AdGroupAdServiceClient adGroupAdService =\n client.GetService(Services.V25.AdGroupAdService);\n\n // Creates a new ad group ad and sets a travel ad info.\n AdGroupAd adGroupAd = new AdGroupAd()\n {\n Ad = new Ad()\n {\n TravelAd = new TravelAdInfo()\n },\n // Set the ad group ad to enabled. Setting this to paused will cause an error for\n // Things to do campaigns. Pausing should happen at either the ad group or campaign\n // level.\n Status = AdGroupAdStatus.Enabled,\n AdGroup = adGroup\n };\n\n MutateAdGroupAdsResponse response = adGroupAdService.MutateAdGroupAds(\n customerId.ToString(), new AdGroupAdOperation[] { new AdGroupAdOperation() {\n Create = adGroupAd\n }}\n );\n\n string adGroupAdResourceName = response.Results[0].ResourceName;\n Console.WriteLine(\"Ad group ad with resource name = '{0}' was added.\",\n adGroupAdResourceName);\n}AddThingsToDoAd.cs\n```\n\nExample:\n```text\nprivate static function addAdGroupAd(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $adGroupResourceName\n) {\n // Creates a new ad group ad and sets a travel ad info.\n $adGroupAd = new AdGroupAd([\n 'ad' => new Ad(['travel_ad' => new TravelAdInfo()]),\n // Set the ad group ad to enabled. Setting this to paused will cause an error for Things\n // to do campaigns. Pausing should happen at either the ad group or campaign level.\n 'status' => AdGroupAdStatus::ENABLED,\n // Sets the ad group.\n 'ad_group' => $adGroupResourceName\n ]);\n\n // Creates an ad group ad operation.\n $adGroupAdOperation = new AdGroupAdOperation();\n $adGroupAdOperation->setCreate($adGroupAd);\n\n // Issues a mutate request to add an ad group ad.\n $adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();\n $response = $adGroupAdServiceClient->mutateAdGroupAds(\n MutateAdGroupAdsRequest::build($customerId, [$adGroupAdOperation])\n );\n\n /** @var AdGroupAd $addedAdGroupAd */\n $addedAdGroupAd = $response->getResults()[0];\n printf(\n \"Added an ad group ad with resource name '%s'.%s\",\n $addedAdGroupAd->getResourceName(),\n PHP_EOL\n );\n}AddThingsToDoAd.php\n```\n\nExample:\n```text\ndef add_ad_group_ad(\n client: GoogleAdsClient, customer_id: str, ad_group_resource_name: str\n) -> None:\n \"\"\"Creates a new ad group ad in the specified ad group.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n ad_group_resource_name: the resource name of ad group that a new ad\n group ad will belong to.\n \"\"\"\n # Creates an ad group ad operation.\n operation: AdGroupAdOperation = client.get_type(\"AdGroupAdOperation\")\n # Creates a new ad group ad and sets a travel ad info.\n ad_group_ad: AdGroupAd = operation.create\n # Sets the ad group ad to enabled. Setting this to paused will cause an error\n # for Things to do campaigns. Pausing should happen at either the ad group\n # or campaign level.\n ad_group_ad.status = client.enums.AdGroupAdStatusEnum.ENABLED\n ad_group_ad.ad.travel_ad = client.get_type(\"TravelAdInfo\")\n # Sets the ad group.\n ad_group_ad.ad_group = ad_group_resource_name\n\n # Issues a mutate request to add an ad group ad.\n ad_group_ad_service: AdGroupAdServiceClient = client.get_service(\n \"AdGroupAdService\"\n )\n response: MutateAdGroupAdsResponse = (\n ad_group_ad_service.mutate_ad_group_ads(\n customer_id=customer_id, operations=[operation]\n )\n )\n\n resource_name: str = response.results[0].resource_name\n print(f\"Added an ad group ad with resource name: '{resource_name}'.\")add_things_to_do_ad.py\n```\n\nExample:\n```text\ndef add_ad_group_ad(client, customer_id, ad_group_resource)\n # Creates a new ad group ad and sets a travel ad info.\n ad_group_ad_operation = client.operation.create_resource.ad_group_ad do |aga|\n aga.ad = client.resource.ad do |ad|\n ad.travel_ad = client.resource.travel_ad_info\n end\n # Set the ad group ad to enabled. Setting this to paused will cause an error\n # for Things to Do campaigns. Pausing should happen at either the ad group\n # or campaign level.\n aga.status = :ENABLED\n\n # Set the ad group.\n aga.ad_group = ad_group_resource\n end\n\n # Issue a mutate request to add the ad group ad.\n ad_group_ad_service = client.service.ad_group_ad\n response = ad_group_ad_service.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [ad_group_ad_operation],\n )\n\n # Fetch the new ad group ad's resource name.\n ad_group_ad_resource = response.results.first.resource_name\n\n puts \"Added an ad group ad with resource name '#{ad_group_ad_resource}'.\"\nendadd_things_to_do_ad.rb\n```\n\nExample:\n```text\nsub add_ad_group_ad {\n my ($api_client, $customer_id, $ad_group_resource_name) = @_;\n\n # Create an ad group ad and set a travel ad info.\n my $ad_group_ad = Google::Ads::GoogleAds::V25::Resources::AdGroupAd->new({\n # Set the ad group.\n adGroup => $ad_group_resource_name,\n ad => Google::Ads::GoogleAds::V25::Resources::Ad->new({\n travelAd => Google::Ads::GoogleAds::V25::Common::TravelAdInfo->new()}\n ),\n # Set the ad group to enabled. Setting this to paused will cause an error\n # for Things to do campaigns. Pausing should happen at either the ad group\n # or campaign level.\n status => Google::Ads::GoogleAds::V25::Enums::AdGroupAdStatusEnum::ENABLED\n });\n\n # Create an ad group ad operation.\n my $ad_group_ad_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation\n ->new({create => $ad_group_ad});\n\n # Add the ad group ad.\n my $ad_group_ad_resource_name = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]})->{results}[0]{resourceName};\n\n printf \"Added an ad group ad with resource name: '%s'.\\n\",\n $ad_group_ad_resource_name;\n\n return $ad_group_ad_resource_name;\n}add_things_to_do_ad.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.263Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":216,"estimatedTokens":1949}}117{"id":"doc-track_performance_google_ads_api_google_for_deve-8204fa01","source":"documentation","title":"Track Performance | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/budgets/track-performance","text":"Example:\n```text\nSELECT\n segments.conversion_action,\n metrics.value_per_conversion\nFROM campaign_budget\nWHERE campaign_budget.id = campaign_budget_id\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.264Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":43}}118{"id":"doc-create_a_things_to_do_ad_group_google_ads_api_go-6a5de7fa","source":"documentation","title":"Create a Things to do ad group | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/things-to-do-ads/create-ad-group","text":"Example:\n```text\nprivate String addAdGroup(\n GoogleAdsClient googleAdsClient, long customerId, String campaignResourceName) {\n // Creates an ad group.\n AdGroup adGroup =\n AdGroup.newBuilder()\n .setName(\"Earth to Mars Cruises #\" + getPrintableDateTime())\n .setCampaign(campaignResourceName)\n // Sets the ad group type to TRAVEL_ADS. This cannot be set to other types.\n .setType(AdGroupType.TRAVEL_ADS)\n .setStatus(AdGroupStatus.ENABLED)\n .build();\n\n // Creates an ad group operation.\n AdGroupOperation operation = AdGroupOperation.newBuilder().setCreate(adGroup).build();\n\n // Issues a mutate request to add an ad group.\n try (AdGroupServiceClient adGroupServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupServiceClient()) {\n MutateAdGroupResult mutateAdGroupResult =\n adGroupServiceClient\n .mutateAdGroups(Long.toString(customerId), Collections.singletonList(operation))\n .getResults(0);\n System.out.printf(\n \"Added an ad group with resource name: '%s'%n\", mutateAdGroupResult.getResourceName());\n return mutateAdGroupResult.getResourceName();\n }\n}AddThingsToDoAd.java\n```\n\nExample:\n```text\nprivate static string CreateAdGroup(GoogleAdsClient client, long customerId,\n string campaign)\n{\n // Get the AdGroupService.\n AdGroupServiceClient adGroupService = client.GetService(Services.V25.AdGroupService);\n\n // Create the ad group.\n AdGroup adGroup = new AdGroup()\n {\n Name = $\"Earth to Mars Cruises #{ExampleUtilities.GetRandomString()}\",\n Status = AdGroupStatus.Enabled,\n Campaign = campaign,\n Type = AdGroupType.TravelAds\n };\n\n MutateAdGroupsResponse response = adGroupService.MutateAdGroups(\n customerId.ToString(), new AdGroupOperation[] { new AdGroupOperation() {\n Create = adGroup\n }}\n );\n\n string adGroupResourceName = response.Results[0].ResourceName;\n Console.WriteLine(\"Ad group with resource name = '{0}' was added.\", adGroupResourceName);\n\n return adGroupResourceName;\n}AddThingsToDoAd.cs\n```\n\nExample:\n```text\nprivate static function addAdGroup(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $campaignResourceName\n) {\n // Creates an ad group.\n $adGroup = new AdGroup([\n 'name' => 'Earth to Mars Cruise #' . Helper::getPrintableDatetime(),\n // Sets the campaign.\n 'campaign' => $campaignResourceName,\n // Sets the ad group type to TRAVEL_ADS. This cannot be set to other types.\n 'type' => AdGroupType::TRAVEL_ADS,\n 'status' => AdGroupStatus::ENABLED,\n ]);\n\n // Creates an ad group operation.\n $adGroupOperation = new AdGroupOperation();\n $adGroupOperation->setCreate($adGroup);\n\n // Issues a mutate request to add an ad group.\n $adGroupServiceClient = $googleAdsClient->getAdGroupServiceClient();\n $response = $adGroupServiceClient->mutateAdGroups(\n MutateAdGroupsRequest::build($customerId, [$adGroupOperation])\n );\n\n /** @var AdGroup $addedAdGroup */\n $addedAdGroup = $response->getResults()[0];\n printf(\n \"Added an ad group with resource name '%s'.%s\",\n $addedAdGroup->getResourceName(),\n PHP_EOL\n );\n\n return $addedAdGroup->getResourceName();\n}AddThingsToDoAd.php\n```\n\nExample:\n```text\ndef add_ad_group(\n client: GoogleAdsClient, customer_id: str, campaign_resource_name: str\n) -> str:\n \"\"\"Creates a new ad group in the specified Things to do campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n campaign_resource_name: the resource name of campaign that a new ad\n group will belong to.\n\n Returns:\n The resource name of the newly created ad group.\n \"\"\"\n # Creates an ad group operation.\n operation: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n # Creates an ad group.\n ad_group: AdGroup = operation.create\n ad_group.name = f\"Earth to Mars cruise #{get_printable_datetime()}\"\n # Sets the campaign.\n ad_group.campaign = campaign_resource_name\n # Sets the ad group type to TRAVEL_ADS. This is the only value allowed\n # for this field on an ad group for a Things to do campaign.\n ad_group.type_ = client.enums.AdGroupTypeEnum.TRAVEL_ADS\n ad_group.status = client.enums.AdGroupStatusEnum.ENABLED\n\n # Issues a mutate request to add an ad group.\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n ad_group_response: MutateAdGroupsResponse = (\n ad_group_service.mutate_ad_groups(\n customer_id=customer_id, operations=[operation]\n )\n )\n\n resource_name: str = ad_group_response.results[0].resource_name\n print(f\"Added an ad group with resource name: '{resource_name}'.\")\n return resource_nameadd_things_to_do_ad.py\n```\n\nExample:\n```text\ndef add_ad_group(client, customer_id, campaign_resource)\n # Create an ad group.\n ad_group_operation = client.operation.create_resource.ad_group do |ag|\n ag.name = generate_random_name_field(\"Earth to Mars Cruise\")\n\n # Set the campaign.\n ag.campaign = campaign_resource\n\n # Set the ad group type to TRAVEL_ADS.\n # This cannot be set to other types.\n ag.type = :TRAVEL_ADS\n ag.status = :ENABLED\n end\n\n # Issue a mutate request to add the ad group.\n ad_group_service = client.service.ad_group\n response = ad_group_service.mutate_ad_groups(\n customer_id: customer_id,\n operations: [ad_group_operation]\n )\n\n # Fetch the new ad group's resource name.\n ad_group_resource = response.results.first.resource_name\n\n puts \"Added an ad group with resource name '#{ad_group_resource}'.\"\n\n ad_group_resource\nendadd_things_to_do_ad.rb\n```\n\nExample:\n```text\nsub add_ad_group {\n my ($api_client, $customer_id, $campaign_resource_name) = @_;\n\n # Create an ad group.\n my $ad_group = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Earth to Mars Cruise #\" . uniqid(),\n # Set the campaign.\n campaign => $campaign_resource_name,\n # Set the ad group type to TRAVEL_ADS.\n # This cannot be set to other types.\n type => TRAVEL_ADS,\n status => Google::Ads::GoogleAds::V25::Enums::AdGroupStatusEnum::ENABLED\n });\n\n # Create an ad group operation.\n my $ad_group_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group});\n\n # Add the ad group.\n my $ad_group_resource_name = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_operation]})->{results}[0]{resourceName};\n\n printf \"Added an ad group with resource name: '%s'.\\n\",\n $ad_group_resource_name;\n\n return $ad_group_resource_name;\n}add_things_to_do_ad.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.265Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":209,"estimatedTokens":1711}}119{"id":"doc-creating_a_shopping_ad_group_google_ads_api_goog-6c1a4a97","source":"documentation","title":"Creating a Shopping Ad Group | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/shopping-ads/create-ad-group","text":"Example:\n```text\nprivate String addShoppingProductAdGroup(\n GoogleAdsClient googleAdsClient, long customerId, String campaignResourceName) {\n // Creates an ad group.\n AdGroup adGroup =\n AdGroup.newBuilder()\n .setName(\"Earth to Mars Cruises #\" + getPrintableDateTime())\n .setCampaign(campaignResourceName)\n // Sets the ad group type to SHOPPING_PRODUCT_ADS. This is the only value possible for\n // ad groups that contain shopping product ads.\n .setType(AdGroupType.SHOPPING_PRODUCT_ADS)\n .setCpcBidMicros(1_000_000L)\n .setStatus(AdGroupStatus.ENABLED)\n .build();\n\n // Creates an ad group operation.\n AdGroupOperation operation = AdGroupOperation.newBuilder().setCreate(adGroup).build();\n\n // Issues a mutate request to add an ad group.\n try (AdGroupServiceClient adGroupServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupServiceClient()) {\n MutateAdGroupResult mutateAdGroupResult =\n adGroupServiceClient\n .mutateAdGroups(Long.toString(customerId), Collections.singletonList(operation))\n .getResults(0);\n System.out.printf(\n \"Added a product shopping ad group with resource name: '%s'%n\",\n mutateAdGroupResult.getResourceName());\n return mutateAdGroupResult.getResourceName();\n }\n}\nAddShoppingProductAd.java\n```\n\nExample:\n```text\nprivate string AddProductShoppingAdGroup(GoogleAdsClient client, long customerId,\n string campaignResourceName)\n{\n // Get the AdGroupService.\n AdGroupServiceClient adGroupService = client.GetService(Services.V25.AdGroupService);\n\n // Creates an ad group.\n AdGroup adGroup = new AdGroup()\n {\n Name = \"Earth to Mars Cruises #\" + ExampleUtilities.GetRandomString(),\n Campaign = campaignResourceName,\n // Sets the ad group type to SHOPPING_PRODUCT_ADS. This is the only value possible\n // for ad groups that contain shopping product ads.\n Type = AdGroupType.ShoppingProductAds,\n CpcBidMicros = 1_000_000L,\n Status = AdGroupStatus.Enabled\n };\n\n // Creates an ad group operation.\n AdGroupOperation operation = new AdGroupOperation()\n {\n Create = adGroup\n };\n\n // Issues a mutate request to add an ad group.\n MutateAdGroupResult mutateAdGroupResult =\n adGroupService\n .MutateAdGroups(customerId.ToString(), new AdGroupOperation[] { operation })\n .Results[0];\n Console.WriteLine(\"Added a product shopping ad group with resource name: '{0}'.\",\n mutateAdGroupResult.ResourceName);\n return mutateAdGroupResult.ResourceName;\n}AddShoppingProductAd.cs\n```\n\nExample:\n```text\nprivate static function addShoppingProductAdGroup(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $campaignResourceName\n) {\n // Creates an ad group.\n $adGroup = new AdGroup([\n 'name' => 'Earth to Mars Cruise #' . Helper::getPrintableDatetime(),\n // Sets the campaign.\n 'campaign' => $campaignResourceName,\n // Sets the ad group type to SHOPPING_PRODUCT_ADS. This is the only value possible for\n // ad groups that contain shopping product ads.\n 'type' => AdGroupType::SHOPPING_PRODUCT_ADS,\n 'cpc_bid_micros' => 10000000,\n 'status' => AdGroupStatus::ENABLED\n ]);\n\n // Creates an ad group operation.\n $adGroupOperation = new AdGroupOperation();\n $adGroupOperation->setCreate($adGroup);\n\n // Issues a mutate request to add an ad group.\n $adGroupServiceClient = $googleAdsClient->getAdGroupServiceClient();\n $response = $adGroupServiceClient->mutateAdGroups(\n MutateAdGroupsRequest::build($customerId, [$adGroupOperation])\n );\n\n /** @var AdGroup $addedAdGroup */\n $addedAdGroup = $response->getResults()[0];\n printf(\n \"Added a shopping product ad group with resource name '%s'.%s\",\n $addedAdGroup->getResourceName(),\n PHP_EOL\n );\n\n return $addedAdGroup->getResourceName();\n}AddShoppingProductAd.php\n```\n\nExample:\n```text\ndef add_shopping_product_ad_group(\n client: GoogleAdsClient,\n customer_id: str,\n campaign_resource_name: str,\n) -> str:\n \"\"\"Creates a new shopping product ad group in the specified campaign.\"\"\"\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n\n # Create ad group.\n ad_group_operation: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group: AdGroup = ad_group_operation.create\n ad_group.name = f\"Earth to Mars cruise {uuid.uuid4()}\"\n ad_group.status = client.enums.AdGroupStatusEnum.ENABLED\n ad_group.campaign = campaign_resource_name\n # Sets the ad group type to SHOPPING_PRODUCT_ADS. This is the only value\n # possible for ad groups that contain shopping product ads.\n ad_group.type_ = client.enums.AdGroupTypeEnum.SHOPPING_PRODUCT_ADS\n ad_group.cpc_bid_micros = 10000000\n\n # Add the ad group.\n ad_group_response = ad_group_service.mutate_ad_groups(\n customer_id=customer_id, operations=[ad_group_operation]\n )\n\n ad_group_resource_name: str = ad_group_response.results[0].resource_name\n\n print(\n \"Added a product shopping ad group with resource name \"\n f\"'{ad_group_resource_name}'.\"\n )\n\n return ad_group_resource_nameadd_shopping_product_ad.py\n```\n\nExample:\n```text\ndef add_shopping_product_ad_group(client, customer_id, campaign_name)\n operation = client.operation.create_resource.ad_group do |ad_group|\n ad_group.name = \"Earth to Mars cruise ##{(Time.new.to_f * 1000).to_i}\"\n ad_group.status = :ENABLED\n ad_group.campaign = campaign_name\n ad_group.type = :SHOPPING_PRODUCT_ADS\n ad_group.cpc_bid_micros = 10_000_000\n end\n\n service = client.service.ad_group\n response = service.mutate_ad_groups(\n customer_id: customer_id,\n operations: [operation],\n )\n\n ad_group_name = response.results.first.resource_name\n\n puts \"Added a product shopping ad group with resource name #{ad_group_name}.\"\n\n ad_group_name\nendadd_shopping_product_ad.rb\n```\n\nExample:\n```text\nsub add_shopping_product_ad_group {\n my ($api_client, $customer_id, $campaign_resource_name) = @_;\n\n # Create an ad group.\n my $ad_group = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Earth to Mars Cruises #\" . uniqid(),\n campaign => $campaign_resource_name,\n # Set the ad group type to SHOPPING_PRODUCT_ADS. This is the only value\n # possible for ad groups that contain shopping product ads.\n type => SHOPPING_PRODUCT_ADS,\n cpcBidMicros => 1000000,\n status => Google::Ads::GoogleAds::V25::Enums::AdGroupStatusEnum::ENABLED\n });\n\n # Create an ad group operation.\n my $ad_group_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group});\n\n # Add the ad group.\n my $ad_group_resource_name = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_operation]})->{results}[0]{resourceName};\n\n printf \"Added a product shopping ad group with resource name: '%s'.\\n\",\n $ad_group_resource_name;\n\n return $ad_group_resource_name;\n}add_shopping_product_ad.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.266Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":210,"estimatedTokens":1808}}120{"id":"doc-reporting_google_ads_api_google_for_developers-92535a09","source":"documentation","title":"Reporting | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/shopping-ads/reporting","text":"Example:\n```text\nSELECT\n segments.product_item_id,\n metrics.clicks,\n metrics.cost_micros,\n metrics.impressions,\n metrics.search_budget_lost_impression_share,\n metrics.search_rank_lost_impression_share,\n metrics.search_budget_lost_absolute_top_impression_share,\n metrics.search_rank_lost_absolute_top_impression_share,\n metrics.conversions,\n metrics.all_conversions\nFROM shopping_performance_view\nWHERE segments.date DURING LAST_30_DAYS\n AND metrics.clicks > 0\nORDER BY\n metrics.all_conversions DESC,\n metrics.conversions DESC,\n metrics.clicks DESC,\n metrics.cost_micros DESC,\n metrics.impressions DESC\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.all_conversions\nFROM product_group_view\nWHERE segments.date DURING LAST_30_DAYS\n AND metrics.impressions > 0\nORDER BY\n metrics.all_conversions DESC,\n metrics.conversions DESC,\n metrics.clicks DESC,\n metrics.impressions DESC\n```\n\nExample:\n```text\nSELECT\n shopping_product.resource_name,\n shopping_product.item_id,\n shopping_product.feed_label,\n shopping_product.merchant_center_id,\n metrics.clicks,\n metrics.impressions,\n metrics.conversions,\n metrics.all_conversions\nFROM shopping_product\nWHERE segments.date DURING LAST_30_DAYS\nORDER BY\n metrics.all_conversions DESC,\n metrics.conversions DESC,\n metrics.clicks DESC,\n metrics.impressions DESC\n```\n\nExample:\n```text\nSELECT\n shopping_product.resource_name,\n shopping_product.merchant_center_id,\n shopping_product.channel,\n shopping_product.language_code,\n shopping_product.feed_label,\n shopping_product.item_id,\n shopping_product.status,\n shopping_product.issues\nFROM shopping_product\n```\n\nExample:\n```text\nSELECT\n shopping_product.resource_name,\n shopping_product.campaign,\n campaign.name,\n shopping_product.merchant_center_id,\n shopping_product.channel,\n shopping_product.language_code,\n shopping_product.feed_label,\n shopping_product.item_id,\n shopping_product.status,\n shopping_product.issues\nFROM shopping_product\nWHERE\n shopping_product.campaign = \"customers/<CUSTOMER_ID>/campaigns/<CAMPAIGN_ID>\"\n```\n\nExample:\n```text\nSELECT\n shopping_product.resource_name,\n shopping_product.campaign,\n campaign.name,\n shopping_product.ad_group,\n ad_group.name,\n shopping_product.merchant_center_id,\n shopping_product.channel,\n shopping_product.language_code,\n shopping_product.feed_label,\n shopping_product.item_id,\n shopping_product.status,\n shopping_product.issues\nFROM shopping_product\nWHERE\n shopping_product.campaign = \"customers/<CUSTOMER_ID>/campaigns/<CAMPAIGN_ID>\"\n AND shopping_product.ad_group = \"customers/<CUSTOMER_ID>/adGroups/<AD_GROUP_ID>\"\n```\n\nExample:\n```text\nSELECT\n shopping_product.resource_name,\n shopping_product.merchant_center_id,\n shopping_product.channel,\n shopping_product.language_code,\n shopping_product.feed_label,\n shopping_product.item_id,\n metrics.clicks,\n metrics.impressions,\n metrics.cost_micros\nFROM shopping_product\nWHERE\n segments.date = '2024-01-01'\n```\n\nExample:\n```text\nSELECT\n segments.product_item_id,\n segments.product_title,\n metrics.average_cart_size,\n metrics.average_order_value_micros,\n metrics.conversions,\n metrics.conversions_value,\n metrics.gross_profit_micros,\n metrics.gross_profit_margin,\n metrics.revenue_micros,\n metrics.units_sold,\n campaign.advertising_channel_type\nFROM shopping_performance_view\nWHERE campaign.advertising_channel_type = 'SHOPPING'\n AND segments.date DURING LAST_30_DAYS\n AND metrics.conversions > 0\nORDER BY\n metrics.gross_profit_margin DESC,\n metrics.revenue_micros DESC,\n metrics.conversions_value DESC\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n campaign.advertising_channel_type,\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.cost_micros,\n metrics.average_order_value_micros,\n metrics.gross_profit_micros,\n metrics.gross_profit_margin\nFROM campaign\nWHERE campaign.advertising_channel_type = 'SHOPPING'\n AND segments.date DURING LAST_30_DAYS\nORDER BY\n metrics.gross_profit_margin DESC,\n metrics.average_order_value_micros DESC,\n metrics.cost_micros DESC,\n metrics.conversions DESC,\n metrics.clicks DESC,\n metrics.impressions DESC\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.267Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":182,"estimatedTokens":1062}}121{"id":"doc-create_a_shopping_campaign_google_ads_api_google-f9af7c89","source":"documentation","title":"Create a Shopping campaign | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/shopping-ads/create-campaign","text":"Example:\n```text\nprivate String addStandardShoppingCampaign(\n GoogleAdsClient googleAdsClient,\n long customerId,\n String budgetResourceName,\n long merchantCenterAccountId) {\n\n // Configures the shopping settings.\n ShoppingSetting shoppingSetting =\n ShoppingSetting.newBuilder()\n // Sets the priority of the campaign. Higher numbers take priority over lower numbers.\n // For Shopping product ad campaigns, allowed values are between 0 and 2, inclusive.\n .setCampaignPriority(0)\n .setMerchantId(merchantCenterAccountId)\n // Enables local inventory ads for this campaign.\n .setEnableLocal(true)\n .build();\n\n // Create the standard shopping campaign.\n Campaign campaign =\n Campaign.newBuilder()\n .setName(\"Interplanetary Cruise #\" + getPrintableDateTime())\n // Configures settings related to shopping campaigns including advertising channel type\n // and shopping setting.\n .setAdvertisingChannelType(AdvertisingChannelType.SHOPPING)\n .setShoppingSetting(shoppingSetting)\n // Recommendation: Sets the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve.\n .setStatus(CampaignStatus.PAUSED)\n // Sets the bidding strategy to Manual CPC\n // Recommendation: Use one of the automated bidding strategies for Shopping campaigns\n // to help you optimize your advertising spend. More information can be found here:\n // https://support.google.com/google-ads/answer/6309029.\n .setManualCpc(ManualCpc.getDefaultInstance())\n // Sets the budget.\n .setCampaignBudget(budgetResourceName)\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n .build();\n\n // Creates a campaign operation.\n CampaignOperation operation = CampaignOperation.newBuilder().setCreate(campaign).build();\n\n // Issues a mutate request to add the campaign.\n try (CampaignServiceClient campaignServiceClient =\n googleAdsClient.getLatestVersion().createCampaignServiceClient()) {\n MutateCampaignsResponse response =\n campaignServiceClient.mutateCampaigns(\n Long.toString(customerId), Collections.singletonList(operation));\n MutateCampaignResult result = response.getResults(0);\n System.out.printf(\n \"Added a standard shopping campaign with resource name: '%s'%n\",\n result.getResourceName());\n return result.getResourceName();\n }\n}\nAddShoppingProductAd.java\n```\n\nExample:\n```text\nprivate string AddStandardShoppingCampaign(GoogleAdsClient client, long customerId,\n string budgetResourceName, long merchantCenterAccountId)\n{\n // Get the CampaignService.\n CampaignServiceClient campaignService =\n client.GetService(Services.V25.CampaignService);\n\n // Configures the shopping settings.\n ShoppingSetting shoppingSetting = new ShoppingSetting()\n {\n // Sets the priority of the campaign. Higher numbers take priority over lower\n // numbers. For Shopping Product Ad campaigns, allowed values are between 0 and 2,\n // inclusive.\n CampaignPriority = 0,\n\n MerchantId = merchantCenterAccountId,\n\n // Enables local inventory ads for this campaign.\n EnableLocal = true\n };\n\n // Create the standard shopping campaign.\n Campaign campaign = new Campaign()\n {\n Name = \"Interplanetary Cruise #\" + ExampleUtilities.GetRandomString(),\n\n // Configures settings related to shopping campaigns including advertising channel\n // type and shopping setting.\n AdvertisingChannelType = AdvertisingChannelType.Shopping,\n\n ShoppingSetting = shoppingSetting,\n\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve\n Status = CampaignStatus.Paused,\n\n // Sets the bidding strategy to Manual CPC.\n // Recommendation: Use one of the automated bidding strategies for Shopping\n // campaigns to help you optimize your advertising spend. More information can be\n // found here: https://support.google.com/google-ads/answer/6309029\n ManualCpc = new ManualCpc(),\n\n // Sets the budget.\n CampaignBudget = budgetResourceName,\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n };\n\n // Creates a campaign operation.\n CampaignOperation operation = new CampaignOperation()\n {\n Create = campaign\n };\n\n // Issues a mutate request to add the campaign.\n MutateCampaignsResponse response =\n campaignService.MutateCampaigns(customerId.ToString(),\n new CampaignOperation[] { operation });\n MutateCampaignResult result = response.Results[0];\n Console.WriteLine(\"Added a standard shopping campaign with resource name: '{0}'.\",\n result.ResourceName);\n return result.ResourceName;\n}AddShoppingProductAd.cs\n```\n\nExample:\n```text\nprivate static function addStandardShoppingCampaign(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $budgetResourceName,\n int $merchantCenterAccountId\n) {\n // Creates a standard shopping campaign.\n $campaign = new Campaign([\n 'name' => 'Interplanetary Cruise Campaign #' . Helper::getPrintableDatetime(),\n // Configures settings related to shopping campaigns including advertising channel type\n // and shopping setting.\n 'advertising_channel_type' => AdvertisingChannelType::SHOPPING,\n // Configures the shopping settings.\n 'shopping_setting' => new ShoppingSetting([\n // Sets the priority of the campaign. Higher numbers take priority over lower\n // numbers. For Shopping product ad campaigns, allowed values are between 0 and 2,\n // inclusive.\n 'campaign_priority' => 0,\n 'merchant_id' => $merchantCenterAccountId,\n // Enables local inventory ads for this campaign\n 'enable_local' => true\n ]),\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve.\n 'status' => CampaignStatus::PAUSED,\n // Sets the bidding strategy to Manual CPC.\n // Recommendation: Use one of the automated bidding strategies for Shopping campaigns\n // to help you optimize your advertising spend. More information can be found here:\n // https://support.google.com/google-ads/answer/6309029.\n 'manual_cpc' => new ManualCpc(),\n // Sets the budget.\n 'campaign_budget' => $budgetResourceName,\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n ]);\n\n // Creates a campaign operation.\n $campaignOperation = new CampaignOperation();\n $campaignOperation->setCreate($campaign);\n\n // Issues a mutate request to add campaigns.\n $campaignServiceClient = $googleAdsClient->getCampaignServiceClient();\n $response = $campaignServiceClient->mutateCampaigns(\n MutateCampaignsRequest::build($customerId, [$campaignOperation])\n );\n\n /** @var Campaign $addedCampaign */\n $addedCampaign = $response->getResults()[0];\n printf(\n \"Added a standard shopping campaign with resource name '%s'.%s\",\n $addedCampaign->getResourceName(),\n PHP_EOL\n );\n\n return $addedCampaign->getResourceName();\n}AddShoppingProductAd.php\n```\n\nExample:\n```text\ndef add_standard_shopping_campaign(\n client: GoogleAdsClient,\n customer_id: str,\n budget_resource_name: str,\n merchant_center_account_id: int,\n) -> str:\n \"\"\"Creates a new standard shopping campaign in the specified client account.\"\"\"\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n # Create standard shopping campaign.\n campaign_operation: CampaignOperation = client.get_type(\"CampaignOperation\")\n campaign: Campaign = campaign_operation.create\n campaign.name = f\"Interplanetary Cruise Campaign {uuid.uuid4()}\"\n\n # Configures settings related to shopping campaigns including advertising\n # channel type and shopping setting.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.SHOPPING\n )\n campaign.shopping_setting.merchant_id = merchant_center_account_id\n\n # Sets the priority of the campaign. Higher numbers take priority over lower\n # numbers. For standard shopping campaigns, allowed values are between 0 and\n # 2, inclusive.\n campaign.shopping_setting.campaign_priority = 0\n\n # Enables local inventory ads for this campaign.\n campaign.shopping_setting.enable_local = True\n\n # Recommendation: Set the campaign to PAUSED when creating it to prevent the\n # ads from immediately serving. Set to ENABLED once you've added targeting\n # and the ads are ready to serve.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n\n # Sets the bidding strategy to Manual CPC.\n # Recommendation: Use one of the automated bidding strategies for Shopping\n # campaigns to help you optimize your advertising spend. More information\n # can be found here: https://support.google.com/google-ads/answer/6309029\n campaign.manual_cpc = client.get_type(\"ManualCpc\")\n\n # Sets the budget.\n campaign.campaign_budget = budget_resource_name\n\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n # Add the campaign.\n campaign_response = campaign_service.mutate_campaigns(\n customer_id=customer_id, operations=[campaign_operation]\n )\n\n campaign_resource_name: str = campaign_response.results[0].resource_name\n\n print(\n \"Added a standard shopping campaign with resource name \"\n f\"'{campaign_resource_name}'.\"\n )\n\n return campaign_resource_nameadd_shopping_product_ad.py\n```\n\nExample:\n```text\ndef add_standard_shopping_campaign(\n client, customer_id, budget_name, merchant_center_id)\n\n operation = client.operation.create_resource.campaign do |campaign|\n campaign.name = \"Interplanetary Cruise Campaign ##{(Time.new.to_f * 1000).to_i}\"\n\n # Shopping campaign specific settings\n campaign.advertising_channel_type = :SHOPPING\n\n campaign.shopping_setting = client.resource.shopping_setting do |shopping_setting|\n shopping_setting.merchant_id = merchant_center_id\n shopping_setting.campaign_priority = 0\n shopping_setting.enable_local = true\n end\n\n campaign.status = :PAUSED\n\n # Sets the bidding strategy to Manual CPC.\n campaign.manual_cpc = client.resource.manual_cpc\n\n campaign.campaign_budget = budget_name\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n end\n\n service = client.service.campaign\n response = service.mutate_campaigns(\n customer_id: customer_id,\n operations: [operation],\n )\n\n campaign_name = response.results.first.resource_name\n\n puts \"Added a standard shopping campaign with resource name #{campaign_name}.\"\n\n campaign_name\nendadd_shopping_product_ad.rb\n```\n\nExample:\n```text\nsub add_standard_shopping_campaign {\n my ($api_client, $customer_id, $budget_resource_name,\n $merchant_center_account_id)\n = @_;\n\n # Create a standard shopping campaign.\n my $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n name => \"Interplanetary Cruise Campaign #\" . uniqid(),\n # Configure settings related to shopping campaigns including advertising\n # channel type and shopping setting.\n advertisingChannelType => SHOPPING,\n shoppingSetting =>\n Google::Ads::GoogleAds::V25::Resources::ShoppingSetting->new({\n merchantId => $merchant_center_account_id,\n # Set the priority of the campaign. Higher numbers take priority over\n # lower numbers. For standard shopping campaigns, allowed values are\n # between 0 and 2, inclusive.\n campaignPriority => 0,\n # Enable local inventory ads for this campaign.\n enableLocal => \"true\"\n }\n ),\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n status => Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n # Set the bidding strategy to Manual CPC.\n # Recommendation: Use one of the automated bidding strategies for shopping\n # campaigns to help you optimize your advertising spend. More information\n # can be found here: https://support.google.com/google-ads/answer/6309029.\n manualCpc => Google::Ads::GoogleAds::V25::Common::ManualCpc->new(),\n # Set the budget.\n campaignBudget => $budget_resource_name,\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n });\n\n # Create a campaign operation.\n my $campaign_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation->\n new({create => $campaign});\n\n # Add the campaign.\n my $campaign_resource_name = $api_client->CampaignService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_operation]})->{results}[0]{resourceName};\n\n printf \"Added a standard shopping campaign with resource name: '%s'.\\n\",\n $campaign_resource_name;\n\n return $campaign_resource_name;\n}add_shopping_product_ad.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.269Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":366,"estimatedTokens":3676}}122{"id":"doc-create_campaign_budgets_google_ads_api_google_fo-b5088a0c","source":"documentation","title":"Create Campaign Budgets | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/budgets/create-budgets","text":"Example:\n```text\nprivate static String addCampaignBudget(GoogleAdsClient googleAdsClient, long customerId) {\n CampaignBudget budget =\n CampaignBudget.newBuilder()\n .setName(\"Interplanetary Cruise Budget #\" + getPrintableDateTime())\n .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)\n .setAmountMicros(500_000)\n .build();\n\n CampaignBudgetOperation op = CampaignBudgetOperation.newBuilder().setCreate(budget).build();\n\n try (CampaignBudgetServiceClient campaignBudgetServiceClient =\n googleAdsClient.getLatestVersion().createCampaignBudgetServiceClient()) {\n MutateCampaignBudgetsResponse response =\n campaignBudgetServiceClient.mutateCampaignBudgets(\n Long.toString(customerId), ImmutableList.of(op));\n String budgetResourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Added budget: %s%n\", budgetResourceName);\n return budgetResourceName;\n }\n}AddCampaigns.java\n```\n\nExample:\n```text\nprivate static string CreateBudget(GoogleAdsClient client, long customerId)\n{\n // Get the BudgetService.\n CampaignBudgetServiceClient budgetService = client.GetService(\n Services.V25.CampaignBudgetService);\n\n // Create the campaign budget.\n CampaignBudget budget = new CampaignBudget()\n {\n Name = \"Interplanetary Cruise Budget #\" + ExampleUtilities.GetRandomString(),\n DeliveryMethod = BudgetDeliveryMethod.Standard,\n AmountMicros = 500000\n };\n\n // Create the operation.\n CampaignBudgetOperation budgetOperation = new CampaignBudgetOperation()\n {\n Create = budget\n };\n\n // Create the campaign budget.\n MutateCampaignBudgetsResponse response = budgetService.MutateCampaignBudgets(\n customerId.ToString(), new CampaignBudgetOperation[] { budgetOperation });\n return response.Results[0].ResourceName;\n}AddCampaigns.cs\n```\n\nExample:\n```text\nprivate static function addCampaignBudget(GoogleAdsClient $googleAdsClient, int $customerId)\n{\n // Creates a campaign budget.\n $budget = new CampaignBudget([\n 'name' => 'Interplanetary Cruise Budget #' . Helper::getPrintableDatetime(),\n 'delivery_method' => BudgetDeliveryMethod::STANDARD,\n 'amount_micros' => 500000\n ]);\n\n // Creates a campaign budget operation.\n $campaignBudgetOperation = new CampaignBudgetOperation();\n $campaignBudgetOperation->setCreate($budget);\n\n // Issues a mutate request.\n $campaignBudgetServiceClient = $googleAdsClient->getCampaignBudgetServiceClient();\n $response = $campaignBudgetServiceClient->mutateCampaignBudgets(\n MutateCampaignBudgetsRequest::build($customerId, [$campaignBudgetOperation])\n );\n\n /** @var CampaignBudget $addedBudget */\n $addedBudget = $response->getResults()[0];\n printf(\"Added budget named '%s'%s\", $addedBudget->getResourceName(), PHP_EOL);\n\n return $addedBudget->getResourceName();\n}AddCampaigns.php\n```\n\nExample:\n```text\n# Create a budget, which can be shared by multiple campaigns.\ncampaign_budget_operation: CampaignBudgetOperation = client.get_type(\n \"CampaignBudgetOperation\"\n)\ncampaign_budget: CampaignBudget = campaign_budget_operation.create\ncampaign_budget.name = f\"Interplanetary Budget {uuid.uuid4()}\"\ncampaign_budget.delivery_method = (\n client.enums.BudgetDeliveryMethodEnum.STANDARD\n)\ncampaign_budget.amount_micros = 500000\n\n# Add budget.\ncampaign_budget_response: MutateCampaignBudgetsResponse\ntry:\n budget_operations: List[CampaignBudgetOperation] = [\n campaign_budget_operation\n ]\n campaign_budget_response = (\n campaign_budget_service.mutate_campaign_budgets(\n customer_id=customer_id,\n operations=budget_operations,\n )\n )\nexcept GoogleAdsException as ex:\n handle_googleads_exception(ex)add_campaigns.py\n```\n\nExample:\n```text\n# Create a budget, which can be shared by multiple campaigns.\ncampaign_budget = client.resource.campaign_budget do |cb|\n cb.name = \"Interplanetary Budget #{(Time.new.to_f * 1000).to_i}\"\n cb.delivery_method = :STANDARD\n cb.amount_micros = 500000\nend\n\noperation = client.operation.create_resource.campaign_budget(campaign_budget)\n\n# Add budget.\nreturn_budget = client.service.campaign_budget.mutate_campaign_budgets(\n customer_id: customer_id,\n operations: [operation],\n)add_campaigns.rb\n```\n\nExample:\n```text\n# Create a campaign budget, which can be shared by multiple campaigns.\nmy $campaign_budget =\n Google::Ads::GoogleAds::V25::Resources::CampaignBudget->new({\n name => \"Interplanetary budget #\" . uniqid(),\n deliveryMethod => STANDARD,\n amountMicros => 500000\n });\n\n# Create a campaign budget operation.\nmy $campaign_budget_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation\n ->new({create => $campaign_budget});\n\n# Add the campaign budget.\nmy $campaign_budgets_response = $api_client->CampaignBudgetService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_budget_operation]});add_campaigns.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.271Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":151,"estimatedTokens":1265}}123{"id":"doc-bidding_strategy_status_google_ads_api_google_fo-ce9c160b","source":"documentation","title":"Bidding Strategy Status | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/bidding/strategy-status","text":"Example:\n```text\nSELECT\n campaign.name,\n campaign.status,\n campaign.bidding_strategy_system_status\nFROM campaign\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.272Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":34}}124{"id":"doc-sharing_campaign_budgets_google_ads_api_google_f-669d44d3","source":"documentation","title":"Sharing Campaign Budgets | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/budgets/share-budgets","text":"Example:\n```text\nprivate String createSharedCampaignBudget(GoogleAdsClient googleAdsClient, long customerId) {\n try (CampaignBudgetServiceClient campaignBudgetServiceClient =\n googleAdsClient.getLatestVersion().createCampaignBudgetServiceClient()) {\n // Creates a shared budget.\n CampaignBudget budget =\n CampaignBudget.newBuilder()\n .setName(\"Shared Interplanetary Budget #\" + getPrintableDateTime())\n .setAmountMicros(50_000_000L)\n .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)\n .setExplicitlyShared(true)\n .build();\n // Constructs an operation that will create a shared budget.\n CampaignBudgetOperation operation =\n CampaignBudgetOperation.newBuilder().setCreate(budget).build();\n // Sends the operation in a mutate request.\n MutateCampaignBudgetsResponse response =\n campaignBudgetServiceClient.mutateCampaignBudgets(\n Long.toString(customerId), Lists.newArrayList(operation));\n\n MutateCampaignBudgetResult mutateCampaignBudgetResult = response.getResults(0);\n // Prints the resource name of the created object.\n System.out.printf(\n \"Created shared budget with resource name: '%s'.%n\",\n mutateCampaignBudgetResult.getResourceName());\n\n return mutateCampaignBudgetResult.getResourceName();\n }\n}UsePortfolioBiddingStrategy.java\n```\n\nExample:\n```text\nprivate string CreateSharedBudget(GoogleAdsClient client, long customerId, string name,\n long amount)\n{\n // Get the CampaignBudgetService.\n CampaignBudgetServiceClient campaignBudgetService =\n client.GetService(Services.V25.CampaignBudgetService);\n\n // Create a shared budget.\n CampaignBudget budget = new CampaignBudget()\n {\n Name = name,\n AmountMicros = amount,\n DeliveryMethod = BudgetDeliveryMethodEnum.Types.BudgetDeliveryMethod.Standard,\n ExplicitlyShared = true\n };\n\n // Create the operation.\n CampaignBudgetOperation campaignBudgetOperation = new CampaignBudgetOperation()\n {\n Create = budget\n };\n\n // Make the mutate request.\n MutateCampaignBudgetsResponse retVal = campaignBudgetService.MutateCampaignBudgets(\n customerId.ToString(), new CampaignBudgetOperation[] { campaignBudgetOperation });\n return retVal.Results[0].ResourceName;\n}UsePortfolioBiddingStrategy.cs\n```\n\nExample:\n```text\nprivate static function createSharedCampaignBudget(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n) {\n // Creates a shared budget.\n $budget = new CampaignBudget([\n 'name' => 'Shared Interplanetary Budget #' . Helper::getPrintableDatetime(),\n 'delivery_method' => BudgetDeliveryMethod::STANDARD,\n // Sets the amount of budget.\n 'amount_micros' => 50000000,\n // Makes the budget explicitly shared.\n 'explicitly_shared' => true\n ]);\n\n // Constructs a campaign budget operation.\n $campaignBudgetOperation = new CampaignBudgetOperation();\n $campaignBudgetOperation->setCreate($budget);\n\n // Issues a mutate request to create the budget.\n $campaignBudgetServiceClient = $googleAdsClient->getCampaignBudgetServiceClient();\n $response = $campaignBudgetServiceClient->mutateCampaignBudgets(\n MutateCampaignBudgetsRequest::build($customerId, [$campaignBudgetOperation])\n );\n\n /** @var CampaignBudget $addedBudget */\n $addedBudget = $response->getResults()[0];\n printf(\n \"Created a shared budget with resource name '%s'.%s\",\n $addedBudget->getResourceName(),\n PHP_EOL\n );\n\n return $addedBudget->getResourceName();\n}UsePortfolioBiddingStrategy.php\n```\n\nExample:\n```text\n# Create a budget, which can be shared by multiple campaigns.\ncampaign_budget_operation: CampaignBudgetOperation = client.get_type(\n \"CampaignBudgetOperation\"\n)\ncampaign_budget: CampaignBudget = campaign_budget_operation.create\ncampaign_budget.name = f\"Interplanetary Budget {uuid.uuid4()}\"\ncampaign_budget.delivery_method = (\n client.enums.BudgetDeliveryMethodEnum.STANDARD\n)\ncampaign_budget.amount_micros = 500000\ncampaign_budget.explicitly_shared = True\n\n# Add budget.\ntry:\n campaign_budget_response: MutateCampaignBudgetsResponse = (\n campaign_budget_service.mutate_campaign_budgets(\n customer_id=customer_id, operations=[campaign_budget_operation]\n )\n )\n campaign_budget_id: str = campaign_budget_response.results[\n 0\n ].resource_name\n print(f'Budget \"{campaign_budget_id}\" was created.')\nexcept GoogleAdsException as ex:\n handle_googleads_exception(ex)use_portfolio_bidding_strategy.py\n```\n\nExample:\n```text\n# Create a budget, which can be shared by multiple campaigns.\nbudget = client.resource.campaign_budget do |cb|\n cb.name = \"Interplanetary budget ##{(Time.new.to_f * 1000).to_i}\"\n cb.amount_micros = 50_000_000\n cb.delivery_method = :STANDARD\n cb.explicitly_shared = true\nend\n\noperation = client.operation.create_resource.campaign_budget(budget)\n\nresponse = client.service.campaign_budget.mutate_campaign_budgets(\n customer_id: customer_id,\n operations: [operation],\n)\nbudget_id = response.results.first.resource_nameuse_portfolio_bidding_strategy.rb\n```\n\nExample:\n```text\nsub create_shared_campaign_buget {\n my ($api_client, $customer_id) = @_;\n\n # Create a shared budget.\n my $campaign_budget =\n Google::Ads::GoogleAds::V25::Resources::CampaignBudget->new({\n name => \"Shared Interplanetary Budget #\" . uniqid(),\n deliveryMethod => STANDARD,\n # Set the amount of budget.\n amountMicros => 50000000,\n # Makes the budget explicitly shared.\n explicitlyShared => 'true'\n });\n\n # Create a campaign budget operation.\n my $campaign_budget_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation\n ->new({create => $campaign_budget});\n\n # Add the campaign budget.\n my $campaign_budgets_response = $api_client->CampaignBudgetService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_budget_operation]});\n\n my $campaign_budget_resource_name =\n $campaign_budgets_response->{results}[0]{resourceName};\n\n printf \"Created a shared budget with resource name: '%s'.\\n\",\n $campaign_budget_resource_name;\n\n return $campaign_budget_resource_name;\n}use_portfolio_bidding_strategy.pl\n```\n\nExample:\n```text\nSELECT campaign_budget.explicitly_shared\nFROM campaign_budget\nWHERE campaign_budget.id = campaign_budget_id\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.273Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":193,"estimatedTokens":1623}}125{"id":"doc-remove_a_campaign_budget_google_ads_api_google_f-44883b45","source":"documentation","title":"Remove a Campaign Budget | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/budgets/remove-budgets","text":"Example:\n```text\nSELECT campaign_budget.reference_count\nFROM campaign_budget\nWHERE campaign_budget.id = campaign_budget_id\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.274Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":8,"estimatedTokens":35}}126{"id":"doc-mutate_ads_google_ads_api_google_for_developers-2e8bb56e","source":"documentation","title":"Mutate Ads | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/ads/mutate-ads","text":"Example:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient, long customerId, long adId) {\n // Creates an AdOperation to update an ad.\n AdOperation.Builder adOperation = AdOperation.newBuilder();\n\n // Creates an Ad in the update field of the operation.\n Ad.Builder adBuilder =\n adOperation\n .getUpdateBuilder()\n .setResourceName(ResourceNames.ad(customerId, adId))\n .addFinalUrls(\"http://www.example.com/\")\n .addFinalMobileUrls(\"http://www.example.com/mobile\");\n\n // Sets the responsive search ad properties to update on the ad.\n adBuilder\n .getResponsiveSearchAdBuilder()\n .addAllHeadlines(\n ImmutableList.of(\n AdTextAsset.newBuilder()\n .setText(\"Cruise to Pluto #\" + getShortPrintableDateTime())\n .setPinnedField(ServedAssetFieldType.HEADLINE_1)\n .build(),\n AdTextAsset.newBuilder().setText(\"Tickets on sale now\").build(),\n AdTextAsset.newBuilder().setText(\"Buy your ticket now\").build()))\n .addAllDescriptions(\n ImmutableList.of(\n AdTextAsset.newBuilder().setText(\"Best space cruise ever.\").build(),\n AdTextAsset.newBuilder()\n .setText(\"The most wonderful space experience you will ever have.\")\n .build()));\n\n // Sets the update mask (the fields which will be modified) to be all the fields we set above.\n adOperation.setUpdateMask(FieldMasks.allSetFieldsOf(adBuilder.build()));\n\n // Creates a service client to connect to the API.\n try (AdServiceClient adServiceClient =\n googleAdsClient.getLatestVersion().createAdServiceClient()) {\n // Issues the mutate request.\n MutateAdsResponse response =\n adServiceClient.mutateAds(\n String.valueOf(customerId), ImmutableList.of(adOperation.build()));\n\n // Displays the result.\n for (MutateAdResult result : response.getResultsList()) {\n System.out.printf(\"Ad with resource name '%s' was updated.%n\", result.getResourceName());\n }\n }\n}UpdateResponsiveSearchAd.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long adId)\n{\n // Get the AdService.\n AdServiceClient adService = client.GetService(Services.V25.AdService);\n\n Ad ad = new Ad()\n {\n ResourceName = ResourceNames.Ad(customerId, adId),\n ResponsiveSearchAd = new ResponsiveSearchAdInfo()\n {\n // Update some properties of the responsive search ad.\n Headlines =\n {\n new AdTextAsset()\n {\n Text = \"Cruise to Pluto #\" + ExampleUtilities.GetShortRandomString(),\n PinnedField = ServedAssetFieldTypeEnum.Types.ServedAssetFieldType.Headline1\n },\n new AdTextAsset() { Text = \"Tickets on sale now\" },\n new AdTextAsset() { Text = \"Buy your ticket now\" }\n },\n Descriptions =\n {\n new AdTextAsset() { Text = \"Best space cruise ever.\" },\n new AdTextAsset() { Text = \"The most wonderful space experience you will ever have.\" },\n }\n },\n FinalUrls = { \"http://www.example.com/\" },\n FinalMobileUrls = { \"http://www.example.com/mobile\" }\n };\n\n AdOperation operation = new AdOperation()\n {\n Update = ad,\n UpdateMask = FieldMasks.AllSetFieldsOf(ad)\n };\n\n try\n {\n // Issue the update request.\n MutateAdsResponse response = adService.MutateAds(customerId.ToString(),\n new[] { operation });\n\n // Display the results.\n foreach (MutateAdResult updatedAd in response.Results)\n {\n Console.WriteLine($\"Ad with resource ID = '{updatedAd.ResourceName}' was \" +\n $\"updated.\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}UpdateResponsiveSearchAd.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adId\n) {\n // Creates an ad with the specified resource name and other changes.\n $ad = new Ad([\n 'resource_name' => ResourceNames::forAd($customerId, $adId),\n 'responsive_search_ad' => new ResponsiveSearchAdInfo([\n // Update some properties of the responsive search ad.\n 'headlines' => [\n new AdTextAsset([\n 'text' => 'Cruise to Pluto #' . Helper::getShortPrintableDatetime(),\n 'pinned_field' => ServedAssetFieldType::HEADLINE_1\n ]),\n new AdTextAsset(['text' => 'Tickets on sale now']),\n new AdTextAsset(['text' => 'Buy your ticket now'])\n ],\n 'descriptions' => [\n new AdTextAsset(['text' => 'Best space cruise ever.']),\n new AdTextAsset([\n 'text' => 'The most wonderful space experience you will ever have.'])\n ]\n ]),\n 'final_urls' => ['http://www.example.com'],\n 'final_mobile_urls' => ['http://www.example.com/mobile']\n ]);\n\n // Constructs an operation that will update the ad, using the FieldMasks to derive the\n // update mask. This mask tells the Google Ads API which attributes of the ad you want to\n // change.\n $adOperation = new AdOperation();\n $adOperation->setUpdate($ad);\n $adOperation->setUpdateMask(FieldMasks::allSetFieldsOf($ad));\n\n // Issues a mutate request to update the ad.\n $adServiceClient = $googleAdsClient->getAdServiceClient();\n $response =\n $adServiceClient->mutateAds(MutateAdsRequest::build($customerId, [$adOperation]));\n\n // Prints the resource name of the updated ad.\n /** @var Ad $updatedAd */\n $updatedAd = $response->getResults()[0];\n printf(\n \"Updated ad with resource name: '%s'.%s\",\n $updatedAd->getResourceName(),\n PHP_EOL\n );\n}UpdateResponsiveSearchAd.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient, customer_id: str, ad_id: str) -> None:\n ad_service: AdServiceClient = client.get_service(\"AdService\")\n ad_operation: AdOperation = client.get_type(\"AdOperation\")\n\n # Update ad operation.\n ad: Ad = ad_operation.update\n ad.resource_name = ad_service.ad_path(customer_id, ad_id)\n\n # Update some properties of the responsive search ad.\n headline_1: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline_1.text = f\"Cruise to Pluto #{uuid4().hex[:8]}\"\n headline_1.pinned_field = client.enums.ServedAssetFieldTypeEnum.HEADLINE_1\n\n headline_2: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline_2.text = \"Tickets on sale now\"\n\n headline_3: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline_3.text = \"Buy your tickets now\"\n\n ad.responsive_search_ad.headlines.extend(\n [headline_1, headline_2, headline_3]\n )\n\n description_1: AdTextAsset = client.get_type(\"AdTextAsset\")\n description_1.text = \"Best space cruise ever.\"\n\n description_2: AdTextAsset = client.get_type(\"AdTextAsset\")\n description_2.text = (\n \"The most wonderful space experience you will ever have.\"\n )\n ad.responsive_search_ad.descriptions.extend([description_1, description_2])\n\n ad.final_urls.append(\"https://www.example.com\")\n ad.final_mobile_urls.append(\"https://www.example.com/mobile\")\n client.copy_from(\n ad_operation.update_mask, protobuf_helpers.field_mask(None, ad._pb)\n )\n\n # Updates the ad.\n operations: List[AdOperation] = [ad_operation]\n ad_response: MutateAdsResponse = ad_service.mutate_ads(\n customer_id=customer_id, operations=operations\n )\n print(\n f'Ad with resource name \"{ad_response.results[0].resource_name}\" '\n \"was updated.\"\n )update_responsive_search_ad.py\n```\n\nExample:\n```text\ndef update_responsive_search_ad(customer_id, ad_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n ad_resource_name = client.path.ad(customer_id, ad_id)\n\n # Create the operation for updating the ad.\n ad_operation = client.operation.update_resource.ad(ad_resource_name) do |ad|\n ad.final_urls << 'http://www.example.com'\n ad.final_mobile_urls << 'http://www.example.com/mobile'\n ad.responsive_search_ad = client.resource.responsive_search_ad_info do |rsa|\n rsa.headlines += [\n client.resource.ad_text_asset do |ata|\n ata.text = \"Cruise to Pluto #{(Time.new.to_f * 100).to_i}\"\n ata.pinned_field = :HEADLINE_1\n end,\n client.resource.ad_text_asset do |ata|\n ata.text = \"Tickets on sale now\"\n end,\n client.resource.ad_text_asset do |ata|\n ata.text = \"Buy your ticket now\"\n end,\n ]\n rsa.descriptions += [\n client.resource.ad_text_asset do |ata|\n ata.text = \"Best space cruise ever\"\n end,\n client.resource.ad_text_asset do |ata|\n ata.text = \"The most wonderful space experience you will ever have\"\n end,\n ]\n end\n end\n\n # Update the ad.\n response = client.service.ad.mutate_ads(\n customer_id: customer_id,\n operations: [ad_operation],\n )\n\n puts \"Updated responsive search ad #{response.results.first.resource_name}.\"\nendupdate_responsive_search_ad.rb\n```\n\nExample:\n```text\nsub update_responsive_search_ad {\n my ($api_client, $customer_id, $ad_id) = @_;\n\n # Create an ad with the proper resource name and any other changes.\n my $ad = Google::Ads::GoogleAds::V25::Resources::Ad->new({\n resourceName => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad(\n $customer_id, $ad_id\n ),\n responsiveSearchAd =>\n Google::Ads::GoogleAds::V25::Common::ResponsiveSearchAdInfo->new({\n # Update some properties of the responsive search ad.\n headlines => [\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Cruise to Pluto #\" . uniqid(),\n pinnedField => HEADLINE_1\n }\n ),\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Tickets on sale now\"\n }\n ),\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Buy your ticket now\"\n }\n ),\n\n ],\n descriptions => [\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Best space cruise ever.\"\n }\n ),\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text =>\n \"The most wonderful space experience you will ever have.\"\n }\n ),\n ]}\n ),\n finalUrls => [\"http://www.example.com/\"],\n finalMobileUrls => [\"http://www.example.com/mobile\"]});\n\n # Create an ad operation for update, using the FieldMasks utility to derive\n # the update mask.\n my $ad_operation =\n Google::Ads::GoogleAds::V25::Services::AdService::AdOperation->new({\n update => $ad,\n updateMask => all_set_fields_of($ad)});\n\n # Issue a mutate request to update the ad.\n my $ads_response = $api_client->AdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_operation]});\n\n printf \"Updated ad with resource name: '%s'.\\n\",\n $ads_response->{results}[0]{resourceName};\n\n return 1;\n}update_responsive_search_ad.pl\n```\n\nExample:\n```text\n// Copyright 2018 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.basicoperations;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.lib.utils.FieldMasks;\nimport com.google.ads.googleads.v25.enums.AdGroupAdStatusEnum.AdGroupAdStatus;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.AdGroupAd;\nimport com.google.ads.googleads.v25.services.AdGroupAdOperation;\nimport com.google.ads.googleads.v25.services.AdGroupAdServiceClient;\nimport com.google.ads.googleads.v25.services.MutateAdGroupAdResult;\nimport com.google.ads.googleads.v25.services.MutateAdGroupAdsResponse;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\n\n/** Changes the status of a given ad to {@code PAUSED}. */\npublic class PauseAd {\n\n private static class PauseAdParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(names = ArgumentNames.AD_GROUP_ID, required = true)\n private Long adGroupId;\n\n @Parameter(names = ArgumentNames.AD_ID, required = true)\n private Long adId;\n }\n\n public static void main(String[] args) {\n PauseAdParams params = new PauseAdParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.adGroupId = Long.parseLong(\"INSERT_AD_GROUP_ID_HERE\");\n params.adId = Long.parseLong(\"INSERT_AD_ID_HERE\");\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new PauseAd().runExample(googleAdsClient, params.customerId, params.adGroupId, params.adId);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param adGroupId the ad group ID.\n * @param adId the ID of the ad to pause.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void runExample(\n GoogleAdsClient googleAdsClient, long customerId, long adGroupId, long adId) {\n\n String adGroupAdResourceName = ResourceNames.adGroupAd(customerId, adGroupId, adId);\n\n // Creates an ad representation with its status set to PAUSED.\n AdGroupAd adGroupAd =\n AdGroupAd.newBuilder()\n .setResourceName(adGroupAdResourceName)\n .setStatus(AdGroupAdStatus.PAUSED)\n .build();\n\n AdGroupAdOperation op =\n AdGroupAdOperation.newBuilder()\n .setUpdate(adGroupAd)\n .setUpdateMask(FieldMasks.allSetFieldsOf(adGroupAd))\n .build();\n\n try (AdGroupAdServiceClient adGroupAdServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupAdServiceClient()) {\n MutateAdGroupAdsResponse response =\n adGroupAdServiceClient.mutateAdGroupAds(Long.toString(customerId), ImmutableList.of(op));\n for (MutateAdGroupAdResult result : response.getResultsList()) {\n System.out.printf(\"Ad with resource name '%s' is paused.%n\", result.getResourceName());\n }\n }\n }\n}\nPauseAd.java\n```\n\nExample:\n```text\n// Copyright 2019 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.Gax.Util;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System;\nusing System.Collections.Generic;\nusing static Google.Ads.GoogleAds.V25.Enums.AdGroupAdStatusEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example pauses a given ad. To list all ads, run GetExpandedTextAds.cs.\n /// </summary>\n public class PauseAd : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"PauseAd\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// The ad group ID that contains the ad.\n /// </summary>\n [Option(\"adGroupId\", Required = true, HelpText =\n \"The ad group ID that contains the ad.\")]\n public long AdGroupId { get; set; }\n\n /// <summary>\n /// AdGroupAdService.\n /// </summary>\n [Option(\"adId\", Required = true, HelpText =\n \"AdGroupAdService.\")]\n public long AdId { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n PauseAd codeExample = new PauseAd();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(),\n options.CustomerId,\n options.AdGroupId,\n options.AdId);\n }\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example pauses a given ad. To list all ads, run GetExpandedTextAds.cs.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The customer ID for which the call is made.</param>\n /// <param name=\"adGroupId\">The ad group ID that contains the ad.</param>\n /// <param name=\"adId\">AdGroupAdService</param>\n public void Run(GoogleAdsClient client, long customerId, long adGroupId, long adId)\n {\n // Get the AdGroupAdService.\n AdGroupAdServiceClient adGroupAdService = client.GetService(\n Services.V25.AdGroupAdService);\n\n // Create the ad group ad.\n AdGroupAd adGroupAd = new AdGroupAd\n {\n ResourceName = ResourceNames.AdGroupAd(customerId, adGroupId, adId),\n Status = AdGroupAdStatus.Paused\n };\n\n // Create the operation.\n AdGroupAdOperation operation = new AdGroupAdOperation\n {\n // Set the Update field to the ad group ad object.\n Update = adGroupAd,\n\n // Use the FieldMasks utility to set the UpdateMask field to a list of all\n // modified fields of the ad group ad.\n UpdateMask = FieldMasks.AllSetFieldsOf(adGroupAd)\n };\n try\n {\n // Update the ad.\n MutateAdGroupAdsResponse response =\n adGroupAdService.MutateAdGroupAds(customerId.ToString(),\n new AdGroupAdOperation[] { operation });\n\n // Display the results.\n foreach (MutateAdGroupAdResult result in response.Results)\n {\n Console.WriteLine($\"Ad with resource name = {result.ResourceName} was \" +\n \"paused.\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n }\n}\nPauseAd.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2018 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\BasicOperations;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Util\\FieldMasks;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdGroupAdStatusEnum\\AdGroupAdStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AdGroupAd;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AdGroupAdOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAdGroupAdsRequest;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * This example changes the status of a given ad to `PAUSED`. To get ad groups, run GetAdGroups.php.\n */\nclass PauseAd\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const AD_GROUP_ID = 'INSERT_AD_GROUP_ID_HERE';\n private const AD_ID = 'INSERT_AD_ID_HERE';\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::AD_GROUP_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::AD_ID => GetOpt::REQUIRED_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::AD_GROUP_ID] ?: self::AD_GROUP_ID,\n $options[ArgumentNames::AD_ID] ?: self::AD_ID\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $adGroupId the ad group ID that the ad group ad belongs to\n * @param int $adId the ID of the ad to pause\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n int $adId\n ) {\n // Creates ad group ad resource name.\n $adGroupAdResourceName = ResourceNames::forAdGroupAd($customerId, $adGroupId, $adId);\n\n // Creates an ad and sets its status to PAUSED.\n $adGroupAd = new AdGroupAd();\n $adGroupAd->setResourceName($adGroupAdResourceName);\n $adGroupAd->setStatus(AdGroupAdStatus::PAUSED);\n\n // Constructs an operation that will pause the ad with the specified resource name,\n // using the FieldMasks utility to derive the update mask. This mask tells the Google Ads\n // API which attributes of the ad group you want to change.\n $adGroupAdOperation = new AdGroupAdOperation();\n $adGroupAdOperation->setUpdate($adGroupAd);\n $adGroupAdOperation->setUpdateMask(FieldMasks::allSetFieldsOf($adGroupAd));\n\n // Issues a mutate request to pause the ad group ad.\n $adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();\n $response = $adGroupAdServiceClient->mutateAdGroupAds(MutateAdGroupAdsRequest::build(\n $customerId,\n [$adGroupAdOperation]\n ));\n\n // Prints the resource name of the paused ad group ad.\n /** @var AdGroupAd $pausedAdGroupAd */\n $pausedAdGroupAd = $response->getResults()[0];\n printf(\n \"Ad group ad with resource name: '%s' is paused.%s\",\n $pausedAdGroupAd->getResourceName(),\n PHP_EOL\n );\n }\n}\n\nPauseAd::main();\nPauseAd.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2018 Google LLC\n#\n# 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.\n\"\"\"This example pauses an ad.\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import List\n\nfrom google.api_core import protobuf_helpers\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.resources.types.ad_group_ad import AdGroupAd\nfrom google.ads.googleads.v24.services.services.ad_group_ad_service import (\n AdGroupAdServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_ad_service import (\n AdGroupAdOperation,\n MutateAdGroupAdsResponse,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n ad_id: str,\n) -> None:\n ad_group_ad_service: AdGroupAdServiceClient = client.get_service(\n \"AdGroupAdService\"\n )\n\n ad_group_ad_operation: AdGroupAdOperation = client.get_type(\n \"AdGroupAdOperation\"\n )\n\n ad_group_ad: AdGroupAd = ad_group_ad_operation.update\n ad_group_ad.resource_name = ad_group_ad_service.ad_group_ad_path(\n customer_id, ad_group_id, ad_id\n )\n ad_group_ad.status = client.enums.AdGroupAdStatusEnum.PAUSED\n client.copy_from(\n ad_group_ad_operation.update_mask,\n protobuf_helpers.field_mask(None, ad_group_ad._pb),\n )\n\n operations: List[AdGroupAdOperation] = [ad_group_ad_operation]\n\n ad_group_ad_response: MutateAdGroupAdsResponse = (\n ad_group_ad_service.mutate_ad_group_ads(\n customer_id=customer_id,\n operations=operations,\n )\n )\n\n print(\n f\"Paused ad group ad {ad_group_ad_response.results[0].resource_name}.\"\n )\n\n\nif __name__ == \"__main__\":\n parser = argparse.ArgumentParser(\n description=(\"Pauses an ad in the specified customer's ad group.\")\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-a\", \"--ad_group_id\", type=str, required=True, help=\"The ad group ID.\"\n )\n parser.add_argument(\n \"-i\", \"--ad_id\", type=str, required=True, help=\"The ad ID.\"\n )\n args: argparse.Namespace = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(googleads_client, args.customer_id, args.ad_group_id, args.ad_id)\n except GoogleAdsException as ex:\n print(\n f'Request with ID \"{ex.request_id}\" failed with status '\n f'\"{ex.error.code().name}\" and includes the following errors:'\n )\n for error in ex.failure.errors:\n print(f'\\tError with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\npause_ad.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2018 Google LLC\n#\n# 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.\n#\n# This example pauses an ad.\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\n\ndef pause_ad(customer_id, ad_group_id, ad_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n aga_resource_name = client.path.ad_group_ad(customer_id, ad_group_id, ad_id)\n\n operation = client.operation.update_resource.ad_group_ad(aga_resource_name) do |aga|\n aga.status = :PAUSED\n end\n\n response = client.service.ad_group_ad.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [operation],\n )\n\n puts \"Paused ad #{response.results.first.resource_name}\"\nend\n\nif __FILE__ == $PROGRAM_NAME\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:ad_group_id] = 'INSERT_AD_GROUP_ID_HERE'\n options[:ad_id] = 'INSERT_AD_ID_HERE'\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-A', '--ad-group-id AD-GROUP-ID', String, 'Ad Group ID') do |v|\n options[:ad_group_id] = v\n end\n\n opts.on('-a', '--ad-id AD-ID', String, 'Ad ID') do |v|\n options[:ad_id] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n pause_ad(options.fetch(:customer_id).tr(\"-\", \"\"), options[:ad_group_id], options[:ad_id])\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\npause_ad.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2019, Google LLC\n#\n# 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# http://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.\n#\n# This example changes the status of a given ad to PAUSED.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::Utils::FieldMasks;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroupAd;\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupAdStatusEnum qw(PAUSED);\nuse Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\nmy $ad_group_id = \"INSERT_AD_GROUP_ID_HERE\";\nmy $ad_id = \"INSERT_AD_ID_HERE\";\n\nsub pause_ad {\n my ($api_client, $customer_id, $ad_group_id, $ad_id) = @_;\n\n # Create an ad group ad with its status set to PAUSED.\n my $ad_group_ad = Google::Ads::GoogleAds::V25::Resources::AdGroupAd->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group_ad(\n $customer_id, $ad_group_id, $ad_id\n ),\n status => PAUSED\n });\n\n # Create an ad group ad operation for update, using the FieldMasks utility\n # to derive the update mask.\n my $ad_group_ad_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation\n ->new({\n update => $ad_group_ad,\n updateMask => all_set_fields_of($ad_group_ad)});\n\n # Update the ad group ad.\n my $ad_group_ads_response = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]});\n\n printf \"Ad with resource name '%s' is paused.\\n\",\n $ad_group_ads_response->{results}[0]{resourceName};\n\n return 1;\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"ad_group_id=i\" => \\$ad_group_id,\n \"ad_id=i\" => \\$ad_id\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id, $ad_group_id, $ad_id);\n\n# Call the example.\npause_ad($api_client, $customer_id =~ s/-//gr, $ad_group_id, $ad_id);\n\n=pod\n\n=head1 NAME\n\npause_ad\n\n=head1 DESCRIPTION\n\nThis example changes the status of a given ad to PAUSED.\n\n=head1 SYNOPSIS\n\npause_ad.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -ad_group_id The ad group ID.\n -ad_id The ad ID.\n\n=cut\npause_ad.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.277Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":1109,"estimatedTokens":9739}}127{"id":"doc-creating_responsive_search_ads_google_ads_api_go-72f2570e","source":"documentation","title":"Creating Responsive Search Ads | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/responsive-search-ads/create-responsive-search-ads","text":"Example:\n```text\n// Copyright 2025 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.advancedoperations;\n\nimport static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.LocationInfo;\nimport com.google.ads.googleads.v25.resources.CampaignCriterion;\nimport com.google.ads.googleads.v25.services.CampaignCriterionOperation;\nimport com.google.ads.googleads.v25.services.CampaignCriterionServiceClient;\nimport com.google.ads.googleads.v25.services.GeoTargetConstantServiceClient;\nimport com.google.ads.googleads.v25.services.GeoTargetConstantSuggestion;\nimport com.google.ads.googleads.v25.services.MutateAdGroupCriteriaResponse;\nimport com.google.ads.googleads.v25.common.KeywordInfo;\nimport com.google.ads.googleads.v25.enums.AdGroupCriterionStatusEnum.AdGroupCriterionStatus;\nimport com.google.ads.googleads.v25.enums.KeywordMatchTypeEnum.KeywordMatchType;\nimport com.google.ads.googleads.v25.resources.AdGroupCriterion;\nimport com.google.ads.googleads.v25.services.AdGroupCriterionOperation;\nimport com.google.ads.googleads.v25.services.AdGroupCriterionServiceClient;\nimport com.google.ads.googleads.v25.services.AdGroupOperation;\nimport com.google.ads.googleads.v25.services.AdGroupServiceClient;\nimport com.google.ads.googleads.v25.common.TargetSpend;\nimport com.google.ads.googleads.v25.enums.AdGroupAdStatusEnum.AdGroupAdStatus;\nimport com.google.ads.googleads.v25.enums.AdGroupStatusEnum.AdGroupStatus;\nimport com.google.ads.googleads.v25.enums.AdGroupTypeEnum.AdGroupType;\nimport com.google.ads.googleads.v25.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType;\nimport com.google.ads.googleads.v25.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod;\nimport com.google.ads.googleads.v25.enums.CampaignStatusEnum.CampaignStatus;\nimport com.google.ads.googleads.v25.enums.ServedAssetFieldTypeEnum.ServedAssetFieldType;\nimport com.google.ads.googleads.v25.resources.AdGroup;\nimport com.google.ads.googleads.v25.resources.Campaign;\nimport com.google.ads.googleads.v25.resources.Campaign.NetworkSettings;\nimport com.google.ads.googleads.v25.resources.CampaignBudget;\nimport com.google.ads.googleads.v25.services.AdGroupAdOperation;\nimport com.google.ads.googleads.v25.services.AdGroupAdServiceClient;\nimport com.google.ads.googleads.v25.services.CampaignBudgetOperation;\nimport com.google.ads.googleads.v25.services.CampaignBudgetServiceClient;\nimport com.google.ads.googleads.v25.services.CampaignOperation;\nimport com.google.ads.googleads.v25.services.CampaignServiceClient;\nimport com.google.ads.googleads.v25.services.MutateAdGroupAdsResponse;\nimport com.google.ads.googleads.v25.services.MutateAdGroupCriterionResult;\nimport com.google.ads.googleads.v25.services.MutateAdGroupsResponse;\nimport com.google.ads.googleads.v25.services.MutateCampaignBudgetsResponse;\nimport com.google.ads.googleads.v25.services.MutateCampaignCriteriaResponse;\nimport com.google.ads.googleads.v25.services.MutateCampaignCriterionResult;\nimport com.google.ads.googleads.v25.services.MutateCampaignsResponse;\nimport com.google.ads.googleads.v25.services.SuggestGeoTargetConstantsRequest;\nimport com.google.ads.googleads.v25.services.SuggestGeoTargetConstantsRequest.LocationNames;\nimport com.google.ads.googleads.v25.services.SuggestGeoTargetConstantsResponse;\nimport com.google.ads.googleads.v25.common.AdTextAsset;\nimport com.google.ads.googleads.v25.common.CustomizerValue;\nimport com.google.ads.googleads.v25.common.ResponsiveSearchAdInfo;\nimport com.google.ads.googleads.v25.enums.CustomizerAttributeTypeEnum.CustomizerAttributeType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.Ad;\nimport com.google.ads.googleads.v25.resources.AdGroupAd;\nimport com.google.ads.googleads.v25.resources.CustomerCustomizer;\nimport com.google.ads.googleads.v25.resources.CustomizerAttribute;\nimport com.google.ads.googleads.v25.services.CustomerCustomizerOperation;\nimport com.google.ads.googleads.v25.services.CustomerCustomizerServiceClient;\nimport com.google.ads.googleads.v25.services.CustomizerAttributeOperation;\nimport com.google.ads.googleads.v25.services.CustomizerAttributeServiceClient;\nimport com.google.ads.googleads.v25.services.MutateCustomerCustomizersResponse;\nimport com.google.ads.googleads.v25.services.MutateCustomizerAttributesResponse;\nimport com.google.common.collect.ImmutableList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport javax.annotation.Nullable;\n\n/**\n * This example shows how to create a complete Responsive Search Ad.\n *\n * <p>Includes creation of: budget, campaign, ad group, ad group ad, keywords, and geo targeting.\n *\n * <p>More details on Responsive Search Ads can be found here:\n * https://support.google.com/google-ads/answer/7684791\n */\npublic class AddResponsiveSearchAdFull {\n\n private static class AddResponsiveSearchAdFullParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n // The name of the customizer attribute to be used in the ad customizer, which must be unique.\n // To run this example multiple times, change this value or specify its corresponding argument.\n // Note that there is a limit for the number of enabled customizer attributes in one account,\n // so you shouldn't run this example more than necessary.\n // Visit\n // https://developers.google.com/google-ads/api/docs/ads/customize-responsive-search-ads?hl=en#rules_and_limitations\n // for details.\n //\n // Specify the customizer attribute name here or the default specified below will be used.\n @Parameter(names = ArgumentNames.CUSTOMIZER_ATTRIBUTE_NAME)\n private String customizerAttributeName = \"Price\";\n }\n\n public static void main(String[] args) {\n AddResponsiveSearchAdFullParams params = new AddResponsiveSearchAdFullParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n\n // Optional: To use a different customizer attribute name from the default (\"Price\"),\n // uncomment the line below and insert the desired customizer attribute name.\n // params.customizerAttributeName = \"INSERT_CUSTOMIZER_ATTRIBUTE_NAME_HERE\";\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new AddResponsiveSearchAdFull()\n .runExample(googleAdsClient, params.customerId, params.customizerAttributeName);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param customizerAttributeName the customizer attribute name.\n */\n private void runExample(\n GoogleAdsClient googleAdsClient, long customerId, String customizerAttributeName) {\n if (customizerAttributeName != null && !customizerAttributeName.isEmpty()) {\n String customizerAttributeResourceName =\n createCustomizerAttribute(googleAdsClient, customerId, customizerAttributeName);\n\n linkCustomizerAttributeToCustomer(\n googleAdsClient, customerId, customizerAttributeResourceName);\n }\n\n String campaignBudget = createCampaignBudget(googleAdsClient, customerId);\n\n String campaignResourceName = createCampaign(googleAdsClient, customerId, campaignBudget);\n\n String adGroupResourceName = createAdGroup(googleAdsClient, customerId, campaignResourceName);\n\n createAdGroupAd(googleAdsClient, customerId, adGroupResourceName, customizerAttributeName);\n\n addKeywords(googleAdsClient, customerId, adGroupResourceName);\n\n addGeoTargeting(googleAdsClient, customerId, campaignResourceName);\n }\n\n /** Creates a customizer attribute with the specified customizer attribute name. */\n private static String createCustomizerAttribute(\n GoogleAdsClient googleAdsClient, long customerId, String customizerAttributeName) {\n // Creates a customizer attribute with the specified name.\n CustomizerAttribute customizerAttribute =\n CustomizerAttribute.newBuilder()\n .setName(customizerAttributeName)\n // Specifies the type to be 'PRICE' so that we can dynamically customize the part of the\n // ad's description that is a price of a product/service we advertise.\n .setType(CustomizerAttributeType.PRICE)\n .build();\n // Creates a customizer attribute operation for creating a customizer attribute.\n CustomizerAttributeOperation operation =\n CustomizerAttributeOperation.newBuilder().setCreate(customizerAttribute).build();\n\n try (CustomizerAttributeServiceClient customizerAttributeServiceClient =\n googleAdsClient.getLatestVersion().createCustomizerAttributeServiceClient()) {\n // Issues a mutate request to add the customizer attribute and prints its information.\n MutateCustomizerAttributesResponse response =\n customizerAttributeServiceClient.mutateCustomizerAttributes(\n Long.toString(customerId), ImmutableList.of(operation));\n String resourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Added a customizer with resource name '%s'.%n\", resourceName);\n return resourceName;\n }\n }\n\n\n /**\n * Links the customizer attribute to the customer by providing a value to be used in a responsive\n * search ad that will be created in a later step.\n */\n private static void linkCustomizerAttributeToCustomer(\n GoogleAdsClient googleAdsClient, long customerId, String customizerAttributeResourceName) {\n // Creates a customer customizer with the value to be used in the responsive search ad.\n CustomerCustomizer customerCustomizer =\n CustomerCustomizer.newBuilder()\n .setCustomizerAttribute(customizerAttributeResourceName)\n // Specify '100USD' as a text value. The ad customizer will dynamically replace the\n // placeholder with this value when the ad serves.\n .setValue(\n CustomizerValue.newBuilder()\n .setType(CustomizerAttributeType.PRICE)\n .setStringValue(\"100USD\")\n .build())\n .build();\n\n // Creates a customer customizer operation.\n CustomerCustomizerOperation operation =\n CustomerCustomizerOperation.newBuilder().setCreate(customerCustomizer).build();\n\n try (CustomerCustomizerServiceClient customerCustomizerServiceClient =\n googleAdsClient.getLatestVersion().createCustomerCustomizerServiceClient()) {\n // Issues a mutate request to add the customer customizer and prints its information.\n MutateCustomerCustomizersResponse response =\n customerCustomizerServiceClient.mutateCustomerCustomizers(\n Long.toString(customerId), ImmutableList.of(operation));\n System.out.printf(\n \"Added a customer customizer with resource name '%s'.%n\",\n response.getResults(0).getResourceName());\n }\n }\n\n\n /**\n * Create an AdTextAsset.\n *\n * @param text: Text for headlines and descriptions.\n * @param pinnedField: To pin a text asset so it always shows in the ad.\n */\n private static AdTextAsset createAdTextAsset(\n String text, @Nullable ServedAssetFieldType pinnedField) {\n AdTextAsset.Builder adTextAsset = AdTextAsset.newBuilder().setText(text);\n if (pinnedField != null) {\n adTextAsset.setPinnedField(pinnedField);\n }\n return adTextAsset.build();\n }\n\n /**\n * Creates an AdTextAsset with a customizer.\n *\n * @param customizerAttributeResourceName: The resource name of the customizer attribute.\n */\n private static AdTextAsset createAdTextAssetWithCustomizer(\n String customizerAttributeResourceName) {\n\n // Creates this particular description using the ad customizer. Visit\n // https://developers.google.com/google-ads/api/docs/ads/customize-responsive-search-ads#ad_customizers_in_responsive_search_ads\n // for details about the placeholder format. The ad customizer replaces the placeholder with\n // the value we previously created and linked to the customer using CustomerCustomizer.\n AdTextAsset.Builder adTextAsset =\n AdTextAsset.newBuilder()\n .setText(String.format(\"Just {CUSTOMIZER.%s:10USD}\", customizerAttributeResourceName));\n\n return adTextAsset.build();\n }\n\n /**\n * Creates the campaign budget resource.\n *\n * @param googleAdsClient: An initialized GoogleAdsClient instance.\n * @param customerId: A client customer ID.\n */\n private static String createCampaignBudget(GoogleAdsClient googleAdsClient, Long customerId) {\n // Creates the budget.\n CampaignBudget budget =\n CampaignBudget.newBuilder()\n .setName(\"Interplanetary Cruise Budget #\" + getPrintableDateTime())\n .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)\n .setAmountMicros(500000)\n .build();\n\n // Creates the operation.\n CampaignBudgetOperation operation =\n CampaignBudgetOperation.newBuilder().setCreate(budget).build();\n\n // Gets the CampaignBudgetService.\n try (CampaignBudgetServiceClient campaignBudgetServiceClient =\n googleAdsClient.getLatestVersion().createCampaignBudgetServiceClient()) {\n // Adds the campaign budget.\n MutateCampaignBudgetsResponse response =\n campaignBudgetServiceClient.mutateCampaignBudgets(\n Long.toString(customerId), ImmutableList.of(operation));\n\n // Displays the results.\n String resourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Added budget with resource name %s.\", resourceName);\n return resourceName;\n }\n }\n\n /**\n * Creates a campaign resource.\n *\n * @param googleAdsClient: An initialized GoogleAdsClient instance.\n * @param customerId: A client customer ID.\n * @param campaignBudget: A budget resource name.\n */\n private static String createCampaign(\n GoogleAdsClient googleAdsClient, Long customerId, String campaignBudget) {\n\n // Creates the campaign.\n Campaign.Builder campaignBuilder = Campaign.newBuilder();\n campaignBuilder.setName(\"Testing RSA via API #\" + getPrintableDateTime());\n campaignBuilder.setAdvertisingChannelType(AdvertisingChannelType.SEARCH);\n\n // Recommendation: Set the campaign to PAUSED when creating it to prevent the ads from\n // immediately serving. Set to ENABLED once you've added targeting and the ads are ready to\n // serve.\n campaignBuilder.setStatus(CampaignStatus.PAUSED);\n\n // Sets the bidding strategy and budget. The bidding strategy for Maximize Clicks is TargetSpend.\n // The targetSpendMicros is deprecated so don't put any value. See other bidding strategies you\n // can select in the link below.\n // https://developers.google.com/google-ads/api/reference/rpc/latest/Campaign#campaign_bidding_strategy\n campaignBuilder.setTargetSpend(TargetSpend.newBuilder().setTargetSpendMicros(0).build());\n campaignBuilder.setCampaignBudget(campaignBudget);\n\n // Sets the campaign network operations.\n campaignBuilder.setNetworkSettings(\n NetworkSettings.newBuilder()\n .setTargetGoogleSearch(true)\n .setTargetSearchNetwork(true)\n .setTargetPartnerSearchNetwork(false)\n // Enables Display Expansion on Search campaigns. For more details see:\n // https://support.google.com/google-ads/answer/7193800\n .setTargetContentNetwork(true)\n .build());\n\n // Optional: Sets the start date.\n // DateTime startTime = DateTime.now().plusDays(1);\n // campaignBuilder.setStartDate(startTime.toDate().toString());\n\n // Optional: Sets the end date.\n // DateTime endTime = startTime.plusWeeks(4);\n // campaignBuilder.setEndDate(endTime.toDate().toString());\n\n // Creates the operation.\n CampaignOperation operation =\n CampaignOperation.newBuilder().setCreate(campaignBuilder.build()).build();\n\n // Gets the CampaignService.\n try (CampaignServiceClient campaignServiceClient =\n googleAdsClient.getLatestVersion().createCampaignServiceClient()) {\n\n // Adds the campaign.\n MutateCampaignsResponse response =\n campaignServiceClient.mutateCampaigns(\n Long.toString(customerId), ImmutableList.of(operation));\n\n String resourceName = response.getResults(0).getResourceName();\n\n // Displays the result.\n System.out.printf(\"Added campaign with resource name %s\", resourceName);\n return resourceName;\n }\n }\n\n /**\n * Creates an ad group.\n *\n * @param googleAdsClient: An initialized GoogleAdsClient instance.\n * @param customerId: A client customer ID.\n * @param campaignResourceName: An campaign resource name.\n */\n private static String createAdGroup(\n GoogleAdsClient googleAdsClient, Long customerId, String campaignResourceName) {\n\n // Creates the ad group.\n AdGroup adGroup =\n AdGroup.newBuilder()\n .setName(\"Testing RSA via API \" + getPrintableDateTime())\n .setStatus(AdGroupStatus.ENABLED)\n .setCampaign(campaignResourceName)\n .setType(AdGroupType.SEARCH_STANDARD)\n .build();\n\n // If you want to set up a max CPC bid, uncomment the line below;\n // adGroup = adGroup.toBuilder().setCpcBidMicros(10000000).build();\n\n // Creates the operation.\n AdGroupOperation operation = AdGroupOperation.newBuilder().setCreate(adGroup).build();\n\n // Gets the AdGroupService.\n try (AdGroupServiceClient adGroupServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupServiceClient()) {\n // Add the ad group.\n MutateAdGroupsResponse response =\n adGroupServiceClient.mutateAdGroups(\n Long.toString(customerId), ImmutableList.of(operation));\n\n // Displays the result.\n String resourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Added ad group with resource name %s.\", resourceName);\n return resourceName;\n }\n }\n\n /**\n * Creates an ad group ad.\n *\n * @param googleAdsClient: An initialized GoogleAdsClient instance.\n * @param customerId: A client customer ID.\n * @param adGroupResourceName: An ad group resource name.\n * @param customizerAttributeName: (optional) If present, indicates the resource name of the\n * custoimizer attribute to use in one of the descriptions.\n */\n private static void createAdGroupAd(\n GoogleAdsClient googleAdsClient,\n Long customerId,\n String adGroupResourceName,\n String customizerAttributeName) {\n\n // Creates an ad group ad to hold the ad.\n AdGroupAd.Builder adGroupAdBuilder = AdGroupAd.newBuilder().setStatus(AdGroupAdStatus.ENABLED);\n adGroupAdBuilder.setAdGroup(adGroupResourceName);\n\n // Creates the ad and set responsive search ad info.\n\n // The list of possible final URLs after all cross-domain redirects for the ad.\n Ad.Builder adBuilder = Ad.newBuilder().addFinalUrls(\"https://www.example.com/\");\n\n // Sets a pinning to always choose this asset for HEADLINE_1. Pinning is optional; if no pinning\n // is set, then headlines and descriptions will be rotated and the ones that perform best will\n // be used more often.\n\n // Headline 1\n AdTextAsset pinned_headline =\n createAdTextAsset(\"Headline 1 testing\", ServedAssetFieldType.HEADLINE_1);\n\n // Headline 2 and 3\n ResponsiveSearchAdInfo.Builder responsiveSearchAdBuilder =\n ResponsiveSearchAdInfo.newBuilder()\n .addAllHeadlines(\n ImmutableList.of(\n pinned_headline,\n createAdTextAsset(\"Headline 2 testing\", null),\n createAdTextAsset(\"Headline 3 testing\", null)));\n\n // Description 1.\n AdTextAsset description1 = createAdTextAsset(\"Desc 1 testing\", null);\n\n // Creates this particular description using the ad customizer.\n AdTextAsset description2 = null;\n if (customizerAttributeName != null) {\n description2 = createAdTextAssetWithCustomizer(customizerAttributeName);\n } else {\n description2 = createAdTextAsset(\"Desc 2 testing\", null);\n }\n responsiveSearchAdBuilder.addAllDescriptions(ImmutableList.of(description1, description2));\n\n // Paths\n // First and second part of text that can be appended to the URL in the ad.\n // If you use the examples below, the ad will show https://www.example.com/all-inclusive/deals\n responsiveSearchAdBuilder.setPath1(\"all-inclusive\");\n responsiveSearchAdBuilder.setPath2(\"deals\");\n\n adBuilder.setResponsiveSearchAd(responsiveSearchAdBuilder);\n adGroupAdBuilder.setAd(adBuilder);\n\n // Creates an ad group ad operation.\n AdGroupAdOperation operation =\n AdGroupAdOperation.newBuilder().setCreate(adGroupAdBuilder).build();\n\n // Gets the AdGroupAdService.\n try (AdGroupAdServiceClient adGroupAdServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupAdServiceClient()) {\n\n // Sends a request to the server to add a responsive search ad\n MutateAdGroupAdsResponse response =\n adGroupAdServiceClient.mutateAdGroupAds(\n Long.toString(customerId), ImmutableList.of(operation));\n\n // Displays the result.\n System.out.printf(\n \"Created responsive search ad with resource name '%s'.%n\",\n response.getResults(0).getResourceName());\n }\n }\n\n /**\n * Creates keywords.\n *\n * <p>For smart bidding, BROAD is the recommended match type.\n *\n * @param googleAdsClient: An initialized GoogleAdsClient instance.\n * @param customerId: A client customer ID.\n * @param adGroupResourceName: An ad group resource name.\n */\n private static void addKeywords(\n GoogleAdsClient googleAdsClient, Long customerId, String adGroupResourceName) {\n\n // Creates keyword.\n AdGroupCriterion.Builder keyword =\n AdGroupCriterion.newBuilder()\n .setAdGroup(adGroupResourceName)\n .setStatus(AdGroupCriterionStatus.ENABLED)\n .setKeyword(\n KeywordInfo.newBuilder()\n .setText(\"example of broad match\")\n .setMatchType(KeywordMatchType.BROAD));\n\n // Uncomment the below line if you want to change this keyword to a negative target.\n // keyword.setNegative(true);\n\n // Optional repeated field\n // keyword.setFinalUrls(\"https://www.example.com\");\n\n AdGroupCriterionOperation operation =\n AdGroupCriterionOperation.newBuilder().setCreate(keyword).build();\n\n // Gets the AdGroupCriterionService.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n\n // Adds the keyword.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n\n // Displays the results.\n for (MutateAdGroupCriterionResult result : response.getResultsList()) {\n System.out.printf(\"Created keyword '%s'.%n\", result.getResourceName());\n }\n }\n }\n\n /**\n * Creates geo targets.\n *\n * @param googleAdsClient: An initialized GoogleAdsClient instance.\n * @param customerId: A client customer ID.\n * @param campaignResourceName: A campaign resource name.\n */\n private static void addGeoTargeting(\n GoogleAdsClient googleAdsClient, Long customerId, String campaignResourceName) {\n\n // Searches by location names from GeoTargetConstantService.suggestGeoTargetConstants() and\n // directly apply GeoTargetConstant.resourceName.\n SuggestGeoTargetConstantsRequest.Builder gtcRequestBuilder =\n SuggestGeoTargetConstantsRequest.newBuilder().setLocale(\"es\").setCountryCode(\"AR\");\n\n // The location names to get suggested geo target constants.\n gtcRequestBuilder.setLocationNames(\n LocationNames.newBuilder()\n .addAllNames(ImmutableList.of(\"Buenos Aires\", \"San Isidro\", \"Mar del Plata\"))\n .build());\n\n try (GeoTargetConstantServiceClient geoTargetConstantServiceClient =\n googleAdsClient.getLatestVersion().createGeoTargetConstantServiceClient()) {\n SuggestGeoTargetConstantsResponse results =\n geoTargetConstantServiceClient.suggestGeoTargetConstants(gtcRequestBuilder.build());\n\n ArrayList<CampaignCriterionOperation> operations =\n new ArrayList<CampaignCriterionOperation>();\n for (GeoTargetConstantSuggestion suggestion : results.getGeoTargetConstantSuggestionsList()) {\n System.out.printf(\n \"geoTargetConstant: %s is found in LOCALE %s with reach %s from search term %s.\",\n suggestion.getGeoTargetConstant().getResourceName(),\n suggestion.getLocale(),\n suggestion.getReach(),\n suggestion.getSearchTerm());\n\n // Creates the campaign criterion for loaction targeting.\n CampaignCriterion campaignCriterion =\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n .setLocation(\n LocationInfo.newBuilder()\n .setGeoTargetConstant(suggestion.getGeoTargetConstant().getResourceName())\n .build())\n .build();\n\n CampaignCriterionOperation operation =\n CampaignCriterionOperation.newBuilder().setCreate(campaignCriterion).build();\n operations.add(operation);\n }\n\n // Gets the CampaignCriterionService.\n try (CampaignCriterionServiceClient campaignCriterionServiceClient =\n googleAdsClient.getLatestVersion().createCampaignCriterionServiceClient()) {\n MutateCampaignCriteriaResponse response =\n campaignCriterionServiceClient.mutateCampaignCriteria(\n Long.toString(customerId), operations);\n\n // Displays the results.\n for (MutateCampaignCriterionResult result : response.getResultsList()) {\n System.out.printf(\"Added campaign criterion %s\", result.getResourceName());\n }\n }\n }\n }\n}\nAddResponsiveSearchAdFull.java\n```\n\nExample:\n```text\n// Copyright 2021 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.Gax.Util;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System;\nusing static Google.Ads.GoogleAds.V25.Enums.AdGroupTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AdGroupAdStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AdGroupCriterionStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AdGroupStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AdvertisingChannelTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.BudgetDeliveryMethodEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.CampaignStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.CustomizerAttributeTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.KeywordMatchTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Services.SuggestGeoTargetConstantsRequest.Types;\nusing Google.Ads.GoogleAds.Config;\nusing Google.Ads.GoogleAds.Extensions.Config;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// Adds a customizer attribute, links the customizer attribute to a customer, and then adds\n /// a responsive search ad with a description using the ad customizer to the specified ad group.\n /// </summary>\n public class AddResponsiveSearchAdFull : ExampleBase\n {\n /// <summary>\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// The Google Ads customizer attribute name ID.\n /// </summary>\n [Option(\"customizerAttributeName\", Required = false, HelpText =\n \"The Google Ads customizer attribute name.\", Default = CUSTOMIZER_ATTRIBUTE_NAME)]\n public string CustomizerAttributeName { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n AddResponsiveSearchAdFull codeExample =\n new AddResponsiveSearchAdFull();\n\n Console.WriteLine(codeExample.Description);\n\n codeExample.Run(\n new GoogleAdsClient(),\n options.CustomerId,\n options.CustomizerAttributeName\n );\n }\n\n // The name of the customizer attribute to be used in the ad customizer must be unique for a\n // given client account. To run this example multiple times, change this value or specify\n // its corresponding argument. Note that there is a limit for the number of enabled\n // customizer attributes in one account, so you shouldn't run this example more than\n // necessary.\n //\n // Visit the following link for more details:\n // https://developers.google.com/google-ads/api/docs/ads/customize-responsive-search-ads#rules_and_limitations\n private const string CUSTOMIZER_ATTRIBUTE_NAME = \"Price\";\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"Adds a customizer attribute, links the customizer attribute to a customer, and then \" +\n \"adds a responsive search ad with a description using the ad customizer to the \" +\n \"specified ad group.\";\n\n /// <summary>\n /// Runs the example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID.</param>\n /// <param name=\"customizerAttributeName\">The customizer attribute name.</param>\n public void Run(\n GoogleAdsClient client,\n long customerId,\n string customizerAttributeName)\n {\n string customizerAttributeResourceName = CreateCustomizerAttribute(\n client,\n customerId,\n customizerAttributeName\n );\n\n LinkCustomizerAttributeToCustomer(client, customerId, customizerAttributeResourceName);\n\n string campaignBudgetResourceName = AddCampaignBudget(client, customerId);\n\n string campaignResourceName = AddCampaign(client, customerId,\n campaignBudgetResourceName);\n\n string adGroupResourceName = AddAdGroup(client, customerId, campaignResourceName);\n\n CreateResponsiveSearchAdWithCustomization(\n client,\n customerId,\n adGroupResourceName,\n customizerAttributeName\n );\n\n AddKeywords(client, customerId, adGroupResourceName);\n\n AddGeoTargeting(client, customerId, campaignResourceName);\n }\n\n /// <summary>\n /// Creates a customizer attribute with the specified customizer attribute name.\n /// </summary>\n /// <param name=\"client\">The Google Ads API client.</param>\n /// <param name=\"customerId\">The customer ID.</param>\n /// <param name=\"customizerAttributeName\">The name of the customizer attribute.</param>\n /// <returns>The created customizer attribute resource name.</returns>\n private string CreateCustomizerAttribute(\n GoogleAdsClient client,\n long customerId,\n string customizerAttributeName)\n {\n // Creates a customizer attribute operation for creating a customizer attribute.\n CustomizerAttributeOperation operation = new CustomizerAttributeOperation() {\n // Creates a customizer attribute with the specified name.\n Create = new CustomizerAttribute() {\n Name = customizerAttributeName,\n\n // Specifies the type to be 'PRICE' so that we can dynamically customize the part of\n // the ad's description that is a price of a product/service we advertise.\n Type = CustomizerAttributeType.Price\n }\n };\n\n CustomizerAttributeServiceClient serviceClient =\n client.GetService(Services.V25.CustomizerAttributeService);\n\n // Issues a mutate request to add the customizer attribute and prints its information.\n MutateCustomizerAttributesResponse response =\n serviceClient.MutateCustomizerAttributes(\n customerId.ToString(),\n new [] { operation }.ToList()\n );\n\n string resourceName = response.Results[0].ResourceName;\n\n Console.WriteLine($\"Added a customizer attribute with resource name '{resourceName}'.\");\n\n return resourceName;\n }\n\n /// <summary>\n /// Links the customizer attribute to the customer by providing a value to be used in a\n /// responsive search ad that will be created in a later step.\n /// </summary>\n /// <param name=\"client\">The Google Ads API client.</param>\n /// <param name=\"customerId\">The customer ID.</param>\n /// <param name=\"customizerAttributeResourceName\">The resource name of the customizer\n /// attribute.</param>\n private void LinkCustomizerAttributeToCustomer(\n GoogleAdsClient client,\n long customerId,\n string customizerAttributeResourceName)\n {\n // Creates a customer customizer operation.\n CustomerCustomizerOperation operation = new CustomerCustomizerOperation() {\n // Creates a customer customizer with the value to be used in the responsive search\n // ad.\n Create = new CustomerCustomizer() {\n CustomizerAttribute = customizerAttributeResourceName,\n\n Value = new CustomizerValue() {\n Type = CustomizerAttributeType.Price,\n\n // Specify '100USD' as a text value. The ad customizer will dynamically\n // replace the placeholder with this value when the ad serves.\n StringValue = \"100USD\"\n }\n }\n };\n\n CustomerCustomizerServiceClient serviceClient =\n client.GetService(Services.V25.CustomerCustomizerService);\n\n // Issues a mutate request to add the customer customizer and prints its information.\n MutateCustomerCustomizersResponse response =\n serviceClient.MutateCustomerCustomizers(\n customerId.ToString(),\n new [] { operation }.ToList()\n );\n\n string resourceName = response.Results[0].ResourceName;\n\n Console.WriteLine($\"Added a customer customizer with resource name '{resourceName}'.\");\n }\n\n /// <summary>\n /// Adds a campaign budget.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <returns>The campaign budget resource name.</returns>\n private static string AddCampaignBudget(GoogleAdsClient client, long customerId)\n {\n // Get the CampaignBudgetService.\n CampaignBudgetServiceClient campaignBudgetService =\n client.GetService(Services.V25.CampaignBudgetService);\n\n // Create the budget.\n CampaignBudget campaignBudget = new CampaignBudget()\n {\n Name = \"Interplanetary Cruise Budget #\" + ExampleUtilities.GetRandomString(),\n AmountMicros = 3_000_000,\n DeliveryMethod = BudgetDeliveryMethod.Standard\n };\n\n // Create the operation.\n CampaignBudgetOperation operation = new CampaignBudgetOperation()\n {\n Create = campaignBudget\n };\n\n // Add the campaign budget.\n MutateCampaignBudgetsResponse response =\n campaignBudgetService.MutateCampaignBudgets(customerId.ToString(),\n new CampaignBudgetOperation[] { operation });\n // Display the result.\n\n string budgetResourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Added budget with resource name '{budgetResourceName}'.\");\n return budgetResourceName;\n }\n\n /// <summary>\n /// Adds a campaign.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"budgetResourceName\">The campaign budget resource name.</param>\n /// <returns>The campaign resource name.</returns>\n private static string AddCampaign(GoogleAdsClient client, long customerId,\n string budgetResourceName)\n {\n // Get the CampaignService.\n CampaignServiceClient campaignService = client.GetService(Services.V25.CampaignService);\n\n // Create the campaign.\n Campaign campaign = new Campaign()\n {\n Name = \"Testing RSA via API #\" + ExampleUtilities.GetRandomString(),\n AdvertisingChannelType = AdvertisingChannelType.Search,\n Status = CampaignStatus.Paused,\n ManualCpc = new ManualCpc(),\n NetworkSettings = new Campaign.Types.NetworkSettings()\n {\n TargetGoogleSearch = true,\n TargetSearchNetwork = true,\n TargetPartnerSearchNetwork = false,\n // Enable Display Expansion on Search campaigns. For more details see:\n // https://support.google.com/google-ads/answer/7193800\n TargetContentNetwork = true\n },\n CampaignBudget = budgetResourceName,\n\n StartDateTime = DateTime.Now.AddDays(1).ToString(\"yyyyMMdd 00:00:00\"),\n EndDateTime = DateTime.Now.AddDays(30).ToString(\"yyyyMMdd 23:59:59\")\n };\n\n // Create the operation.\n CampaignOperation operation = new CampaignOperation()\n {\n Create = campaign\n };\n\n // Add the campaign.\n MutateCampaignsResponse response =\n campaignService.MutateCampaigns(customerId.ToString(),\n new CampaignOperation[] { operation });\n\n // Displays the result.\n string campaignResourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Added campaign with resource name '{campaignResourceName}'.\");\n return campaignResourceName;\n }\n\n /// <summary>Adds an ad group.</summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"campaignResourceName\">The campaign resource name.</param>\n /// <returns>The ad group resource name.</returns>\n private static string AddAdGroup(GoogleAdsClient client, long customerId,\n string campaignResourceName)\n {\n // Get the AdGroupService.\n AdGroupServiceClient adGroupService = client.GetService(Services.V25.AdGroupService);\n\n // Create the ad group.\n AdGroup adGroup = new AdGroup()\n {\n Name = \"Testing RSA via API #\" + ExampleUtilities.GetRandomString(),\n Campaign = campaignResourceName,\n Type = AdGroupType.SearchStandard,\n Status = AdGroupStatus.Enabled,\n\n // If you want to set up a max CPC bid, uncomment the line below.\n // CpcBidMicros = 50_000\n };\n\n // Create the operation.\n AdGroupOperation operation = new AdGroupOperation()\n {\n Create = adGroup\n };\n\n // Add the ad group.\n MutateAdGroupsResponse response =\n adGroupService.MutateAdGroups(customerId.ToString(),\n new AdGroupOperation[] { operation });\n\n // Display the results.\n string adGroupResourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Added ad group with resource name '{adGroupResourceName}'.\");\n\n return adGroupResourceName;\n }\n\n /// <summary>\n /// Creates a responsive search ad that uses the specified customizer attribute.\n /// </summary>\n /// <param name=\"client\">The Google Ads API client.</param>\n /// <param name=\"customerId\">The customer ID.</param>\n /// <param name=\"adGroupResourceName\">The resource name of the ad group.</param>\n /// <param name=\"customizerAttributeName\">The name of the customizer attribute.</param>\n private void CreateResponsiveSearchAdWithCustomization(\n GoogleAdsClient client,\n long customerId,\n string adGroupResourceName,\n string customizerAttributeName)\n {\n // Creates an ad group ad operation.\n AdGroupAdOperation operation = new AdGroupAdOperation() {\n\n // Creates an ad group ad to hold the ad.\n Create = new AdGroupAd() {\n\n AdGroup = adGroupResourceName,\n Status = AdGroupAdStatus.Enabled,\n\n // Creates an ad and sets responsive search ad info.\n Ad = new Ad() {\n\n ResponsiveSearchAd = new ResponsiveSearchAdInfo() {\n Headlines =\n {\n // Sets a pinning to always choose this asset for HEADLINE_1.\n // Pinning is optional; if no pinning is set, then headlines and\n // descriptions will be rotated and the ones that perform best will\n //be used more often.\n new AdTextAsset() { Text = \"Cruise to Mars\" },\n new AdTextAsset() { Text = \"Best Space Cruise Line\" },\n new AdTextAsset() { Text = \"Experience the Stars\" }\n },\n\n Descriptions =\n {\n new AdTextAsset() { Text = \"Buy your tickets now\" },\n\n // Creates this particular description using the ad customizer. For\n // details about the placeholder format, visit the following:\n // https://developers.google.com/google-ads/api/docs/ads/customize-responsive-search-ads#ad_customizers_in_responsive_search_ads\n //\n // The ad customizer replaces the placeholder with the value we\n // previously created and linked to the customer using\n // `CustomerCustomizer`.\n new AdTextAsset()\n {\n Text = $\"Just {{CUSTOMIZER.{customizerAttributeName}:10USD}}\"\n }\n },\n\n Path1 = \"all-inclusive\",\n Path2 = \"deals\"\n },\n\n FinalUrls = { \"http://www.example.com\" }\n }\n }\n };\n\n // Issues a mutate request to add the ad group ad and prints its information.\n AdGroupAdServiceClient serviceClient = client.GetService(Services.V25.AdGroupAdService);\n\n MutateAdGroupAdsResponse response = serviceClient.MutateAdGroupAds(\n customerId.ToString(),\n new [] { operation }.ToList()\n );\n\n string resourceName = response.Results[0].ResourceName;\n\n Console.WriteLine($\"Created responsive search ad with resource name '{resourceName}'.\");\n }\n\n /// <summary>\n /// Creates 3 keyword match types: EXACT, PHRASE, and BROAD.\n /// EXACT: ads may show on searches that ARE the same meaning as your keyword.\n /// PHRASE: ads may show on searches that INCLUDE the meaning of your keyword.\n /// BROAD: ads may show on searches that RELATE to your keyword.\n /// For smart bidding, BROAD is the recommended one.\n /// </summary>\n /// <param name=\"client\">The Google Ads API client.</param>\n /// <param name=\"customerId\">The customer ID.</param>\n /// <param name=\"adGroupResourceName\">The resource name of the ad group.</param>\n private void AddKeywords(GoogleAdsClient client, long customerId,\n string adGroupResourceName)\n {\n // Get the AdGroupCriterionService.\n AdGroupCriterionServiceClient adGroupCriterionService =\n client.GetService(Services.V25.AdGroupCriterionService);\n\n List<AdGroupCriterionOperation> operations = new List<AdGroupCriterionOperation>();\n\n AdGroupCriterionOperation exactMatchOperation = new AdGroupCriterionOperation()\n {\n Create = new AdGroupCriterion()\n {\n AdGroup = adGroupResourceName,\n Status = AdGroupCriterionStatus.Enabled,\n Keyword = new KeywordInfo()\n {\n Text = \"example of exact match\",\n MatchType = KeywordMatchType.Exact\n },\n // Uncomment the line below if you want to change this keyword to a negative\n // target.\n // Negative = true\n }\n };\n // Optional repeated field\n // exactMatchOperation.Create.FinalUrls.Add(\"https://www.example.com\");\n operations.Add(exactMatchOperation);\n\n AdGroupCriterionOperation phraseMatchOperation = new AdGroupCriterionOperation()\n {\n Create = new AdGroupCriterion()\n {\n AdGroup = adGroupResourceName,\n Status = AdGroupCriterionStatus.Enabled,\n Keyword = new KeywordInfo()\n {\n Text = \"example of phrase match\",\n MatchType = KeywordMatchType.Phrase\n },\n // Uncomment the line below if you want to change this keyword to a negative\n // target.\n // Negative = true\n }\n };\n // Optional repeated field\n // phraseMatchOperation.Create.FinalUrls.Add(\"https://www.example.com\");\n operations.Add(phraseMatchOperation);\n\n AdGroupCriterionOperation broadMatchOperation = new AdGroupCriterionOperation()\n {\n Create = new AdGroupCriterion()\n {\n AdGroup = adGroupResourceName,\n Status = AdGroupCriterionStatus.Enabled,\n Keyword = new KeywordInfo()\n {\n Text = \"example of broad match\",\n MatchType = KeywordMatchType.Broad\n },\n // Uncomment the line below if you want to change this keyword to a negative\n // target.\n // Negative = true\n }\n };\n // Optional repeated field\n // broadMatchOperation.Create.FinalUrls.Add(\"https://www.example.com\");\n operations.Add(broadMatchOperation);\n\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionService.MutateAdGroupCriteria(customerId.ToString(), operations);\n\n // Display the results.\n foreach (MutateAdGroupCriterionResult newAdGroupCriterion in response.Results)\n {\n Console.WriteLine(\"Keyword with resource name '{0}' was created.\",\n newAdGroupCriterion.ResourceName);\n }\n }\n\n /// <summary>\n /// Creates geo targets.\n /// </summary>\n /// <param name=\"client\">The Google Ads API client.</param>\n /// <param name=\"customerId\">The customer ID.</param>\n /// <param name=\"campaignResourceName\">The resource name of the campaign.</param>\n private void AddGeoTargeting(GoogleAdsClient client, long customerId,\n string campaignResourceName)\n {\n GeoTargetConstantServiceClient geoTargetConstantService =\n client.GetService(Services.V25.GeoTargetConstantService);\n\n SuggestGeoTargetConstantsRequest suggestGeoTargetConstantsRequest =\n new SuggestGeoTargetConstantsRequest()\n {\n // Locale uses the ISO 639-1 format.\n Locale = \"es\",\n // A list of available country codes can be referenced here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n CountryCode = \"AR\",\n LocationNames = new LocationNames()\n {\n Names = {\"Buenos aires\", \"San Isidro\", \"Mar del Plata\"}\n }\n };\n\n SuggestGeoTargetConstantsResponse suggestGeoTargetConstantsResponse =\n geoTargetConstantService.SuggestGeoTargetConstants(\n suggestGeoTargetConstantsRequest);\n\n List<CampaignCriterionOperation> operations = new List<CampaignCriterionOperation>();\n foreach (GeoTargetConstantSuggestion suggestion in\n suggestGeoTargetConstantsResponse.GeoTargetConstantSuggestions)\n {\n Console.WriteLine($\"Geo target constant: {suggestion.GeoTargetConstant.Name} was \" +\n $\"found in locale ({suggestion.Locale}) with reach ({suggestion.Reach}) from \" +\n $\"search term ({suggestion.SearchTerm})\");\n\n CampaignCriterionOperation operation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n Location = new LocationInfo()\n {\n GeoTargetConstant = suggestion.GeoTargetConstant.ResourceName\n }\n }\n };\n operations.Add(operation);\n }\n\n CampaignCriterionServiceClient campaignCriterionService =\n client.GetService(Services.V25.CampaignCriterionService);\n\n MutateCampaignCriteriaResponse mutateCampaignCriteriaResponse =\n campaignCriterionService.MutateCampaignCriteria(customerId.ToString(), operations);\n\n foreach (MutateCampaignCriterionResult result in mutateCampaignCriteriaResponse.Results)\n {\n Console.WriteLine($\"Added campaign criterion {result.ResourceName}\");\n }\n }\n }\n}\nAddResponsiveSearchAdFull.cs\n```\n\nExample:\n```text\nThis example is not yet available in PHP; you can take a look at the other languages.\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2023 Google LLC\n#\n# 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.\n\"\"\"\nThis example shows how to create a complete Responsive Search ad.\n\nIncludes creation of: budget, campaign, ad group, ad group ad,\nkeywords, and geo targeting.\n\nMore details on Responsive Search ads can be found here:\nhttps://support.google.com/google-ads/answer/7684791\n\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import List, Optional\nimport uuid\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.common.types.ad_asset import AdTextAsset\nfrom google.ads.googleads.v24.enums.types.served_asset_field_type import (\n ServedAssetFieldTypeEnum,\n)\nfrom google.ads.googleads.v24.resources.types.ad_group import AdGroup\nfrom google.ads.googleads.v24.resources.types.ad_group_ad import AdGroupAd\nfrom google.ads.googleads.v24.resources.types.ad_group_criterion import (\n AdGroupCriterion,\n)\nfrom google.ads.googleads.v24.resources.types.campaign import Campaign\nfrom google.ads.googleads.v24.resources.types.campaign_budget import (\n CampaignBudget,\n)\nfrom google.ads.googleads.v24.resources.types.campaign_criterion import (\n CampaignCriterion,\n)\nfrom google.ads.googleads.v24.resources.types.customer_customizer import (\n CustomerCustomizer,\n)\nfrom google.ads.googleads.v24.resources.types.customizer_attribute import (\n CustomizerAttribute,\n)\nfrom google.ads.googleads.v24.services.services.ad_group_ad_service import (\n AdGroupAdServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.ad_group_criterion_service import (\n AdGroupCriterionServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.ad_group_service import (\n AdGroupServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.campaign_budget_service import (\n CampaignBudgetServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.campaign_criterion_service import (\n CampaignCriterionServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.campaign_service import (\n CampaignServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.customer_customizer_service import (\n CustomerCustomizerServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.customizer_attribute_service import (\n CustomizerAttributeServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.geo_target_constant_service import (\n GeoTargetConstantServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.geo_target_constant_service import (\n SuggestGeoTargetConstantsRequest,\n SuggestGeoTargetConstantsResponse,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_ad_service import (\n AdGroupAdOperation,\n MutateAdGroupAdsResponse,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_criterion_service import (\n AdGroupCriterionOperation,\n MutateAdGroupCriteriaResponse,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_service import (\n AdGroupOperation,\n MutateAdGroupsResponse,\n)\nfrom google.ads.googleads.v24.services.types.campaign_budget_service import (\n CampaignBudgetOperation,\n MutateCampaignBudgetsResponse,\n)\nfrom google.ads.googleads.v24.services.types.campaign_criterion_service import (\n CampaignCriterionOperation,\n MutateCampaignCriteriaResponse,\n)\nfrom google.ads.googleads.v24.services.types.campaign_service import (\n CampaignOperation,\n MutateCampaignsResponse,\n)\nfrom google.ads.googleads.v24.services.types.customer_customizer_service import (\n CustomerCustomizerOperation,\n MutateCustomerCustomizersResponse,\n)\nfrom google.ads.googleads.v24.services.types.customizer_attribute_service import (\n CustomizerAttributeOperation,\n MutateCustomizerAttributesResponse,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\n# Keywords from user.\nKEYWORD_TEXT_EXACT = \"example of exact match\"\nKEYWORD_TEXT_PHRASE = \"example of phrase match\"\nKEYWORD_TEXT_BROAD = \"example of broad match\"\n\n# Geo targeting from user.\nGEO_LOCATION_1 = \"Buenos aires\"\nGEO_LOCATION_2 = \"San Isidro\"\nGEO_LOCATION_3 = \"Mar del Plata\"\n\n# LOCALE and COUNTRY_CODE are used for geo targeting.\n# LOCALE is using ISO 639-1 format. If an invalid LOCALE is given,\n# 'es' is used by default.\nLOCALE = \"es\"\n\n# A list of country codes can be referenced here:\n# https://developers.google.com/google-ads/api/reference/data/geotargets\nCOUNTRY_CODE = \"AR\"\n\n\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n customizer_attribute_name: Optional[str] = None,\n) -> None:\n \"\"\"\n The main method that creates all necessary entities for the example.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n customizer_attribute_name: The name of the customizer attribute to be\n created\n \"\"\"\n if customizer_attribute_name:\n customizer_attribute_resource_name: str = create_customizer_attribute(\n client, customer_id, customizer_attribute_name\n )\n\n link_customizer_attribute_to_customer(\n client, customer_id, customizer_attribute_resource_name\n )\n\n # Create a budget, which can be shared by multiple campaigns.\n campaign_budget: str = create_campaign_budget(client, customer_id)\n\n campaign_resource_name: str = create_campaign(\n client, customer_id, campaign_budget\n )\n\n ad_group_resource_name: str = create_ad_group(\n client, customer_id, campaign_resource_name\n )\n\n create_ad_group_ad(\n client, customer_id, ad_group_resource_name, customizer_attribute_name\n )\n\n add_keywords(client, customer_id, ad_group_resource_name)\n\n add_geo_targeting(client, customer_id, campaign_resource_name)\n\n\ndef create_customizer_attribute(\n client: GoogleAdsClient, customer_id: str, customizer_attribute_name: str\n) -> str:\n \"\"\"Creates a customizer attribute with the given customizer attribute name.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n customizer_attribute_name: the name for the customizer attribute.\n\n Returns:\n A resource name for a customizer attribute.\n \"\"\"\n # Create a customizer attribute operation for creating a customizer\n # attribute.\n operation: CustomizerAttributeOperation = client.get_type(\n \"CustomizerAttributeOperation\"\n )\n # Create a customizer attribute with the specified name.\n customizer_attribute: CustomizerAttribute = operation.create\n customizer_attribute.name = customizer_attribute_name\n # Specify the type to be 'PRICE' so that we can dynamically customize the\n # part of the ad's description that is a price of a product/service we\n # advertise.\n customizer_attribute.type_ = client.enums.CustomizerAttributeTypeEnum.PRICE\n\n # Issue a mutate request to add the customizer attribute and prints its\n # information.\n customizer_attribute_service: CustomizerAttributeServiceClient = (\n client.get_service(\"CustomizerAttributeService\")\n )\n response: MutateCustomizerAttributesResponse = (\n customizer_attribute_service.mutate_customizer_attributes(\n customer_id=customer_id, operations=[operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n\n print(f\"Added a customizer attribute with resource name: '{resource_name}'\")\n\n return resource_name\n\n\ndef link_customizer_attribute_to_customer(\n client: GoogleAdsClient,\n customer_id: str,\n customizer_attribute_resource_name: str,\n) -> None:\n \"\"\"Links the customizer attribute to the customer.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n customizer_attribute_resource_name: a resource name for customizer\n attribute.\n \"\"\"\n # Create a customer customizer operation.\n operation: CustomerCustomizerOperation = client.get_type(\n \"CustomerCustomizerOperation\"\n )\n # Create a customer customizer with the value to be used in the responsive\n # search ad.\n customer_customizer: CustomerCustomizer = operation.create\n customer_customizer.customizer_attribute = (\n customizer_attribute_resource_name\n )\n customer_customizer.value.type_ = (\n client.enums.CustomizerAttributeTypeEnum.PRICE\n )\n # The ad customizer will dynamically replace the placeholder with this value\n # when the ad serves.\n customer_customizer.value.string_value = \"100USD\"\n\n customer_customizer_service: CustomerCustomizerServiceClient = (\n client.get_service(\"CustomerCustomizerService\")\n )\n # Issue a mutate request to create the customer customizer and prints its\n # information.\n response: MutateCustomerCustomizersResponse = (\n customer_customizer_service.mutate_customer_customizers(\n customer_id=customer_id, operations=[operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n\n print(\n f\"Added a customer customizer to the customer with resource name: '{resource_name}'\"\n )\n\n\ndef create_ad_text_asset(\n client: GoogleAdsClient,\n text: str,\n pinned_field: Optional[\n ServedAssetFieldTypeEnum.ServedAssetFieldType\n ] = None,\n) -> AdTextAsset:\n \"\"\"Create an AdTextAsset.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n text: text for headlines and descriptions.\n pinned_field: to pin a text asset so it always shows in the ad.\n\n Returns:\n An AdTextAsset.\n \"\"\"\n ad_text_asset: AdTextAsset = client.get_type(\"AdTextAsset\")\n ad_text_asset.text = text\n if pinned_field:\n ad_text_asset.pinned_field = pinned_field\n return ad_text_asset\n\n\ndef create_ad_text_asset_with_customizer(\n client: GoogleAdsClient, customizer_attribute_resource_name: str\n) -> AdTextAsset:\n \"\"\"Create an AdTextAsset.\n Args:\n client: an initialized GoogleAdsClient instance.\n customizer_attribute_resource_name: The resource name of the customizer attribute.\n\n Returns:\n An AdTextAsset.\n \"\"\"\n ad_text_asset: AdTextAsset = client.get_type(\"AdTextAsset\")\n\n # Create this particular description using the ad customizer. Visit\n # https://developers.google.com/google-ads/api/docs/ads/customize-responsive-search-ads#ad_customizers_in_responsive_search_ads\n # for details about the placeholder format. The ad customizer replaces the\n # placeholder with the value we previously created and linked to the\n # customer using CustomerCustomizer.\n ad_text_asset.text = (\n f\"Just {{CUSTOMIZER.{customizer_attribute_resource_name}:10USD}}\"\n )\n\n return ad_text_asset\n\n\ndef create_campaign_budget(client: GoogleAdsClient, customer_id: str) -> str:\n \"\"\"Creates campaign budget resource.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n Campaign budget resource name.\n \"\"\"\n # Create a budget, which can be shared by multiple campaigns.\n campaign_budget_service: CampaignBudgetServiceClient = client.get_service(\n \"CampaignBudgetService\"\n )\n campaign_budget_operation: CampaignBudgetOperation = client.get_type(\n \"CampaignBudgetOperation\"\n )\n campaign_budget: CampaignBudget = campaign_budget_operation.create\n campaign_budget.name = f\"Campaign budget {uuid.uuid4()}\"\n campaign_budget.delivery_method = (\n client.enums.BudgetDeliveryMethodEnum.STANDARD\n )\n campaign_budget.amount_micros = 500000\n\n # Add budget.\n campaign_budget_response: MutateCampaignBudgetsResponse = (\n campaign_budget_service.mutate_campaign_budgets(\n customer_id=customer_id, operations=[campaign_budget_operation]\n )\n )\n\n return campaign_budget_response.results[0].resource_name\n\n\ndef create_campaign(\n client: GoogleAdsClient, customer_id: str, campaign_budget: str\n) -> str:\n \"\"\"Creates campaign resource.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n campaign_budget: a budget resource name.\n\n Returns:\n Campaign resource name.\n \"\"\"\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n campaign_operation: CampaignOperation = client.get_type(\"CampaignOperation\")\n campaign: Campaign = campaign_operation.create\n campaign.name = f\"Testing RSA via API {uuid.uuid4()}\"\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.SEARCH\n )\n\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n\n # Set the bidding strategy and budget.\n # The bidding strategy for Maximize Clicks is TargetSpend.\n # The target_spend_micros is deprecated so don't put any value.\n # See other bidding strategies you can select in the link below.\n # https://developers.google.com/google-ads/api/reference/rpc/latest/Campaign#campaign_bidding_strategy\n campaign.target_spend.target_spend_micros = 0\n campaign.campaign_budget = campaign_budget\n\n # Set the campaign network options.\n campaign.network_settings.target_google_search = True\n campaign.network_settings.target_search_network = True\n campaign.network_settings.target_partner_search_network = False\n # Enable Display Expansion on Search campaigns. For more details see:\n # https://support.google.com/google-ads/answer/7193800\n campaign.network_settings.target_content_network = True\n\n # # Optional: Set the start date.\n # start_time = datetime.date.today() + datetime.timedelta(days=1)\n # campaign.start_date_time = datetime.date.strftime(start_time, \"%Y%m%d 00:00:00\")\n\n # # Optional: Set the end date.\n # end_time = start_time + datetime.timedelta(weeks=4)\n # campaign.end_date_time = datetime.date.strftime(end_time, \"%Y%m%d 23:59:59\")\n\n # Add the campaign.\n campaign_response: MutateCampaignsResponse = (\n campaign_service.mutate_campaigns(\n customer_id=customer_id, operations=[campaign_operation]\n )\n )\n resource_name: str = campaign_response.results[0].resource_name\n print(f\"Created campaign {resource_name}.\")\n return resource_name\n\n\ndef create_ad_group(\n client: GoogleAdsClient,\n customer_id: str,\n campaign_resource_name: str,\n) -> str:\n \"\"\"Creates ad group.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n campaign_resource_name: a campaign resource name.\n\n Returns:\n Ad group ID.\n \"\"\"\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n\n ad_group_operation: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group: AdGroup = ad_group_operation.create\n ad_group.name = f\"Testing RSA via API {uuid.uuid4()}\"\n ad_group.status = client.enums.AdGroupStatusEnum.ENABLED\n ad_group.campaign = campaign_resource_name\n ad_group.type_ = client.enums.AdGroupTypeEnum.SEARCH_STANDARD\n\n # If you want to set up a max CPC bid uncomment line below.\n # ad_group.cpc_bid_micros = 10000000\n\n # Add the ad group.\n ad_group_response: MutateAdGroupsResponse = (\n ad_group_service.mutate_ad_groups(\n customer_id=customer_id, operations=[ad_group_operation]\n )\n )\n ad_group_resource_name: str = ad_group_response.results[0].resource_name\n print(f\"Created ad group {ad_group_resource_name}.\")\n return ad_group_resource_name\n\n\ndef create_ad_group_ad(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_resource_name: str,\n customizer_attribute_name: Optional[str],\n) -> None:\n \"\"\"Creates ad group ad.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n ad_group_resource_name: an ad group resource name.\n customizer_attribute_name: (optional) If present, indicates the resource\n name of the customizer attribute to use in one of the descriptions\n\n Returns:\n None.\n \"\"\"\n ad_group_ad_service: AdGroupAdServiceClient = client.get_service(\n \"AdGroupAdService\"\n )\n\n ad_group_ad_operation: AdGroupAdOperation = client.get_type(\n \"AdGroupAdOperation\"\n )\n ad_group_ad: AdGroupAd = ad_group_ad_operation.create\n ad_group_ad.status = client.enums.AdGroupAdStatusEnum.ENABLED\n ad_group_ad.ad_group = ad_group_resource_name\n\n # Set responsive search ad info.\n # https://developers.google.com/google-ads/api/reference/rpc/latest/ResponsiveSearchAdInfo\n\n # The list of possible final URLs after all cross-domain redirects for the ad.\n ad_group_ad.ad.final_urls.append(\"https://www.example.com/\")\n\n # Set a pinning to always choose this asset for HEADLINE_1. Pinning is\n # optional; if no pinning is set, then headlines and descriptions will be\n # rotated and the ones that perform best will be used more often.\n\n # Headline 1\n served_asset_enum: ServedAssetFieldTypeEnum = (\n client.enums.ServedAssetFieldTypeEnum\n )\n pinned_headline: AdTextAsset = create_ad_text_asset(\n client, \"Headline 1 testing\", served_asset_enum\n )\n\n # Headline 2 and 3\n ad_group_ad.ad.responsive_search_ad.headlines.extend(\n [\n pinned_headline,\n create_ad_text_asset(client, \"Headline 2 testing\"),\n create_ad_text_asset(client, \"Headline 3 testing\"),\n ]\n )\n\n # Description 1 and 2\n description_1: AdTextAsset = create_ad_text_asset(client, \"Desc 1 testing\")\n description_2: Optional[AdTextAsset] = None\n\n if customizer_attribute_name:\n description_2 = create_ad_text_asset_with_customizer(\n client, customizer_attribute_name\n )\n else:\n description_2 = create_ad_text_asset(client, \"Desc 2 testing\")\n\n ad_group_ad.ad.responsive_search_ad.descriptions.extend(\n [description_1, description_2]\n )\n\n # Paths\n # First and second part of text that can be appended to the URL in the ad.\n # If you use the examples below, the ad will show\n # https://www.example.com/all-inclusive/deals\n ad_group_ad.ad.responsive_search_ad.path1 = \"all-inclusive\"\n ad_group_ad.ad.responsive_search_ad.path2 = \"deals\"\n\n # Send a request to the server to add a responsive search ad.\n ad_group_ad_response: MutateAdGroupAdsResponse = (\n ad_group_ad_service.mutate_ad_group_ads(\n customer_id=customer_id, operations=[ad_group_ad_operation]\n )\n )\n\n for result in ad_group_ad_response.results:\n print(\n f\"Created responsive search ad with resource name \"\n f'\"{result.resource_name}\".'\n )\n\n\ndef add_keywords(\n client: GoogleAdsClient, customer_id: str, ad_group_resource_name: str\n) -> None:\n \"\"\"Creates keywords.\n\n Creates 3 keyword match types: EXACT, PHRASE, and BROAD.\n\n EXACT: ads may show on searches that ARE the same meaning as your keyword.\n PHRASE: ads may show on searches that INCLUDE the meaning of your keyword.\n BROAD: ads may show on searches that RELATE to your keyword.\n For smart bidding, BROAD is the recommended one.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n ad_group_resource_name: an ad group resource name.\n \"\"\"\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n\n operations: List[AdGroupCriterionOperation] = []\n # Create keyword 1.\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n ad_group_criterion: AdGroupCriterion = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = ad_group_resource_name\n ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED\n ad_group_criterion.keyword.text = KEYWORD_TEXT_EXACT\n ad_group_criterion.keyword.match_type = (\n client.enums.KeywordMatchTypeEnum.EXACT\n )\n\n # Uncomment the below line if you want to change this keyword to a negative target.\n # ad_group_criterion.negative = True\n\n # Optional repeated field\n # ad_group_criterion.final_urls.append('https://www.example.com')\n\n # Add operation\n operations.append(ad_group_criterion_operation)\n\n # Create keyword 2.\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n ad_group_criterion: AdGroupCriterion = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = ad_group_resource_name\n ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED\n ad_group_criterion.keyword.text = KEYWORD_TEXT_PHRASE\n ad_group_criterion.keyword.match_type = (\n client.enums.KeywordMatchTypeEnum.PHRASE\n )\n\n # Uncomment the below line if you want to change this keyword to a negative target.\n # ad_group_criterion.negative = True\n\n # Optional repeated field\n # ad_group_criterion.final_urls.append('https://www.example.com')\n\n # Add operation\n operations.append(ad_group_criterion_operation)\n\n # Create keyword 3.\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n ad_group_criterion: AdGroupCriterion = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = ad_group_resource_name\n ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED\n ad_group_criterion.keyword.text = KEYWORD_TEXT_BROAD\n ad_group_criterion.keyword.match_type = (\n client.enums.KeywordMatchTypeEnum.BROAD\n )\n\n # Uncomment the below line if you want to change this keyword to a negative target.\n # ad_group_criterion.negative = True\n\n # Optional repeated field\n # ad_group_criterion.final_urls.append('https://www.example.com')\n\n # Add operation\n operations.append(ad_group_criterion_operation)\n\n # Add keywords\n ad_group_criterion_response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id,\n operations=operations,\n )\n )\n for result in ad_group_criterion_response.results:\n print(\"Created keyword \" f\"{result.resource_name}.\")\n\n\ndef add_geo_targeting(\n client: GoogleAdsClient, customer_id: str, campaign_resource_name: str\n) -> None:\n \"\"\"Creates geo targets.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n campaign_resource_name: an campaign resource name.\n\n Returns:\n None.\n \"\"\"\n geo_target_constant_service: GeoTargetConstantServiceClient = (\n client.get_service(\"GeoTargetConstantService\")\n )\n\n # Search by location names from\n # GeoTargetConstantService.suggest_geo_target_constants() and directly\n # apply GeoTargetConstant.resource_name.\n gtc_request: SuggestGeoTargetConstantsRequest = client.get_type(\n \"SuggestGeoTargetConstantsRequest\"\n )\n gtc_request.locale = LOCALE\n gtc_request.country_code = COUNTRY_CODE\n\n # The location names to get suggested geo target constants.\n gtc_request.location_names.names.extend(\n [GEO_LOCATION_1, GEO_LOCATION_2, GEO_LOCATION_3]\n )\n\n results: SuggestGeoTargetConstantsResponse = (\n geo_target_constant_service.suggest_geo_target_constants(gtc_request)\n )\n\n operations: List[CampaignCriterionOperation] = []\n for suggestion in results.geo_target_constant_suggestions:\n print(\n \"geo_target_constant: \"\n f\"{suggestion.geo_target_constant.resource_name} \"\n f\"is found in LOCALE ({suggestion.locale}) \"\n f\"with reach ({suggestion.reach}) \"\n f\"from search term ({suggestion.search_term}).\"\n )\n # Create the campaign criterion for location targeting.\n campaign_criterion_operation: CampaignCriterionOperation = (\n client.get_type(\"CampaignCriterionOperation\")\n )\n campaign_criterion: CampaignCriterion = (\n campaign_criterion_operation.create\n )\n campaign_criterion.campaign = campaign_resource_name\n campaign_criterion.location.geo_target_constant = (\n suggestion.geo_target_constant.resource_name\n )\n operations.append(campaign_criterion_operation)\n\n campaign_criterion_service: CampaignCriterionServiceClient = (\n client.get_service(\"CampaignCriterionService\")\n )\n campaign_criterion_response: MutateCampaignCriteriaResponse = (\n campaign_criterion_service.mutate_campaign_criteria(\n customer_id=customer_id, operations=[*operations]\n )\n )\n\n for result in campaign_criterion_response.results:\n print(f'Added campaign criterion \"{result.resource_name}\".')\n\n\nif __name__ == \"__main__\":\n parser: argparse.ArgumentParser = argparse.ArgumentParser(\n description=(\"Creates a Responsive Search Ad for specified customer.\")\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n\n # The name of the customizer attribute used in the ad customizer, which\n # must be unique for a given customer account. To run this example multiple\n # times, specify a unique value as a command line argument. Note that there is\n # a limit for the number of enabled customizer attributes in one account\n # For more details visit:\n # https://developers.google.com/google-ads/api/docs/ads/customize-responsive-search-ads#rules_and_limitations\n parser.add_argument(\n \"-n\",\n \"--customizer_attribute_name\",\n type=str,\n default=None,\n help=(\n \"The name of the customizer attribute to be created. The name must \"\n \"be unique across a client account, so be sure not to use \"\n \"the same value more than once.\"\n ),\n )\n\n args: argparse.Namespace = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(\n googleads_client,\n args.customer_id,\n args.customizer_attribute_name,\n )\n except GoogleAdsException as ex:\n print(\n f'Request with ID \"{ex.request_id}\" failed with status '\n f'\"{ex.error.code().name}\" and includes the following errors:'\n )\n for error in ex.failure.errors:\n print(f'Error with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nadd_responsive_search_ad_full.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2025 Google LLC\n#\n# 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.\n#\n# This example shows how to create a complete Responsive Search ad.\n#\n# Includes creation of: budget, campaign, ad group, ad group ad,\n# keywords, and geo targeting.\n#\n# More details on Responsive Search ads can be found here:\n# https://support.google.com/google-ads/answer/7684791\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\nrequire 'date'\n\n# The name of the customizer attribute to be used in the ad customizer must be unique for a\n# given client account. To run this example multiple times, change this value or specify\n# its corresponding argument. Note that there is a limit for the number of enabled\n# customizer attributes in one account, so you shouldn't run this example more than\n# necessary.\n#\n# Visit the following link for more details:\n# https://developers.google.com/google-ads/api/docs/ads/customize-responsive-search-ads#rules_and_limitations\n\nDEFAULT_CUSTOMIZER_ATTRIBUTE_NAME = \"Price\"\n\n# Keywords from user.\nKEYWORD_TEXT_EXACT = \"example of exact match\"\nKEYWORD_TEXT_PHRASE = \"example of phrase match\"\nKEYWORD_TEXT_BROAD = \"example of broad match\"\n\n# Geo targeting from user.\nGEO_LOCATION_1 = \"Buenos aires\"\nGEO_LOCATION_2 = \"San Isidro\"\nGEO_LOCATION_3 = \"Mar del Plata\"\n\n# LOCALE and COUNTRY_CODE are used for geo targeting.\n# LOCALE is using ISO 639-1 format. If an invalid LOCALE is given,\n# 'es' is used by default.\nLOCALE = \"es\"\n\n# A list of country codes can be referenced here:\n# https://developers.google.com/google-ads/api/reference/data/geotargets\nCOUNTRY_CODE = \"AR\"\n\n# Creates a customizer attribute with the given customizer attribute name.\n#\n# Args:\n# client: An initialized GoogleAdsClient instance.\n# customer_id: A client customer ID.\n# customizer_attribute_name: The name for the customizer attribute.\n#\n# Returns:\n# A resource name for a customizer attribute.\ndef create_customizer_attribute(client, customer_id, customizer_attribute_name)\n # Create a customizer attribute operation for creating a customizer attribute.\n operation = client.operation.create_resource.customizer_attribute do |ca|\n ca.name = customizer_attribute_name\n # Specify the type to be 'PRICE' so that we can dynamically customize the\n # part of the ad's description that is a price of a product/service we\n # advertise.\n ca.type = :PRICE\n end\n\n # Issue a mutate request to add the customizer attribute and print its\n # information.\n customizer_attribute_service = client.service.customizer_attribute\n response = customizer_attribute_service.mutate_customizer_attributes(\n customer_id: customer_id,\n operations: [operation],\n )\n resource_name = response.results.first.resource_name\n\n puts \"Added a customizer attribute with resource name: '#{resource_name}'\"\n\n resource_name\nend\n\n# Links the customizer attribute to the customer.\n#\n# Args:\n# client: An initialized GoogleAdsClient instance.\n# customer_id: A client customer ID.\n# customizer_attribute_resource_name: A resource name for a customizer attribute.\ndef link_customizer_attribute_to_customer(client, customer_id, customizer_attribute_resource_name)\n # Create a customer customizer operation.\n operation = client.operation.create_customer_customizer do |cc|\n cc.customizer_attribute = customizer_attribute_resource_name\n cc.value = client.resource.customizer_value do |val|\n val.type = :PRICE\n # The ad customizer will dynamically replace the placeholder with this value\n # when the ad serves.\n val.string_value = \"100USD\"\n end\n end\n\n customer_customizer_service = client.service.customer_customizer\n # Issue a mutate request to create the customer customizer and prints its\n # information.\n response = customer_customizer_service.mutate_customer_customizers(\n customer_id: customer_id,\n operations: [operation],\n )\n resource_name = response.results.first.resource_name\n\n puts \"Added a customer customizer to the customer with resource name: '#{resource_name}'\"\nend\n\n# Helper function to create an AdTextAsset.\n#\n# Args:\n# client: An initialized GoogleAdsClient instance.\n# text: Text for headlines and descriptions.\n# pinned_field: To pin a text asset so it always shows in the ad.\n#\n# Returns:\n# An AdTextAsset.\ndef create_ad_text_asset(client, text, pinned_field = nil)\n client.resource.ad_text_asset do |ad_text_asset|\n ad_text_asset.text = text\n unless pinned_field.nil?\n ad_text_asset.pinned_field = pinned_field\n end\n end\nend\n\n# Helper function to create an AdTextAsset with a customizer.\n#\n# Args:\n# client: An initialized GoogleAdsClient instance.\n# customizer_attribute_resource_name: The resource name of the customizer attribute.\n#\n# Returns:\n# An AdTextAsset.\ndef create_ad_text_asset_with_customizer(client, customizer_attribute_resource_name)\n client.resource.ad_text_asset do |ad_text_asset|\n # Create this particular description using the ad customizer. Visit\n # https://developers.google.com/google-ads/api/docs/ads/customize-responsive-search-ads#ad_customizers_in_responsive_search_ads\n # for details about the placeholder format. The ad customizer replaces the\n # placeholder with the value we previously created and linked to the\n # customer using CustomerCustomizer.\n ad_text_asset.text = \"Just {CUSTOMIZER.#{customizer_attribute_resource_name}:10USD}\"\n end\nend\n\n# Creates a campaign budget resource.\n#\n# Args:\n# client: An initialized GoogleAdsClient instance.\n# customer_id: A client customer ID.\n#\n# Returns:\n# Campaign budget resource name.\ndef create_campaign_budget(client, customer_id)\n # Create a budget, which can be shared by multiple campaigns.\n campaign_budget_service = client.service.campaign_budget\n operation = client.operation.create_resource.campaign_budget do |cb|\n cb.name = \"Campaign budget \\#{SecureRandom.uuid}\"\n cb.delivery_method = :STANDARD\n cb.amount_micros = 500_000 # 500,000\n end\n\n # Add budget.\n response = campaign_budget_service.mutate_campaign_budgets(\n customer_id: customer_id,\n operations: [operation],\n )\n resource_name = response.results.first.resource_name\n puts \"Created campaign budget #{resource_name}\"\n resource_name\nend\n\n# Creates a campaign resource.\n#\n# Args:\n# client: An initialized GoogleAdsClient instance.\n# customer_id: A client customer ID.\n# campaign_budget_resource_name: A budget resource name.\n#\n# Returns:\n# Campaign resource name.\ndef create_campaign(client, customer_id, campaign_budget_resource_name)\n campaign_service = client.service.campaign\n operation = client.operation.create_campaign do |campaign|\n campaign.name = \"Testing RSA via API \\#{SecureRandom.uuid}\"\n campaign.advertising_channel_type = :SEARCH\n\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n campaign.status = :PAUSED\n\n # Set the bidding strategy and budget.\n # The bidding strategy for Maximize Clicks is TargetSpend.\n # The target_spend_micros is deprecated so don't put any value.\n # See other bidding strategies you can select in the link below.\n # https://developers.google.com/google-ads/api/reference/rpc/latest/Campaign#campaign_bidding_strategy\n campaign.bidding_strategy_type = :TARGET_SPEND\n campaign.target_spend = client.resource.target_spend do |ts|\n ts.target_spend_micros = 0\n end\n\n campaign.campaign_budget = campaign_budget_resource_name\n\n # Set the campaign network options.\n campaign.network_settings = client.resource.network_settings do |ns|\n ns.target_google_search = true\n ns.target_search_network = true\n ns.target_partner_search_network = false\n # Enable Display Expansion on Search campaigns. For more details see:\n # https://support.google.com/google-ads/answer/7193800\n ns.target_content_network = true\n end\n\n # Optional: Set the start date.\n #c.start_date_time = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d %H:%M:%S')\n\n # Optional: Set the end date.\n #c.end_date_time = DateTime.parse((Date.today.next_year).to_s).strftime('%Y%m%d %H:%M:%S')\n end\n\n # Add the campaign.\n response = campaign_service.mutate_campaigns(\n customer_id: customer_id,\n operations: [operation],\n )\n resource_name = response.results.first.resource_name\n puts \"Created campaign #{resource_name}.\"\n resource_name\nend\n\n# Creates an ad group.\n#\n# Args:\n# client: An initialized GoogleAdsClient instance.\n# customer_id: A client customer ID.\n# campaign_resource_name: A campaign resource name.\n# customizer_attribute_resource_name: (optional) If present, indicates the resource\n# name of the customizer attribute to use in one of the descriptions.\n#\n# Returns:\n# Ad group resource name.\ndef create_ad_group(client, customer_id, campaign_resource_name)\n ad_group_service = client.service.ad_group\n\n operation = client.operation.create_ad_group do |ag|\n ag.name = \"Testing RSA via API \\#{SecureRandom.uuid}\"\n ag.status = :ENABLED\n ag.campaign = campaign_resource_name\n ag.type = :SEARCH_STANDARD\n # If you want to set up a max CPC bid uncomment line below.\n # ag.cpc_bid_micros = 1_000_000 # 1,000,000\n end\n\n # Add the ad group.\n response = ad_group_service.mutate_ad_groups(\n customer_id: customer_id,\n operations: [operation],\n )\n ad_group_resource_name = response.results.first.resource_name\n puts \"Created ad group #{ad_group_resource_name}.\"\n ad_group_resource_name\nend\n\n# Creates an ad group ad (Responsive Search Ad).\n#\n# Args:\n# client: An initialized GoogleAdsClient instance.\n# customer_id: A client customer ID.\n# ad_group_resource_name: An ad group resource name.\n#\n# Returns:\n# Ad group ad resource name.\ndef create_ad_group_ad(client, customer_id, ad_group_resource_name, customizer_attribute_resource_name=nil)\n ad_group_ad_service = client.service.ad_group_ad\n\n operation = client.operation.create_ad_group_ad do |aga|\n aga.status = :ENABLED\n aga.ad_group = ad_group_resource_name\n\n # Set responsive search ad info.\n # https://developers.google.com/google-ads/api/reference/rpc/latest/ResponsiveSearchAdInfo\n aga.ad = client.resource.ad do |ad|\n ad.final_urls << \"https://www.example.com/\"\n\n # Headline 1 (pinned)\n pinned_headline = create_ad_text_asset(\n client,\n \"Headline 1 testing\",\n client.enum.served_asset_field_type.HEADLINE_1,\n )\n\n # Headlines\n ad.responsive_search_ad = client.resource.responsive_search_ad_info do |rsa|\n rsa.headlines << pinned_headline\n rsa.headlines << create_ad_text_asset(client, \"Headline 2 testing\")\n rsa.headlines << create_ad_text_asset(client, \"Headline 3 testing\")\n\n # Descriptions\n description_1 = create_ad_text_asset(client, \"Desc 1 testing\")\n description_2 = if customizer_attribute_resource_name\n create_ad_text_asset_with_customizer(client, customizer_attribute_resource_name)\n else\n create_ad_text_asset(client, \"Desc 2 testing\")\n end\n\n rsa.descriptions << description_1\n rsa.descriptions << description_2\n\n # Paths\n rsa.path1 = \"all-inclusive\"\n rsa.path2 = \"deals\"\n end\n end\n end\n\n # Send a request to the server to add a responsive search ad.\n response = ad_group_ad_service.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [operation],\n )\n\n response.results.each do |result|\n puts \"Created responsive search ad with resource name \"#{result.resource_name}\".\"\n end\nend\n\n# Creates keywords.\n#\n# Creates 3 keyword match types: EXACT, PHRASE, and BROAD.\n#\n# Args:\n# client: An initialized GoogleAdsClient instance.\n# customer_id: A client customer ID.\n# ad_group_resource_name: An ad group resource name.\ndef add_keywords(client, customer_id, ad_group_resource_name)\n ad_group_criterion_service = client.service.ad_group_criterion\n\n keywords_to_add = [\n { text: KEYWORD_TEXT_EXACT, match_type: :EXACT },\n { text: KEYWORD_TEXT_PHRASE, match_type: :PHRASE },\n { text: KEYWORD_TEXT_BROAD, match_type: :BROAD },\n ]\n\n operations = keywords_to_add.map do |keyword_info|\n client.operation.create_ad_group_criterion do |agc|\n agc.ad_group = ad_group_resource_name\n agc.status = :ENABLED\n agc.keyword = client.resource.keyword_info do |ki|\n ki.text = keyword_info[:text]\n ki.match_type = keyword_info[:match_type]\n end\n # Optional: agc.negative = true\n # Optional: agc.final_urls << 'https://www.example.com'\n end\n end\n\n # Add keywords\n response = ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: operations,\n )\n response.results.each do |result|\n puts \"Created keyword #{result.resource_name}.\"\n end\nend\n\n# Creates geo targets.\n#\n# Args:\n# client: An initialized GoogleAdsClient instance.\n# customer_id: A client customer ID.\n# campaign_resource_name: A campaign resource name.\ndef add_geo_targeting(client, customer_id, campaign_resource_name)\n geo_target_constant_service = client.service.geo_target_constant\n\n # Search by location names from\n # GeoTargetConstantService.suggest_geo_target_constants() and directly\n # apply GeoTargetConstant.resource_name.\n gtc_request = client.request.suggest_geo_target_constants do |req|\n req.locale = LOCALE\n req.country_code = COUNTRY_CODE\n # The location names to get suggested geo target constants.\n req.location_names = client.resource.location_names do |ln|\n ln.names << GEO_LOCATION_1\n ln.names << GEO_LOCATION_2\n ln.names << GEO_LOCATION_3\n end\n end\n\n response = geo_target_constant_service.suggest_geo_target_constants(gtc_request)\n\n operations = response.geo_target_constant_suggestions.map do |suggestion|\n puts \"geo_target_constant: #{suggestion.geo_target_constant.resource_name} \"\\\n \"is found in LOCALE (#{suggestion.locale}) \"\\\n \"with reach (#{suggestion.reach}) \"\\\n \"from search term (#{suggestion.search_term}).\"\n\n # Create the campaign criterion for location targeting.\n client.operation.create_campaign_criterion do |cc|\n cc.campaign = campaign_resource_name\n cc.location = client.resource.location_info do |li|\n li.geo_target_constant = suggestion.geo_target_constant.resource_name\n end\n end\n end\n\n unless operations.empty?\n campaign_criterion_service = client.service.campaign_criterion\n response = campaign_criterion_service.mutate_campaign_criteria(\n customer_id: customer_id,\n operations: operations,\n )\n\n response.results.each do |result|\n puts \"Added campaign criterion \"#{result.resource_name}\".\"\n end\n else\n puts \"No geo target suggestions found for the given locations. Skipping campaign criteria creation.\"\n end\nend\n\n# Main function that creates all necessary entities for the example.\n#\n# Args:\n# client: An initialized GoogleAdsClient instance.\n# customer_id: A client customer ID.\n# customizer_attribute_name: The name of the customizer attribute to be created.\ndef main_function(client, customer_id, customizer_attribute_name = nil)\n customizer_attribute_resource_name = nil\n if customizer_attribute_name\n customizer_attribute_resource_name = create_customizer_attribute(\n client,\n customer_id,\n customizer_attribute_name,\n )\n\n link_customizer_attribute_to_customer(\n client,\n customer_id,\n customizer_attribute_resource_name,\n )\n end\n\n # Create a budget, which can be shared by multiple campaigns.\n campaign_budget_resource_name = create_campaign_budget(client, customer_id)\n\n campaign_resource_name = create_campaign(\n client,\n customer_id,\n campaign_budget_resource_name,\n )\n\n ad_group_resource_name = create_ad_group(\n client,\n customer_id,\n campaign_resource_name,\n )\n\n create_ad_group_ad(\n client,\n customer_id,\n ad_group_resource_name,\n customizer_attribute_resource_name, # Pass the resource name here\n )\n\n add_keywords(client, customer_id, ad_group_resource_name)\n\n add_geo_targeting(client, customer_id, campaign_resource_name)\nend\n\n# Entry point of the script\nif __FILE__ == $0\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify them here or provide them as command-line arguments.\n #\n # e.g. add_responsive_search_ad_full.rb -C YOUR_CUSTOMER_ID\n OptionParser.new do |opts|\n opts.banner = \"Usage: add_responsive_search_ad_full.rb [options]\"\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-N', '--customizer-attribute-name CUSTOMIZER-ATTRIBUTE-NAME', String, 'Customizer attribute name') do |v|\n options[:customizer_attribute_name] = v\n end\n end.parse!\n\n if options[:customizer_attribute_name].nil?\n options[:customizer_attribute_name] = DEFAULT_CUSTOMIZER_ATTRIBUTE_NAME\n end\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n begin\n main_function(\n client,\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options.fetch(:customizer_attribute_name),\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.puts \"\tError with message '#{error.message}'.\"\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.puts \"\t\tOn field: #{field_path_element.field_name}\"\n end\n end\n end\n exit 1\n rescue OptionParser::MissingArgument, KeyError\n puts \"Missing required argument: -C or --customer-id\"\n exit 1\n end\nend\nadd_responsive_search_ad_full.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2024, Google LLC\n#\n# 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# http://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.\n#\n# This example shows how to create a complete Responsive Search ad.\n# Includes creation of: budget, campaign, ad group, ad group ad, keywords,\n# and geo targeting. More details on Responsive Search ads can be found here:\n# https://support.google.com/google-ads/answer/7684791\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::Campaign;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignBudget;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignCriterion;\nuse Google::Ads::GoogleAds::V25::Resources::CustomizerAttribute;\nuse Google::Ads::GoogleAds::V25::Resources::CustomerCustomizer;\nuse Google::Ads::GoogleAds::V25::Resources::Ad;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroup;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroupAd;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion;\nuse Google::Ads::GoogleAds::V25::Resources::NetworkSettings;\nuse Google::Ads::GoogleAds::V25::Common::AdTextAsset;\nuse Google::Ads::GoogleAds::V25::Common::CustomizerValue;\nuse Google::Ads::GoogleAds::V25::Common::ImageDimension;\nuse Google::Ads::GoogleAds::V25::Common::KeywordInfo;\nuse Google::Ads::GoogleAds::V25::Common::LocationInfo;\nuse Google::Ads::GoogleAds::V25::Common::ResponsiveSearchAdInfo;\nuse Google::Ads::GoogleAds::V25::Common::TargetSpend;\nuse Google::Ads::GoogleAds::V25::Enums::AdvertisingChannelTypeEnum qw(SEARCH);\nuse Google::Ads::GoogleAds::V25::Enums::BudgetDeliveryMethodEnum qw(STANDARD);\nuse Google::Ads::GoogleAds::V25::Enums::CustomizerAttributeTypeEnum qw(PRICE);\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupAdStatusEnum;\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupCriterionStatusEnum;\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupStatusEnum;\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupTypeEnum qw(SEARCH_STANDARD);\nuse Google::Ads::GoogleAds::V25::Enums::AssetTypeEnum qw(IMAGE);\nuse Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum qw(PAUSED);\nuse Google::Ads::GoogleAds::V25::Enums::KeywordMatchTypeEnum\n qw(BROAD EXACT PHRASE);\nuse Google::Ads::GoogleAds::V25::Enums::MimeTypeEnum qw(IMAGE_PNG);\nuse Google::Ads::GoogleAds::V25::Enums::ServedAssetFieldTypeEnum qw(HEADLINE_1);\nuse Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation;\nuse Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation;\nuse Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation;\nuse Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CustomizerAttributeService::CustomizerAttributeOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CustomerCustomizerService::CustomerCustomizerOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::GeoTargetConstantService::LocationNames;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\n\n# Keywords from the user.\nuse constant KEYWORD_TEXT_EXACT => \"example of exact match\";\nuse constant KEYWORD_TEXT_PHRASE => \"example of phrase match\";\nuse constant KEYWORD_TEXT_BROAD => \"example of broad match\";\n\n# Geo targeting from the user.\nuse constant GEO_LOCATION_1 => \"Buenos Aires\";\nuse constant GEO_LOCATION_2 => \"San Isidro\";\nuse constant GEO_LOCATION_3 => \"Mar del Plata\";\n\n# LOCALE and COUNTRY_CODE are used for geo targeting.\n# LOCALE is using ISO 639-1 format. If an invalid LOCALE is given,\n# 'es' is used by default.\nuse constant LOCALE => \"es\";\n# A list of country codes can be referenced here:\n# https://developers.google.com/google-ads/api/reference/data/geotargets\nuse constant COUNTRY_CODE => \"AR\";\n\nuse constant IMAGE_URL => \"https://gaagl.page.link/bjYi\";\n\nsub add_responsive_search_ad_full {\n my ($api_client, $customer_id, $customizer_attribute_name) = @_;\n\n # If a customizer attribute name is provided, create the customizer\n # attribute and link it to the customer.\n # For more information on customizer attributes, visit:\n # https://developers.google.com/google-ads/api/docs/ads/customize-responsive-search-ads\n if (defined $customizer_attribute_name) {\n my $customizer_attribute_resource_name =\n create_customizer_attribute($api_client, $customer_id,\n $customizer_attribute_name);\n\n link_customizer_attribute_to_customer($api_client, $customer_id,\n $customizer_attribute_resource_name);\n }\n\n # Create a budget, which can be shared by multiple campaigns.\n my $campaign_budget = create_campaign_budget($api_client, $customer_id);\n\n # Create a search campaign.\n my $campaign_resource_name =\n create_campaign($api_client, $customer_id, $campaign_budget);\n\n # Create an empty ad group.\n my $ad_group_resource_name =\n create_ad_group($api_client, $customer_id, $campaign_resource_name);\n\n # Create a responsive search ad within the ad group we just created.\n create_ad_group_ad($api_client, $customer_id, $ad_group_resource_name,\n $customizer_attribute_name);\n\n # Create 3 keywords of match type EXACT, PHRASE, and BROAD, and add them\n # as criteria on our ad group.\n add_keywords($api_client, $customer_id, $ad_group_resource_name);\n\n # Create geo targets and add them as criteria on our campaign.\n add_geo_targeting($api_client, $customer_id, $campaign_resource_name);\n\n return 1;\n}\n\n# Creates a customizer attribute with the given customizer attribute name.\nsub create_customizer_attribute {\n my ($api_client, $customer_id, $customizer_attribute_name) = @_;\n\n my $customizer_attribute =\n Google::Ads::GoogleAds::V25::Resources::CustomizerAttribute->new({\n name => $customizer_attribute_name,\n # Specify the type to be 'PRICE' so that we can dynamically customize the part\n # of the ad's description that is a price of a product/service we advertise.\n type => PRICE\n });\n\n # Create a customizer attribute operation for creating a customizer attribute.\n my $operation =\n Google::Ads::GoogleAds::V25::Services::CustomizerAttributeService::CustomizerAttributeOperation\n ->new({\n create => $customizer_attribute\n });\n\n my $response = $api_client->CustomizerAttributeService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n my $resource_name =\n $response->{results}[0]{resourceName};\n printf \"Added a customizer attribute with resource name '%s'.\\n\",\n $resource_name;\n\n return $resource_name;\n}\n\n# Links the customizer attribute to the customer.\nsub link_customizer_attribute_to_customer {\n my ($api_client, $customer_id, $customizer_attribute_resource_name) = @_;\n\n # Create a customer customizer with the value to be used in the responsive search ad.\n my $customer_customizer =\n Google::Ads::GoogleAds::V25::Resources::CustomerCustomizer->new({\n customizerAttribute => $customizer_attribute_resource_name,\n # Specify '100USD' as a text value. The ad customizer will dynamically replace\n # the placeholder with this value when the ad serves.\n value => Google::Ads::GoogleAds::V25::Common::CustomizerValue->new({\n type => PRICE,\n stringValue => \"100USD\"\n })});\n\n # Create a customer customizer operation.\n my $operation =\n Google::Ads::GoogleAds::V25::Services::CustomerCustomizerService::CustomerCustomizerOperation\n ->new({\n create => $customer_customizer\n });\n\n # Issue a mutate request to add the customer customizer and print its information.\n my $response = $api_client->CustomerCustomizerService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n printf \"Added a customer customizer with resource name '%s'.\\n\",\n $response->{results}[0]{resourceName};\n}\n\n# Creates an AdTextAsset.\nsub create_ad_text_asset {\n my ($api_client, $text, $pinned_field) = @_;\n\n my $ad_text_asset = Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => $text\n });\n\n if (defined $pinned_field) {\n $ad_text_asset->{pinnedField} = $pinned_field;\n }\n\n return $ad_text_asset;\n}\n\n# Creates an AdTextAsset with a customizer in the text.\nsub create_ad_text_asset_with_customizer {\n my ($api_client, $customizer_attribute_resource_name) = @_;\n\n my $ad_text_asset = create_ad_text_asset($api_client,\n \"Just {CUSTOMIZER.$customizer_attribute_resource_name:10USD}\");\n\n return $ad_text_asset;\n}\n\n# Creates a campaign budget.\nsub create_campaign_budget {\n my ($api_client, $customer_id) = @_;\n\n # Create a campaign budget.\n my $campaign_budget =\n Google::Ads::GoogleAds::V25::Resources::CampaignBudget->new({\n name => \"Campaign budget \" . uniqid(),\n amountMicros => 50000000,\n deliveryMethod => STANDARD\n });\n\n # Create a campaign budget operation.\n my $campaign_budget_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation\n ->new({\n create => $campaign_budget\n });\n\n # Issue a mutate request to add the campaign budget.\n my $campaign_budgets_response = $api_client->CampaignBudgetService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_budget_operation]});\n\n my $campaign_budget_resource_name =\n $campaign_budgets_response->{results}[0]{resourceName};\n\n return $campaign_budget_resource_name;\n}\n\n# Creates a campaign.\nsub create_campaign {\n my ($api_client, $customer_id, $campaign_budget) = @_;\n\n # Create a campaign.\n my $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n name => \"Testing RSA via API \" . uniqid(),\n campaignBudget => $campaign_budget,\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n status => PAUSED,\n advertisingChannelType => SEARCH,\n # Set the bidding strategy and budget.\n # The bidding strategy for Maximize Clicks is TargetSpend.\n # The target_spend_micros is deprecated so don't put any value.\n # See other bidding strategies you can select in the link below.\n # https://developers.google.com/google-ads/api/reference/rpc/latest/Campaign#campaign_bidding_strategy\n targetSpend => Google::Ads::GoogleAds::V25::Common::TargetSpend->new(),\n # Set the campaign network options\n networkSettings =>\n Google::Ads::GoogleAds::V25::Resources::NetworkSettings->new({\n targetGoogleSearch => \"true\",\n targetSearchNetwork => \"true\",\n # Enable Display Expansion on Search campaigns. See\n # https://support.google.com/google-ads/answer/7193800 to learn more.\n targetContentNetwork => \"true\",\n targetPartnerSearchNetwork => \"false\"\n }\n ),\n # Optional: Set the start datetime. The campaign starts tomorrow.\n # startDateTime => strftime(\"%Y%m%d 00:00:00\", localtime(time + 60 * 60 * 24)),\n # Optional: Set the end datetime. The campaign runs for 30 days.\n # endDateTime => strftime(\"%Y%m%d 23:59:59\", localtime(time + 60 * 60 * 24 * 30)),\n });\n\n # Create a campaign operation.\n my $campaign_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation->\n new({\n create => $campaign\n });\n\n # Issue a mutate request to add the campaign.\n my $campaigns_response = $api_client->CampaignService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_operation]});\n\n my $resource_name =\n $campaigns_response->{results}[0]{resourceName};\n printf \"Created App campaign with resource name: '%s'.\\n\", $resource_name;\n\n return $resource_name;\n}\n\n# Creates an ad group.\nsub create_ad_group {\n my ($api_client, $customer_id, $campaign_resource_name) = @_;\n\n # Create an ad group, setting an optional CPC value.\n my $ad_group = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Testing RSA via API \" . uniqid(),\n status => Google::Ads::GoogleAds::V25::Enums::AdGroupStatusEnum::ENABLED,\n campaign => $campaign_resource_name,\n type => SEARCH_STANDARD,\n # If you want to set up a max CPC bid, uncomment the line below.\n # cpcBidMicros => 10000000\n });\n\n # Create an ad group operation.\n my $ad_group_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group});\n\n # Add the ad group.\n my $ad_groups_response = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_operation]});\n\n my $ad_group_resource_name = $ad_groups_response->{results}[0]{resourceName};\n printf \"Created ad group '%s'.\\n\", $ad_group_resource_name;\n return $ad_group_resource_name;\n}\n\n# Creates an ad group ad.\nsub create_ad_group_ad {\n my ($api_client, $customer_id, $ad_group_resource_name,\n $customizer_attribute_name)\n = @_;\n\n # Set a pinning to always choose this asset for HEADLINE_1. Pinning is optional; if no\n # pinning is set, then headlines and descriptions will be rotated and the ones that perform\n # best will be used more often.\n my $pinned_headline =\n create_ad_text_asset($api_client, \"Headline 1 testing\", HEADLINE_1);\n\n my $ad_group_ad = Google::Ads::GoogleAds::V25::Resources::AdGroupAd->new({\n adGroup => $ad_group_resource_name,\n status =>\n Google::Ads::GoogleAds::V25::Enums::AdGroupAdStatusEnum::ENABLED,\n ad => Google::Ads::GoogleAds::V25::Resources::Ad->new({\n # Set responsive search ad info.\n # https://developers.google.com/google-ads/api/reference/rpc/latest/ResponsiveSearchAdInfo\n responsiveSearchAd =>\n Google::Ads::GoogleAds::V25::Common::ResponsiveSearchAdInfo->new({\n headlines => [\n $pinned_headline,\n create_ad_text_asset($api_client, \"Headline 2 testing\"),\n create_ad_text_asset($api_client, \"Headline 3 testing\"),\n ],\n descriptions => [\n create_ad_text_asset($api_client, \"Desc 1 testing\"),\n defined $customizer_attribute_name\n ? create_ad_text_asset_with_customizer($api_client,\n $customizer_attribute_name)\n : create_ad_text_asset($api_client, \"Desc 2 testing\"),\n ],\n # First and second part of text that can be appended to the URL in the ad.\n # If you use the examples below, the ad will show\n # https://www.example.com/all-inclusive/deals\n path1 => \"all-inclusive\",\n path2 => \"deals\"\n }\n ),\n finalUrls => [\"https://www.example.com\"]})});\n\n # Create an ad group ad operation.\n my $operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation\n ->new({\n create => $ad_group_ad\n });\n\n # Issue a mutate request to add the ad group ad and print its information.\n my $response = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n printf \"Created responsive search ad with resource name '%s'.\\n\",\n $response->{results}[0]{resourceName};\n}\n\n# Creates keywords of 3 keyword match types: EXACT, PHRASE, and BROAD.\n# EXACT: ads may show on searches that ARE the same meaning as your keyword.\n# PHRASE: ads may show on searches that INCLUDE the meaning of your keyword.\n# BROAD: ads may show on searches that RELATE to your keyword.\n# For smart bidding, BROAD is the recommended one.\nsub add_keywords {\n my ($api_client, $customer_id, $ad_group_resource_name) = @_;\n\n my $operations = [];\n\n # Create a hash of keyword match types to keyword text to simplify ad group\n # criteria construction.\n my $keywords = {\n EXACT() => KEYWORD_TEXT_EXACT,\n BROAD() => KEYWORD_TEXT_BROAD,\n PHRASE() => KEYWORD_TEXT_PHRASE,\n };\n\n foreach my $keyword (keys %$keywords) {\n my $keyword_info = Google::Ads::GoogleAds::V25::Common::KeywordInfo->new({\n text => $keywords->{$keyword},\n matchType => $keyword,\n });\n\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n adGroup => $ad_group_resource_name,\n status =>\n Google::Ads::GoogleAds::V25::Enums::AdGroupCriterionStatusEnum::ENABLED,\n keyword => $keyword_info,\n # Uncomment the below line if you want to change this keyword to a negative target.\n # negative => \"true\",\n\n # Optional repeated field.\n # finalUrls => [\"https://www.example.com\"],\n });\n\n my $ad_group_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({create => $ad_group_criterion});\n\n push @$operations, $ad_group_criterion_operation;\n }\n\n my $response = $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => $operations\n });\n\n foreach my $result (@{$response->{results}}) {\n printf \"Created keyword with resource name: '%s'.\\n\",\n $result->{resourceName};\n }\n}\n\n# Creates geo targets.\nsub add_geo_targeting {\n my ($api_client, $customer_id, $campaign_resource_name) = @_;\n\n my $suggest_response = $api_client->GeoTargetConstantService()->suggest({\n locale => LOCALE,\n countryCode => COUNTRY_CODE,\n locationNames =>\n Google::Ads::GoogleAds::V25::Services::GeoTargetConstantService::LocationNames\n ->new({\n names => [GEO_LOCATION_1, GEO_LOCATION_2, GEO_LOCATION_3]})});\n\n my $operations = [];\n foreach my $geo_target_constant_suggestion (\n @{$suggest_response->{geoTargetConstantSuggestions}})\n {\n printf \"geo target constant: '%s' is found in locale '%s' with reach %d\" .\n \" for the search term '%s'.\\n\",\n $geo_target_constant_suggestion->{geoTargetConstant}{resourceName},\n $geo_target_constant_suggestion->{locale},\n $geo_target_constant_suggestion->{reach},\n $geo_target_constant_suggestion->{searchTerm};\n\n # Create the campaign criterion for location targeting.\n my $campaign_criterion =\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n location => Google::Ads::GoogleAds::V25::Common::LocationInfo->new({\n geoTargetConstant =>\n $geo_target_constant_suggestion->{geoTargetConstant}{resourceName}\n }\n ),\n campaign => $campaign_resource_name\n });\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create => $campaign_criterion\n });\n }\n\n # Return if operations is empty.\n if (scalar @$operations == 0) {\n return;\n }\n my $campaign_criterion_response =\n $api_client->CampaignCriterionService()->mutate({\n customerId => $customer_id,\n operations => $operations\n });\n my $campaign_criterion_results = $campaign_criterion_response->{results};\n printf \"Added %d campaign criteria:\\n\", scalar @$campaign_criterion_results;\n\n foreach my $campaign_criterion_result (@$campaign_criterion_results) {\n printf \"\\t%s\\n\", $campaign_criterion_result->{resourceName};\n }\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\nmy $customer_id;\nmy $customizer_attribute_name;\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"customizer_attribute_name=s\" => \\$customizer_attribute_name,\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2)\n if not check_params($customer_id);\n\n# Call the example.\nadd_responsive_search_ad_full($api_client, $customer_id =~ s/-//gr,\n $customizer_attribute_name);\n\n=pod\n\n=head1 NAME\n\nadd_responsive_search_ad_full\n\n=head1 DESCRIPTION\n\nThis example shows how to create a complete Responsive Search ad.\nIncludes creation of: budget, campaign, ad group, ad group ad, keywords,\nand geo targeting. More details on Responsive Search ads can be found here:\nhttps://support.google.com/google-ads/answer/7684791\n\n=head1 SYNOPSIS\n\nadd_responsive_search_ad_full.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -customizer_attribute_name [optional] The name of the customizer attribute.\n\n=cut\nadd_responsive_search_ad_full.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.285Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":3112,"estimatedTokens":30729}}128{"id":"doc-google_ads_api_agent_skills_google_for_developer-4deaa754","source":"documentation","title":"Google Ads API agent skills | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/developer-toolkit/agent-skills","text":"Example:\n```text\nnpx skills add google/skills --agent=antigravity\n```\n\nExample:\n```text\nclaude plugin marketplace add google/skills\nclaude plugin install google-ads@skills\n```\n\nExample:\n```text\nnpx skills add google/skills\n```\n\nExample:\n```text\nnpx skills update --all\n```\n\nExample:\n```text\nclaude plugin update google-ads@skills\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.302Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":27,"estimatedTokens":87}}129{"id":"doc-handle_api_errors_google_ads_api_google_for_deve-159d0a3a","source":"documentation","title":"Handle API errors | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/get-started/handle-errors","text":"Example:\n```text\n{\n \"code\": 3,\n \"message\": \"Request contains an invalid argument.\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.ads.googleads.v25.errors.GoogleAdsFailure\",\n \"errors\": [\n {\n \"errorCode\": {\n \"requestError\": \"REQUIRED_FIELD_MISSING\"\n },\n \"message\": \"Required field is missing\",\n \"location\": {\n \"fieldPathElements\": [\n {\n \"fieldName\": \"ad_group\",\n \"index\": 0\n },\n {\n \"fieldName\": \"name\"\n }\n ]\n }\n }\n ],\n \"requestId\": \"unique_request_id_12345\"\n }\n ]\n}\n```\n\nExample:\n```text\ntry {\n // Make an API call.\n ...\n} catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n}\n```\n\nExample:\n```text\ntry\n{\n // Make an API call.\n ...\n}\ncatch (GoogleAdsException e)\n{\n Console.WriteLine($\"Request with ID '{e.RequestId}' has failed.\");\n Console.WriteLine(\"Google Ads failure details:\");\n\n foreach (GoogleAdsError error in e.Failure.Errors)\n {\n Console.WriteLine($\"{error.ErrorCode}: {error.Message}\");\n }\n}\n```\n\nExample:\n```text\ntry {\n // Make an API call.\n ...\n} catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n}\n```\n\nExample:\n```text\ntry:\n # Make an API call.\n ...\nexcept GoogleAdsException as ex:\n print(\n f\"Request with ID '{ex.request_id}' failed with status \"\n f\"'{ex.error.code().name}' and includes the following errors:\"\n )\n for error in ex.failure.errors:\n print(f\"\\tError with message '{error.message}' and code '{error.error_code}'.\")\n```\n\nExample:\n```text\nbegin\n # Make an API call.\n ...\nrescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n puts \"API call failed with request ID: #{e.request_id}\"\n e.failure.errors.each do |error|\n puts \"\\t#{error.error_code}: #{error.message}\"\n end\nend\n```\n\nExample:\n```text\n# Try sending a mutate request to add the ad group ad.\n...\nif ($response->isa(\"Google::Ads::GoogleAds::GoogleAdsException\")) {\n printf \"Google Ads failure details:\\n\";\n foreach my $error (@{$response->get_google_ads_failure()->{errors}}) {\n printf \"\\t%s: %s\\n\", [keys %{$error->{errorCode}}]->[0], $error->{message};\n }\n}\n```\n\nExample:\n```text\nusing Google.Ads.GoogleAds.Util;\n...\n\n// Detailed logs.\nTraceUtilities.Configure(TraceUtilities.DETAILED_REQUEST_LOGS_SOURCE,\n \"/path/to/your/logs/details.log\", System.Diagnostics.SourceLevels.All);\n\n// Summary logs.\nTraceUtilities.Configure(TraceUtilities.SUMMARY_REQUEST_LOGS_SOURCE,\n \"/path/to/your/logs/summary.log\", System.Diagnostics.SourceLevels.All);\n```\n\nExample:\n```text\n[LOGGING]\n; Optional logging settings.\nlogFilePath = \"path/to/your/file.log\"\nlogLevel = \"NOTICE\"\n```\n\nExample:\n```text\nGoogle::Ads::GoogleAds::Logging::GoogleAdsLogger::enable_all_logging();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.305Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":163,"estimatedTokens":976}}130{"id":"doc-install_and_use_the_google_ads_api_developer_ass-dcdc7228","source":"documentation","title":"Install and use the Google Ads API Developer Assistant | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/developer-toolkit/ai-assistant","text":"Example:\n```text\ncd <full path>/google-ads-api-developer-assistant\n```\n\nExample:\n```text\n./install.sh\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.307Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":30}}131{"id":"doc-google_ads_mcp_server_developer_integration_guid-2134c137","source":"documentation","title":"Google Ads MCP server: Developer integration guide | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/developer-toolkit/mcp-server","text":"Example:\n```text\n{\n \"mcpServers\": {\n \"google-ads-mcp\": {\n \"command\": \"pipx\",\n \"args\": [\n \"run\",\n \"--spec\",\n \"git+https://github.com/googleads/google-ads-mcp.git\",\n \"google-ads-mcp\"\n ],\n \"env\": {\n \"GOOGLE_PROJECT_ID\": \"YOUR_PROJECT_ID\",\n \"GOOGLE_ADS_DEVELOPER_TOKEN\": \"YOUR_DEVELOPER_TOKEN\"\n }\n }\n }\n}\n```\n\nExample:\n```text\ngcloud config set project YOUR_PROJECT_ID\n```\n\nExample:\n```text\ngcloud artifacts repositories create mcp-servers --repository-format=docker --location=us-central1\n```\n\nExample:\n```text\ngcloud builds submit --tag us-central1-docker.pkg.dev/YOUR_PROJECT_ID/mcp-servers/google-ads-mcp:latest .\n```\n\nExample:\n```text\ngcloud run deploy google-ads-mcp \\\n --image us-central1-docker.pkg.dev/YOUR_PROJECT_ID/mcp-servers/google-ads-mcp:latest \\\n --platform managed \\\n --region us-central1 \\\n --allow-unauthenticated \\\n --set-env-vars=\"GOOGLE_PROJECT_ID=YOUR_PROJECT_ID,GOOGLE_ADS_DEVELOPER_TOKEN=YOUR_DEVELOPER_TOKEN,GOOGLE_ADS_MCP_OAUTH_CLIENT_ID=YOUR_CLIENT_ID,GOOGLE_ADS_MCP_OAUTH_CLIENT_SECRET=YOUR_CLIENT_SECRET,GOOGLE_ADS_MCP_BASE_URL=YOUR_BASE_URL,FASTMCP_HOST=0.0.0.0\"\n```\n\nExample:\n```text\n{\n \"mcpServers\": {\n \"google-ads-mcp\": {\n \"httpUrl\": \"https://your-cloud-run-url.a.run.app/mcp\"\n }\n }\n}\n```\n\nExample:\n```text\nWhat can the google-ads-mcp server do?\n```\n\nExample:\n```text\nWhat customers do I have access to?\n```\n\nExample:\n```text\nHow many active campaigns do I have?\nHow is my campaign performance this week?\nGive me a report of the top spending campaigns split by device category over the\nlast 7 days for account 1234567890\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.308Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":76,"estimatedTokens":416}}132{"id":"doc-quick_start_google_ads_api_google_for_developers-30d0926b","source":"documentation","title":"Quick start | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/get-started/make-first-call","text":"Example:\n```text\n:~$ gcloud version\nGoogle Cloud SDK 492.0.0\nalpha 2024.09.06\nbeta 2024.09.06\nbq 2.1.8\nbundled-python3-unix 3.11.9\ncore 2024.09.06\nenterprise-certificate-proxy 0.3.2\ngcloud-crc32c 1.0.0\ngsutil 5.30\n```\n\nExample:\n```text\n<dependency>\n <groupId>com.google.api-ads</groupId>\n <artifactId>google-ads</artifactId>\n <version>44.0.0</version>\n</dependency>\n```\n\nExample:\n```text\nimplementation 'com.google.api-ads:google-ads:44.0.0'\n```\n\nExample:\n```text\napi.googleads.serviceAccountSecretsPath=JSON_KEY_FILE_PATH\napi.googleads.developerToken=INSERT_DEVELOPER_TOKEN_HERE\napi.googleads.loginCustomerId=INSERT_LOGIN_CUSTOMER_ID_HERE\n```\n\nExample:\n```text\nGoogleAdsClient googleAdsClient = null;\ntry {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n} catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\",\n fnfe);\n System.exit(1);\n} catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n}\n```\n\nExample:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient, long customerId) {\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n String query = \"SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id\";\n // Constructs the SearchGoogleAdsStreamRequest.\n SearchGoogleAdsStreamRequest request =\n SearchGoogleAdsStreamRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setQuery(query)\n .build();\n\n // Creates and issues a search Google Ads stream request that will retrieve all campaigns.\n ServerStream<SearchGoogleAdsStreamResponse> stream =\n googleAdsServiceClient.searchStreamCallable().call(request);\n\n // Iterates through and prints all of the results in the stream response.\n for (SearchGoogleAdsStreamResponse response : stream) {\n for (GoogleAdsRow googleAdsRow : response.getResultsList()) {\n System.out.printf(\n \"Campaign with ID %d and name '%s' was found.%n\",\n googleAdsRow.getCampaign().getId(), googleAdsRow.getCampaign().getName());\n }\n }\n }\n}GetCampaigns.java\n```\n\nExample:\n```text\ndotnet add package Google.Ads.GoogleAds --version 26.1.0\n```\n\nExample:\n```text\nGoogleAdsConfig config = new GoogleAdsConfig()\n{\n DeveloperToken = \"******\",\n OAuth2Mode = OAuth2Flow.SERVICE_ACCOUNT,\n OAuth2SecretsJsonPath = \"PATH_TO_CREDENTIALS_JSON\",\n LoginCustomerId = ******\n};\nGoogleAdsClient client = new GoogleAdsClient(config);\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId)\n{\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsService = client.GetService(\n Services.V25.GoogleAdsService);\n\n // Create a query that will retrieve all campaigns.\n string query = @\"SELECT\n campaign.id,\n campaign.name,\n campaign.network_settings.target_content_network\n FROM campaign\n ORDER BY campaign.id\";\n\n try\n {\n // Issue a search request.\n googleAdsService.SearchStream(customerId.ToString(), query,\n delegate (SearchGoogleAdsStreamResponse resp)\n {\n foreach (GoogleAdsRow googleAdsRow in resp.Results)\n {\n Console.WriteLine(\"Campaign with ID {0} and name '{1}' was found.\",\n googleAdsRow.Campaign.Id, googleAdsRow.Campaign.Name);\n }\n }\n );\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}GetCampaigns.cs\n```\n\nExample:\n```text\ncomposer require googleads/google-ads-php:33.6.0\n```\n\nExample:\n```text\n[GOOGLE_ADS]\ndeveloperToken = \"INSERT_DEVELOPER_TOKEN_HERE\"\nloginCustomerId = \"INSERT_LOGIN_CUSTOMER_ID_HERE\"\n\n[OAUTH2]\njsonKeyFilePath = \"INSERT_ABSOLUTE_PATH_TO_OAUTH2_JSON_KEY_FILE_HERE\"\nscopes = \"https://www.googleapis.com/auth/adwords\"\n```\n\nExample:\n```text\n$oAuth2Credential = (new OAuth2TokenBuilder())\n ->fromFile('/path/to/google_ads_php.ini')\n ->build();\n\n$googleAdsClient = (new GoogleAdsClientBuilder())\n ->fromFile('/path/to/google_ads_php.ini')\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n```\n\nExample:\n```text\npublic static function runExample(GoogleAdsClient $googleAdsClient, int $customerId)\n{\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n // Creates a query that retrieves all campaigns.\n $query = 'SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id';\n // Issues a search stream request.\n /** @var GoogleAdsServerStreamDecorator $stream */\n $stream = $googleAdsServiceClient->searchStream(\n SearchGoogleAdsStreamRequest::build($customerId, $query)\n );\n\n // Iterates over all rows in all messages and prints the requested field values for\n // the campaign in each row.\n foreach ($stream->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n printf(\n \"Campaign with ID %d and name '%s' was found.%s\",\n $googleAdsRow->getCampaign()->getId(),\n $googleAdsRow->getCampaign()->getName(),\n PHP_EOL\n );\n }\n}GetCampaigns.php\n```\n\nExample:\n```text\npython -m pip install google-ads==31.2.0\n```\n\nExample:\n```text\ndeveloper_token: INSERT_DEVELOPER_TOKEN_HERE\nlogin_customer_id: INSERT_LOGIN_CUSTOMER_ID_HERE\njson_key_file_path: JSON_KEY_FILE_PATH_HERE\n```\n\nExample:\n```text\nfrom google.ads.googleads.client import GoogleAdsClient\nclient = GoogleAdsClient.load_from_storage(\"path/to/google-ads.yaml\")\n```\n\nExample:\n```text\nimport logging\nimport sys\n\nlogger = logging.getLogger('google.ads.googleads.client')\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient, customer_id: str) -> None:\n ga_service: GoogleAdsServiceClient = client.get_service(\"GoogleAdsService\")\n\n query: str = \"\"\"\n SELECT\n campaign.id,\n campaign.name\n FROM campaign\n ORDER BY campaign.id\"\"\"\n\n # Issues a search request using streaming.\n stream: Iterator[SearchGoogleAdsStreamResponse] = ga_service.search_stream(\n customer_id=customer_id, query=query\n )\n\n for batch in stream:\n rows: List[GoogleAdsRow] = batch.results\n for row in rows:\n print(\n f\"Campaign with ID {row.campaign.id} and name \"\n f'\"{row.campaign.name}\" was found.'\n )get_campaigns.py\n```\n\nExample:\n```text\ngem 'google-ads-googleads', '~> 43.0.0'\n```\n\nExample:\n```text\nbundle install\n```\n\nExample:\n```text\nGoogle::Ads::GoogleAds::Config.new do |c|\n c.developer_token = 'INSERT_DEVELOPER_TOKEN_HERE'\n c.login_customer_id = 'INSERT_LOGIN_CUSTOMER_ID_HERE'\n c.keyfile = 'JSON_KEY_FILE_PATH'\nend\n```\n\nExample:\n```text\nclient = Google::Ads::GoogleAds::GoogleAdsClient.new('path/to/google_ads_config.rb')\n```\n\nExample:\n```text\ndef get_campaigns(customer_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n responses = client.service.google_ads.search_stream(\n customer_id: customer_id,\n query: 'SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id',\n )\n\n responses.each do |response|\n response.results.each do |row|\n puts \"Campaign with ID #{row.campaign.id} and name '#{row.campaign.name}' was found.\"\n end\n end\nendget_campaigns.rb\n```\n\nExample:\n```text\ngit clone https://github.com/googleads/google-ads-perl.git\n```\n\nExample:\n```text\ncd google-ads-perlcpan install Module::Buildperl Build.PLperl Build installdeps\n```\n\nExample:\n```text\njsonKeyFilePath=JSON_KEY_FILE_PATH\ndeveloperToken=INSERT_DEVELOPER_TOKEN_HERE\nloginCustomerId=INSERT_LOGIN_CUSTOMER_ID_HERE\n```\n\nExample:\n```text\nmy $properties_file = \"/path/to/googleads.properties\";\n\nmy $api_client = Google::Ads::GoogleAds::Client->new({\n properties_file => $properties_file\n});\n```\n\nExample:\n```text\nsub get_campaigns {\n my ($api_client, $customer_id) = @_;\n\n # Create a search Google Ads stream request that will retrieve all campaigns.\n my $search_stream_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsStreamRequest\n ->new({\n customerId => $customer_id,\n query =>\n \"SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id\"\n });\n\n # Get the GoogleAdsService.\n my $google_ads_service = $api_client->GoogleAdsService();\n\n my $search_stream_handler =\n Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({\n service => $google_ads_service,\n request => $search_stream_request\n });\n\n # Issue a search request and process the stream response to print the requested\n # field values for the campaign in each row.\n $search_stream_handler->process_contents(\n sub {\n my $google_ads_row = shift;\n printf \"Campaign with ID %d and name '%s' was found.\\n\",\n $google_ads_row->{campaign}{id}, $google_ads_row->{campaign}{name};\n });\n\n return 1;\n}get_campaigns.pl\n```\n\nExample:\n```text\ngcloud auth login --cred-file=PATH_TO_CREDENTIALS_JSON\n```\n\nExample:\n```text\ngcloud auth \\\n print-access-token \\\n --scopes='https://www.googleapis.com/auth/adwords'\n```\n\nExample:\n```text\ncurl -i -X POST https://googleads.googleapis.com/v25/customers/CUSTOMER_ID/googleAds:searchStream \\\n -H \"Content-Type: application/json\" \\\n -H \"Authorization: Bearer ACCESS_TOKEN\" \\\n -H \"developer-token: DEVELOPER_TOKEN\" \\\n -H \"login-customer-id: LOGIN_CUSTOMER_ID\" \\\n --data-binary \"@query.json\"\n```\n\nExample:\n```text\n{\n \"query\": \"SELECT campaign.id, campaign.name, campaign.network_settings.target_content_network FROM campaign ORDER BY campaign.id\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.311Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":32,"totalLines":382,"estimatedTokens":2541}}133{"id":"doc-creating_a_shopping_ad_group_ad_google_ads_api_g-6dff6439","source":"documentation","title":"Creating a Shopping Ad Group Ad | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/shopping-ads/create-ad-group-ad","text":"Example:\n```text\nprivate String addShoppingProductAdGroupAd(\n GoogleAdsClient googleAdsClient, long customerId, String adGroupResourceName) {\n // Creates a new shopping product ad.\n Ad ad =\n Ad.newBuilder().setShoppingProductAd(ShoppingProductAdInfo.newBuilder().build()).build();\n // Creates a new ad group ad and sets the shopping product ad to it.\n AdGroupAd adGroupAd =\n AdGroupAd.newBuilder()\n // Sets the ad to the ad created above.\n .setAd(ad)\n .setStatus(AdGroupAdStatus.PAUSED)\n // Sets the ad group.\n .setAdGroup(adGroupResourceName)\n .build();\n\n // Creates an ad group ad operation.\n AdGroupAdOperation operation = AdGroupAdOperation.newBuilder().setCreate(adGroupAd).build();\n\n // Issues a mutate request to add an ad group ad.\n try (AdGroupAdServiceClient adGroupAdServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupAdServiceClient()) {\n MutateAdGroupAdResult mutateAdGroupAdResult =\n adGroupAdServiceClient\n .mutateAdGroupAds(Long.toString(customerId), Collections.singletonList(operation))\n .getResults(0);\n System.out.printf(\n \"Added a product shopping ad group ad with resource name: '%s'%n\",\n mutateAdGroupAdResult.getResourceName());\n return mutateAdGroupAdResult.getResourceName();\n }\n}\nAddShoppingProductAd.java\n```\n\nExample:\n```text\nprivate string AddProductShoppingAdGroupAd(GoogleAdsClient client, long customerId,\n string adGroupResourceName)\n{\n // Get the AdGroupAdService.\n AdGroupAdServiceClient adGroupAdService = client.GetService(\n Services.V25.AdGroupAdService);\n\n // Creates a new shopping product ad.\n Ad ad = new Ad()\n {\n ShoppingProductAd = new ShoppingProductAdInfo()\n {\n }\n };\n\n // Creates a new ad group ad and sets the shopping product ad to it.\n AdGroupAd adGroupAd = new AdGroupAd()\n {\n // Sets the ad to the ad created above.\n Ad = ad,\n\n Status = AdGroupAdStatus.Paused,\n\n // Sets the ad group.\n AdGroup = adGroupResourceName\n };\n\n // Creates an ad group ad operation.\n AdGroupAdOperation operation = new AdGroupAdOperation()\n {\n Create = adGroupAd\n };\n\n // Issues a mutate request to add an ad group ad.\n MutateAdGroupAdResult mutateAdGroupAdResult = adGroupAdService.MutateAdGroupAds(\n customerId.ToString(), new AdGroupAdOperation[] { operation }).Results[0];\n Console.WriteLine(\"Added a product shopping ad group ad with resource name: '{0}'.\",\n mutateAdGroupAdResult.ResourceName);\n return mutateAdGroupAdResult.ResourceName;\n}AddShoppingProductAd.cs\n```\n\nExample:\n```text\nprivate static function addShoppingProductAdGroupAd(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $adGroupResourceName\n) {\n // Creates a new shopping product ad.\n $ad = new Ad(['shopping_product_ad' => new ShoppingProductAdInfo()]);\n\n // Creates a new ad group ad and sets the shopping product ad to it.\n $adGroupAd = new AdGroupAd([\n 'ad' => $ad,\n 'status' => AdGroupAdStatus::PAUSED,\n // Sets the ad group.\n 'ad_group' => $adGroupResourceName\n ]);\n\n // Creates an ad group ad operation.\n $adGroupAdOperation = new AdGroupAdOperation();\n $adGroupAdOperation->setCreate($adGroupAd);\n\n // Issues a mutate request to add an ad group ad.\n $adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();\n $response = $adGroupAdServiceClient->mutateAdGroupAds(\n MutateAdGroupAdsRequest::build($customerId, [$adGroupAdOperation])\n );\n\n /** @var AdGroupAd $addedAdGroupAd */\n $addedAdGroupAd = $response->getResults()[0];\n printf(\n \"Added a shopping product ad group ad with resource name '%s'.%s\",\n $addedAdGroupAd->getResourceName(),\n PHP_EOL\n );\n}AddShoppingProductAd.php\n```\n\nExample:\n```text\ndef add_shopping_product_ad_group_ad(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_resource_name: str,\n) -> str:\n \"\"\"Creates a new shopping product ad group ad in the specified ad group.\"\"\"\n ad_group_ad_service: AdGroupAdServiceClient = client.get_service(\n \"AdGroupAdService\"\n )\n\n # Creates a new ad group ad and sets the product ad to it.\n ad_group_ad_operation: AdGroupAdOperation = client.get_type(\n \"AdGroupAdOperation\"\n )\n ad_group_ad: AdGroupAd = ad_group_ad_operation.create\n ad_group_ad.ad_group = ad_group_resource_name\n ad_group_ad.status = client.enums.AdGroupAdStatusEnum.PAUSED\n # The Ad object itself is not directly manipulated for Shopping Product Ads.\n # Instead, we copy ShoppingProductAdInfo into the ad's shopping_product_ad field.\n client.copy_from(\n ad_group_ad.ad.shopping_product_ad,\n client.get_type(\"ShoppingProductAdInfo\"),\n )\n\n # Add the ad group ad.\n ad_group_ad_response = ad_group_ad_service.mutate_ad_group_ads(\n customer_id=customer_id, operations=[ad_group_ad_operation]\n )\n\n ad_group_ad_resource_name: str = ad_group_ad_response.results[\n 0\n ].resource_name\n\n print(\n f\"Created shopping product ad group ad '{ad_group_ad_resource_name}'.\"\n )add_shopping_product_ad.py\n```\n\nExample:\n```text\ndef add_shopping_product_ad_group_ad(client, customer_id, ad_group_name)\n\n operation = client.operation.create_resource.ad_group_ad do |ad_group_ad|\n ad_group_ad.ad_group = ad_group_name\n ad_group_ad.status = :PAUSED\n ad_group_ad.ad = client.resource.ad do |ad|\n ad.shopping_product_ad = client.resource.shopping_product_ad_info\n end\n end\n\n service = client.service.ad_group_ad\n response = service.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [operation],\n )\n\n puts \"Created shopping product ad group ad \" \\\n \"#{response.results.first.resource_name}\"\nendadd_shopping_product_ad.rb\n```\n\nExample:\n```text\nsub add_shopping_product_ad_group_ad {\n my ($api_client, $customer_id, $ad_group_resource_name) = @_;\n\n # Create an ad group ad and set a shopping product ad to it.\n my $ad_group_ad = Google::Ads::GoogleAds::V25::Resources::AdGroupAd->new({\n # Set the ad group.\n adGroup => $ad_group_resource_name,\n # Set the ad to a new shopping product ad.\n ad => Google::Ads::GoogleAds::V25::Resources::Ad->new({\n shoppingProductAd =>\n Google::Ads::GoogleAds::V25::Common::ShoppingProductAdInfo->new()}\n ),\n status => Google::Ads::GoogleAds::V25::Enums::AdGroupAdStatusEnum::PAUSED\n });\n\n # Create an ad group ad operation.\n my $ad_group_ad_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation\n ->new({create => $ad_group_ad});\n\n # Add the ad group ad.\n my $ad_group_ad_resource_name = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]})->{results}[0]{resourceName};\n\n printf \"Added a product shopping ad group ad with resource name: '%s'.\\n\",\n $ad_group_ad_resource_name;\n\n return $ad_group_ad_resource_name;\n}add_shopping_product_ad.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.313Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":216,"estimatedTokens":1792}}134{"id":"doc-campaign_budget_assignment_google_ads_api_google-83652d2f","source":"documentation","title":"Campaign Budget Assignment | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/budgets/assign-budgets","text":"Example:\n```text\n// Creates the campaign.\nCampaign campaign =\n Campaign.newBuilder()\n .setName(\"Interplanetary Cruise #\" + getPrintableDateTime())\n .setAdvertisingChannelType(AdvertisingChannelType.SEARCH)\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve\n .setStatus(CampaignStatus.PAUSED)\n // Sets the bidding strategy and budget.\n .setManualCpc(ManualCpc.newBuilder().build())\n .setCampaignBudget(budgetResourceName)\n // Adds the networkSettings configured above.\n .setNetworkSettings(networkSettings)\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n // Optional: Sets the start & end dates.\n .setStartDateTime(new DateTime().plusDays(1).toString(\"yyyy-MM-dd 00:00:00\"))\n .setEndDateTime(new DateTime().plusDays(30).toString(\"yyyy-MM-dd 23:59:59\"))\n .build();AddCampaigns.java\n```\n\nExample:\n```text\n// Create the campaign.\nCampaign campaign = new Campaign()\n{\n Name = \"Interplanetary Cruise #\" + ExampleUtilities.GetRandomString(),\n AdvertisingChannelType = AdvertisingChannelType.Search,\n\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve\n Status = CampaignStatus.Paused,\n\n // Set the bidding strategy and budget.\n ManualCpc = new ManualCpc(),\n CampaignBudget = budget,\n\n // Set the campaign network options.\n NetworkSettings = new NetworkSettings\n {\n TargetGoogleSearch = true,\n TargetSearchNetwork = true,\n // Enable Display Expansion on Search campaigns. See\n // https://support.google.com/google-ads/answer/7193800 to learn more.\n TargetContentNetwork = true,\n TargetPartnerSearchNetwork = false\n },\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n\n // Optional: Set the start date.\n StartDateTime = DateTime.Now.AddDays(1).ToString(\"yyyyMMdd 00:00:00\"),\n\n // Optional: Set the end date.\n EndDateTime = DateTime.Now.AddYears(1).ToString(\"yyyyMMdd 23:59:59\"),\n};AddCampaigns.cs\n```\n\nExample:\n```text\n$campaign = new Campaign([\n 'name' => 'Interplanetary Cruise #' . Helper::getPrintableDatetime(),\n 'advertising_channel_type' => AdvertisingChannelType::SEARCH,\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve.\n 'status' => CampaignStatus::PAUSED,\n // Sets the bidding strategy and budget.\n 'manual_cpc' => new ManualCpc(),\n 'campaign_budget' => $budgetResourceName,\n // Adds the network settings configured above.\n 'network_settings' => $networkSettings,\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n // Optional: Sets the start and end dates.\n 'start_date_time' => date('Y-m-d 00:00:00', strtotime('+1 day')),\n 'end_date_time' => date('Y-m-d 23:59:59', strtotime('+1 month'))\n]);AddCampaigns.php\n```\n\nExample:\n```text\n# Create campaign.\ncampaign_operation: CampaignOperation = client.get_type(\"CampaignOperation\")\ncampaign: Campaign = campaign_operation.create\ncampaign.name = f\"Interplanetary Cruise {uuid.uuid4()}\"\ncampaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.SEARCH\n)\n\n# Recommendation: Set the campaign to PAUSED when creating it to prevent\n# the ads from immediately serving. Set to ENABLED once you've added\n# targeting and the ads are ready to serve.\ncampaign.status = client.enums.CampaignStatusEnum.PAUSED\n\n# Set the bidding strategy and budget.\ncampaign.manual_cpc = client.get_type(\"ManualCpc\")\ncampaign.campaign_budget = campaign_budget_response.results[0].resource_name\n\n# Set the campaign network options.\ncampaign.network_settings.target_google_search = True\ncampaign.network_settings.target_search_network = True\ncampaign.network_settings.target_partner_search_network = False\n# Enable Display Expansion on Search campaigns. For more details see:\n# https://support.google.com/google-ads/answer/7193800\ncampaign.network_settings.target_content_network = True\n\n# Declare whether or not this campaign serves political ads targeting the\n# EU. Valid values are:\n# CONTAINS_EU_POLITICAL_ADVERTISING\n# DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\ncampaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n)\n\n# Optional: Set the start date.\nstart_time: datetime.date = datetime.date.today() + datetime.timedelta(\n days=1\n)\ncampaign.start_date_time = datetime.date.strftime(\n start_time, _START_DATE_FORMAT\n)\n\n# Optional: Set the end date.\nend_time: datetime.date = start_time + datetime.timedelta(weeks=4)\ncampaign.end_date_time = datetime.date.strftime(end_time, _END_DATE_FORMAT)add_campaigns.py\n```\n\nExample:\n```text\n# Create campaign.\ncampaign = client.resource.campaign do |c|\n c.name = \"Interplanetary Cruise #{(Time.new.to_f * 1000).to_i}\"\n c.advertising_channel_type = :SEARCH\n\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n c.status = :PAUSED\n\n # Set the bidding strategy and budget.\n c.manual_cpc = client.resource.manual_cpc\n c.campaign_budget = return_budget.results.first.resource_name\n\n # Set the campaign network options.\n c.network_settings = client.resource.network_settings do |ns|\n ns.target_google_search = true\n ns.target_search_network = true\n # Enable Display Expansion on Search campaigns. See\n # https://support.google.com/google-ads/answer/7193800 to learn more.\n ns.target_content_network = true\n ns.target_partner_search_network = false\n end\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n # Optional: Set the start date.\n c.start_date_time = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d %H:%M:%S')\n\n # Optional: Set the end date.\n c.end_date_time = DateTime.parse((Date.today.next_year).to_s).strftime('%Y%m%d %H:%M:%S')\nendadd_campaigns.rb\n```\n\nExample:\n```text\n# Create a campaign.\nmy $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n name => \"Interplanetary Cruise #\" . uniqid(),\n advertisingChannelType => SEARCH,\n # Recommendation: Set the campaign to PAUSED when creating it to stop\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n status => PAUSED,\n # Set the bidding strategy and budget.\n manualCpc => Google::Ads::GoogleAds::V25::Common::ManualCpc->new(),\n campaignBudget => $campaign_budgets_response->{results}[0]{resourceName},\n # Set the campaign network options.\n networkSettings =>\n Google::Ads::GoogleAds::V25::Resources::NetworkSettings->new({\n targetGoogleSearch => \"true\",\n targetSearchNetwork => \"true\",\n # Enable Display Expansion on Search campaigns. See\n # https://support.google.com/google-ads/answer/7193800 to learn more.\n targetContentNetwork => \"true\",\n targetPartnerSearchNetwork => \"false\"\n }\n ),\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n # Optional: Set the start datetime. The campaign starts tomorrow.\n startDateTime =>\n strftime(\"%Y%m%d 00:00:00\", localtime(time + 60 * 60 * 24)),\n # Optional: Set the end datetime. The campaign runs for 30 days.\n endDateTime =>\n strftime(\"%Y%m%d 23:59:59\", localtime(time + 60 * 60 * 24 * 30)),\n });add_campaigns.pl\n```\n\nExample:\n```text\nSELECT campaign.id\nFROM campaign\nWHERE campaign_budget.id = campaign_budget_id\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.315Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":219,"estimatedTokens":2186}}135{"id":"doc-manage_bid_modifiers_google_ads_api_google_for_d-6af54c16","source":"documentation","title":"Manage Bid Modifiers | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/bidding/manage-bid-modifiers","text":"Example:\n```text\nprivate void runExample(\n GoogleAdsClient googleAdsClient, long customerId, long adGroupId, double bidModifier) {\n\n // Creates an ad group bid modifier for mobile devices with the specified ad group ID and\n // bid modifier value.\n AdGroupBidModifier adGroupBidModifier =\n AdGroupBidModifier.newBuilder()\n .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))\n .setBidModifier(bidModifier)\n .setDevice(DeviceInfo.newBuilder().setType(Device.MOBILE))\n .build();\n\n // Creates an ad group bid modifier operation for creating an ad group bid modifier.\n AdGroupBidModifierOperation adGroupBidModifierOperation =\n AdGroupBidModifierOperation.newBuilder().setCreate(adGroupBidModifier).build();\n\n // Issues a mutate request to add the ad group bid modifier.\n try (AdGroupBidModifierServiceClient adGroupBidModifierServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupBidModifierServiceClient()) {\n MutateAdGroupBidModifiersResponse response =\n adGroupBidModifierServiceClient.mutateAdGroupBidModifiers(\n Long.toString(customerId), ImmutableList.of(adGroupBidModifierOperation));\n\n System.out.printf(\"Added %d ad group bid modifiers:%n\", response.getResultsCount());\n for (MutateAdGroupBidModifierResult mutateAdGroupBidModifierResult :\n response.getResultsList()) {\n System.out.printf(\"\\t%s%n\", mutateAdGroupBidModifierResult.getResourceName());\n }\n }\n}AddAdGroupBidModifier.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long adGroupId,\n double bidModifierValue)\n{\n // Get the AdGroupBidModifierService.\n AdGroupBidModifierServiceClient adGroupBidModifierService =\n client.GetService(Services.V25.AdGroupBidModifierService);\n\n // Creates an ad group bid modifier for mobile devices with the specified ad group\n // ID and bid modifier value.\n AdGroupBidModifier adGroupBidModifier = new AdGroupBidModifier()\n {\n AdGroup = ResourceNames.AdGroup(customerId, adGroupId),\n BidModifier = bidModifierValue,\n Device = new DeviceInfo()\n {\n Type = Device.Mobile\n }\n };\n\n // Creates an ad group bid modifier operation for creating an ad group bid modifier.\n AdGroupBidModifierOperation adGroupBidModifierOperation =\n new AdGroupBidModifierOperation()\n {\n Create = adGroupBidModifier\n };\n\n // Send the operation in a mutate request.\n try\n {\n MutateAdGroupBidModifiersResponse response =\n adGroupBidModifierService.MutateAdGroupBidModifiers(customerId.ToString(),\n new AdGroupBidModifierOperation[] { adGroupBidModifierOperation });\n Console.WriteLine(\"Added {0} ad group bid modifiers:\", response.Results.Count);\n foreach (MutateAdGroupBidModifierResult result in response.Results)\n {\n Console.WriteLine($\"\\t{result.ResourceName}\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}AddAdGroupBidModifier.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n float $bidModifierValue\n) {\n // Creates an ad group bid modifier for mobile devices with the specified ad group ID and\n // bid modifier value.\n $adGroupBidModifier = new AdGroupBidModifier([\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'bid_modifier' => $bidModifierValue,\n 'device' => new DeviceInfo(['type' => Device::MOBILE])\n ]);\n\n // Creates an ad group bid modifier operation for creating an ad group bid modifier.\n $adGroupBidModifierOperation = new AdGroupBidModifierOperation();\n $adGroupBidModifierOperation->setCreate($adGroupBidModifier);\n\n // Issues a mutate request to add the ad group bid modifier.\n $adGroupBidModifierServiceClient = $googleAdsClient->getAdGroupBidModifierServiceClient();\n $response = $adGroupBidModifierServiceClient->mutateAdGroupBidModifiers(\n MutateAdGroupBidModifiersRequest::build($customerId, [$adGroupBidModifierOperation])\n );\n\n printf(\"Added %d ad group bid modifier:%s\", $response->getResults()->count(), PHP_EOL);\n\n foreach ($response->getResults() as $addedAdGroupBidModifier) {\n /** @var AdGroupBidModifier $addedAdGroupBidModifier */\n print $addedAdGroupBidModifier->getResourceName() . PHP_EOL;\n }\n}AddAdGroupBidModifier.php\n```\n\nExample:\n```text\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n bid_modifier_value: float,\n) -> None:\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n ad_group_bm_service: AdGroupBidModifierServiceClient = client.get_service(\n \"AdGroupBidModifierService\"\n )\n\n # Create ad group bid modifier for mobile devices with the specified ad\n # group ID and bid modifier value.\n ad_group_bid_modifier_operation: AdGroupBidModifierOperation = (\n client.get_type(\"AdGroupBidModifierOperation\")\n )\n ad_group_bid_modifier: AdGroupBidModifier = (\n ad_group_bid_modifier_operation.create\n )\n\n # Set the ad group.\n ad_group_bid_modifier.ad_group = ad_group_service.ad_group_path(\n customer_id, ad_group_id\n )\n\n # Set the bid modifier.\n ad_group_bid_modifier.bid_modifier = bid_modifier_value\n\n # Sets the device.\n device_enum: DeviceEnum = client.enums.DeviceEnum\n ad_group_bid_modifier.device.type_ = device_enum.MOBILE\n\n # Add the ad group bid modifier.\n ad_group_bm_response: MutateAdGroupBidModifiersResponse = (\n ad_group_bm_service.mutate_ad_group_bid_modifiers(\n customer_id=customer_id,\n operations=[ad_group_bid_modifier_operation],\n )\n )add_ad_group_bid_modifier.py\n```\n\nExample:\n```text\ndef add_ad_group_bid_modifier(customer_id, ad_group_id, bid_modifier_value)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Creates an ad group bid modifier for mobile devices with the specified\n # ad group ID and bid modifier value.\n ad_group_bid_modifier = client.resource.ad_group_bid_modifier do |mod|\n # Sets the ad group.\n mod.ad_group = client.path.ad_group(customer_id, ad_group_id)\n\n # Sets the Bid Modifier.\n mod.bid_modifier = bid_modifier_value\n\n # Sets the Device.\n mod.device = client.resource.device_info do |device|\n device.type = :MOBILE\n end\n end\n\n # Create the operation.\n operation = client.operation.create_resource.ad_group_bid_modifier(ad_group_bid_modifier)\n\n # Add the ad group ad.\n response = client.service.ad_group_bid_modifier.mutate_ad_group_bid_modifiers(\n customer_id: customer_id,\n operations: [operation]\n )\n\n puts \"Added #{response.results.size} ad group bid modifiers:\"\n response.results.each do |added_ad_group_bid_modifier|\n puts \"\\t#{added_ad_group_bid_modifier.resource_name}\"\n end\nendadd_ad_group_bid_modifier.rb\n```\n\nExample:\n```text\nsub add_ad_group_bid_modifier {\n my ($api_client, $customer_id, $ad_group_id, $bid_modifier_value) = @_;\n\n # Create an ad group bid modifier for mobile devices with the specified ad group ID and\n # bid modifier value.\n my $ad_group_bid_modifier =\n Google::Ads::GoogleAds::V25::Resources::AdGroupBidModifier->new({\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n bidModifier => $bid_modifier_value,\n device => Google::Ads::GoogleAds::V25::Common::DeviceInfo->new({\n type => MOBILE\n })});\n\n # Create an ad group bid modifier operation.\n my $ad_group_bid_modifier_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupBidModifierService::AdGroupBidModifierOperation\n ->new({\n create => $ad_group_bid_modifier\n });\n\n # Add the ad group bid modifier.\n my $ad_group_bid_modifiers_response =\n $api_client->AdGroupBidModifierService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_bid_modifier_operation]});\n\n printf \"Created ad group bid modifier '%s'.\\n\",\n $ad_group_bid_modifiers_response->{results}[0]{resourceName};\n\n return 1;\n}add_ad_group_bid_modifier.pl\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n ad_group.id,\n ad_group_bid_modifier.bid_modifier,\n ad_group_bid_modifier.criterion_id\nFROM ad_group_bid_modifier\nWHERE ad_group.id = ad_group_id\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.317Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":252,"estimatedTokens":2197}}136{"id":"doc-create_data_exclusions_google_ads_api_google_for-15fce69e","source":"documentation","title":"Create data exclusions | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/bidding/data-exclusions","text":"Example:\n```text\nBiddingDataExclusion DataExclusion =\n BiddingDataExclusion.newBuilder()\n // A unique name is required for every data exclusion.\n .setName(\"Data exclusion #\" + getPrintableDateTime())\n // The CHANNEL scope applies the data exclusion to all campaigns of specific\n // advertising channel types. In this example, the exclusion will only apply to\n // Search campaigns. Use the CAMPAIGN scope to instead limit the scope to specific\n // campaigns.\n .setScope(SeasonalityEventScope.CHANNEL)\n .addAdvertisingChannelTypes(AdvertisingChannelType.SEARCH)\n // If setting scope CAMPAIGN, add individual campaign resource name(s) according to\n // the commented out line below.\n // .addCampaigns(\"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\")\n .setStartDateTime(startDateTime)\n .setEndDateTime(endDateTime)\n .build();\n\nBiddingDataExclusionOperation operation =\n BiddingDataExclusionOperation.newBuilder().setCreate(DataExclusion).build();\n\nMutateBiddingDataExclusionsResponse response =\n DataExclusionServiceClient.mutateBiddingDataExclusions(\n customerId.toString(), ImmutableList.of(operation));\nSystem.out.printf(\n \"Added data exclusion with resource name: %s%n\",\n response.getResults(0).getResourceName());AddBiddingDataExclusion.java\n```\n\nExample:\n```text\nBiddingDataExclusion dataExclusion = new BiddingDataExclusion()\n{\n // A unique name is required for every data exclusion.\n Name = \"Data exclusion #\" + ExampleUtilities.GetRandomString(),\n // The CHANNEL scope applies the data exclusion to all campaigns of specific\n // advertising channel types. In this example, the the exclusion will only apply to\n // Search campaigns. Use the CAMPAIGN scope to instead limit the scope to specific\n // campaigns.\n Scope = SeasonalityEventScope.Channel,\n AdvertisingChannelTypes = { AdvertisingChannelType.Search },\n // The date range should be less than 14 days.\n StartDateTime = startDateTime,\n EndDateTime = endDateTime,\n};\nBiddingDataExclusionOperation operation = new BiddingDataExclusionOperation()\n{\n Create = dataExclusion\n};\n\ntry\n{\n MutateBiddingDataExclusionsResponse response =\n biddingDataExclusionService.MutateBiddingDataExclusions(\n customerId.ToString(), new[] { operation });\n Console.WriteLine($\"Added data exclusion with resource name: \" +\n $\"{response.Results[0].ResourceName}\");\n}\ncatch (GoogleAdsException e)\n{\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n}AddBiddingDataExclusion.cs\n```\n\nExample:\n```text\n// Creates a bidding data exclusion.\n$dataExclusion = new BiddingDataExclusion([\n // A unique name is required for every data exclusion.\n 'name' => 'Data exclusion #' . Helper::getPrintableDatetime(),\n // The CHANNEL scope applies the data exclusion to all campaigns of specific\n // advertising channel types. In this example, the exclusion will only apply to\n // Search campaigns. Use the CAMPAIGN scope to instead limit the scope to specific\n // campaigns.\n 'scope' => SeasonalityEventScope::CHANNEL,\n 'advertising_channel_types' => [AdvertisingChannelType::SEARCH],\n // If setting scope CAMPAIGN, add individual campaign resource name(s) according to\n // the commented out line below.\n // 'campaigns' => ['INSERT_CAMPAIGN_RESOURCE_NAME_HERE'],\n 'start_date_time' => $startDateTime,\n 'end_date_time' => $endDateTime\n]);\n\n// Creates a bidding data exclusion operation.\n$biddingDataExclusionOperation = new BiddingDataExclusionOperation();\n$biddingDataExclusionOperation->setCreate($dataExclusion);\n\n// Submits the bidding data exclusion operation to add the bidding data exclusion.\n$biddingDataExclusionServiceClient =\n $googleAdsClient->getBiddingDataExclusionServiceClient();\n$response = $biddingDataExclusionServiceClient->mutateBiddingDataExclusions(\n MutateBiddingDataExclusionsRequest::build($customerId, [$biddingDataExclusionOperation])\n);\n\nprintf(\n \"Added bidding data exclusion with resource name: '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n);AddBiddingDataExclusion.php\n```\n\nExample:\n```text\nbidding_data_exclusion_service: BiddingDataExclusionServiceClient = (\n client.get_service(\"BiddingDataExclusionService\")\n)\noperation: BiddingDataExclusionOperation = client.get_type(\n \"BiddingDataExclusionOperation\"\n)\nbidding_data_exclusion: BiddingDataExclusion = operation.create\n# A unique name is required for every data exclusion\nbidding_data_exclusion.name = f\"Data exclusion #{uuid4()}\"\n# The CHANNEL scope applies the data exclusion to all campaigns of specific\n# advertising channel types. In this example, the exclusion will only\n# apply to Search campaigns. Use the CAMPAIGN scope to instead limit the\n# scope to specific campaigns.\nbidding_data_exclusion.scope = (\n client.enums.SeasonalityEventScopeEnum.CHANNEL\n)\nbidding_data_exclusion.advertising_channel_types.append(\n client.enums.AdvertisingChannelTypeEnum.SEARCH\n)\n# If setting scope CAMPAIGN, add individual campaign resource name(s)\n# according to the commented out line below.\n#\n# bidding_data_exclusion.campaigns.append(\n# \"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\"\n# )\n\nbidding_data_exclusion.start_date_time = start_date_time\nbidding_data_exclusion.end_date_time = end_date_time\n\nresponse: MutateBiddingDataExclusionsResponse = (\n bidding_data_exclusion_service.mutate_bidding_data_exclusions(\n customer_id=customer_id, operations=[operation]\n )\n)\n\nresource_name: str = response.results[0].resource_name\n\nprint(f\"Added data exclusion with resource name: '{resource_name}'\")add_bidding_data_exclusion.py\n```\n\nExample:\n```text\nclient = Google::Ads::GoogleAds::GoogleAdsClient.new\n\noperation = client.operation.create_resource.bidding_data_exclusion do |bda|\n # A unique name is required for every data excluseion.\n bda.name = \"Seasonality Adjustment #{(Time.new.to_f * 1000).to_i}\"\n\n # The CHANNEL scope applies the data exclusion to all campaigns of specific\n # advertising channel types. In this example, the conversion_rate_modifier\n # will only apply to Search campaigns. Use the CAMPAIGN scope to instead\n # limit the scope to specific campaigns.\n bda.scope = :CHANNEL\n bda.advertising_channel_types << :SEARCH\n\n # If setting scope CAMPAIGN, add individual campaign resource name(s)\n # according to the commented out line below.\n #\n # bda.campaigns << \"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\"\n\n bda.start_date_time = start_date_time\n bda.end_date_time = end_date_time\nend\n\nresponse = client.service.bidding_data_exclusion.mutate_bidding_data_exclusions(\n customer_id: customer_id,\n operations: [operation],\n)\n\nputs \"Added data exclusion with resource name #{response.results.first.resource_name}.\"add_bidding_data_exclusion.rb\n```\n\nExample:\n```text\nmy $data_exclusion =\n Google::Ads::GoogleAds::V25::Resources::BiddingDataExclusion->new({\n # A unique name is required for every data exclusion.\n name => \"Data exclusion #\" . uniqid(),\n # The CHANNEL scope applies the data exclusion to all campaigns of specific\n # advertising channel types. In this example, the exclusion will only apply\n # to Search campaigns. Use the CAMPAIGN scope to instead limit the scope to\n # specific campaigns.\n scope => CHANNEL,\n advertisingChannelTypes => [SEARCH],\n # If setting scope CAMPAIGN, add individual campaign resource name(s)\n # according to the commented out line below.\n # campaigns => [\"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\"],\n startDateTime => $start_date_time,\n endDateTime => $end_date_time\n });\n\nmy $operation =\n Google::Ads::GoogleAds::V25::Services::BiddingDataExclusionService::BiddingDataExclusionOperation\n ->new({\n create => $data_exclusion\n });\n\nmy $response = $api_client->BiddingDataExclusionService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n\nprintf \"Added data exclusion with resource name: '%s'.\\n\",\n $response->{results}[0]{resourceName};add_bidding_data_exclusion.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.318Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":214,"estimatedTokens":2062}}137{"id":"doc-create_seasonality_adjustments_google_ads_api_go-10f26e61","source":"documentation","title":"Create Seasonality Adjustments | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/bidding/seasonality-adjustments","text":"Example:\n```text\nBiddingSeasonalityAdjustment seasonalityAdjustment =\n BiddingSeasonalityAdjustment.newBuilder()\n // A unique name is required for every seasonality adjustment.\n .setName(\"Seasonality adjustment #\" + getPrintableDateTime())\n // The CHANNEL scope applies the conversionRateModifier to all campaigns of specific\n // advertising channel types. In this example, the conversionRateModifier will only\n // apply to Search campaigns. Use the CAMPAIGN scope to instead limit the scope to\n // specific campaigns.\n .setScope(SeasonalityEventScope.CHANNEL)\n .addAdvertisingChannelTypes(AdvertisingChannelType.SEARCH)\n // If setting scope CAMPAIGN, add individual campaign resource name(s) according to\n // the commented out line below.\n // .addCampaigns(\"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\")\n .setStartDateTime(startDateTime)\n .setEndDateTime(endDateTime)\n // The conversionRateModifier is the expected future conversion rate change. When this\n // field is unset or set to 1.0, no adjustment will be applied to traffic. The allowed\n // range is 0.1 to 10.0.\n .setConversionRateModifier(conversionRateModifier)\n .build();\n\nBiddingSeasonalityAdjustmentOperation operation =\n BiddingSeasonalityAdjustmentOperation.newBuilder()\n .setCreate(seasonalityAdjustment)\n .build();\n\nMutateBiddingSeasonalityAdjustmentsResponse response =\n seasonalityAdjustmentServiceClient.mutateBiddingSeasonalityAdjustments(\n customerId.toString(), ImmutableList.of(operation));\nSystem.out.printf(\n \"Added seasonality adjustment with resource name: %s%n\",\n response.getResults(0).getResourceName());AddBiddingSeasonalityAdjustment.java\n```\n\nExample:\n```text\nBiddingSeasonalityAdjustment seasonalityAdjustment =\n new BiddingSeasonalityAdjustment()\n {\n // A unique name is required for every seasonality adjustment.\n Name = \"Seasonality adjustment #\" + ExampleUtilities.GetRandomString(),\n // The CHANNEL scope applies the conversionRateModifier to all campaigns of\n // specific advertising channel types. In this example, the\n // conversionRateModifier will only apply to Search campaigns. Use the\n // CAMPAIGN scope to instead limit the scope to specific campaigns.\n Scope = SeasonalityEventScope.Channel,\n AdvertisingChannelTypes = { AdvertisingChannelType.Search },\n // If setting scope CAMPAIGN, add individual campaign resource name(s)\n // according to the commented out line below.\n // Campaigns = { \"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\" },\n // The date range should be less than 14 days.\n StartDateTime = startDateTime,\n EndDateTime = endDateTime,\n // The conversionRateModifier is the expected future conversion rate change.\n // When this field is unset or set to 1.0, no adjustment will be applied to\n // traffic. The allowed range is 0.1 to 10.0.\n ConversionRateModifier = conversionRateModifier\n };\n\nBiddingSeasonalityAdjustmentOperation operation =\n new BiddingSeasonalityAdjustmentOperation()\n {\n Create = seasonalityAdjustment\n };\n\ntry\n{\n MutateBiddingSeasonalityAdjustmentsResponse response =\n biddingSeasonalityAdjustmentService.MutateBiddingSeasonalityAdjustments(\n customerId.ToString(), new[] { operation });\n Console.WriteLine($\"Added seasonality adjustment with resource name: \" +\n $\"{response.Results[0].ResourceName}\");\n}\ncatch (GoogleAdsException e)\n{\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n}AddBiddingSeasonalityAdjustment.cs\n```\n\nExample:\n```text\n// Creates a bidding seasonality adjustment.\n$seasonalityAdjustment = new BiddingSeasonalityAdjustment([\n // A unique name is required for every seasonality adjustment.\n 'name' => 'Seasonality adjustment #' . Helper::getPrintableDatetime(),\n // The CHANNEL scope applies the conversionRateModifier to all campaigns of specific\n // advertising channel types. In this example, the conversionRateModifier will only\n // apply to Search campaigns. Use the CAMPAIGN scope to instead limit the scope to\n // specific campaigns.\n 'scope' => SeasonalityEventScope::CHANNEL,\n 'advertising_channel_types' => [AdvertisingChannelType::SEARCH],\n // If setting scope CAMPAIGN, add individual campaign resource name(s) according to\n // the commented out line below.\n // 'campaigns' => ['INSERT_CAMPAIGN_RESOURCE_NAME_HERE'],\n 'start_date_time' => $startDateTime,\n 'end_date_time' => $endDateTime,\n // The conversionRateModifier is the expected future conversion rate change. When this\n // field is unset or set to 1.0, no adjustment will be applied to traffic. The allowed\n // range is 0.1 to 10.0.\n 'conversion_rate_modifier' => $conversionRateModifier\n]);\n\n// Creates a bidding seasonality adjustment operation.\n$biddingSeasonalityAdjustmentOperation = new BiddingSeasonalityAdjustmentOperation();\n$biddingSeasonalityAdjustmentOperation->setCreate($seasonalityAdjustment);\n\n// Submits the bidding seasonality adjustment operation to add the bidding seasonality\n// adjustment.\n$biddingSeasonalityAdjustmentServiceClient =\n $googleAdsClient->getBiddingSeasonalityAdjustmentServiceClient();\n$response = $biddingSeasonalityAdjustmentServiceClient->mutateBiddingSeasonalityAdjustments(\n MutateBiddingSeasonalityAdjustmentsRequest::build(\n $customerId,\n [$biddingSeasonalityAdjustmentOperation]\n )\n);\n\nprintf(\n \"Added seasonality adjustment with resource name: '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n);AddBiddingSeasonalityAdjustment.php\n```\n\nExample:\n```text\nbidding_seasonality_adjustment_service: (\n BiddingSeasonalityAdjustmentServiceClient\n) = client.get_service(\"BiddingSeasonalityAdjustmentService\")\noperation: BiddingSeasonalityAdjustmentOperation = client.get_type(\n \"BiddingSeasonalityAdjustmentOperation\"\n)\nbidding_seasonality_adjustment: BiddingSeasonalityAdjustment = (\n operation.create\n)\n# A unique name is required for every seasonality adjustment.\nbidding_seasonality_adjustment.name = f\"Seasonality adjustment #{uuid4()}\"\n# The CHANNEL scope applies the conversion_rate_modifier to all campaigns of\n# specific advertising channel types. In this example, the\n# conversion_rate_modifier will only apply to Search campaigns. Use the\n# CAMPAIGN scope to instead limit the scope to specific campaigns.\nbidding_seasonality_adjustment.scope = (\n client.enums.SeasonalityEventScopeEnum.CHANNEL\n)\nbidding_seasonality_adjustment.advertising_channel_types.append(\n client.enums.AdvertisingChannelTypeEnum.SEARCH\n)\n# If setting scope CAMPAIGN, add individual campaign resource name(s)\n# according to the commented out line below.\n#\n# bidding_seasonality_adjustment.campaigns.append(\n# \"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\"\n# )\n\nbidding_seasonality_adjustment.start_date_time = start_date_time\nbidding_seasonality_adjustment.end_date_time = end_date_time\n# The conversion_rate_modifier is the expected future conversion rate\n# change. When this field is unset or set to 1.0, no adjustment will be\n# applied to traffic. The allowed range is 0.1 to 10.0.\nbidding_seasonality_adjustment.conversion_rate_modifier = (\n conversion_rate_modifier\n)\n\nresponse: MutateBiddingSeasonalityAdjustmentsResponse = (\n bidding_seasonality_adjustment_service.mutate_bidding_seasonality_adjustments(\n customer_id=customer_id, operations=[operation]\n )\n)\n\nresource_name: str = response.results[0].resource_name\n\nprint(f\"Added seasonality adjustment with resource name: '{resource_name}'\")add_bidding_seasonality_adjustment.py\n```\n\nExample:\n```text\nclient = Google::Ads::GoogleAds::GoogleAdsClient.new\n\noperation = client.operation.create_resource.bidding_seasonality_adjustment do |bsa|\n # A unique name is required for every seasonality adjustment.\n bsa.name = \"Seasonality Adjustment #{(Time.new.to_f * 1000).to_i}\"\n\n # The CHANNEL scope applies the conversion_rate_modifier to all campaigns\n # of specific advertising channel types. In this example, the\n # conversion_rate_modifier will only apply to Search campaigns. Use the\n # CAMPAIGN scope to instead limit the scope to specific campaigns.\n bsa.scope = :CHANNEL\n bsa.advertising_channel_types << :SEARCH\n\n # If setting scope CAMPAIGN, add individual campaign resource name(s)\n # according to the commented out line below.\n #\n # bsa.campaigns << \"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\"\n\n bsa.start_date_time = start_date_time\n bsa.end_date_time = end_date_time\n\n # The conversion_rate_modifier is the expected future conversion rate\n # change. When this field is unset or set to 1.0, no adjustment will be\n # applied to traffic. The allowed range is 0.1 to 10.0.\n bsa.conversion_rate_modifier = conversion_rate_modifier\nend\n\nresponse = client.service.bidding_seasonality_adjustment.mutate_bidding_seasonality_adjustments(\n customer_id: customer_id,\n operations: [operation],\n)\n\nputs \"Added seasonality adjustment with resource name #{response.results.first.resource_name}\"add_bidding_seasonality_adjustment.rb\n```\n\nExample:\n```text\nmy $seasonality_adjustment =\n Google::Ads::GoogleAds::V25::Resources::BiddingSeasonalityAdjustment->new({\n # A unique name is required for every seasonality adjustment.\n name => \"Seasonality adjustment #\" . uniqid(),\n # The CHANNEL scope applies the conversion_rate_modifier to all campaigns\n # of specific advertising channel types. In this example, the conversion_rate_modifier\n # will only apply to Search campaigns. Use the CAMPAIGN scope to instead\n # limit the scope to specific campaigns.\n scope => CHANNEL,\n advertisingChannelTypes => [SEARCH],\n # If setting scope CAMPAIGN, add individual campaign resource name(s)\n # according to the commented out line below.\n # campaigns => [\"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\"],\n startDateTime => $start_date_time,\n endDateTime => $end_date_time,\n # The conversion_rate_modifier is the expected future conversion rate change.\n # When this field is unset or set to 1.0, no adjustment will be applied to traffic.\n # The allowed range is 0.1 to 10.0.\n conversionRateModifier => $conversion_rate_modifier\n });\n\nmy $operation =\n Google::Ads::GoogleAds::V25::Services::BiddingSeasonalityAdjustmentService::BiddingSeasonalityAdjustmentOperation\n ->new({\n create => $seasonality_adjustment\n });\n\nmy $response = $api_client->BiddingSeasonalityAdjustmentService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n\nprintf \"Added seasonality adjustment with resource name: '%s'.\\n\",\n $response->{results}[0]{resourceName};add_bidding_seasonality_adjustment.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.320Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":255,"estimatedTokens":2747}}138{"id":"doc-set_bids_manually_google_ads_api_google_for_deve-d6b16fe9","source":"documentation","title":"Set Bids Manually | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/bidding/set-bids","text":"Example:\n```text\npublic static void main(String[] args) {\n UpdateAdGroupParams params = new UpdateAdGroupParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.adGroupId = Long.parseLong(\"INSERT_AD_GROUP_ID_HERE\");\n params.cpcBidMicroAmount = Long.parseLong(\"INSERT_CPC_BID_MICRO_AMOUNT_HERE\");\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new UpdateAdGroup()\n .runExample(\n googleAdsClient, params.customerId, params.adGroupId, params.cpcBidMicroAmount);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n}UpdateAdGroup.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long adGroupId,\n long? cpcBidMicroAmount)\n{\n AdGroupServiceClient adGroupService = client.GetService(Services.V25.AdGroupService);\n\n // Create an ad group with the specified ID.\n AdGroup adGroup = new AdGroup();\n adGroup.ResourceName = ResourceNames.AdGroup(customerId, adGroupId);\n\n // Pause the ad group.\n adGroup.Status = AdGroupStatusEnum.Types.AdGroupStatus.Paused;\n\n // Update the CPC bid if specified.\n if (cpcBidMicroAmount != null)\n {\n adGroup.CpcBidMicros = cpcBidMicroAmount.Value;\n }\n\n // Create the operation.\n AdGroupOperation operation = new AdGroupOperation()\n {\n Update = adGroup,\n UpdateMask = FieldMasks.AllSetFieldsOf(adGroup)\n };\n\n try\n {\n // Update the ad group.\n MutateAdGroupsResponse retVal = adGroupService.MutateAdGroups(\n customerId.ToString(), new AdGroupOperation[] { operation });\n\n // Display the results.\n MutateAdGroupResult adGroupResult = retVal.Results[0];\n\n Console.WriteLine($\"Ad group with resource name '{adGroupResult.ResourceName}' \" +\n \"was updated.\");\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}UpdateAdGroup.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n $bidMicroAmount\n) {\n // Creates an ad group object with the specified resource name and other changes.\n $adGroup = new AdGroup([\n 'resource_name' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'cpc_bid_micros' => $bidMicroAmount,\n 'status' => AdGroupStatus::PAUSED\n ]);\n\n // Constructs an operation that will update the ad group with the specified resource name,\n // using the FieldMasks utility to derive the update mask. This mask tells the Google Ads\n // API which attributes of the ad group you want to change.\n $adGroupOperation = new AdGroupOperation();\n $adGroupOperation->setUpdate($adGroup);\n $adGroupOperation->setUpdateMask(FieldMasks::allSetFieldsOf($adGroup));\n\n // Issues a mutate request to update the ad group.\n $adGroupServiceClient = $googleAdsClient->getAdGroupServiceClient();\n $response = $adGroupServiceClient->mutateAdGroups(MutateAdGroupsRequest::build(\n $customerId,\n [$adGroupOperation]\n ));\n\n // Prints the resource name of the updated ad group.\n /** @var AdGroup $updatedAdGroup */\n $updatedAdGroup = $response->getResults()[0];\n printf(\n \"Updated ad group with resource name: '%s'%s\",\n $updatedAdGroup->getResourceName(),\n PHP_EOL\n );\n}UpdateAdGroup.php\n```\n\nExample:\n```text\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n cpc_bid_micro_amount: int,\n) -> None:\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n\n # Create ad group operation.\n ad_group_operation: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group: AdGroup = ad_group_operation.update\n ad_group.resource_name = ad_group_service.ad_group_path(\n customer_id, ad_group_id\n )\n ad_group.status = client.enums.AdGroupStatusEnum.PAUSED\n ad_group.cpc_bid_micros = cpc_bid_micro_amount\n client.copy_from(\n ad_group_operation.update_mask,\n protobuf_helpers.field_mask(None, ad_group._pb),\n )\n\n operations: List[AdGroupAdOperation] = [ad_group_operation]\n\n # Update the ad group.\n ad_group_response: MutateAdGroupsResponse = (\n ad_group_service.mutate_ad_groups(\n customer_id=customer_id,\n operations=operations,\n )\n )\n\n print(f\"Updated ad group {ad_group_response.results[0].resource_name}.\")update_ad_group.py\n```\n\nExample:\n```text\ndef update_ad_group(customer_id, ad_group_id, bid_micro_amount)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n resource_name = client.path.ad_group(customer_id, ad_group_id)\n\n operation = client.operation.update_resource.ad_group(resource_name) do |ag|\n ag.status = :PAUSED\n ag.cpc_bid_micros = bid_micro_amount\n end\n\n response = client.service.ad_group.mutate_ad_groups(\n customer_id: customer_id,\n operations: [operation],\n )\n\n puts \"Ad group with resource name = '#{response.results.first.resource_name}' was updated.\"\nendupdate_ad_group.rb\n```\n\nExample:\n```text\nsub update_ad_group {\n my ($api_client, $customer_id, $ad_group_id, $cpc_bid_micro_amount) = @_;\n\n # Create an ad group with the proper resource name and any other changes.\n my $ad_group = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n status => PAUSED,\n cpcBidMicros => $cpc_bid_micro_amount\n });\n\n # Create an ad group operation for update, using the FieldMasks utility to\n # derive the update mask.\n my $ad_group_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({\n update => $ad_group,\n updateMask => all_set_fields_of($ad_group)});\n\n # Update the ad group.\n my $ad_groups_response = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_operation]});\n\n printf \"Updated ad group with resource name: '%s'.\\n\",\n $ad_groups_response->{results}[0]{resourceName};\n\n return 1;\n}update_ad_group.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.321Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":233,"estimatedTokens":1924}}139{"id":"doc-portfolio_and_standard_bidding_strategies_google-05bec3d6","source":"documentation","title":"Portfolio and Standard Bidding Strategies | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/bidding/assign-strategies","text":"Example:\n```text\nprivate String createBiddingStrategy(GoogleAdsClient googleAdsClient, long customerId) {\n try (BiddingStrategyServiceClient biddingStrategyServiceClient =\n googleAdsClient.getLatestVersion().createBiddingStrategyServiceClient()) {\n // Creates a portfolio bidding strategy.\n TargetSpend targetSpend = TargetSpend.newBuilder().setCpcBidCeilingMicros(2_000_000L).build();\n BiddingStrategy portfolioBiddingStrategy =\n BiddingStrategy.newBuilder()\n .setName(\"Maximize Clicks #\" + getPrintableDateTime())\n .setTargetSpend(targetSpend)\n .build();\n // Constructs an operation that will create a portfolio bidding strategy.\n BiddingStrategyOperation operation =\n BiddingStrategyOperation.newBuilder().setCreate(portfolioBiddingStrategy).build();\n // Sends the operation in a mutate request.\n MutateBiddingStrategiesResponse response =\n biddingStrategyServiceClient.mutateBiddingStrategies(\n Long.toString(customerId), Lists.newArrayList(operation));\n\n MutateBiddingStrategyResult mutateBiddingStrategyResult = response.getResults(0);\n // Prints the resource name of the created object.\n System.out.printf(\n \"Created portfolio bidding strategy with resource name: '%s'.%n\",\n mutateBiddingStrategyResult.getResourceName());\n\n return mutateBiddingStrategyResult.getResourceName();\n }\n}UsePortfolioBiddingStrategy.java\n```\n\nExample:\n```text\nprivate string CreatePortfolioBiddingStrategy(GoogleAdsClient client,\n long customerId, string name, long bidCeiling)\n{\n // Get the BiddingStrategyService.\n BiddingStrategyServiceClient biddingStrategyService = client.GetService(\n Services.V25.BiddingStrategyService);\n\n // Create a portfolio bidding strategy.\n BiddingStrategy biddingStrategy = new BiddingStrategy()\n {\n Name = name,\n\n TargetSpend = new TargetSpend()\n {\n CpcBidCeilingMicros = bidCeiling,\n }\n };\n\n // Create operation.\n BiddingStrategyOperation biddingOperation = new BiddingStrategyOperation()\n {\n Create = biddingStrategy\n };\n\n // Create the portfolio bidding strategy.\n MutateBiddingStrategiesResponse biddingResponse =\n biddingStrategyService.MutateBiddingStrategies(\n customerId.ToString(), new BiddingStrategyOperation[] { biddingOperation });\n\n return biddingResponse.Results[0].ResourceName;\n}UsePortfolioBiddingStrategy.cs\n```\n\nExample:\n```text\nprivate static function createBiddingStrategy(GoogleAdsClient $googleAdsClient, int $customerId)\n{\n // Creates a portfolio bidding strategy.\n $portfolioBiddingStrategy = new BiddingStrategy([\n 'name' => 'Maximize Clicks #' . Helper::getPrintableDatetime(),\n 'target_spend' => new TargetSpend([\n 'cpc_bid_ceiling_micros' => 2000000\n ])\n ]);\n\n // Constructs an operation that will create a portfolio bidding strategy.\n $biddingStrategyOperation = new BiddingStrategyOperation();\n $biddingStrategyOperation->setCreate($portfolioBiddingStrategy);\n\n // Issues a mutate request to create the bidding strategy.\n $biddingStrategyServiceClient = $googleAdsClient->getBiddingStrategyServiceClient();\n $response = $biddingStrategyServiceClient->mutateBiddingStrategies(\n MutateBiddingStrategiesRequest::build($customerId, [$biddingStrategyOperation])\n );\n /** @var BiddingStrategy $addedBiddingStrategy */\n $addedBiddingStrategy = $response->getResults()[0];\n\n // Prints out the resource name of the created bidding strategy.\n printf(\n \"Created portfolio bidding strategy with resource name: '%s'.%s\",\n $addedBiddingStrategy->getResourceName(),\n PHP_EOL\n );\n\n return $addedBiddingStrategy->getResourceName();\n}UsePortfolioBiddingStrategy.php\n```\n\nExample:\n```text\n# Create a portfolio bidding strategy.\nbidding_strategy_operation: BiddingStrategyOperation = client.get_type(\n \"BiddingStrategyOperation\"\n)\nbidding_strategy: BiddingStrategy = bidding_strategy_operation.create\nbidding_strategy.name = f\"Enhanced CPC {uuid.uuid4()}\"\ntarget_spend: TargetSpend = bidding_strategy.target_spend\ntarget_spend.cpc_bid_ceiling_micros = 2000000\n\n# Add portfolio bidding strategy.\ntry:\n bidding_strategy_response: MutateBiddingStrategiesResponse = (\n bidding_strategy_service.mutate_bidding_strategies(\n customer_id=customer_id, operations=[bidding_strategy_operation]\n )\n )\n bidding_strategy_id: str = bidding_strategy_response.results[\n 0\n ].resource_name\n print(f'Created portfolio bidding strategy \"{bidding_strategy_id}\".')\nexcept GoogleAdsException as ex:\n handle_googleads_exception(ex)use_portfolio_bidding_strategy.py\n```\n\nExample:\n```text\n# Create a portfolio bidding strategy.\nbidding_strategy = client.resource.bidding_strategy do |bs|\n bs.name = \"Enhanced CPC ##{(Time.new.to_f * 1000).to_i}\"\n bs.target_spend = client.resource.target_spend do |ts|\n ts.cpc_bid_ceiling_micros = 2_000_000\n end\nend\n\noperation = client.operation.create_resource.bidding_strategy(bidding_strategy)\n\nresponse = client.service.bidding_strategy.mutate_bidding_strategies(\n customer_id: customer_id,\n operations: [operation],\n)\nbidding_id = response.results.first.resource_name\n\nputs \"Portfolio bidding strategy #{bidding_id} was created\"use_portfolio_bidding_strategy.rb\n```\n\nExample:\n```text\nsub create_bidding_strategy {\n my ($api_client, $customer_id) = @_;\n\n # Create a portfolio bidding strategy.\n my $portfolio_bidding_strategy =\n Google::Ads::GoogleAds::V25::Resources::BiddingStrategy->new({\n name => \"Maximize Clicks #\" . uniqid(),\n targetSpend => Google::Ads::GoogleAds::V25::Common::TargetSpend->new({\n cpcBidCeilingMicros => 2000000\n }\n ),\n });\n\n # Create a bidding strategy operation.\n my $bidding_strategy_operation =\n Google::Ads::GoogleAds::V25::Services::BiddingStrategyService::BiddingStrategyOperation\n ->new({\n create => $portfolio_bidding_strategy\n });\n\n # Add the bidding strategy.\n my $bidding_strategies_response =\n $api_client->BiddingStrategyService()->mutate({\n customerId => $customer_id,\n operations => [$bidding_strategy_operation]});\n\n my $bidding_strategy_resource_name =\n $bidding_strategies_response->{results}[0]{resourceName};\n\n printf \"Created portfolio bidding strategy with resource name: '%s'.\\n\",\n $bidding_strategy_resource_name;\n\n return $bidding_strategy_resource_name;\n}use_portfolio_bidding_strategy.pl\n```\n\nExample:\n```text\nCampaign campaign =\n Campaign.newBuilder()\n .setName(\"Interplanetary Cruise #\" + getPrintableDateTime())\n .setStatus(CampaignStatus.PAUSED)\n .setCampaignBudget(campaignBudgetResourceName)\n .setBiddingStrategy(biddingStrategyResourceName)\n .setAdvertisingChannelType(AdvertisingChannelType.SEARCH)\n .setNetworkSettings(networkSettings)\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n .build();UsePortfolioBiddingStrategy.java\n```\n\nExample:\n```text\n// Create the campaign.\nCampaign campaign = new Campaign()\n{\n Name = name,\n AdvertisingChannelType = AdvertisingChannelType.Search,\n\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve.\n Status = CampaignStatus.Paused,\n\n // Set the campaign budget.\n CampaignBudget = campaignBudgetResourceName,\n\n // Set bidding strategy (required).\n BiddingStrategy = biddingStrategyResourceName,\n\n // Set the campaign network options.\n NetworkSettings = new NetworkSettings()\n {\n TargetGoogleSearch = true,\n TargetSearchNetwork = true,\n TargetContentNetwork = true,\n TargetPartnerSearchNetwork = false\n },\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n};UsePortfolioBiddingStrategy.cs\n```\n\nExample:\n```text\n// Creates a Search campaign.\n$campaign = new Campaign([\n 'name' => 'Interplanetary Cruise #' . Helper::getPrintableDatetime(),\n 'advertising_channel_type' => AdvertisingChannelType::SEARCH,\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving. Set to ENABLED once you've added\n // targeting and the ads are ready to serve.\n 'status' => CampaignStatus::PAUSED,\n // Configures the campaign network options.\n 'network_settings' => new NetworkSettings([\n 'target_google_search' => true,\n 'target_search_network' => true,\n 'target_content_network' => true,\n ]),\n // Sets the bidding strategy and budget.\n 'bidding_strategy' => $biddingStrategyResourceName,\n 'campaign_budget' => $campaignBudgetResourceName,\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n]);UsePortfolioBiddingStrategy.php\n```\n\nExample:\n```text\n# Create campaign.\ncampaign_operation: CampaignOperation = client.get_type(\"CampaignOperation\")\ncampaign: Campaign = campaign_operation.create\ncampaign.name = f\"Interplanetary Cruise {uuid.uuid4()}\"\ncampaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.SEARCH\n)\n\n# Recommendation: Set the campaign to PAUSED when creating it to prevent the\n# ads from immediately serving. Set to ENABLED once you've added targeting\n# and the ads are ready to serve.\ncampaign.status = client.enums.CampaignStatusEnum.PAUSED\n\n# Set the bidding strategy and budget.\ncampaign.bidding_strategy = bidding_strategy_id\ncampaign.manual_cpc.enhanced_cpc_enabled = True\ncampaign.campaign_budget = campaign_budget_id\n\n# Set the campaign network options.\ncampaign.network_settings.target_google_search = True\ncampaign.network_settings.target_search_network = True\ncampaign.network_settings.target_content_network = False\ncampaign.network_settings.target_partner_search_network = False\ncampaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n)use_portfolio_bidding_strategy.py\n```\n\nExample:\n```text\n# Create campaigns.\ncampaigns = 2.times.map do |i|\n client.resource.campaign do |c|\n c.name = \"Interplanetary Cruise ##{(Time.new.to_f * 1000).to_i + i}\"\n c.status = :PAUSED\n c.bidding_strategy = bidding_id\n c.campaign_budget = budget_id\n c.advertising_channel_type = :SEARCH\n c.network_settings = client.resource.network_settings do |ns|\n ns.target_google_search = true\n ns.target_search_network = true\n ns.target_content_network = false\n ns.target_partner_search_network = false\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n end\n end\nenduse_portfolio_bidding_strategy.rb\n```\n\nExample:\n```text\n# Create a search campaign.\nmy $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n name => \"Interplanetary Cruise #\" . uniqid(),\n advertisingChannelType => SEARCH,\n # Recommendation: Set the campaign to PAUSED when creating it to stop\n # the ads from immediately serving. Set to ENABLED once you've added\n # targeting and the ads are ready to serve.\n status => PAUSED,\n # Configures the campaign network options.\n networkSettings =>\n Google::Ads::GoogleAds::V25::Resources::NetworkSettings->new({\n targetGoogleSearch => \"true\",\n targetSearchNetwork => \"true\",\n targetContentNetwork => \"true\"\n }\n ),\n # Set the bidding strategy and budget.\n biddingStrategy => $bidding_strategy_resource_name,\n campaignBudget => $campaign_budget_resource_name,\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n });use_portfolio_bidding_strategy.pl\n```\n\nExample:\n```text\n/** Creates a MutateOperation that creates a new Performance Max campaign. */\nprivate MutateOperation createPerformanceMaxCampaignOperation(\n long customerId, boolean brandGuidelinesEnabled) {\n TextGuidelines textGuidelines =\n TextGuidelines.newBuilder()\n // Specifies a list of terms that should not be used in any auto-generated\n // text assets.\n .addAllTermExclusions(ImmutableList.of(\"cheap\", \"free\"))\n // Specifies freeform messaging restriction prompts that will apply to all\n // auto-generated text assets.\n .addMessagingRestrictions(\n MessagingRestriction.newBuilder()\n .setRestrictionText(\"Don't mention competitor names\")\n .setRestrictionType(\n MessagingRestrictionType.RESTRICTION_BASED_EXCLUSION)\n .build())\n .build();\n Campaign performanceMaxCampaign =\n Campaign.newBuilder()\n .setName(\"Performance Max campaign #\" + getPrintableDateTime())\n // Sets the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n .setStatus(CampaignStatus.PAUSED)\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n .setAdvertisingChannelType(AdvertisingChannelType.PERFORMANCE_MAX)\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n .setMaximizeConversionValue(\n MaximizeConversionValue.newBuilder().setTargetRoas(3.5).build())\n // Sets if the campaign is enabled for brand guidelines. For more information on brand\n // guidelines, see https://support.google.com/google-ads/answer/14934472.\n .setBrandGuidelinesEnabled(brandGuidelinesEnabled)\n // Sets the text guidelines.\n .setTextGuidelines(textGuidelines)\n // Assigns the resource name with a temporary ID.\n .setResourceName(\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID))\n // Sets the budget using the given budget resource name.\n .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n // Optional fields.\n .setStartDateTime(new DateTime().plusDays(1).toString(\"yyyy-MM-dd 00:00:00\"))\n .setEndDateTime(new DateTime().plusDays(365).toString(\"yyyy-MM-dd 23:59:59\"))\n // Configures the optional opt-in/out status for asset automation settings.\n .addAllAssetAutomationSettings(ImmutableList.of(\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_IMAGE_EXTRACTION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(\n AssetAutomationType.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_ENHANCED_YOUTUBE_VIDEOS)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_IMAGE_ENHANCEMENT)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build()))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignOperation(\n CampaignOperation.newBuilder().setCreate(performanceMaxCampaign).build())\n .build();\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n/// Creates a MutateOperation that creates a new Performance Max campaign.\n/// <param name=\"campaignResourceName\">The campaign resource name.</param>\n/// <param name=\"campaignBudgetResourceName\">The campaign budget resource name.</param>\n/// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n/// <returns>A MutateOperations that will create this new campaign.</returns>\nprivate MutateOperation CreatePerformanceMaxCampaignOperation(\n string campaignResourceName,\n string campaignBudgetResourceName,\n bool brandGuidelinesEnabled)\n{\n Campaign.Types.TextGuidelines textGuidelines =\n new Campaign.Types.TextGuidelines();\n textGuidelines.TermExclusions.AddRange([\"cheap\", \"free\"]);\n textGuidelines.MessagingRestrictions.Add(\n new Campaign.Types.MessagingRestriction()\n {\n RestrictionText = \"Don't mention competitor names\",\n RestrictionType = MessagingRestrictionType.RestrictionBasedExclusion\n }\n );\n\n Campaign campaign = new Campaign()\n {\n Name = \"Performance Max campaign #\" + ExampleUtilities.GetRandomString(),\n\n // Set the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n Status = CampaignStatus.Paused,\n\n // All Performance Max campaigns have an AdvertisingChannelType of\n // PerformanceMax. The AdvertisingChannelSubType should not be set.\n AdvertisingChannelType = AdvertisingChannelType.PerformanceMax,\n\n // Bidding strategy must be set directly on the campaign. Setting a\n // portfolio bidding strategy by resource name is not supported. Max\n // Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns. BiddingStrategyType is\n // read-only and cannot be set by the API. An optional ROAS (Return on\n // Advertising Spend) can be set to enable the MaximizeConversionValue\n // bidding strategy. The ROAS value must be specified as a ratio in the API.\n // It is calculated by dividing \"total value\" by \"total spend\".\n //\n // For more information on Maximize Conversion Value, see the support\n // article:\n // http://support.google.com/google-ads/answer/7684216.\n //\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n MaximizeConversionValue = new MaximizeConversionValue()\n {\n TargetRoas = 3.5\n },\n\n // Use the temporary resource name created earlier\n ResourceName = campaignResourceName,\n\n // Set the budget using the given budget resource name.\n CampaignBudget = campaignBudgetResourceName,\n\n // Set if the campaign is enabled for brand guidelines. For more information\n // on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n BrandGuidelinesEnabled = brandGuidelinesEnabled,\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n\n TextGuidelines = textGuidelines,\n\n // Optional fields\n StartDateTime = DateTime.Now.AddDays(1).ToString(\"yyyyMMdd 00:00:00\"),\n EndDateTime = DateTime.Now.AddDays(365).ToString(\"yyyyMMdd 23:59:59\")\n };\n\n campaign.AssetAutomationSettings.AddRange(new[]{\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateImageExtraction,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.FinalUrlExpansionTextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.TextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateEnhancedYoutubeVideos,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateImageEnhancement,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n });\n\n MutateOperation operation = new MutateOperation()\n {\n CampaignOperation = new CampaignOperation()\n {\n Create = campaign\n }\n };\n\n return operation;\n}\nAddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createPerformanceMaxCampaignOperation(\n int $customerId,\n bool $brandGuidelinesEnabled\n): MutateOperation {\n // Creates a mutate operation that creates a campaign operation.\n return new MutateOperation([\n 'campaign_operation' => new CampaignOperation([\n 'create' => new Campaign([\n 'name' => 'Performance Max campaign #' . Helper::getPrintableDatetime(),\n // Assigns the resource name with a temporary ID.\n 'resource_name' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n // Sets the budget using the given budget resource name.\n 'campaign_budget' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n // The campaign is the only entity in the mutate request that should have its\n // status set.\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving.\n 'status' => CampaignStatus::PAUSED,\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n 'advertising_channel_type' => AdvertisingChannelType::PERFORMANCE_MAX,\n\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n 'maximize_conversion_value' => new MaximizeConversionValue([\n 'target_roas' => 3.5\n ]),\n\n 'asset_automation_settings' => [\n new AssetAutomationSetting([\n 'asset_automation_type' => AssetAutomationType::TEXT_ASSET_AUTOMATION,\n 'asset_automation_status' => AssetAutomationStatus::OPTED_IN\n ]),\n new AssetAutomationSetting([\n 'asset_automation_type' => AssetAutomationType::URL_EXPANSION,\n 'asset_automation_status' => AssetAutomationStatus::OPTED_IN\n ])\n ],\n\n\n // Sets if the campaign is enabled for brand guidelines. For more information\n // on brand guidelines, see\n // https://support.google.com/google-ads/answer/14934472.\n 'brand_guidelines_enabled' => $brandGuidelinesEnabled,\n\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n\n // Optional fields.\n 'start_date_time' => date('Y-m-d 00:00:00', strtotime('+1 day')),\n 'end_date_time' => date('Y-m-d 23:59:59', strtotime('+365 days'))\n ])\n ])\n ]);\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\ndef create_performance_max_campaign_operation(\n client: GoogleAdsClient,\n customer_id: str,\n brand_guidelines_enabled: bool,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new Performance Max campaign.\n\n A temporary ID will be assigned to this campaign so that it can\n be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n\n Returns:\n a MutateOperation that creates a campaign.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign: Campaign = mutate_operation.campaign_operation.create\n campaign.name = f\"Performance Max campaign #{uuid4()}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.PERFORMANCE_MAX\n )\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n campaign.bidding_strategy_type = (\n client.enums.BiddingStrategyTypeEnum.MAXIMIZE_CONVERSION_VALUE\n )\n campaign.maximize_conversion_value.target_roas = 3.5\n\n # Set if the campaign is enabled for brand guidelines. For more information\n # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n campaign.brand_guidelines_enabled = brand_guidelines_enabled\n\n # Assign the resource name with a temporary ID.\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n campaign.resource_name = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n # Set the budget using the given budget resource name.\n campaign.campaign_budget = campaign_service.campaign_budget_path(\n customer_id, _BUDGET_TEMPORARY_ID\n )\n\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n # Optional fields\n campaign.start_date_time = (datetime.now() + timedelta(1)).strftime(\n \"%Y%m%d 00:00:00\"\n )\n campaign.end_date_time = (datetime.now() + timedelta(365)).strftime(\n \"%Y%m%d 23:59:59\"\n )\n\n campaign.text_guidelines.term_exclusions = [\"cheap\", \"free\"]\n messaging_restriction = campaign.MessagingRestriction()\n messaging_restriction.restriction_text = \"Don't mention competitor names\"\n messaging_restriction.restriction_type = (\n client.enums.MessagingRestrictionTypeEnum.RESTRICTION_BASED_EXCLUSION\n )\n campaign.text_guidelines.messaging_restrictions.append(\n messaging_restriction\n )\n\n # Configures the optional opt-in/out status for asset automation settings.\n for asset_automation_type_enum in [\n client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_EXTRACTION,\n client.enums.AssetAutomationTypeEnum.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,\n client.enums.AssetAutomationTypeEnum.TEXT_ASSET_AUTOMATION,\n client.enums.AssetAutomationTypeEnum.GENERATE_ENHANCED_YOUTUBE_VIDEOS,\n client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_ENHANCEMENT,\n ]:\n asset_automattion_setting: Campaign.AssetAutomationSetting = (\n client.get_type(\"Campaign\").AssetAutomationSetting()\n )\n asset_automattion_setting.asset_automation_type = (\n asset_automation_type_enum\n )\n asset_automattion_setting.asset_automation_status = (\n client.enums.AssetAutomationStatusEnum.OPTED_IN\n )\n campaign.asset_automation_settings.append(asset_automattion_setting)\n\n return mutate_operationadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates a MutateOperation that creates a new Performance Max campaign.\n#\n# A temporary ID will be assigned to this campaign so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_performance_max_campaign_operation(\n client,\n customer_id,\n brand_guidelines_enabled)\n client.operation.mutate do |m|\n m.campaign_operation = client.operation.create_resource.campaign do |c|\n c.name = \"Performance Max campaign #{SecureRandom.uuid}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n c.status = :PAUSED\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n c.advertising_channel_type = :PERFORMANCE_MAX\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n c.bidding_strategy_type = :MAXIMIZE_CONVERSION_VALUE\n c.maximize_conversion_value = client.resource.maximize_conversion_value do |mcv|\n mcv.target_roas = 3.5\n end\n\n # Configures the optional opt-in/out status for asset automation settings.\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_IMAGE_EXTRACTION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :TEXT_ASSET_AUTOMATION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_ENHANCED_YOUTUBE_VIDEOS\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_IMAGE_ENHANCEMENT\n aas.asset_automation_status = :OPTED_IN\n end\n\n # Set if the campaign is enabled for brand guidelines. For more\n # information on brand guidelines, see\n # https://support.google.com/google-ads/answer/14934472.\n c.brand_guidelines_enabled = brand_guidelines_enabled\n\n # Assign the resource name with a temporary ID.\n c.resource_name = client.path.campaign(customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n # Set the budget using the given budget resource name.\n c.campaign_budget = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n # Optional fields\n c.start_date_time = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d %H:%M:%S')\n c.end_date_time = DateTime.parse(Date.today.next_year.to_s).strftime('%Y%m%d %H:%M:%S')\n end\n end\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_performance_max_campaign_operation {\n my ($customer_id, $brand_guidelines_enabled) = @_;\n # Configures the optional opt-in/out status for asset automation settings.\n # When we create the campaign object, we set campaign->{assetAutomationSettings}\n # equal to $asset_automation_settings.\n my $asset_automation_settings = [];\n my $asset_automation_types = [\n GENERATE_IMAGE_EXTRACTION, FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,\n TEXT_ASSET_AUTOMATION, GENERATE_ENHANCED_YOUTUBE_VIDEOS,\n GENERATE_IMAGE_ENHANCEMENT\n ];\n foreach my $asset_automation_type (@$asset_automation_types) {\n push @$asset_automation_settings,\n Google::Ads::GoogleAds::V25::Resources::AssetAutomationSetting->new({\n assetAutomationStatus => OPTED_IN,\n assetAutomationType => $asset_automation_type\n });\n }\n\n my $text_guidelines =\n Google::Ads::GoogleAds::V25::Resources::TextGuidelines->new({\n termExclusions => [\"cheap\", \"free\"],\n messagingRestrictions => [\n Google::Ads::GoogleAds::V25::Resources::MessagingRestriction->new({\n restrictionText => \"Don't mention competitor names\",\n restrictionType => RESTRICTION_BASED_EXCLUSION\n })]});\n\n # Create a mutate operation that creates a campaign operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n # Assign the resource name with a temporary ID.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n name => \"Performance Max campaign #\" . uniqid(),\n # Set the budget using the given budget resource name.\n campaignBudget =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n status =>\n Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n # All Performance Max campaigns have an advertisingChannelType of\n # PERFORMANCE_MAX. The advertisingChannelSubType should not be set.\n advertisingChannelType => PERFORMANCE_MAX,\n\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximizeConversionValue. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n maximizeConversionValue =>\n Google::Ads::GoogleAds::V25::Common::MaximizeConversionValue->\n new({\n targetRoas => 3.5\n }\n ),\n\n # Set if the campaign is enabled for brand guidelines. For more information\n # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n brandGuidelinesEnabled => $brand_guidelines_enabled,\n\n # Configures the optional opt-in/out status for asset automation settings.\n assetAutomationSettings => $asset_automation_settings,\n\n # Set the text guidelines.\n textGuidelines => $text_guidelines,\n\n # Optional fields.\n startDateTime =>\n strftime(\"%Y%m%d 00:00:00\", localtime(time + 60 * 60 * 24)),\n endDateTime => strftime(\n \"%Y%m%d 23:59:59\",\n localtime(time + 60 * 60 * 24 * 365)\n ),\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n })})});\n}add_performance_max_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.324Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":903,"estimatedTokens":9951}}140{"id":"doc-cross_account_bidding_strategies_google_ads_api_-bed157b0","source":"documentation","title":"Cross-account Bidding Strategies | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/bidding/cross-account-strategies","text":"Example:\n```text\nprivate String createBiddingStrategy(GoogleAdsClient googleAdsClient, long managerCustomerId) {\n try (BiddingStrategyServiceClient biddingStrategyServiceClient =\n googleAdsClient.getLatestVersion().createBiddingStrategyServiceClient()) {\n // Creates a portfolio bidding strategy.\n BiddingStrategy portfolioBiddingStrategy =\n BiddingStrategy.newBuilder()\n .setName(\"Maximize Clicks #\" + getPrintableDateTime())\n .setTargetSpend(TargetSpend.getDefaultInstance())\n // Sets the currency of the new bidding strategy. If not provided, the bidding\n // strategy uses the manager account's default currency.\n .setCurrencyCode(\"USD\")\n .build();\n // Constructs an operation that will create a portfolio bidding strategy.\n BiddingStrategyOperation operation =\n BiddingStrategyOperation.newBuilder().setCreate(portfolioBiddingStrategy).build();\n // Sends the operation in a mutate request.\n MutateBiddingStrategiesResponse response =\n biddingStrategyServiceClient.mutateBiddingStrategies(\n Long.toString(managerCustomerId), ImmutableList.of(operation));\n\n // Prints the resource name of the created cross-account bidding strategy.\n MutateBiddingStrategyResult mutateBiddingStrategyResult = response.getResults(0);\n String resourceName = mutateBiddingStrategyResult.getResourceName();\n System.out.printf(\"Created cross-account bidding strategy: '%s'.%n\", resourceName);\n\n return resourceName;\n }\n}UseCrossAccountBiddingStrategy.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a new TargetSpend (Maximize Clicks) cross-account bidding strategy in the\n/// specified manager account.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"managerCustomerId\">The manager customer ID.</param>\n/// <returns>The resource name of the newly created bidding strategy.</returns>\nprivate string CreateBiddingStrategy(GoogleAdsClient client, long managerCustomerId)\n{\n BiddingStrategyServiceClient biddingStrategyServiceClient =\n client.GetService(Services.V25.BiddingStrategyService);\n\n // Create a portfolio bidding strategy.\n BiddingStrategy portfolioBiddingStrategy = new BiddingStrategy\n {\n Name = $\"Maximize clicks #{ExampleUtilities.GetRandomString()}\",\n TargetSpend = new TargetSpend(),\n // Set the currency of the new bidding strategy. If not provided, the bidding\n // strategy uses the manager account's default currency.\n CurrencyCode = \"USD\"\n };\n\n // Send a create operation that will create the portfolio bidding strategy.\n MutateBiddingStrategiesResponse mutateBiddingStrategiesResponse =\n biddingStrategyServiceClient.MutateBiddingStrategies(managerCustomerId.ToString(),\n new[]\n {\n new BiddingStrategyOperation\n {\n Create = portfolioBiddingStrategy\n }\n });\n\n // Print and return the resource name of the newly created cross-account bidding\n // strategy.\n string biddingStrategyResourceName =\n mutateBiddingStrategiesResponse.Results.First().ResourceName;\n Console.WriteLine(\"Created cross-account bidding strategy \" +\n $\"'{biddingStrategyResourceName}'.\");\n\n return biddingStrategyResourceName;\n}UseCrossAccountBiddingStrategy.cs\n```\n\nExample:\n```text\nprivate static function createBiddingStrategy(\n GoogleAdsClient $googleAdsClient,\n int $managerCustomerId\n): string {\n // Creates a portfolio bidding strategy.\n $portfolioBiddingStrategy = new BiddingStrategy([\n 'name' => 'Maximize Clicks #' . Helper::getPrintableDatetime(),\n 'target_spend' => new TargetSpend(),\n // Optional: Sets the currency of the new bidding strategy to match the currency of the\n // client account with which this bidding strategy is shared.\n // If not provided, the bidding strategy uses the manager account's default currency.\n 'currency_code' => 'USD'\n ]);\n\n // Constructs an operation that will create a portfolio bidding strategy.\n $biddingStrategyOperation = new BiddingStrategyOperation();\n $biddingStrategyOperation->setCreate($portfolioBiddingStrategy);\n\n // Issues a mutate request to create the bidding strategy.\n $biddingStrategyServiceClient = $googleAdsClient->getBiddingStrategyServiceClient();\n $response = $biddingStrategyServiceClient->mutateBiddingStrategies(\n MutateBiddingStrategiesRequest::build($managerCustomerId, [$biddingStrategyOperation])\n );\n /** @var BiddingStrategy $addedBiddingStrategy */\n $addedBiddingStrategy = $response->getResults()[0];\n\n // Prints out the resource name of the created bidding strategy.\n printf(\n \"Created cross-account bidding strategy with resource name: '%s'.%s\",\n $addedBiddingStrategy->getResourceName(),\n PHP_EOL\n );\n\n return $addedBiddingStrategy->getResourceName();\n}UseCrossAccountBiddingStrategy.php\n```\n\nExample:\n```text\ndef create_bidding_strategy(\n client: GoogleAdsClient, manager_customer_id: str\n) -> str:\n \"\"\"Creates a new cross-account bidding strategy in the manager account.\n\n The cross-account bidding strategy is of type TargetSpend (Maximize Clicks).\n\n Args:\n client: An initialized GoogleAdsClient instance.\n manager_customer_id: A manager customer ID.\n\n Returns:\n The ID of the newly created bidding strategy.\n \"\"\"\n bidding_strategy_service: BiddingStrategyServiceClient = client.get_service(\n \"BiddingStrategyService\"\n )\n # Creates a portfolio bidding strategy.\n # Constructs an operation that will create a portfolio bidding strategy.\n bidding_strategy_operation: BiddingStrategyOperation = client.get_type(\n \"BiddingStrategyOperation\"\n )\n bidding_strategy: BiddingStrategy = bidding_strategy_operation.create\n bidding_strategy.name = f\"Maximize Clicks #{uuid4()}\"\n # Sets target_spend to an empty TargetSpend object without setting any\n # of its nested fields.\n bidding_strategy.target_spend = client.get_type(\"TargetSpend\")\n # Sets the currency of the new bidding strategy. If not provided, the\n # bidding strategy uses the manager account's default currency.\n bidding_strategy.currency_code = \"USD\"\n\n # Sends the operation in a mutate request.\n response: MutateBiddingStrategiesResponse = (\n bidding_strategy_service.mutate_bidding_strategies(\n customer_id=manager_customer_id,\n operations=[bidding_strategy_operation],\n )\n )\n\n # Prints the resource name of the created cross-account bidding strategy.\n resource_name: str = response.results[0].resource_name\n print(f\"Created cross-account bidding strategy: '{resource_name}'\")\n\n return resource_nameuse_cross_account_bidding_strategy.py\n```\n\nExample:\n```text\ndef create_bidding_strategy(client, manager_customer_id)\n # Constructs an operation that will create a portfolio bidding strategy.\n operation = client.operation.create_resource.bidding_strategy do |b|\n b.name = \"Maximize Clicks ##{(Time.new.to_f * 1000).to_i}\"\n b.target_spend = client.resource.target_spend\n # Sets the currency of the new bidding strategy. If not provided, the\n # bidding strategy uses the manager account's default currency.\n b.currency_code = \"USD\"\n end\n\n # Sends the operation in a mutate request.\n response = client.service.bidding_strategy.mutate_bidding_strategies(\n customer_id: manager_customer_id,\n operations: [operation],\n )\n\n resource_name = response.results.first.resource_name\n puts \"Created cross-account bidding strategy: `#{resource_name}`\"\n\n resource_name\nenduse_cross_account_bidding_strategy.rb\n```\n\nExample:\n```text\n# Creates a new TargetSpend (Maximize Clicks) cross-account bidding strategy in\n# the specified manager account.\nsub _create_bidding_strategy {\n my ($api_client, $manager_customer_id) = @_;\n\n # Create a portfolio bidding strategy.\n my $portfolio_bidding_strategy =\n Google::Ads::GoogleAds::V25::Resources::BiddingStrategy->new({\n name => \"Maximize clicks #\" . uniqid(),\n targetSpend => Google::Ads::GoogleAds::V25::Common::TargetSpend->new(),\n # Sets the currency of the new bidding strategy. If not provided, the\n # bidding strategy uses the manager account's default currency.\n currencyCode => \"USD\"\n });\n\n # Send a create operation that will create the portfolio bidding strategy.\n my $mutate_bidding_strategies_response =\n $api_client->BiddingStrategyService()->mutate({\n customerId => $manager_customer_id,\n operations => [\n Google::Ads::GoogleAds::V25::Services::BiddingStrategyService::BiddingStrategyOperation\n ->new({\n create => $portfolio_bidding_strategy\n })]});\n\n my $resource_name =\n $mutate_bidding_strategies_response->{results}[0]{resourceName};\n\n printf \"Created cross-account bidding strategy with resource name '%s'.\\n\",\n $resource_name;\n\n return $resource_name;\n}use_cross_account_bidding_strategy.pl\n```\n\nExample:\n```text\nBiddingStrategy portfolioBiddingStrategy =\n BiddingStrategy.newBuilder()\n .setName(\"Maximize Clicks #\" + getPrintableDateTime())\n .setTargetSpend(TargetSpend.getDefaultInstance())\n // Sets the currency of the new bidding strategy. If not provided, the bidding\n // strategy uses the manager account's default currency.\n .setCurrencyCode(\"USD\")\n .build();UseCrossAccountBiddingStrategy.java\n```\n\nExample:\n```text\nBiddingStrategy portfolioBiddingStrategy = new BiddingStrategy\n{\n Name = $\"Maximize clicks #{ExampleUtilities.GetRandomString()}\",\n TargetSpend = new TargetSpend(),\n // Set the currency of the new bidding strategy. If not provided, the bidding\n // strategy uses the manager account's default currency.\n CurrencyCode = \"USD\"\n};UseCrossAccountBiddingStrategy.cs\n```\n\nExample:\n```text\n$portfolioBiddingStrategy = new BiddingStrategy([\n 'name' => 'Maximize Clicks #' . Helper::getPrintableDatetime(),\n 'target_spend' => new TargetSpend(),\n // Optional: Sets the currency of the new bidding strategy to match the currency of the\n // client account with which this bidding strategy is shared.\n // If not provided, the bidding strategy uses the manager account's default currency.\n 'currency_code' => 'USD'\n]);UseCrossAccountBiddingStrategy.php\n```\n\nExample:\n```text\n# Constructs an operation that will create a portfolio bidding strategy.\nbidding_strategy_operation: BiddingStrategyOperation = client.get_type(\n \"BiddingStrategyOperation\"\n)\nbidding_strategy: BiddingStrategy = bidding_strategy_operation.create\nbidding_strategy.name = f\"Maximize Clicks #{uuid4()}\"\n# Sets target_spend to an empty TargetSpend object without setting any\n# of its nested fields.\nbidding_strategy.target_spend = client.get_type(\"TargetSpend\")\n# Sets the currency of the new bidding strategy. If not provided, the\n# bidding strategy uses the manager account's default currency.\nbidding_strategy.currency_code = \"USD\"use_cross_account_bidding_strategy.py\n```\n\nExample:\n```text\noperation = client.operation.create_resource.bidding_strategy do |b|\n b.name = \"Maximize Clicks ##{(Time.new.to_f * 1000).to_i}\"\n b.target_spend = client.resource.target_spend\n # Sets the currency of the new bidding strategy. If not provided, the\n # bidding strategy uses the manager account's default currency.\n b.currency_code = \"USD\"use_cross_account_bidding_strategy.rb\n```\n\nExample:\n```text\nmy $portfolio_bidding_strategy =\n Google::Ads::GoogleAds::V25::Resources::BiddingStrategy->new({\n name => \"Maximize clicks #\" . uniqid(),\n targetSpend => Google::Ads::GoogleAds::V25::Common::TargetSpend->new(),\n # Sets the currency of the new bidding strategy. If not provided, the\n # bidding strategy uses the manager account's default currency.\n currencyCode => \"USD\"\n });use_cross_account_bidding_strategy.pl\n```\n\nExample:\n```text\nprivate void listManagerOwnedBiddingStrategies(\n GoogleAdsClient googleAdsClient, long managerCustomerId) throws GoogleAdsException {\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n String query =\n \"SELECT bidding_strategy.id, \"\n + \"bidding_strategy.name, \"\n + \"bidding_strategy.type, \"\n + \"bidding_strategy.currency_code \"\n + \"FROM bidding_strategy\";\n // Constructs the SearchGoogleAdsStreamRequest.\n SearchGoogleAdsStreamRequest request =\n SearchGoogleAdsStreamRequest.newBuilder()\n .setCustomerId(Long.toString(managerCustomerId))\n .setQuery(query)\n .build();\n\n // Creates and issues a search Google Ads stream request that will retrieve all bidding\n // strategies.\n ServerStream<SearchGoogleAdsStreamResponse> stream =\n googleAdsServiceClient.searchStreamCallable().call(request);\n\n // Iterates through and prints all of the results in the stream response.\n System.out.printf(\n \"Cross-account bid strategies in manager account %d: %n\", managerCustomerId);\n for (SearchGoogleAdsStreamResponse response : stream) {\n for (GoogleAdsRow googleAdsRow : response.getResultsList()) {\n BiddingStrategy bs = googleAdsRow.getBiddingStrategy();\n System.out.printf(\" ID: %d%n\", bs.getId());\n System.out.printf(\" Name: %s%n\", bs.getName());\n System.out.printf(\" Strategy type: %s%n\", bs.getType());\n System.out.printf(\" Currency: %s%n\", bs.getCurrencyCode());\n System.out.println();\n }\n }\n }\n}UseCrossAccountBiddingStrategy.java\n```\n\nExample:\n```text\n/// <summary>\n/// Lists all cross-account bidding strategies in a specified manager account.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"managerCustomerId\">The manager customer ID.</param>\nprivate void ListManagerOwnedBiddingStrategies(GoogleAdsClient client,\n long managerCustomerId)\n{\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n // Create a GAQL query that will retrieve all cross-account bidding strategies.\n string query = @\"\n SELECT\n bidding_strategy.id,\n bidding_strategy.name,\n bidding_strategy.type,\n bidding_strategy.currency_code\n FROM bidding_strategy\";\n\n // Issue a streaming search request, then iterate through and print the results.\n googleAdsServiceClient.SearchStream(managerCustomerId.ToString(), query,\n delegate(SearchGoogleAdsStreamResponse resp)\n {\n Console.WriteLine(\"Cross-account bid strategies in manager account \" +\n $\"{managerCustomerId}:\");\n\n foreach (GoogleAdsRow googleAdsRow in resp.Results)\n {\n BiddingStrategy biddingStrategy = googleAdsRow.BiddingStrategy;\n\n Console.WriteLine($\"\\tID: {biddingStrategy.Id}\\n\" +\n $\"\\tName: {biddingStrategy.Name}\\n\" +\n \"\\tStrategy type: \" +\n $\"{Enum.GetName(typeof(BiddingStrategyType), biddingStrategy.Type)}\\n\" +\n $\"\\tCurrency: {biddingStrategy.CurrencyCode}\\n\\n\");\n }\n }\n );\n}UseCrossAccountBiddingStrategy.cs\n```\n\nExample:\n```text\nprivate static function listManagerOwnedBiddingStrategies(\n GoogleAdsClient $googleAdsClient,\n int $managerCustomerId\n) {\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n // Creates a query that retrieves all bidding strategies.\n $query = 'SELECT bidding_strategy.id, bidding_strategy.name, '\n . 'bidding_strategy.type, bidding_strategy.currency_code '\n . 'FROM bidding_strategy';\n // Issues a search stream request.\n /** @var GoogleAdsServerStreamDecorator $stream */\n $stream = $googleAdsServiceClient->searchStream(\n SearchGoogleAdsStreamRequest::build($managerCustomerId, $query)\n );\n\n // Iterates over all rows in all messages and prints the requested field values for\n // the bidding strategy in each row.\n printf(\n \"Cross-account bid strategies in manager account ID %d:%s\",\n $managerCustomerId,\n PHP_EOL\n );\n foreach ($stream->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n printf(\n ' ID: %1$d%2$s Name: \"%3$s\"%2$s Strategy type: \"%4$s\"%2$s'\n . ' Currency: \"%5$s\"%2$s%2$s',\n $googleAdsRow->getBiddingStrategy()->getId(),\n PHP_EOL,\n $googleAdsRow->getBiddingStrategy()->getName(),\n BiddingStrategyType::name($googleAdsRow->getBiddingStrategy()->getType()),\n $googleAdsRow->getBiddingStrategy()->getCurrencyCode()\n );\n }\n}UseCrossAccountBiddingStrategy.php\n```\n\nExample:\n```text\ndef list_manager_owned_bidding_strategies(\n client: GoogleAdsClient, manager_customer_id: str\n) -> None:\n \"\"\"List all cross-account bidding strategies in the manager account.\n\n Args:\n client: An initialized GoogleAdsClient instance.\n manager_customer_id: A manager customer ID.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n query = \"\"\"\n SELECT\n bidding_strategy.id,\n bidding_strategy.name,\n bidding_strategy.type,\n bidding_strategy.currency_code\n FROM bidding_strategy\"\"\"\n\n # Creates and issues a search Google Ads stream request that will retrieve\n # all bidding strategies.\n stream: Iterator[SearchGoogleAdsStreamResponse] = (\n googleads_service.search_stream(\n customer_id=manager_customer_id, query=query\n )\n )\n\n # Iterates through and prints all of the results in the stream response.\n print(\n \"Cross-account bid strategies in manager account: \"\n f\"{manager_customer_id}\"\n )\n response: SearchGoogleAdsStreamResponse\n for response in stream:\n row: GoogleAdsRow\n for row in response.results:\n bs: BiddingStrategy = row.bidding_strategy\n print(\n f\"\\tID: {bs.id}\\n\"\n f\"\\tName: {bs.name}\\n\"\n f\"\\tStrategy type: {bs.type_.name}\\n\"\n f\"\\tCurrency: {bs.currency_code}\\n\\n\"\n )use_cross_account_bidding_strategy.py\n```\n\nExample:\n```text\ndef list_manager_owned_bidding_strategies(client, manager_customer_id)\n query = <<~QUERY\n SELECT bidding_strategy.id,\n bidding_strategy.name,\n bidding_strategy.type,\n bidding_strategy.currency_code\n FROM bidding_strategy\n QUERY\n\n responses = client.service.google_ads.search_stream(\n customer_id: manager_customer_id,\n query: query,\n )\n\n puts \"Cross-account bid strategies in manager account #{manager_customer_id}:\"\n responses.each do |response|\n response.results.each do |row|\n b = row.bidding_strategy\n puts \"ID: #{b.id}\"\n puts \"Name: #{b.name}\"\n puts \"Strategy type: #{b.type}\"\n puts \"Currency: #{b.currency_code}\"\n puts\n end\n end\nenduse_cross_account_bidding_strategy.rb\n```\n\nExample:\n```text\n# Lists all cross-account bidding strategies in a specified manager account.\nsub _list_manager_owned_bidding_strategies {\n my ($api_client, $manager_customer_id) = @_;\n\n # Create a GAQL query that will retrieve all cross-account bidding\n # strategies.\n my $query = \"SELECT\n bidding_strategy.id,\n bidding_strategy.name,\n bidding_strategy.type,\n bidding_strategy.currency_code\n FROM bidding_strategy\";\n\n # Issue a streaming search request, then iterate through and print the\n # results.\n my $search_stream_handler =\n Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({\n service => $api_client->GoogleAdsService(),\n request =>\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsStreamRequest\n ->new({\n customerId => $manager_customer_id,\n query => $query\n })});\n\n printf\n \"Cross-account bid strategies in manager account $manager_customer_id:\\n\";\n $search_stream_handler->process_contents(\n sub {\n my $google_ads_row = shift;\n my $bidding_strategy = $google_ads_row->{biddingStrategy};\n printf \"\\tID: $bidding_strategy->{id}\\n\" .\n \"\\tName: $bidding_strategy->{name}\\n\" .\n \"\\tStrategy type: $bidding_strategy->{type}\\n\" .\n \"\\tCurrency: $bidding_strategy->{currencyCode}\\n\\n\";\n });\n}use_cross_account_bidding_strategy.pl\n```\n\nExample:\n```text\nprivate void listCustomerAccessibleBiddingStrategies(\n GoogleAdsClient googleAdsClient, long clientCustomerId) throws GoogleAdsException {\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n String query =\n \"SELECT accessible_bidding_strategy.id, \"\n + \"accessible_bidding_strategy.name, \"\n + \"accessible_bidding_strategy.type, \"\n + \"accessible_bidding_strategy.owner_customer_id, \"\n + \"accessible_bidding_strategy.owner_descriptive_name \"\n + \"FROM accessible_bidding_strategy \"\n // Uncomment the following WHERE clause to filter results to *only* cross-account bidding\n // strategies shared with the current customer by a manager (and not also include the\n // current customer's portfolio bidding strategies).\n // + \"WHERE accessible_bidding_strategy.owner_customer_id != \" + clientCustomerId;\n ;\n\n // Constructs the SearchGoogleAdsStreamRequest.\n SearchGoogleAdsStreamRequest request =\n SearchGoogleAdsStreamRequest.newBuilder()\n .setCustomerId(Long.toString(clientCustomerId))\n .setQuery(query)\n .build();\n\n // Creates and issues a search Google Ads stream request that will retrieve all accessible\n // bidding strategies.\n ServerStream<SearchGoogleAdsStreamResponse> stream =\n googleAdsServiceClient.searchStreamCallable().call(request);\n\n // Iterates through and prints all of the results in the stream response.\n System.out.printf(\"All bid strategies accessible by account %d: %n\", clientCustomerId);\n for (SearchGoogleAdsStreamResponse response : stream) {\n for (GoogleAdsRow googleAdsRow : response.getResultsList()) {\n AccessibleBiddingStrategy bs = googleAdsRow.getAccessibleBiddingStrategy();\n System.out.printf(\" ID: %d%n\", bs.getId());\n System.out.printf(\" Name: %s%n\", bs.getName());\n System.out.printf(\" Strategy type: %s%n\", bs.getType());\n System.out.printf(\" Owner customer ID: %d%n\", bs.getOwnerCustomerId());\n System.out.printf(\" Owner description: %s%n\", bs.getOwnerDescriptiveName());\n System.out.println();\n }\n }\n }\n}UseCrossAccountBiddingStrategy.java\n```\n\nExample:\n```text\n/// <summary>\n/// Lists all bidding strategies available to specified client customer account. This\n/// includes both portfolio bidding strategies owned by the client customer account and\n/// cross-account bidding strategies shared by any of its managers.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads client customer ID for which the call is\n/// made.</param>\nprivate void ListCustomerAccessibleBiddingStrategies(GoogleAdsClient client,\n long customerId)\n{\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n // Create a GAQL query that will retrieve all accessible bidding strategies.\n string query = @\"\n SELECT\n accessible_bidding_strategy.resource_name,\n accessible_bidding_strategy.id,\n accessible_bidding_strategy.name,\n accessible_bidding_strategy.type,\n accessible_bidding_strategy.owner_customer_id,\n accessible_bidding_strategy.owner_descriptive_name\n FROM accessible_bidding_strategy\";\n\n // Uncomment the following WHERE clause addition to the query to filter results to\n // *only* cross-account bidding strategies shared with the current customer by a manager\n // (and not also include the current customer's portfolio bidding strategies).\n // query += $\" WHERE accessible_bidding_strategy.owner_customer_id != {customerId}\";\n\n // Issue a streaming search request, then iterate through and print the results.\n googleAdsServiceClient.SearchStream(customerId.ToString(), query,\n delegate(SearchGoogleAdsStreamResponse resp)\n {\n Console.WriteLine($\"All bid strategies accessible by account {customerId}:\");\n\n foreach (GoogleAdsRow googleAdsRow in resp.Results)\n {\n AccessibleBiddingStrategy biddingStrategy =\n googleAdsRow.AccessibleBiddingStrategy;\n\n Console.WriteLine($\"\\tID: {biddingStrategy.Id}\\n\" +\n $\"\\tName: {biddingStrategy.Name}\\n\" +\n $\"\\tStrategy type: {biddingStrategy.Type.ToString()}\\n\" +\n $\"\\tOwner customer ID: {biddingStrategy.OwnerCustomerId}\\n\" +\n $\"\\tOwner description: {biddingStrategy.OwnerDescriptiveName}\\n\\n\");\n }\n }\n );\n}UseCrossAccountBiddingStrategy.cs\n```\n\nExample:\n```text\nprivate static function listCustomerAccessibleBiddingStrategies(\n GoogleAdsClient $googleAdsClient,\n int $clientCustomerId\n) {\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n // Creates a query that retrieves all bidding strategies.\n $query = 'SELECT accessible_bidding_strategy.id, '\n . 'accessible_bidding_strategy.name, '\n . 'accessible_bidding_strategy.type, '\n . 'accessible_bidding_strategy.owner_customer_id, '\n . 'accessible_bidding_strategy.owner_descriptive_name '\n . 'FROM accessible_bidding_strategy '\n // Uncomment the following WHERE clause to filter results to *only* cross-account\n // bidding strategies shared with the current customer by a manager (and not also\n // include the current customer's portfolio bidding strategies).\n // . 'WHERE accessible_bidding_strategy.owner_customer_id != ' . $clientCustomerId\n ;\n // Issues a search stream request.\n /** @var GoogleAdsServerStreamDecorator $stream */\n $stream = $googleAdsServiceClient->searchStream(\n SearchGoogleAdsStreamRequest::build($clientCustomerId, $query)\n );\n\n // Iterates over all rows in all messages and prints the requested field values for\n // each accessible bidding strategy.\n printf(\n \"All bid strategies accessible by the customer ID %d:%s\",\n $clientCustomerId,\n PHP_EOL\n );\n foreach ($stream->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n printf(\n ' ID: %1$d%2$s Name: \"%3$s\"%2$s Strategy type: \"%4$s\"%2$s'\n . ' Owner customer ID: %5$d%2$s Owner customer description: \"%6$s\"%2$s%2$s',\n $googleAdsRow->getAccessibleBiddingStrategy()->getId(),\n PHP_EOL,\n $googleAdsRow->getAccessibleBiddingStrategy()->getName(),\n BiddingStrategyType::name($googleAdsRow->getAccessibleBiddingStrategy()->getType()),\n $googleAdsRow->getAccessibleBiddingStrategy()->getOwnerCustomerId(),\n $googleAdsRow->getAccessibleBiddingStrategy()->getOwnerDescriptiveName()\n );\n }\n}UseCrossAccountBiddingStrategy.php\n```\n\nExample:\n```text\ndef list_customer_accessible_bidding_strategies(\n client: GoogleAdsClient, customer_id: str\n) -> None:\n \"\"\"Lists all bidding strategies available to the client account.\n\n This includes both portfolio bidding strategies owned by account and\n cross-account bidding strategies shared by any of its managers.\n\n Args:\n client: An initialized GoogleAdsClient instance.\n customer_id: A client customer ID.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n query = \"\"\"\n SELECT\n accessible_bidding_strategy.id,\n accessible_bidding_strategy.name,\n accessible_bidding_strategy.type,\n accessible_bidding_strategy.owner_customer_id,\n accessible_bidding_strategy.owner_descriptive_name\n FROM accessible_bidding_strategy\"\"\"\n # Uncomment the following WHERE clause to filter results to *only*\n # cross-account bidding strategies shared with the current customer by a\n # manager (and not also include the current customer's portfolio\n # bidding strategies).\n #\n # query += f\"WHERE accessible_bidding_strategy.owner_customer_id != {customer_id}\"\n\n # Creates and issues a search Google Ads stream request that will retrieve\n # all bidding strategies.\n stream: Iterator[SearchGoogleAdsStreamResponse] = (\n googleads_service.search_stream(customer_id=customer_id, query=query)\n )\n\n # Iterates through and prints all of the results in the stream response.\n print(f\"All bid strategies accessible by account '{customer_id}'\\n\")\n response: SearchGoogleAdsStreamResponse\n for response in stream:\n row: GoogleAdsRow\n for row in response.results:\n bs: AccessibleBiddingStrategy = row.accessible_bidding_strategy\n print(\n f\"\\tID: {bs.id}\\n\"\n f\"\\tName: {bs.name}\\n\"\n f\"\\tStrategy type: {bs.type_.name}\\n\"\n f\"\\tOwner customer ID: {bs.owner_customer_id}\\n\"\n f\"\\tOwner description: {bs.owner_descriptive_name}\\n\\n\"\n )use_cross_account_bidding_strategy.py\n```\n\nExample:\n```text\ndef list_customer_accessible_bidding_strategies(client, customer_id)\n query = <<~QUERY\n SELECT accessible_bidding_strategy.id,\n accessible_bidding_strategy.name,\n accessible_bidding_strategy.type,\n accessible_bidding_strategy.owner_customer_id,\n accessible_bidding_strategy.owner_descriptive_name\n FROM accessible_bidding_strategy\n QUERY\n # Add the following WHERE clause to filter results to *only*\n # cross-account bidding strategies shared with the current customer by a\n # manager (and not also include the current customer's portfolio bidding\n # strategies).\n # query += <<~QUERY\n # WHERE accessible_bidding_strategy.owner_customer_id != #{customer_id}\n # QUERY\n\n responses = client.service.google_ads.search_stream(\n customer_id: customer_id,\n query: query,\n )\n\n puts \"All bid strategies accessible by account #{customer_id}:\"\n responses.each do |response|\n response.results.each do |row|\n b = row.accessible_bidding_strategy\n puts \"ID: #{b.id}\"\n puts \"Name: #{b.name}\"\n puts \"Strategy type: #{b.type}\"\n puts \"Owner customer ID: #{b.owner_customer_id}\"\n puts \"Owner description: #{b.owner_descriptive_name}\"\n puts\n end\n end\nenduse_cross_account_bidding_strategy.rb\n```\n\nExample:\n```text\n# Lists all bidding strategies available to specified client customer account.\n# This includes both portfolio bidding strategies owned by the client customer\n# account and cross-account bidding strategies shared by any of its managers.\nsub _list_customer_accessible_bidding_strategies {\n my ($api_client, $customer_id) = @_;\n\n # Create a GAQL query that will retrieve all accessible bidding strategies.\n my $query = \"SELECT\n accessible_bidding_strategy.resource_name,\n accessible_bidding_strategy.id,\n accessible_bidding_strategy.name,\n accessible_bidding_strategy.type,\n accessible_bidding_strategy.owner_customer_id,\n accessible_bidding_strategy.owner_descriptive_name\n FROM accessible_bidding_strategy\";\n\n # Uncomment the following WHERE clause addition to the query to filter results\n # to *only* cross-account bidding strategies shared with the current customer\n # by a manager (and not also include the current customer's portfolio bidding\n # strategies).\n # $query .=\n # \" WHERE accessible_bidding_strategy.owner_customer_id != $customer_id\";\n\n # Issue a streaming search request, then iterate through and print the\n # results.\n my $search_stream_handler =\n Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({\n service => $api_client->GoogleAdsService(),\n request =>\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsStreamRequest\n ->new({\n customerId => $customer_id,\n query => $query\n })});\n\n printf \"All bid strategies accessible by account $customer_id:\\n\";\n $search_stream_handler->process_contents(\n sub {\n my $google_ads_row = shift;\n my $bidding_strategy = $google_ads_row->{accessibleBiddingStrategy};\n printf \"\\tID: $bidding_strategy->{id}\\n\" .\n \"\\tName: $bidding_strategy->{name}\\n\" .\n \"\\tStrategy type: $bidding_strategy->{type}\\n\" .\n \"\\tOwner customer ID: $bidding_strategy->{ownerCustomerId}\\n\" .\n \"\\tOwner description: $bidding_strategy->{ownerDescriptiveName}\\n\\n\";\n });\n}use_cross_account_bidding_strategy.pl\n```\n\nExample:\n```text\nSELECT campaign.id,\n campaign.name,\n campaign.bidding_strategy,\n campaign.bidding_strategy_type,\n accessible_bidding_strategy.id,\n accessible_bidding_strategy.name,\n accessible_bidding_strategy.type,\n accessible_bidding_strategy.owner_customer_id,\n accessible_bidding_strategy.owner_descriptive_name,\n bidding_strategy.name,\n bidding_strategy.type\nFROM campaign\nWHERE campaign.status != REMOVED\n```\n\nExample:\n```text\nprivate void attachCrossAccountBiddingStrategyToCampaign(\n GoogleAdsClient googleAdsClient,\n long clientCustomerId,\n long campaignId,\n String biddingStrategyResourceName)\n throws GoogleAdsException {\n\n try (CampaignServiceClient campaignServiceClient =\n googleAdsClient.getLatestVersion().createCampaignServiceClient()) {\n Campaign campaign =\n Campaign.newBuilder()\n .setResourceName(ResourceNames.campaign(clientCustomerId, campaignId))\n .setBiddingStrategy(biddingStrategyResourceName)\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n .build();\n CampaignOperation operation =\n CampaignOperation.newBuilder()\n .setUpdate(campaign)\n .setUpdateMask(FieldMasks.allSetFieldsOf(campaign))\n .build();\n // Sends the operation in a mutate request.\n MutateCampaignsResponse response =\n campaignServiceClient.mutateCampaigns(\n Long.toString(clientCustomerId), ImmutableList.of(operation));\n\n MutateCampaignResult mutateCampaignResult = response.getResults(0);\n // Prints the resource name of the updated campaign.\n System.out.printf(\n \"Updated campaign with resource name: '%s'.%n\", mutateCampaignResult.getResourceName());\n }\n}UseCrossAccountBiddingStrategy.java\n```\n\nExample:\n```text\n/// <summary>\n/// Attaches a specified cross-account bidding strategy to a campaign owned by a specified\n/// client customer account.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads client customer ID for which the call is\n/// made.</param>\n/// <param name=\"campaignId\">The ID of the campaign owned by the customer ID to which the\n/// cross-account bidding strategy will be attached.</param>\n/// <param name=\"biddingStrategyResourceName\">A cross-account bidding strategy resource\n/// name.</param>\nprivate void AttachCrossAccountBiddingStrategyToCampaign(GoogleAdsClient client,\n long customerId, long campaignId, string biddingStrategyResourceName)\n{\n CampaignServiceClient campaignServiceClient =\n client.GetService(Services.V25.CampaignService);\n\n Campaign campaign = new Campaign\n {\n ResourceName = ResourceNames.Campaign(customerId, campaignId),\n BiddingStrategy = biddingStrategyResourceName\n };\n\n // Mutate the campaign and print the resource name of the updated campaign.\n MutateCampaignsResponse mutateCampaignsResponse =\n campaignServiceClient.MutateCampaigns(customerId.ToString(), new[]\n {\n new CampaignOperation\n {\n Update = campaign,\n UpdateMask = FieldMasks.AllSetFieldsOf(campaign)\n }\n });\n\n Console.WriteLine(\"Updated campaign with resource name \" +\n $\"'{mutateCampaignsResponse.Results.First().ResourceName}'.\");\n}UseCrossAccountBiddingStrategy.cs\n```\n\nExample:\n```text\nprivate static function attachCrossAccountBiddingStrategyToCampaign(\n GoogleAdsClient $googleAdsClient,\n int $clientCustomerId,\n int $campaignId,\n string $biddingStrategyResourceName\n) {\n // Creates a campaign using the specified campaign ID and the bidding strategy ID.\n // Note that a cross-account bidding strategy's resource name should use the\n // client's customer ID when attaching it to a campaign, not that of the manager that owns\n // the strategy.\n $campaign = new Campaign([\n 'resource_name' => ResourceNames::forCampaign($clientCustomerId, $campaignId),\n 'bidding_strategy' => $biddingStrategyResourceName\n ]);\n\n // Constructs an operation that will update the campaign with the specified resource name,\n // using the FieldMasks utility to derive the update mask. This mask tells the Google Ads\n // API which attributes of the campaign you want to change.\n $campaignOperation = new CampaignOperation();\n $campaignOperation->setUpdate($campaign);\n $campaignOperation->setUpdateMask(FieldMasks::allSetFieldsOf($campaign));\n\n // Issues a mutate request to update the campaign.\n $campaignServiceClient = $googleAdsClient->getCampaignServiceClient();\n $response = $campaignServiceClient->mutateCampaigns(\n MutateCampaignsRequest::build($clientCustomerId, [$campaignOperation])\n );\n\n // Prints information about the updated campaign.\n printf(\n \"Updated campaign with resource name: '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n}UseCrossAccountBiddingStrategy.php\n```\n\nExample:\n```text\ndef attach_cross_account_bidding_strategy_to_campaign(\n client: GoogleAdsClient,\n customer_id: str,\n campaign_id: str,\n bidding_strategy_resource_name: str,\n) -> None:\n \"\"\"Attaches the cross-account bidding strategy to the given campaign.\n\n Args:\n client: An initialized GoogleAdsClient instance.\n customer_id: A client customer ID.\n campaign_id: The ID of an existing campaign in the client customer's\n account.\n bidding_strategy_resource_name: The ID of a bidding strategy\n \"\"\"\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n campaign_operation: CampaignOperation = client.get_type(\"CampaignOperation\")\n campaign: Campaign = campaign_operation.update\n campaign.resource_name = campaign_service.campaign_path(\n customer_id, campaign_id\n )\n campaign.bidding_strategy = bidding_strategy_resource_name\n client.copy_from(\n campaign_operation.update_mask,\n protobuf_helpers.field_mask(None, campaign._pb),\n )\n\n # Sends the operation in a mutate request.\n response: MutateCampaignsResponse = campaign_service.mutate_campaigns(\n customer_id=customer_id, operations=[campaign_operation]\n )\n\n # Prints the resource name of the updated campaign.\n print(\n \"Updated campaign with resource name: \"\n f\"'{response.results[0].resource_name}'\"\n )use_cross_account_bidding_strategy.py\n```\n\nExample:\n```text\ndef attach_cross_account_bidding_strategy_to_campaign(\n client,\n customer_id,\n campaign_id,\n bidding_strategy_resource_name)\n operation = client.operation.update_resource.campaign(\n client.path.campaign(customer_id, campaign_id)) do |c|\n c.bidding_strategy = bidding_strategy_resource_name\n end\n\n # Sends the operation in a mutate request.\n response = client.service.campaign.mutate_campaigns(\n customer_id: customer_id,\n operations: [operation],\n )\n\n puts \"Updated campaign with resource name: \" \\\n \"`#{response.results.first.resource_name}`\"\nenduse_cross_account_bidding_strategy.rb\n```\n\nExample:\n```text\n# Attaches a specified cross-account bidding strategy to a campaign owned by a\n# specified client customer account.\nsub _attach_cross_account_bidding_strategy_to_campaign {\n my ($api_client, $customer_id, $campaign_id, $bidding_strategy_resource_name)\n = @_;\n\n my $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $campaign_id\n ),\n biddingStrategy => $bidding_strategy_resource_name\n });\n\n my $campaign_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation->\n new({\n update => $campaign,\n updateMask => all_set_fields_of($campaign)});\n\n my $campaigns_response = $api_client->CampaignService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_operation]});\n\n printf \"Updated campaign with resource name '%s'.\\n\",\n $campaigns_response->{results}[0]{resourceName};\n}use_cross_account_bidding_strategy.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.327Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":31,"totalLines":1069,"estimatedTokens":10420}}141{"id":"doc-create_uploaded_display_ads_google_ads_api_googl-2bdd57b3","source":"documentation","title":"Create Uploaded Display Ads | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/display-upload-ads/create-display-upload-ad","text":"Example:\n```text\n// Copyright 2020 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.advancedoperations;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.AdMediaBundleAsset;\nimport com.google.ads.googleads.v25.common.DisplayUploadAdInfo;\nimport com.google.ads.googleads.v25.common.MediaBundleAsset;\nimport com.google.ads.googleads.v25.enums.AdGroupAdStatusEnum.AdGroupAdStatus;\nimport com.google.ads.googleads.v25.enums.AssetTypeEnum.AssetType;\nimport com.google.ads.googleads.v25.enums.DisplayUploadProductTypeEnum.DisplayUploadProductType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.Ad;\nimport com.google.ads.googleads.v25.resources.AdGroupAd;\nimport com.google.ads.googleads.v25.resources.Asset;\nimport com.google.ads.googleads.v25.services.AdGroupAdOperation;\nimport com.google.ads.googleads.v25.services.AdGroupAdServiceClient;\nimport com.google.ads.googleads.v25.services.AssetOperation;\nimport com.google.ads.googleads.v25.services.AssetServiceClient;\nimport com.google.ads.googleads.v25.services.MutateAdGroupAdsResponse;\nimport com.google.ads.googleads.v25.services.MutateAssetsResponse;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.io.ByteStreams;\nimport com.google.protobuf.ByteString;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.net.URL;\n\n/** Adds a display upload ad to a given ad group. To get ad groups, run GetAdGroups.java. */\npublic class AddDisplayUploadAd {\n\n private static final String BUNDLE_URL = \"https://gaagl.page.link/ib87\";\n\n private static class AddDisplayUploadAdParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(names = ArgumentNames.AD_GROUP_ID, required = true)\n private Long adGroupId;\n }\n\n public static void main(String[] args) throws IOException {\n AddDisplayUploadAdParams params = new AddDisplayUploadAdParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.adGroupId = Long.parseLong(\"INSERT_AD_GROUP_ID_HERE\");\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new AddDisplayUploadAd().runExample(googleAdsClient, params.customerId, params.adGroupId);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param adGroupId the ad group ID.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void runExample(GoogleAdsClient googleAdsClient, long customerId, long adGroupId)\n throws IOException {\n // There are several types of display upload ads. For this example, we will create\n // an HTML5 upload ad, which requires a media bundle.\n // The DisplayUploadProductType field lists the available display upload types:\n // https://developers.google.com/google-ads/api/reference/rpc/v4/DisplayUploadAdInfo\n\n // Creates a new media bundle asset and returns the resource name.\n String adAssetResourceName = createMediaBundleAsset(googleAdsClient, customerId);\n\n // Creates a new display upload ad and associates it with the specified ad group.\n createDisplayUploadAdGroupAd(googleAdsClient, customerId, adGroupId, adAssetResourceName);\n }\n\n /**\n * Creates a media bundle from the assets in a zip file. The zip file contains the HTML5\n * components.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @return the resource name of the newly created media bundle.\n * @throws IOException if there is an error reading the media bundle.\n */\n private String createMediaBundleAsset(GoogleAdsClient googleAdsClient, long customerId)\n throws IOException {\n // The HTML5 zip file contains all the HTML, CSS, and images needed for the\n // HTML5 ad. For help on creating an HTML5 zip file, check out Google Web\n // Designer (https://www.google.com/webdesigner/).\n //\n // There are several types of display upload ads. For this example, we will create\n // an HTML5 upload ad, which requires a media bundle.\n // The DisplayUploadProductType field lists the available display upload types:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/DisplayUploadAdInfo\n byte[] html5Zip = ByteStreams.toByteArray(new URL(BUNDLE_URL).openStream());\n\n // Creates the media bundle asset.\n Asset asset =\n Asset.newBuilder()\n .setName(\"Ad Media Bundle\")\n .setType(AssetType.MEDIA_BUNDLE)\n .setMediaBundleAsset(\n MediaBundleAsset.newBuilder().setData(ByteString.copyFrom(html5Zip)).build())\n .build();\n\n // Creates the asset operation.\n AssetOperation operation = AssetOperation.newBuilder().setCreate(asset).build();\n\n // Gets the AssetService.\n try (AssetServiceClient assetServiceClient =\n googleAdsClient.getLatestVersion().createAssetServiceClient()) {\n // Adds the asset to the client account.\n MutateAssetsResponse response =\n assetServiceClient.mutateAssets(Long.toString(customerId), ImmutableList.of(operation));\n // Displays and returns the resulting resource name.\n String uploadedAssetResourceName = response.getResults(0).getResourceName();\n System.out.printf(\n \"Uploaded media bundle with resource name: '%s'.%n\", uploadedAssetResourceName);\n return uploadedAssetResourceName;\n }\n }\n\n /**\n * Creates a new HTML5 display upload ad and adds it to the specified ad group.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param adGroupId the ad group ID.\n * @param adAssetResourceName The ID of the ad group to which the new ad will be added.\n */\n private void createDisplayUploadAdGroupAd(\n GoogleAdsClient googleAdsClient,\n long customerId,\n long adGroupId,\n String adAssetResourceName) {\n // Creates the ad with the required fields.\n Ad displayUploadAd =\n Ad.newBuilder()\n .setName(\"Ad for HTML5\")\n .addFinalUrls(\"http://example.com/html5\")\n // Exactly one ad data field must be included to specify the ad type. See\n // https://developers.google.com/google-ads/api/reference/rpc/v4/Ad for the full\n // list of available types.\n .setDisplayUploadAd(\n DisplayUploadAdInfo.newBuilder()\n .setDisplayUploadProductType(DisplayUploadProductType.HTML5_UPLOAD_AD)\n .setMediaBundle(\n AdMediaBundleAsset.newBuilder().setAsset(adAssetResourceName).build())\n .build())\n .build();\n\n // Creates an ad group ad for the new ad.\n AdGroupAd adGroupAd =\n AdGroupAd.newBuilder()\n .setAd(displayUploadAd)\n .setStatus(AdGroupAdStatus.PAUSED)\n .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))\n .build();\n\n // Creates the ad group ad operation.\n AdGroupAdOperation operation = AdGroupAdOperation.newBuilder().setCreate(adGroupAd).build();\n\n // Creates the ad group ad service client.\n try (AdGroupAdServiceClient adGroupAdServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupAdServiceClient()) {\n // Adds the ad group ad to the client account.\n MutateAdGroupAdsResponse response =\n adGroupAdServiceClient.mutateAdGroupAds(\n Long.toString(customerId), ImmutableList.of(operation));\n\n // Displays the resulting ad group ad's resource name.\n System.out.printf(\n \"Created new ad group ad with resource name: '%s'.%n\",\n response.getResults(0).getResourceName());\n }\n }\n}\nAddDisplayUploadAd.java\n```\n\nExample:\n```text\n// Copyright 2020 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.Gax.Util;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.Util;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Enums;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing Google.Protobuf;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing static Google.Ads.GoogleAds.V25.Enums.DisplayUploadProductTypeEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example adds a display upload ad to a given ad group. To get ad groups,\n /// run GetAdGroups.cs.\n /// </summary>\n public class AddDisplayUploadAd : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"AddDisplayUploadAd\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// The ID of the ad group to which the new ad will be added.\n /// </summary>\n [Option(\"adGroupId\", Required = true, HelpText =\n \"The ID of the ad group to which the new ad will be added.\")]\n public long AdGroupId { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n AddDisplayUploadAd codeExample = new AddDisplayUploadAd();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.AdGroupId);\n }\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example adds a display upload ad to a given ad group. To get ad groups, \" +\n \"run GetAdGroups.cs.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"adGroupId\">The ID of the ad group to which the new ad will be\n /// added.</param>\n public void Run(GoogleAdsClient client, long customerId, long adGroupId)\n {\n try\n {\n // There are several types of display upload ads. For this example, we will create\n // an HTML5 upload ad, which requires a media bundle.\n // This feature is only available to allowlisted accounts.\n // See https://support.google.com/google-ads/answer/1722096 for more details.\n // The DisplayUploadProductType field lists the available display upload types:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/DisplayUploadAdInfo\n // Creates a new media bundle asset and returns the resource name.\n string adAssetResourceName = CreateMediaBundleAsset(client, customerId);\n\n // Creates a new display upload ad and associates it with the specified ad group.\n CreateDisplayUploadAdGroupAd(client, customerId, adGroupId, adAssetResourceName);\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Creates a media bundle from the assets in a zip file. The zip file contains the\n /// HTML5 components.\n /// </summary>\n /// <param name=\"client\">The Google Ads API client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <returns>The string resource name of the newly uploaded media bundle.</returns>\n private string CreateMediaBundleAsset(GoogleAdsClient client, long customerId)\n {\n // Gets the AssetService.\n AssetServiceClient assetServiceClient = client.GetService(Services.V25.AssetService);\n\n // The HTML5 zip file contains all the HTML, CSS, and images needed for the\n // HTML5 ad. For help on creating an HTML5 zip file, check out Google Web\n // Designer (https://www.google.com/webdesigner/).\n byte[] html5Zip = MediaUtilities.GetAssetDataFromUrl(\"https://gaagl.page.link/ib87\",\n client.Config);\n\n // Creates the media bundle asset.\n Asset mediaBundleAsset = new Asset()\n {\n Type = AssetTypeEnum.Types.AssetType.MediaBundle,\n MediaBundleAsset = new MediaBundleAsset()\n {\n Data = ByteString.CopyFrom(html5Zip)\n },\n Name = \"Ad Media Bundle\"\n };\n\n // Creates the asset operation.\n AssetOperation operation = new AssetOperation()\n {\n Create = mediaBundleAsset\n };\n\n // Adds the asset to the client account.\n MutateAssetsResponse response = assetServiceClient.MutateAssets(customerId.ToString(),\n new[] { operation });\n\n // Displays the resulting resource name.\n string uploadedAssetResourceName = response.Results.First().ResourceName;\n Console.WriteLine($\"Uploaded media bundle: {uploadedAssetResourceName}\");\n\n return uploadedAssetResourceName;\n }\n\n /// <summary>\n /// Creates a new HTML5 display upload ad and adds it to the specified ad group.\n /// </summary>\n /// <param name=\"client\">The Google Ads API client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"adGroupId\">The ID of the ad group to which the new ad will be\n /// added.</param>\n /// <param name=\"adAssetResourceName\">The resource name of the media bundle containing\n /// the HTML5 components.</param>\n private void CreateDisplayUploadAdGroupAd(GoogleAdsClient client, long customerId,\n long adGroupId, string adAssetResourceName)\n {\n // Get the AdGroupAdService.\n AdGroupAdServiceClient adGroupAdServiceClient =\n client.GetService(Services.V25.AdGroupAdService);\n\n // Creates the ad with the required fields.\n Ad displayUploadAd = new Ad()\n {\n Name = \"Ad for HTML5\",\n FinalUrls = { \"http://example.com/html5\" },\n // Exactly one ad data field must be included to specify the ad type. See\n // https://developers.google.com/google-ads/api/reference/rpc/latest/Ad for the\n // full list of available types.\n DisplayUploadAd = new DisplayUploadAdInfo()\n {\n DisplayUploadProductType = DisplayUploadProductType.Html5UploadAd,\n MediaBundle = new AdMediaBundleAsset()\n {\n Asset = adAssetResourceName\n }\n }\n };\n\n // Creates an ad group ad for the new ad.\n AdGroupAd adGroupAd = new AdGroupAd()\n {\n Ad = displayUploadAd,\n Status = AdGroupAdStatusEnum.Types.AdGroupAdStatus.Paused,\n AdGroup = ResourceNames.AdGroup(customerId, adGroupId),\n };\n\n // Creates the ad group ad operation.\n AdGroupAdOperation operation = new AdGroupAdOperation()\n {\n Create = adGroupAd\n };\n\n // Adds the ad group ad to the client account.\n MutateAdGroupAdsResponse response = adGroupAdServiceClient.MutateAdGroupAds\n (customerId.ToString(), new[] { operation });\n\n // Displays the resulting ad group ad's resource name.\n Console.WriteLine($\"Created new ad group ad{response.Results.First().ResourceName}.\");\n }\n }\n}\nAddDisplayUploadAd.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2020 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\AdvancedOperations;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\AdMediaBundleAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\DisplayUploadAdInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\MediaBundleAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdGroupAdStatusEnum\\AdGroupAdStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetTypeEnum\\AssetType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\DisplayUploadProductTypeEnum\\DisplayUploadProductType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Ad;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AdGroupAd;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Asset;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AdGroupAdOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAdGroupAdsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAdGroupAdsResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAssetsRequest;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * This code example adds a display upload ad to a given ad group.\n * To get ad groups, run GetAdGroups.php.\n */\nclass AddDisplayUploadAd\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const AD_GROUP_ID = 'INSERT_AD_GROUP_ID_HERE';\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::AD_GROUP_ID => GetOpt::REQUIRED_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::AD_GROUP_ID] ?: self::AD_GROUP_ID\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $adGroupId the ad group ID to add a display upload ad to\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId\n ) {\n // There are several types of display upload ads. For this example, we will create\n // an HTML5 upload ad, which requires a media bundle.\n // The DisplayUploadProductType field lists the available display upload types:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/DisplayUploadAdInfo\n // Creates a new media bundle asset and returns the resource name.\n $adAssetResourceName = self::createMediaBundleAsset($googleAdsClient, $customerId);\n\n // Creates a new display upload ad and associates it with the specified ad group.\n self::createDisplayUploadAdGroupAd(\n $googleAdsClient,\n $customerId,\n $adGroupId,\n $adAssetResourceName\n );\n }\n\n /**\n * Creates a media bundle from the assets in a zip file. The zip file contains the HTML5\n * components.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @return string the resource name of the newly uploaded media bundle asset\n */\n private static function createMediaBundleAsset(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n ) {\n // The HTML5 zip file contains all the HTML, CSS, and images needed for the\n // HTML5 ad. For help on creating an HTML5 zip file, check out Google Web\n // Designer (https://www.google.com/webdesigner/).\n $html5Zip = file_get_contents('https://gaagl.page.link/ib87');\n\n // Creates the media bundle asset.\n $asset = new Asset([\n 'name' => 'Ad Media Bundle',\n 'type' => AssetType::MEDIA_BUNDLE,\n 'media_bundle_asset' => new MediaBundleAsset(['data' => $html5Zip])\n ]);\n\n // Creates an asset operation.\n $assetOperation = new AssetOperation();\n $assetOperation->setCreate($asset);\n\n // Issues a mutate request to add the asset.\n $assetServiceClient = $googleAdsClient->getAssetServiceClient();\n $response = $assetServiceClient->mutateAssets(\n MutateAssetsRequest::build($customerId, [$assetOperation])\n );\n\n // Prints the resource name of the added media bundle asset.\n $addedMediaBundleAssetResourceName = $response->getResults()[0]->getResourceName();\n printf(\n \"Uploaded media bundle asset with resource name: '%s'.%s\",\n $addedMediaBundleAssetResourceName,\n PHP_EOL\n );\n\n return $addedMediaBundleAssetResourceName;\n }\n\n /**\n * Creates a new HTML5 display upload ad and adds it to the specified ad group.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $adGroupId the ad group ID where the new ad will be added to\n * @param string $adAssetResourceName the resource name of the media bundle containing the\n * HTML5 components\n */\n private static function createDisplayUploadAdGroupAd(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n string $adAssetResourceName\n ) {\n // Creates an ad group ad for the new ad.\n $adGroupAd = new AdGroupAd([\n 'ad' => new Ad([\n 'name' => 'Ad for HTML5',\n 'final_urls' => ['http://example.com/html5'],\n // Exactly one ad data field must be included to specify the ad type. See\n // https://developers.google.com/google-ads/api/reference/rpc/latest/Ad for the full\n // list of available types.\n 'display_upload_ad' => new DisplayUploadAdInfo([\n 'display_upload_product_type' => DisplayUploadProductType::HTML5_UPLOAD_AD,\n 'media_bundle' => new AdMediaBundleAsset(['asset' => $adAssetResourceName])\n ])\n ]),\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'status' => AdGroupAdStatus::PAUSED\n ]);\n\n // Creates an ad group ad operation.\n $adGroupAdOperation = new AdGroupAdOperation();\n $adGroupAdOperation->setCreate($adGroupAd);\n\n // Issues a mutate request to add the ad group ad.\n $adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();\n /** @var MutateAdGroupAdsResponse $adGroupAdResponse */\n $adGroupAdResponse = $adGroupAdServiceClient->mutateAdGroupAds(\n MutateAdGroupAdsRequest::build($customerId, [$adGroupAdOperation])\n );\n\n // Prints information about the newly created ad group ad.\n $adGroupAdResourceName = $adGroupAdResponse->getResults()[0]->getResourceName();\n printf(\"Created ad group ad with resource name: '%s'.%s\", $adGroupAdResourceName, PHP_EOL);\n }\n}\n\nAddDisplayUploadAd::main();\nAddDisplayUploadAd.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2020 Google LLC\n#\n# 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.\n\"\"\"Adds a display upload ad to a given ad group.\n\nTo get ad groups, run get_ad_groups.py.\n\"\"\"\n\nimport argparse\nimport logging\nimport requests\nimport sys\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.resources.types.ad import Ad\nfrom google.ads.googleads.v24.resources.types.ad_group_ad import AdGroupAd\nfrom google.ads.googleads.v24.resources.types.asset import Asset\nfrom google.ads.googleads.v24.services.services.ad_group_ad_service import (\n AdGroupAdServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.asset_service import (\n AssetServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_ad_service import (\n AdGroupAdOperation,\n MutateAdGroupAdsResponse,\n)\nfrom google.ads.googleads.v24.services.types.asset_service import (\n AssetOperation,\n MutateAssetsResponse,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\nBUNDLE_URL: str = \"https://gaagl.page.link/ib87\"\n\n\ndef main(client: GoogleAdsClient, customer_id: str, ad_group_id: str) -> None:\n \"\"\"Adds a display upload ad to a given ad group.\n\n Args:\n client: An initialized Google Ads client.\n customer_id: The Google Ads customer ID.\n ad_group_id: The ID of the ad group to which the new ad will be added.\n \"\"\"\n # There are several types of display upload ads. For this example, we will\n # create an HTML5 upload ad, which requires a media bundle.\n # This feature is only available to allowlisted accounts.\n # See https://support.google.com/google-ads/answer/1722096 for more details.\n # The DisplayUploadProductType field lists the available display upload types:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/DisplayUploadAdInfo\n\n # Creates a new media bundle asset and returns the resource name.\n ad_asset_resource_name: str = create_media_bundle_asset(client, customer_id)\n\n # Creates a new display upload ad and associates it with the specified\n # ad group.\n create_display_upload_ad_group_ad(\n client, customer_id, ad_group_id, ad_asset_resource_name\n )\n\n\ndef create_media_bundle_asset(client: GoogleAdsClient, customer_id: str) -> str:\n \"\"\"Creates a media bundle from the assets in a zip file.\n\n The zip file contains the HTML5 components.\n\n Args:\n client: An initialized Google Ads client.\n customer_id: The Google Ads customer ID for which the call is made.\n Returns:\n The string resource name of the newly uploaded media bundle.\n \"\"\"\n # Get the AssetService client.\n asset_service: AssetServiceClient = client.get_service(\"AssetService\")\n\n # Construct an asset operation and populate its fields.\n asset_operation: AssetOperation = client.get_type(\"AssetOperation\")\n media_bundle_asset: Asset = asset_operation.create\n media_bundle_asset.type_ = client.enums.AssetTypeEnum.MEDIA_BUNDLE\n media_bundle_asset.name = \"Ad Media Bundle\"\n # The HTML5 zip file contains all the HTML, CSS, and images needed for the\n # HTML5 ad. For help on creating an HTML5 zip file, check out Google Web\n # Designer (https://www.google.com/webdesigner/).\n # Download the ZIP as bytes from the URL\n media_bundle_asset.media_bundle_asset.data = requests.get(\n BUNDLE_URL\n ).content\n\n # Adds the asset to the client account.\n mutate_asset_response: MutateAssetsResponse = asset_service.mutate_assets(\n customer_id=customer_id, operations=[asset_operation]\n )\n\n # Display and return the resulting resource name.\n uploaded_asset_resource_name: str = mutate_asset_response.results[\n 0\n ].resource_name\n print(f\"Uploaded file with resource name '{uploaded_asset_resource_name}'.\")\n\n return uploaded_asset_resource_name\n\n\ndef create_display_upload_ad_group_ad(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n ad_asset_resource_name: str,\n) -> None:\n \"\"\"Creates a new HTML5 display upload ad and adds it to the given ad group.\n\n Args:\n client: An initialized Google Ads client.\n customer_id: The Google Ads customer ID.\n ad_group_id: The ID of the ad group to which the new ad will be added.\n ad_asset_resource_name: The resource name of the media bundle containing\n the HTML5 components.\n \"\"\"\n # Get the AdGroupAdService client.\n ad_group_ad_service: AdGroupAdServiceClient = client.get_service(\n \"AdGroupAdService\"\n )\n\n # Create an AdGroupAdOperation.\n ad_group_ad_operation: AdGroupAdOperation = client.get_type(\n \"AdGroupAdOperation\"\n )\n\n # Configure the ad group ad fields.\n ad_group_ad: AdGroupAd = ad_group_ad_operation.create\n ad_group_ad.status = client.enums.AdGroupAdStatusEnum.PAUSED\n ad_group_ad.ad_group = client.get_service(\"AdGroupService\").ad_group_path(\n customer_id, ad_group_id\n )\n\n # Configured the ad as a display upload ad.\n display_upload_ad: Ad = ad_group_ad.ad\n display_upload_ad.name = \"Ad for HTML5\"\n display_upload_ad.final_urls.append(\"http://example.com/html5\")\n # Exactly one of the ad_data \"oneof\" fields must be included to specify the\n # ad type. See: https://developers.google.com/google-ads/api/reference/rpc/latest/Ad for\n # the full list of available types. By setting a \"display_upload_ad\"\n # subfield it sets that as the \"oneof\" field for the Ad.\n display_upload_ad.display_upload_ad.media_bundle.asset = (\n ad_asset_resource_name\n )\n display_upload_ad.display_upload_ad.display_upload_product_type = (\n client.enums.DisplayUploadProductTypeEnum.HTML5_UPLOAD_AD\n )\n\n # Add the ad group ad to the client account and display the resulting\n # ad's resource name.\n mutate_ad_group_ads_response: MutateAdGroupAdsResponse = (\n ad_group_ad_service.mutate_ad_group_ads(\n customer_id=customer_id, operations=[ad_group_ad_operation]\n )\n )\n print(\n \"Created new ad group ad with resource name \"\n f\"'{mutate_ad_group_ads_response.results[0].resource_name}'.\"\n )\n\n\nif __name__ == \"__main__\":\n parser = argparse.ArgumentParser(\n description=\"Adds a display upload ad to a given ad group.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-a\",\n \"--ad_group_id\",\n type=str,\n required=True,\n help=\"The ID of the ad group to which the new ad will be added.\",\n )\n args: argparse.Namespace = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(googleads_client, args.customer_id, args.ad_group_id)\n except GoogleAdsException as ex:\n print(\n f'Request with ID \"{ex.request_id}\" failed with status '\n f'\"{ex.error.code().name}\" and includes the following errors:'\n )\n for error in ex.failure.errors:\n print(f'\\tError with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nadd_display_upload_ad.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2020 Google LLC\n#\n# 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.\n#\n# This code example adds a display upload ad to a given ad group.\n# To get ad groups, run get_ad_groups.rb.\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\nrequire 'open-uri'\n\ndef add_display_upload_ad(customer_id, ad_group_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Creates a new media bundle asset and returns the resource name.\n # There are several types of display upload ads. For this example, we will\n # create an HTML5 upload ad, which requires a media bundle.\n # The display_upload_product_type field lists the available display\n # upload types:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/DisplayUploadAdInfo\n ad_asset_resource_name = create_media_bundle_asset(client, customer_id)\n\n # Creates a new display upload ad and associates it with the specified\n # ad group.\n create_display_upload_ad_group_ad(\n client,\n customer_id,\n ad_group_id,\n ad_asset_resource_name,\n )\nend\n\n# Creates a media bundle from the assets in a zip file. The zip file contains\n# the HTML5 components.\ndef create_media_bundle_asset(client, customer_id)\n # The HTML5 zip file contains all the HTML, CSS, and images needed for the\n # HTML5 ad. For help on creating an HTML5 zip file, check out Google Web\n # Designer (https://www.google.com/webdesigner/).\n html5_zip = open(\"https://gaagl.page.link/ib87\") { |f| f.read }\n\n # Creates the media bundle asset.\n operation = client.operation.create_resource.asset do |asset|\n asset.type = :MEDIA_BUNDLE\n asset.name = \"Ad Media Bundle\"\n asset.media_bundle_asset = client.resource.media_bundle_asset do |media|\n media.data = html5_zip\n end\n end\n\n # Issues a mutate request to add the asset.\n response = client.service.asset.mutate_assets(\n customer_id: customer_id,\n operations: [operation],\n )\n\n # Prints the resource name of the added media bundle asset.\n ad_asset_resource_name = response.results.first.resource_name\n puts \"Uploaded media bundle asset with resource name: \" \\\n \"#{ad_asset_resource_name}\"\n\n ad_asset_resource_name\nend\n\n# Creates a new HTML5 display upload ad and adds it to the specified ad group.\ndef create_display_upload_ad_group_ad(\n client,\n customer_id,\n ad_group_id,\n ad_asset_resource_name)\n # Creates an ad group ad for the new ad.\n operation = client.operation.create_resource.ad_group_ad do |aga|\n aga.ad = client.resource.ad do |ad|\n ad.name = \"Ad for HTML 5\"\n ad.final_urls << \"http://example.com/html5\"\n # Exactly one ad data field must be included to specify the ad type. See\n # https://developers.google.com/google-ads/api/reference/rpc/latest/Ad\n # for the full list of available types.\n ad.display_upload_ad = client.resource.display_upload_ad_info do |info|\n info.display_upload_product_type = :HTML5_UPLOAD_AD\n info.media_bundle = client.resource.ad_media_bundle_asset do |bundle|\n bundle.asset = ad_asset_resource_name\n end\n end\n aga.ad_group = client.path.ad_group(customer_id, ad_group_id)\n aga.status = :PAUSED\n end\n end\n\n # Issues a mutate request to add the ad group ad.\n response = client.service.ad_group_ad.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [operation],\n )\n\n # Prints information about the newly created ad group ad.\n puts \"Created ad group ad with resource name: \" \\\n \"#{response.results.first.resource_name}\"\nend\n\nif __FILE__ == $0\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:ad_group_id] = 'INSERT_AD_GROUP_ID_HERE'\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-A', '--ad-group-id AD-GROUP-ID', String, 'AdGroup ID') do |v|\n options[:ad_group_id] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n add_display_upload_ad(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options[:ad_group_id],\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\nadd_display_upload_ad.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2020, Google LLC\n#\n# 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# http://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.\n#\n# This code example adds a display upload ad to a given ad group.\n# To get ad groups, run get_ad_groups.pl.\n#\n# This feature is only available to allowlisted accounts.\n# See https://support.google.com/google-ads/answer/1722096 for more details.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::Utils::MediaUtils;\nuse Google::Ads::GoogleAds::V25::Resources::Ad;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroupAd;\nuse Google::Ads::GoogleAds::V25::Resources::Asset;\nuse Google::Ads::GoogleAds::V25::Common::AdMediaBundleAsset;\nuse Google::Ads::GoogleAds::V25::Common::DisplayUploadAdInfo;\nuse Google::Ads::GoogleAds::V25::Common::MediaBundleAsset;\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupAdStatusEnum qw(PAUSED);\nuse Google::Ads::GoogleAds::V25::Enums::DisplayUploadProductTypeEnum\n qw(HTML5_UPLOAD_AD);\nuse Google::Ads::GoogleAds::V25::Enums::AssetTypeEnum qw(MEDIA_BUNDLE);\nuse Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation;\nuse Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\n\n# The HTML5 zip file contains all the HTML, CSS, and images needed for the\n# HTML5 ad. For help on creating an HTML5 zip file, check out Google Web\n# Designer (https://www.google.com/webdesigner/).\nuse constant BUNDLE_URL => \"https://gaagl.page.link/ib87\";\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\nmy $ad_group_id = \"INSERT_AD_GROUP_ID_HERE\";\n\nsub add_display_upload_ad {\n my ($api_client, $customer_id, $ad_group_id) = @_;\n\n # There are several types of display upload ads. For this example, we will\n # create an HTML5 upload ad, which requires a media bundle.\n # The DisplayUploadProductType field lists the available display upload types:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/DisplayUploadAdInfo\n\n # Create a new media bundle asset and return the resource name.\n my $ad_asset_resource_name =\n create_media_bundle_asset($api_client, $customer_id);\n\n # Create a new display upload ad and associate it with the specified ad group.\n create_display_upload_ad_group_ad($api_client, $customer_id, $ad_group_id,\n $ad_asset_resource_name);\n\n return 1;\n}\n\n# Creates a media bundle from the assets in a zip file. The zip file contains the\n# HTML5 components.\nsub create_media_bundle_asset {\n my ($api_client, $customer_id) = @_;\n\n # Create an HTML5 zip file media bundle content.\n my $bundle_content = get_base64_data_from_url(BUNDLE_URL);\n\n # Create an asset.\n my $asset = Google::Ads::GoogleAds::V25::Resources::Asset->new({\n name => \"Ad Media Bundle\",\n type => MEDIA_BUNDLE,\n mediaBundleAsset =>\n Google::Ads::GoogleAds::V25::Common::MediaBundleAsset->new({\n data => $bundle_content\n })});\n\n # Create an asset operation.\n my $asset_operation =\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->new({\n create => $asset\n });\n\n # Issue a mutate request to add the asset.\n my $assets_response = $api_client->AssetService()->mutate({\n customerId => $customer_id,\n operations => [$asset_operation]});\n\n # Print out information about the newly added asset.\n my $asset_resource_name = $assets_response->{results}[0]{resourceName};\n printf \"The media bundle asset has been added with resource name: '%s'.\\n\",\n $asset_resource_name;\n\n return $asset_resource_name;\n}\n\n# Creates a new HTML5 display upload ad and adds it to the specified ad group.\nsub create_display_upload_ad_group_ad {\n my ($api_client, $customer_id, $ad_group_id, $ad_asset_resource_name) = @_;\n\n # Create a display upload ad info.\n my $display_upload_ad_info =\n Google::Ads::GoogleAds::V25::Common::DisplayUploadAdInfo->new({\n displayUploadProductType => HTML5_UPLOAD_AD,\n mediaBundle =>\n Google::Ads::GoogleAds::V25::Common::AdMediaBundleAsset->new({\n asset => $ad_asset_resource_name,\n })});\n\n # Create a display upload ad.\n my $display_upload_ad = Google::Ads::GoogleAds::V25::Resources::Ad->new({\n name => \"Ad for HTML5\",\n finalUrls => [\"http://example.com/html5\"],\n # Exactly one ad data field must be included to specify the ad type. See\n # https://developers.google.com/google-ads/api/reference/rpc/latest/Ad for the\n # full list of available types.\n displayUploadAd => $display_upload_ad_info,\n });\n\n # Create an ad group ad.\n my $ad_group_ad = Google::Ads::GoogleAds::V25::Resources::AdGroupAd->new({\n ad => $display_upload_ad,\n status => PAUSED,\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n )});\n\n # Create an ad group ad operation.\n my $ad_group_ad_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation\n ->new({create => $ad_group_ad});\n\n # Add the ad group ad.\n my $response = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]});\n\n # Display the resulting ad group ad's resource name.\n printf \"Created new ad group ad '%s'.\\n\",\n $response->{results}[0]{resourceName};\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"ad_group_id=i\" => \\$ad_group_id,\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id, $ad_group_id);\n\n# Call the example.\nadd_display_upload_ad($api_client, $customer_id =~ s/-//gr, $ad_group_id);\n\n=pod\n\n=head1 NAME\n\nadd_display_upload_ad\n\n=head1 DESCRIPTION\n\nThis code example adds a display upload ad to a given ad group.\nTo get ad groups, run get_ad_groups.pl\n\nThis feature is only available to allowlisted accounts.\nSee https://support.google.com/google-ads/answer/1722096 for more details.\n\n=head1 SYNOPSIS\n\nadd_display_upload_ad.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -ad_group_id The ID of the ad group to which the new ad will be added.\n\n=cut\nadd_display_upload_ad.pl\n```\n\nExample:\n```text\n# Copyright 2025 Google LLC\n\n# 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.\n\n# Creates a media bundle asset.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n#\n# MEDIA_BUNDLE_DATA: A base64-encoded string for Media bundle (ZIP file)\n# asset data.\ncurl -f --request POST \\\n\"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/assets:mutate\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data @- <<EOF\n{\n \"operations\": [\n {\n \"create\": {\n \"mediaBundleAsset\": {\n \"data\": \"${MEDIA_BUNDLE_DATA}\"\n },\n \"name\": \"Ad Media Bundle\"\n }\n }\n ]\n}\nEOF\n\n# Adds a display upload ad to a given ad group.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n#\n# FINAL_URL: The final URL of the ad.\n# AD_ASSET_RESOURCE_NAME: The resource name of the media bundle asset created\n# in the previous request.\n# AD_GROUP_RESOURCE_NAME: The resource name of the ad group to add the ad to.\ncurl -f --request POST \\\n\"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/adGroupAds:mutate\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data @- <<EOF\n{\n \"operations\": [\n {\n \"create\": {\n \"status\": \"PAUSED\",\n \"ad\": {\n \"name\": \"Ad for HTML5\",\n \"finalUrls\": [\n \"${FINAL_URL}\"\n ],\n \"displayUploadAd\": {\n \"mediaBundle\": {\n \"asset\": \"${AD_ASSET_RESOURCE_NAME}\"\n },\n \"displayUploadProductType\": \"HTML5_UPLOAD_AD\"\n }\n },\n \"adGroup\": \"${AD_GROUP_RESOURCE_NAME}\"\n }\n }\n ]\n}\nEOF\nadd_display_upload_ad.sh\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.330Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":1397,"estimatedTokens":13436}}142{"id":"doc-create_shopping_listing_groups_google_ads_api_go-d6142655","source":"documentation","title":"Create shopping listing groups | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/shopping-ads/create-listing-groups","text":"Example:\n```text\nprivate void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n long adGroupId,\n boolean replaceExistingTree) {\n // 1) Optional: Removes the existing listing group tree, if it already exists on the ad group.\n if (replaceExistingTree) {\n removeListingGroupTree(googleAdsClient, customerId, adGroupId);\n }\n // Creates a list of ad group criterion to add.q\n List<AdGroupCriterionOperation> operations = new ArrayList<>();\n\n // 2) Constructs the listing group tree \"root\" node.\n\n // Subdivision node: (Root node)\n AdGroupCriterion adGroupCriterionRoot =\n createListingGroupSubdivisionRoot(customerId, adGroupId, -1L);\n // Get the resource name that will be used for the root node.\n // This resource has not been created yet and will include the temporary ID as part of the\n // criterion ID.\n String adGroupCriterionResourceNameRoot = adGroupCriterionRoot.getResourceName();\n operations.add(AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionRoot).build());\n\n // 3) Construct the listing group unit nodes for NEW, USED and other\n\n // Biddable Unit node: (Condition NEW node)\n // * Product Condition: NEW\n // * CPC bid: $0.20\n AdGroupCriterion adGroupCriterionConditionNew =\n createListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameRoot,\n ListingDimensionInfo.newBuilder()\n .setProductCondition(\n ProductConditionInfo.newBuilder().setCondition(ProductCondition.NEW).build())\n .build(),\n 200_000L);\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionConditionNew).build());\n\n // Biddable Unit node: (Condition USED node)\n // * Product Condition: USED\n // * CPC bid: $0.10\n AdGroupCriterion adGroupCriterionConditionUsed =\n createListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameRoot,\n ListingDimensionInfo.newBuilder()\n .setProductCondition(\n ProductConditionInfo.newBuilder().setCondition(ProductCondition.USED).build())\n .build(),\n 100_000L);\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionConditionUsed).build());\n\n // Sub-division node: (Condition \"other\" node)\n // * Product Condition: (not specified)\n AdGroupCriterion adGroupCriterionConditionOther =\n createListingGroupSubdivision(\n customerId,\n adGroupId,\n -2L,\n adGroupCriterionResourceNameRoot,\n ListingDimensionInfo.newBuilder()\n // All sibling nodes must have the same dimension type, even if they don't contain a\n // bid.\n // parent\n .setProductCondition(ProductConditionInfo.newBuilder().build())\n .build());\n // Gets the resource name that will be used for the condition other node.\n // This resource has not been created yet and will include the temporary ID as part of the\n // criterion ID.\n String adGroupCriterionResourceNameConditionOther =\n adGroupCriterionConditionOther.getResourceName();\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionConditionOther).build());\n\n // 4) Constructs the listing group unit nodes for CoolBrand, CheapBrand and other\n\n // Biddable Unit node: (Brand CoolBrand node)\n // * Brand: CoolBrand\n // * CPC bid: $0.90\n AdGroupCriterion adGroupCriterionBrandCoolBrand =\n createListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n ListingDimensionInfo.newBuilder()\n .setProductBrand(ProductBrandInfo.newBuilder().setValue(\"CoolBrand\").build())\n .build(),\n 900_000L);\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionBrandCoolBrand).build());\n\n // Biddable Unit node: (Brand CheapBrand node)\n // * Brand: CheapBrand\n // * CPC bid: $0.01\n AdGroupCriterion adGroupCriterionBrandCheapBrand =\n createListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n ListingDimensionInfo.newBuilder()\n .setProductBrand(ProductBrandInfo.newBuilder().setValue(\"CheapBrand\").build())\n .build(),\n 10_000L);\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionBrandCheapBrand).build());\n\n // Biddable Unit node: (Brand other node)\n // * Brand: CheapBrand\n // * CPC bid: $0.01\n AdGroupCriterion adGroupCriterionBrandOther =\n createListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n ListingDimensionInfo.newBuilder()\n .setProductBrand(ProductBrandInfo.newBuilder().build())\n .build(),\n 50_000L);\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionBrandOther).build());\n\n // Issues a mutate request to add the ad group criterion to the ad group.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n List<MutateAdGroupCriterionResult> mutateAdGroupCriteriaResults =\n adGroupCriterionServiceClient\n .mutateAdGroupCriteria(Long.toString(customerId), operations)\n .getResultsList();\n for (MutateAdGroupCriterionResult mutateAdGroupCriterionResult :\n mutateAdGroupCriteriaResults) {\n System.out.printf(\n \"Added ad group criterion for listing group with resource name: '%s'%n\",\n mutateAdGroupCriterionResult.getResourceName());\n }\n }\n}\nAddShoppingProductListingGroupTree.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long adGroupId,\n bool replaceExistingTree)\n{\n // Get the AdGroupCriterionService.\n AdGroupCriterionServiceClient adGroupCriterionService =\n client.GetService(Services.V25.AdGroupCriterionService);\n\n try\n {\n // 1) Optional: Remove the existing listing group tree, if it already exists on the\n // ad group.\n if (replaceExistingTree)\n {\n RemoveListingGroupTree(client, customerId, adGroupId);\n }\n // Create a list of ad group criterion to add\n List<AdGroupCriterionOperation> operations = new List<AdGroupCriterionOperation>();\n\n // 2) Construct the listing group tree \"root\" node.\n\n // Subdivision node: (Root node)\n AdGroupCriterion adGroupCriterionRoot = CreateListingGroupSubdivisionRoot(\n customerId, adGroupId, -1L);\n\n // Get the resource name that will be used for the root node.\n // This resource has not been created yet and will include the temporary ID as\n // part of the criterion ID.\n String adGroupCriterionResourceNameRoot = adGroupCriterionRoot.ResourceName;\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionRoot\n });\n\n // 3) Construct the listing group unit nodes for NEW, USED and other\n\n // Biddable Unit node: (Condition NEW node)\n // * Product Condition: NEW\n // * CPC bid: $0.20\n AdGroupCriterion adGroupCriterionConditionNew =\n CreateListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo()\n {\n ProductCondition = new ProductConditionInfo()\n {\n Condition = ProductCondition.New\n }\n },\n 200_000L);\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionConditionNew\n });\n\n // Biddable Unit node: (Condition USED node)\n // * Product Condition: USED\n // * CPC bid: $0.10\n AdGroupCriterion adGroupCriterionConditionUsed =\n CreateListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo()\n {\n ProductCondition = new ProductConditionInfo()\n {\n Condition = ProductCondition.Used\n }\n },\n 100_000L\n );\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionConditionUsed\n });\n\n // Sub-division node: (Condition \"other\" node)\n // * Product Condition: (not specified)\n AdGroupCriterion adGroupCriterionConditionOther =\n CreateListingGroupSubdivision(\n customerId,\n adGroupId,\n -2L,\n adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo()\n {\n // All sibling nodes must have the same dimension type, even if they\n // don't contain a bid.\n ProductCondition = new ProductConditionInfo()\n }\n );\n // Get the resource name that will be used for the condition other node.\n // This resource has not been created yet and will include the temporary ID as\n // part of the criterion ID.\n String adGroupCriterionResourceNameConditionOther =\n adGroupCriterionConditionOther.ResourceName;\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionConditionOther\n });\n\n // 4) Construct the listing group unit nodes for CoolBrand, CheapBrand and other\n\n // Biddable Unit node: (Brand CoolBrand node)\n // * Brand: CoolBrand\n // * CPC bid: $0.90\n AdGroupCriterion adGroupCriterionBrandCoolBrand =\n CreateListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo()\n {\n ProductBrand = new ProductBrandInfo()\n {\n Value = \"CoolBrand\"\n }\n },\n 900_000L);\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionBrandCoolBrand\n });\n\n // Biddable Unit node: (Brand CheapBrand node)\n // * Brand: CheapBrand\n // * CPC bid: $0.01\n AdGroupCriterion adGroupCriterionBrandCheapBrand =\n CreateListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo()\n {\n ProductBrand = new ProductBrandInfo()\n {\n Value = \"CheapBrand\"\n }\n },\n 10_000L);\n\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionBrandCheapBrand\n });\n\n // Biddable Unit node: (Brand other node)\n // * Brand: CheapBrand\n // * CPC bid: $0.01\n AdGroupCriterion adGroupCriterionBrandOther =\n CreateListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo()\n {\n ProductBrand = new ProductBrandInfo()\n },\n 50_000L);\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionBrandOther\n });\n\n // Issues a mutate request to add the ad group criterion to the ad group.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionService.MutateAdGroupCriteria(\n customerId.ToString(), operations);\n\n // Display the results.\n foreach (MutateAdGroupCriterionResult mutateAdGroupCriterionResult\n in response.Results)\n {\n Console.WriteLine(\"Added ad group criterion for listing group with resource \" +\n $\"name: '{mutateAdGroupCriterionResult.ResourceName}.\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}AddShoppingProductListingGroupTree.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n bool $replaceExistingTree\n) {\n // 1) Optional: Remove the existing listing group tree, if it already exists on the ad\n // group.\n if ($replaceExistingTree === 'true') {\n self::removeListingGroupTree($googleAdsClient, $customerId, $adGroupId);\n }\n // Create a list of ad group criteria to add.\n $operations = [];\n\n // 2) Construct the listing group tree \"root\" node.\n\n // Subdivision node: (Root node)\n $adGroupCriterionRoot = self::createListingGroupSubdivision($customerId, $adGroupId);\n // Get the resource name that will be used for the root node.\n // This resource has not been created yet and will include the temporary ID as part of the\n // criterion ID.\n $adGroupCriterionResourceNameRoot = $adGroupCriterionRoot->getResourceName();\n $operations[] = new AdGroupCriterionOperation(['create' => $adGroupCriterionRoot]);\n\n // 3) Construct the listing group unit nodes for NEW, USED and other.\n\n // Biddable Unit node: (Condition NEW node)\n // * Product Condition: NEW\n // * CPC bid: $0.20\n $adGroupCriterionConditionNew = self::createListingGroupUnitBiddable(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo([\n 'product_condition' => new ProductConditionInfo(\n ['condition' => ProductCondition::PBNEW]\n )\n ]),\n 200000\n );\n $operations[] = new AdGroupCriterionOperation(['create' => $adGroupCriterionConditionNew]);\n\n // Biddable Unit node: (Condition USED node)\n // * Product Condition: USED\n // * CPC bid: $0.10\n $adGroupCriterionConditionUsed = self::createListingGroupUnitBiddable(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo([\n 'product_condition' => new ProductConditionInfo(\n ['condition' => ProductCondition::USED]\n )\n ]),\n 100000\n );\n $operations[] = new AdGroupCriterionOperation(['create' => $adGroupCriterionConditionUsed]);\n\n // Sub-division node: (Condition \"other\" node)\n // * Product Condition: (not specified)\n $adGroupCriterionConditionOther = self::createListingGroupSubdivision(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo([\n // All sibling nodes must have the same dimension type, even if they don't contain a\n // bid.\n 'product_condition' => new ProductConditionInfo()\n ])\n );\n $operations[] =\n new AdGroupCriterionOperation(['create' => $adGroupCriterionConditionOther]);\n\n // Get the resource name that will be used for the condition other node.\n // This resource has not been created yet and will include the temporary ID as part of the\n // criterion ID.\n $adGroupCriterionResourceNameConditionOther =\n $adGroupCriterionConditionOther->getResourceName();\n\n // 4) Construct the listing group unit nodes for CoolBrand, CheapBrand and other.\n\n // Biddable Unit node: (Brand CoolBrand node)\n // * Brand: CoolBrand\n // * CPC bid: $0.90\n $adGroupCriterionBrandCoolBrand = self::createListingGroupUnitBiddable(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo([\n 'product_brand' => new ProductBrandInfo(['value' => 'CoolBrand'])\n ]),\n 900000\n );\n $operations[] =\n new AdGroupCriterionOperation(['create' => $adGroupCriterionBrandCoolBrand]);\n\n // Biddable Unit node: (Brand CheapBrand node)\n // * Brand: CheapBrand\n // * CPC bid: $0.01\n $adGroupCriterionBrandCheapBrand = self::createListingGroupUnitBiddable(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo([\n 'product_brand' => new ProductBrandInfo(['value' => 'CheapBrand'])\n ]),\n 10000\n );\n $operations[] =\n new AdGroupCriterionOperation(['create' => $adGroupCriterionBrandCheapBrand]);\n\n // Biddable Unit node: (Brand other node)\n // * CPC bid: $0.05\n $adGroupCriterionBrandOtherBrand = self::createListingGroupUnitBiddable(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo([\n 'product_brand' => new ProductBrandInfo()\n ]),\n 50000\n );\n $operations[] =\n new AdGroupCriterionOperation(['create' => $adGroupCriterionBrandOtherBrand]);\n\n // Issues a mutate request.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, $operations)\n );\n printf(\n 'Added %d ad group criteria for listing group tree with the following resource '\n . 'names:%s',\n $response->getResults()->count(),\n PHP_EOL\n );\n foreach ($response->getResults() as $addedAdGroupCriterion) {\n /** @var AdGroupCriterion $addedAdGroupCriterion */\n print $addedAdGroupCriterion->getResourceName() . PHP_EOL;\n }\n}AddShoppingProductListingGroupTree.php\n```\n\nExample:\n```text\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n replace_existing_tree: bool,\n) -> None:\n \"\"\"Adds a shopping listing group tree to a shopping ad group.\n\n Args:\n client: An initialized Google Ads client.\n customer_id: The Google Ads customer ID.\n ad_group_id: The ad group ID to which the node will be added.\n replace_existing_tree: Boolean, whether to replace the existing listing\n group tree on the ad group. Defaults to false.\n \"\"\"\n # Get the AdGroupCriterionService client.\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n\n # Optional: Remove the existing listing group tree, if it already exists\n # on the ad group. The example will throw a LISTING_GROUP_ALREADY_EXISTS\n # error if a listing group tree already exists and this option is not\n # set to true.\n if replace_existing_tree:\n remove_listing_group_tree(client, customer_id, ad_group_id)\n\n # Create a list of ad group criteria operations.\n operations: List[AdGroupCriterionOperation] = []\n\n # Construct the listing group tree \"root\" node.\n # Subdivision node: (Root node)\n ad_group_criterion_root_operation: AdGroupCriterionOperation = (\n create_listing_group_subdivision(client, customer_id, ad_group_id)\n )\n\n # Get the resource name that will be used for the root node.\n # This resource has not been created yet and will include the temporary\n # ID as part of the criterion ID.\n ad_group_criterion_root_resource_name: str = (\n ad_group_criterion_root_operation.create.resource_name\n )\n operations.append(ad_group_criterion_root_operation)\n\n # Construct the listing group unit nodes for NEW, USED, and other.\n product_condition_enum: ProductConditionEnum = (\n client.enums.ProductConditionEnum\n )\n condition_dimension_info: ListingDimensionInfo = client.get_type(\n \"ListingDimensionInfo\"\n )\n\n # Biddable Unit node: (Condition NEW node)\n # * Product Condition: NEW\n # * CPC bid: $0.20\n condition_dimension_info.product_condition.condition = (\n product_condition_enum.NEW\n )\n operations.append(\n create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n condition_dimension_info,\n 200_000,\n )\n )\n\n # Biddable Unit node: (Condition USED node)\n # * Product Condition: USED\n # * CPC bid: $0.10\n condition_dimension_info.product_condition.condition = (\n product_condition_enum.USED\n )\n operations.append(\n create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n condition_dimension_info,\n 100_000,\n )\n )\n\n # Sub-division node: (Condition \"other\" node)\n # * Product Condition: (not specified)\n # Note that all sibling nodes must have the same dimension type, even if\n # they don't contain a bid.\n client.copy_from(\n condition_dimension_info.product_condition,\n client.get_type(\"ProductConditionInfo\"),\n )\n ad_group_criterion_other_operation: AdGroupCriterionOperation = (\n create_listing_group_subdivision(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n condition_dimension_info,\n )\n )\n # Get the resource name that will be used for the condition other node.\n # This resource has not been created yet and will include the temporary\n # ID as part of the criterion ID.\n ad_group_criterion_other_resource_name: str = (\n ad_group_criterion_other_operation.create.resource_name\n )\n operations.append(ad_group_criterion_other_operation)\n\n # Build the listing group nodes for CoolBrand, CheapBrand, and other.\n brand_dimension_info: ListingDimensionInfo = client.get_type(\n \"ListingDimensionInfo\"\n )\n\n # Biddable Unit node: (Brand CoolBrand node)\n # * Brand: CoolBrand\n # * CPC bid: $0.90\n brand_dimension_info.product_brand.value = \"CoolBrand\"\n operations.append(\n create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_other_resource_name,\n brand_dimension_info,\n 900_000,\n )\n )\n\n # Biddable Unit node: (Brand CheapBrand node)\n # * Brand: CheapBrand\n # * CPC bid: $0.01\n brand_dimension_info.product_brand.value = \"CheapBrand\"\n operations.append(\n create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_other_resource_name,\n brand_dimension_info,\n 10_000,\n )\n )\n\n # Biddable Unit node: (Brand other node)\n # * CPC bid: $0.05\n client.copy_from(\n brand_dimension_info.product_brand,\n client.get_type(\"ProductBrandInfo\"),\n )\n operations.append(\n create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_other_resource_name,\n brand_dimension_info,\n 50_000,\n )\n )\n\n # Add the ad group criteria.\n mutate_ad_group_criteria_response: MutateGoogleAdsResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=operations\n )\n )\n\n # Print the results of the successful mutates.\n print(\n \"Added ad group criteria for the listing group tree with the \"\n \"following resource names:\"\n )\n for result in mutate_ad_group_criteria_response.results:\n print(f\"\\t{result.resource_name}\")\n\n print(f\"{len(mutate_ad_group_criteria_response.results)} criteria added.\")add_shopping_product_listing_group_tree.py\n```\n\nExample:\n```text\ndef add_shopping_product_listing_group_tree(\n customer_id,\n ad_group_id,\n should_replace_existing_tree\n)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # 1) Optional: Remove the existing listing group tree, if it already exists\n # on the ad group.\n if should_replace_existing_tree\n remove_listing_group_tree(client, customer_id, ad_group_id)\n end\n\n # 2) Construct the listing group tree \"root\" node.\n\n # Subdivision node: (Root node)\n ad_group_criterion_root = create_listing_group_subdivision(\n client,\n customer_id,\n ad_group_id,\n )\n # This resource has not been created yet and will include the temporary ID as\n # part of the criterion ID.\n ad_group_criterion_root_resource_name = ad_group_criterion_root.resource_name\n operations = [client.operation.create_resource.ad_group_criterion(ad_group_criterion_root)]\n\n # 3) Construct the listing group unit nodes for NEW, USED, and other.\n\n # Biddable Unit node: (Condition NEW node)\n # * Product Condition: NEW\n # * CPC bid: $0.20\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_condition = client.resource.product_condition_info do |pci|\n pci.condition = :NEW\n end\n end\n\n ad_group_criterion_condition_new = create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n listing_dimension_info,\n 200_000,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_condition_new\n )\n operations << operation\n\n # Biddable Unit node: (Condition USED node)\n # * Product Condition: USED\n # * CPC bid: $0.10\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_condition = client.resource.product_condition_info do |pci|\n pci.condition = :USED\n end\n end\n ad_group_criterion_condition_used = create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n listing_dimension_info,\n 100_000,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_condition_used\n )\n operations << operation\n\n # Sub-division node: (Condition \"other\" node)\n # * Product Condition: (not specified)\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_condition = client.resource.product_condition_info\n end\n ad_group_criterion_condition_other = create_listing_group_subdivision(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n listing_dimension_info,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_condition_other\n )\n operations << operation\n\n ad_group_criterion_condition_other_resource_name =\n ad_group_criterion_condition_other.resource_name\n\n # 4) Construct the listing group unit nodes for CoolBrand, CheapBrand, and\n # other.\n\n # Biddable Unit node: (Brand CoolBrand node)\n # * Brand: CoolBrand\n # * CPC bid: $0.90\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_brand = client.resource.product_brand_info do |pbi|\n pbi.value = \"CoolBrand\"\n end\n end\n\n ad_group_criterion_brand_cool_brand = create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_condition_other_resource_name,\n listing_dimension_info,\n 900_000,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_brand_cool_brand\n )\n operations << operation\n\n # Biddable Unit node: (Brand CheapBrand node)\n # * Brand: CheapBrand\n # * CPC bid: $0.01\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_brand = client.resource.product_brand_info do |pbi|\n pbi.value = \"CheapBrand\"\n end\n end\n ad_group_criterion_brand_cheap_brand = create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_condition_other_resource_name,\n listing_dimension_info,\n 10_000,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_brand_cheap_brand\n )\n operations << operation\n\n # Biddable Unit node: (Brand other node)\n # * CPC bid: $0.05\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_brand = client.resource.product_brand_info\n end\n ad_group_criterion_brand_other_brand = create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_condition_other_resource_name,\n listing_dimension_info,\n 50_000,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_brand_other_brand\n )\n operations << operation\n\n # Issue the mutate request.\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: operations,\n )\n\n total_count = 0\n response.results.each do |added_criterion|\n puts \"Added ad group criterion with name: #{added_criterion.resource_name}\"\n total_count += 1\n end\n puts \"#{total_count} criteria added in total.\"\nendadd_shopping_product_listing_group_tree.rb\n```\n\nExample:\n```text\nsub add_shopping_product_listing_group_tree {\n my ($api_client, $customer_id, $ad_group_id, $replace_existing_tree) = @_;\n\n # 1) Optional: Remove the existing listing group tree, if it already exists\n # on the ad group.\n if ($replace_existing_tree) {\n remove_listing_group_tree($api_client, $customer_id, $ad_group_id);\n }\n\n # Create a list of ad group criteria operations to add.\n my $operations = [];\n\n # 2) Construct the listing group tree \"root\" node.\n\n # Subdivision node: (Root node)\n my $ad_group_criterion_root =\n create_listing_group_subdivision($customer_id, $ad_group_id);\n # Get the resource name that will be used for the root node.\n # This resource has not been created yet and will include the temporary ID as\n # part of the criterion ID.\n my $ad_group_criterion_root_resource_name =\n $ad_group_criterion_root->{resourceName};\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_root\n });\n\n # 3) Construct the listing group unit nodes for NEW, USED, and other.\n\n # Biddable Unit node: (Condition NEW node)\n # * Product Condition: NEW\n # * CPC bid: $0.20\n my $ad_group_criterion_condition_new = create_listing_group_unit_biddable(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_root_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n productCondition =>\n Google::Ads::GoogleAds::V25::Common::ProductConditionInfo->new({\n condition => NEW\n })}\n ),\n 200000\n );\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_condition_new\n });\n\n # Biddable Unit node: (Condition USED node)\n # * Product Condition: USED\n # * CPC bid: $0.10\n my $ad_group_criterion_condition_used = create_listing_group_unit_biddable(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_root_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n productCondition =>\n Google::Ads::GoogleAds::V25::Common::ProductConditionInfo->new({\n condition => USED\n })}\n ),\n 100000\n );\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_condition_used\n });\n\n # Sub-division node: (Condition \"other\" node)\n # * Product Condition: (not specified)\n my $ad_group_criterion_condition_other = create_listing_group_subdivision(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_root_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n # All sibling nodes must have the same dimension type, even if they\n # don't contain a bid.\n productCondition =>\n Google::Ads::GoogleAds::V25::Common::ProductConditionInfo->new()}));\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_condition_other\n });\n\n # Get the resource name that will be used for the condition other node.\n # This resource has not been created yet and will include the temporary ID as\n # part of the criterion ID.\n my $ad_group_criterion_condition_other_resource_name =\n $ad_group_criterion_condition_other->{resourceName};\n\n # 4) Construct the listing group unit nodes for CoolBrand, CheapBrand, and\n # other.\n\n # Biddable Unit node: (Brand CoolBrand node)\n # * Brand: CoolBrand\n # * CPC bid: $0.90\n my $ad_group_criterion_brand_cool_brand = create_listing_group_unit_biddable(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_condition_other_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Common::ProductBrandInfo->new(\n {value => \"CoolBrand\"})}\n ),\n 900000\n );\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_brand_cool_brand\n });\n\n # Biddable Unit node: (Brand CheapBrand node)\n # * Brand: CheapBrand\n # * CPC bid: $0.01\n my $ad_group_criterion_brand_cheap_brand = create_listing_group_unit_biddable(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_condition_other_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Common::ProductBrandInfo->new(\n {value => \"CheapBrand\"})}\n ),\n 10000\n );\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_brand_cheap_brand\n });\n\n # Biddable Unit node: (Brand other node)\n # * CPC bid: $0.05\n my $ad_group_criterion_brand_other_brand = create_listing_group_unit_biddable(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_condition_other_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Common::ProductBrandInfo->new()}\n ),\n 50000\n );\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_brand_other_brand\n });\n\n # Add the ad group criterion.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => $operations\n });\n\n printf \"Added %d ad group criteria for listing group tree with the \" .\n \"following resource names:\\n\",\n scalar @{$ad_group_criteria_response->{results}};\n\n foreach my $result (@{$ad_group_criteria_response->{results}}) {\n print $result->{resourceName}, \"\\n\";\n }\n\n return 1;\n}add_shopping_product_listing_group_tree.pl\n```\n\nExample:\n```text\n// Copyright 2018 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.shoppingads;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.ListingDimensionInfo;\nimport com.google.ads.googleads.v25.common.ListingGroupInfo;\nimport com.google.ads.googleads.v25.common.ProductBrandInfo;\nimport com.google.ads.googleads.v25.common.ProductConditionInfo;\nimport com.google.ads.googleads.v25.enums.AdGroupCriterionStatusEnum.AdGroupCriterionStatus;\nimport com.google.ads.googleads.v25.enums.ListingGroupTypeEnum.ListingGroupType;\nimport com.google.ads.googleads.v25.enums.ProductConditionEnum.ProductCondition;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.AdGroupCriterion;\nimport com.google.ads.googleads.v25.services.AdGroupCriterionOperation;\nimport com.google.ads.googleads.v25.services.AdGroupCriterionServiceClient;\nimport com.google.ads.googleads.v25.services.GoogleAdsRow;\nimport com.google.ads.googleads.v25.services.GoogleAdsServiceClient;\nimport com.google.ads.googleads.v25.services.GoogleAdsServiceClient.SearchPagedResponse;\nimport com.google.ads.googleads.v25.services.MutateAdGroupCriteriaResponse;\nimport com.google.ads.googleads.v25.services.MutateAdGroupCriterionResult;\nimport com.google.ads.googleads.v25.services.SearchGoogleAdsRequest;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Adds a shopping listing group tree to a shopping ad group. The example will clear an existing\n * listing group tree and rebuild it include the following tree structure:\n *\n * <pre>\n * ProductCanonicalCondition NEW $0.20\n * ProductCanonicalCondition USED $0.10\n * ProductCanonicalCondition null (everything else)\n * ProductBrand CoolBrand $0.90\n * ProductBrand CheapBrand $0.01\n * ProductBrand null (everything else) $0.50\n * </pre>\n */\npublic class AddShoppingProductListingGroupTree {\n\n private static class AddShoppingListingGroupParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(names = ArgumentNames.AD_GROUP_ID, required = true)\n private Long adGroupId;\n\n @Parameter(names = ArgumentNames.REPLACE_EXISTING_TREE, required = true, arity = 1)\n private Boolean replaceExistingTree;\n }\n\n public static void main(String[] args) {\n AddShoppingListingGroupParams params = new AddShoppingListingGroupParams();\n if (!params.parseArguments(args)) {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.adGroupId = Long.parseLong(\"INSERT_AD_GROUP_ID_HERE\");\n // Optional: To replace the existing listing group tree on an ad group set this parameter to\n // true.\n // This option will remove the existing listing group tree before creating a replacement.\n params.replaceExistingTree = Boolean.parseBoolean(\"INSERT_REPLACE_EXISTING_TREE_HERE\");\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new AddShoppingProductListingGroupTree()\n .runExample(\n googleAdsClient, params.customerId, params.adGroupId, params.replaceExistingTree);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param adGroupId the ID of the ad group.\n * @param replaceExistingTree replace the existing listing group tree on the ad group, if it\n * already exists. The example will throw a 'LISTING_GROUP_ALREADY_EXISTS' error if listing\n * group tree already exists and this option is not set to true.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n long adGroupId,\n boolean replaceExistingTree) {\n // 1) Optional: Removes the existing listing group tree, if it already exists on the ad group.\n if (replaceExistingTree) {\n removeListingGroupTree(googleAdsClient, customerId, adGroupId);\n }\n // Creates a list of ad group criterion to add.q\n List<AdGroupCriterionOperation> operations = new ArrayList<>();\n\n // 2) Constructs the listing group tree \"root\" node.\n\n // Subdivision node: (Root node)\n AdGroupCriterion adGroupCriterionRoot =\n createListingGroupSubdivisionRoot(customerId, adGroupId, -1L);\n // Get the resource name that will be used for the root node.\n // This resource has not been created yet and will include the temporary ID as part of the\n // criterion ID.\n String adGroupCriterionResourceNameRoot = adGroupCriterionRoot.getResourceName();\n operations.add(AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionRoot).build());\n\n // 3) Construct the listing group unit nodes for NEW, USED and other\n\n // Biddable Unit node: (Condition NEW node)\n // * Product Condition: NEW\n // * CPC bid: $0.20\n AdGroupCriterion adGroupCriterionConditionNew =\n createListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameRoot,\n ListingDimensionInfo.newBuilder()\n .setProductCondition(\n ProductConditionInfo.newBuilder().setCondition(ProductCondition.NEW).build())\n .build(),\n 200_000L);\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionConditionNew).build());\n\n // Biddable Unit node: (Condition USED node)\n // * Product Condition: USED\n // * CPC bid: $0.10\n AdGroupCriterion adGroupCriterionConditionUsed =\n createListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameRoot,\n ListingDimensionInfo.newBuilder()\n .setProductCondition(\n ProductConditionInfo.newBuilder().setCondition(ProductCondition.USED).build())\n .build(),\n 100_000L);\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionConditionUsed).build());\n\n // Sub-division node: (Condition \"other\" node)\n // * Product Condition: (not specified)\n AdGroupCriterion adGroupCriterionConditionOther =\n createListingGroupSubdivision(\n customerId,\n adGroupId,\n -2L,\n adGroupCriterionResourceNameRoot,\n ListingDimensionInfo.newBuilder()\n // All sibling nodes must have the same dimension type, even if they don't contain a\n // bid.\n // parent\n .setProductCondition(ProductConditionInfo.newBuilder().build())\n .build());\n // Gets the resource name that will be used for the condition other node.\n // This resource has not been created yet and will include the temporary ID as part of the\n // criterion ID.\n String adGroupCriterionResourceNameConditionOther =\n adGroupCriterionConditionOther.getResourceName();\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionConditionOther).build());\n\n // 4) Constructs the listing group unit nodes for CoolBrand, CheapBrand and other\n\n // Biddable Unit node: (Brand CoolBrand node)\n // * Brand: CoolBrand\n // * CPC bid: $0.90\n AdGroupCriterion adGroupCriterionBrandCoolBrand =\n createListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n ListingDimensionInfo.newBuilder()\n .setProductBrand(ProductBrandInfo.newBuilder().setValue(\"CoolBrand\").build())\n .build(),\n 900_000L);\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionBrandCoolBrand).build());\n\n // Biddable Unit node: (Brand CheapBrand node)\n // * Brand: CheapBrand\n // * CPC bid: $0.01\n AdGroupCriterion adGroupCriterionBrandCheapBrand =\n createListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n ListingDimensionInfo.newBuilder()\n .setProductBrand(ProductBrandInfo.newBuilder().setValue(\"CheapBrand\").build())\n .build(),\n 10_000L);\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionBrandCheapBrand).build());\n\n // Biddable Unit node: (Brand other node)\n // * Brand: CheapBrand\n // * CPC bid: $0.01\n AdGroupCriterion adGroupCriterionBrandOther =\n createListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n ListingDimensionInfo.newBuilder()\n .setProductBrand(ProductBrandInfo.newBuilder().build())\n .build(),\n 50_000L);\n operations.add(\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterionBrandOther).build());\n\n // Issues a mutate request to add the ad group criterion to the ad group.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n List<MutateAdGroupCriterionResult> mutateAdGroupCriteriaResults =\n adGroupCriterionServiceClient\n .mutateAdGroupCriteria(Long.toString(customerId), operations)\n .getResultsList();\n for (MutateAdGroupCriterionResult mutateAdGroupCriterionResult :\n mutateAdGroupCriteriaResults) {\n System.out.printf(\n \"Added ad group criterion for listing group with resource name: '%s'%n\",\n mutateAdGroupCriterionResult.getResourceName());\n }\n }\n }\n\n\n /**\n * Removes all the ad group criteria that define the existing listing group tree for an ad group.\n * Returns without an error if all listing group criterion are successfully removed.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param adGroupId the ID of the ad group that the new listing group tree will be removed from.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void removeListingGroupTree(\n GoogleAdsClient googleAdsClient, long customerId, long adGroupId) {\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n String searchQuery =\n \"SELECT ad_group_criterion.resource_name \"\n + \"FROM ad_group_criterion \"\n + \"WHERE ad_group_criterion.type = LISTING_GROUP \"\n + \"AND ad_group_criterion.listing_group.parent_ad_group_criterion IS NULL \"\n + String.format(\"AND ad_group.id = %d\", adGroupId);\n\n // Creates a request that will retrieve all listing groups where the parent ad group criterion\n // is NULL (and hence the root node in the tree) for a given ad group id.\n SearchGoogleAdsRequest request =\n SearchGoogleAdsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setQuery(searchQuery)\n .build();\n\n // Issues the search request.\n SearchPagedResponse searchPagedResponse = googleAdsServiceClient.search(request);\n // Iterates over all rows in all pages to find the ad group criterion to remove.\n for (GoogleAdsRow googleAdsRow : searchPagedResponse.iterateAll()) {\n AdGroupCriterion adGroupCriterion = googleAdsRow.getAdGroupCriterion();\n System.out.printf(\n \"Found ad group criterion with the resource name: '%s'.%n\",\n adGroupCriterion.getResourceName());\n\n AdGroupCriterionOperation operation =\n AdGroupCriterionOperation.newBuilder()\n .setRemove(adGroupCriterion.getResourceName())\n .build();\n\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), Collections.singletonList(operation));\n System.out.printf(\"Removed %d ad group criteria.%n\", response.getResultsCount());\n }\n }\n }\n }\n\n /**\n * Creates a new criterion containing a biddable unit listing group node.\n *\n * @param customerId the client customer ID.\n * @param adGroupId the ID of the ad group.\n * @param parentAdGroupCriterionResourceName the resource name of the parent of this criterion.\n * @param listingDimensionInfo the ListingDimensionInfo to be set for this listing group.\n * @param cpcBidMicros the CPC bid for items in this listing group. This value should be specified\n * in micros.\n * @return the ad group criterion object that contains the biddable unit listing group node.\n */\n private AdGroupCriterion createListingGroupUnitBiddable(\n long customerId,\n long adGroupId,\n String parentAdGroupCriterionResourceName,\n ListingDimensionInfo listingDimensionInfo,\n long cpcBidMicros) {\n\n String adGroupResourceName = ResourceNames.adGroup(customerId, adGroupId);\n // Note: There are two approaches for creating new unit nodes:\n // (1) Set the ad group resource name on the criterion (no temporary ID required).\n // (2) Use a temporary ID to construct the criterion resource name and set it using\n // setResourceName.\n // In both cases you must set the parentAdGroupCriterionResourceName on the listing\n // group for non-root nodes.\n // This example demonstrates method (1).\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n // The ad group the listing group will be attached to.\n .setAdGroup(adGroupResourceName)\n .setStatus(AdGroupCriterionStatus.ENABLED)\n .setListingGroup(\n ListingGroupInfo.newBuilder()\n // Sets the type as a UNIT, which will allow the group to be biddable.\n .setType(ListingGroupType.UNIT)\n // Sets the ad group criterion resource name for the parent listing group.\n // This can include a temporary ID if the parent criterion is not yet created.\n // Use StringValue to convert from a String to a compatible argument type.\n .setParentAdGroupCriterion(parentAdGroupCriterionResourceName)\n // Case values contain the listing dimension used for the node.\n .setCaseValue(listingDimensionInfo)\n .build())\n // Sets the bid for this listing group unit.\n // This will be used as the CPC bid for items that are included in this listing group\n .setCpcBidMicros(cpcBidMicros)\n .build();\n\n return adGroupCriterion;\n }\n\n /**\n * Creates a new criterion containing a subdivision listing group node.\n *\n * @param customerId the client customer ID.\n * @param adGroupId the ID of the ad group.\n * @param adGroupCriterionId the ID of the criterion. This value will used to construct the\n * resource name. This can be a negative number if the criterion is yet to be created.\n * @param parentAdGroupCriterionResourceName the resource name of the parent of this criterion.\n * @param listingDimensionInfo the ListingDimensionInfo to be set for this listing group.\n * @return the ad group criterion object that contains the subdivision listing group node.\n */\n private AdGroupCriterion createListingGroupSubdivision(\n long customerId,\n long adGroupId,\n long adGroupCriterionId,\n String parentAdGroupCriterionResourceName,\n ListingDimensionInfo listingDimensionInfo) {\n\n String adGroupCriterionResourceName =\n ResourceNames.adGroupCriterion(customerId, adGroupId, adGroupCriterionId);\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n // The resource name the criterion will be created with. This will define the ID for the\n // ad group criterion.\n .setResourceName(adGroupCriterionResourceName)\n .setStatus(AdGroupCriterionStatus.ENABLED)\n .setListingGroup(\n ListingGroupInfo.newBuilder()\n // Sets the type as a SUBDIVISION, which will allow the node to be the parent of\n // another sub-tree.\n .setType(ListingGroupType.SUBDIVISION)\n // Sets the ad group criterion resource name for the parent listing group.\n // This can include a temporary ID if the parent criterion is not yet created.\n // Uses StringValue to convert from a String to a compatible argument type.\n .setParentAdGroupCriterion(parentAdGroupCriterionResourceName)\n // Case values contain the listing dimension used for the node.\n .setCaseValue(listingDimensionInfo)\n .build())\n .build();\n\n return adGroupCriterion;\n }\n\n /**\n * Creates a new criterion containing a root subdivision listing group node.\n *\n * @param customerId the client customer ID.\n * @param adGroupId the ID of the ad group.\n * @param adGroupCriterionId the ID of the criterion. This value will used to construct the\n * resource name. This can be a negative number if the criterion is yet to be created.\n * @return the ad group criterion object that contains the listing group root node.\n */\n private AdGroupCriterion createListingGroupSubdivisionRoot(\n long customerId, long adGroupId, long adGroupCriterionId) {\n\n String adGroupCriterionResourceName =\n ResourceNames.adGroupCriterion(customerId, adGroupId, adGroupCriterionId);\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n // The resource name the criterion will be created with. This will define the ID for the\n // ad group criterion.\n .setResourceName(adGroupCriterionResourceName)\n .setStatus(AdGroupCriterionStatus.ENABLED)\n .setListingGroup(\n ListingGroupInfo.newBuilder()\n // Sets the type as a SUBDIVISION, which will allow the node to be the parent of\n // another sub-tree.\n .setType(ListingGroupType.SUBDIVISION)\n .build())\n .build();\n\n return adGroupCriterion;\n }\n}\nAddShoppingProductListingGroupTree.java\n```\n\nExample:\n```text\n// Copyright 2019 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing static Google.Ads.GoogleAds.V25.Enums.AdGroupCriterionStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.ListingGroupTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.ProductConditionEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example shows how to add a shopping listing group tree to a shopping ad group.\n /// The example will clear an existing listing group tree and rebuild it include the following\n /// tree structure:\n ///\n /// <code>\n /// ProductCanonicalCondition NEW $0.20\n /// ProductCanonicalCondition USED $0.10\n /// ProductCanonicalCondition null (everything else)\n /// ProductBrand CoolBrand $0.90\n /// ProductBrand CheapBrand $0.01\n /// ProductBrand null (everything else) $0.50\n /// </code>\n /// </summary>\n public class AddShoppingProductListingGroupTree : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"AddShoppingProductListingGroupTree\"/>\n /// example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// The ID of the ad group.\n /// </summary>\n [Option(\"adGroupId\", Required = true, HelpText =\n \"The ID of the ad group.\")]\n public long AdGroupId { get; set; }\n\n /// <summary>\n /// The boolean to indicate whether to replace the existing listing group tree on the\n /// ad group, if it already exists. The example will throw a\n /// LISTING_GROUP_ALREADY_EXISTS error if listing group tree already exists and this\n /// option is not set to true.\n /// </summary>\n [Option(\"replaceExistingTree\", Required = true, HelpText =\n \"The boolean to indicate whether to replace the existing listing group tree on \" +\n \"the ad group, if it already exists. The example will throw a \" +\n \"LISTING_GROUP_ALREADY_EXISTS error if listing group tree already exists and \" +\n \"this option is not set to true.\")]\n public bool ReplaceExistingTree { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n AddShoppingProductListingGroupTree codeExample =\n new AddShoppingProductListingGroupTree();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.AdGroupId,\n options.ReplaceExistingTree);\n }\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example shows how to add a shopping listing group tree to a shopping ad \" +\n \"group. The example will clear an existing listing group tree and rebuild it include \" +\n \"the following tree structure:\\n\" +\n \"ProductCanonicalCondition NEW $0.20\\n\" +\n \"ProductCanonicalCondition USED $0.10\\n\" +\n \"ProductCanonicalCondition null (everything else)\\n\" +\n \" ProductBrand CoolBrand $0.90\\n\" +\n \" ProductBrand CheapBrand $0.01\\n\" +\n \" ProductBrand null (everything else) $0.50\\n\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"adGroupId\">The ID of the ad group.</param>\n /// <param name=\"replaceExistingTree\">The boolean to indicate whether to replace the\n /// existing listing group tree on the ad group, if it already exists. The example will\n /// throw a <code>LISTING_GROUP_ALREADY_EXISTS</code> error if listing group tree already\n /// exists and this option is not set to true.</param>\n public void Run(GoogleAdsClient client, long customerId, long adGroupId,\n bool replaceExistingTree)\n {\n // Get the AdGroupCriterionService.\n AdGroupCriterionServiceClient adGroupCriterionService =\n client.GetService(Services.V25.AdGroupCriterionService);\n\n try\n {\n // 1) Optional: Remove the existing listing group tree, if it already exists on the\n // ad group.\n if (replaceExistingTree)\n {\n RemoveListingGroupTree(client, customerId, adGroupId);\n }\n // Create a list of ad group criterion to add\n List<AdGroupCriterionOperation> operations = new List<AdGroupCriterionOperation>();\n\n // 2) Construct the listing group tree \"root\" node.\n\n // Subdivision node: (Root node)\n AdGroupCriterion adGroupCriterionRoot = CreateListingGroupSubdivisionRoot(\n customerId, adGroupId, -1L);\n\n // Get the resource name that will be used for the root node.\n // This resource has not been created yet and will include the temporary ID as\n // part of the criterion ID.\n String adGroupCriterionResourceNameRoot = adGroupCriterionRoot.ResourceName;\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionRoot\n });\n\n // 3) Construct the listing group unit nodes for NEW, USED and other\n\n // Biddable Unit node: (Condition NEW node)\n // * Product Condition: NEW\n // * CPC bid: $0.20\n AdGroupCriterion adGroupCriterionConditionNew =\n CreateListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo()\n {\n ProductCondition = new ProductConditionInfo()\n {\n Condition = ProductCondition.New\n }\n },\n 200_000L);\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionConditionNew\n });\n\n // Biddable Unit node: (Condition USED node)\n // * Product Condition: USED\n // * CPC bid: $0.10\n AdGroupCriterion adGroupCriterionConditionUsed =\n CreateListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo()\n {\n ProductCondition = new ProductConditionInfo()\n {\n Condition = ProductCondition.Used\n }\n },\n 100_000L\n );\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionConditionUsed\n });\n\n // Sub-division node: (Condition \"other\" node)\n // * Product Condition: (not specified)\n AdGroupCriterion adGroupCriterionConditionOther =\n CreateListingGroupSubdivision(\n customerId,\n adGroupId,\n -2L,\n adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo()\n {\n // All sibling nodes must have the same dimension type, even if they\n // don't contain a bid.\n ProductCondition = new ProductConditionInfo()\n }\n );\n // Get the resource name that will be used for the condition other node.\n // This resource has not been created yet and will include the temporary ID as\n // part of the criterion ID.\n String adGroupCriterionResourceNameConditionOther =\n adGroupCriterionConditionOther.ResourceName;\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionConditionOther\n });\n\n // 4) Construct the listing group unit nodes for CoolBrand, CheapBrand and other\n\n // Biddable Unit node: (Brand CoolBrand node)\n // * Brand: CoolBrand\n // * CPC bid: $0.90\n AdGroupCriterion adGroupCriterionBrandCoolBrand =\n CreateListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo()\n {\n ProductBrand = new ProductBrandInfo()\n {\n Value = \"CoolBrand\"\n }\n },\n 900_000L);\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionBrandCoolBrand\n });\n\n // Biddable Unit node: (Brand CheapBrand node)\n // * Brand: CheapBrand\n // * CPC bid: $0.01\n AdGroupCriterion adGroupCriterionBrandCheapBrand =\n CreateListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo()\n {\n ProductBrand = new ProductBrandInfo()\n {\n Value = \"CheapBrand\"\n }\n },\n 10_000L);\n\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionBrandCheapBrand\n });\n\n // Biddable Unit node: (Brand other node)\n // * Brand: CheapBrand\n // * CPC bid: $0.01\n AdGroupCriterion adGroupCriterionBrandOther =\n CreateListingGroupUnitBiddable(\n customerId,\n adGroupId,\n adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo()\n {\n ProductBrand = new ProductBrandInfo()\n },\n 50_000L);\n operations.Add(new AdGroupCriterionOperation()\n {\n Create = adGroupCriterionBrandOther\n });\n\n // Issues a mutate request to add the ad group criterion to the ad group.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionService.MutateAdGroupCriteria(\n customerId.ToString(), operations);\n\n // Display the results.\n foreach (MutateAdGroupCriterionResult mutateAdGroupCriterionResult\n in response.Results)\n {\n Console.WriteLine(\"Added ad group criterion for listing group with resource \" +\n $\"name: '{mutateAdGroupCriterionResult.ResourceName}.\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Removes all the ad group criteria that define the existing listing group tree for an\n /// ad group. Returns without an error if all listing group criterion are successfully\n /// removed.\n /// </summary>\n /// <param name=\"client\">The Google Ads API client..</param>\n /// <param name=\"customerId\">The client customer ID.</param>\n /// <param name=\"adGroupId\">The ID of the ad group that the new listing group tree will\n /// be removed from.</param>\n /// <exception cref=\"GoogleAdsException\">Thrown if an API request failed with one or more\n /// service errors.</exception>\n private void RemoveListingGroupTree(GoogleAdsClient client, long customerId,\n long adGroupId)\n {\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsService = client.GetService(\n Services.V25.GoogleAdsService);\n\n // Get the AdGroupCriterionService.\n AdGroupCriterionServiceClient adGroupCriterionService =\n client.GetService(Services.V25.AdGroupCriterionService);\n\n String searchQuery = \"SELECT ad_group_criterion.resource_name FROM \" +\n \"ad_group_criterion WHERE ad_group_criterion.type = LISTING_GROUP AND \" +\n \"ad_group_criterion.listing_group.parent_ad_group_criterion IS NULL \" +\n $\"AND ad_group.id = {adGroupId}\";\n\n // Creates a request that will retrieve all listing groups where the parent ad group\n // criterion is NULL (and hence the root node in the tree) for a given ad group ID.\n SearchGoogleAdsRequest request = new SearchGoogleAdsRequest()\n {\n CustomerId = customerId.ToString(),\n Query = searchQuery\n };\n\n // Issues the search request.\n GoogleAdsRow googleAdsRow = googleAdsService.Search(request).FirstOrDefault();\n\n if (googleAdsRow == null)\n {\n return;\n }\n\n AdGroupCriterion adGroupCriterion = googleAdsRow.AdGroupCriterion;\n Console.WriteLine(\"Found ad group criterion with the resource name: '{0}'.\",\n adGroupCriterion.ResourceName);\n\n AdGroupCriterionOperation operation = new AdGroupCriterionOperation()\n {\n Remove = adGroupCriterion.ResourceName\n };\n\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionService.MutateAdGroupCriteria(\n customerId.ToString(), new AdGroupCriterionOperation[] { operation });\n Console.WriteLine($\"Removed {response.Results.Count}.\");\n }\n\n /// <summary>\n /// Creates a new criterion containing a biddable unit listing group node.\n /// </summary>\n /// <param name=\"customerId\">The client customer ID.</param>\n /// <param name=\"adGroupId\">The ID of the ad group.</param>\n /// <param name=\"parentAdGroupCriterionResourceName\">The resource name of the parent of\n /// this criterion.</param>\n /// <param name=\"listingDimensionInfo\">The ListingDimensionInfo to be set for this listing\n /// group.</param>\n /// <param name=\"cpcBidMicros\">The CPC bid for items in this listing group. This value\n /// should be specified in micros.</param>\n /// <returns>The ad group criterion object that contains the biddable unit listing group\n /// node.</returns>\n private AdGroupCriterion CreateListingGroupUnitBiddable(long customerId, long adGroupId,\n String parentAdGroupCriterionResourceName, ListingDimensionInfo listingDimensionInfo,\n long cpcBidMicros)\n {\n String adGroupResourceName = ResourceNames.AdGroup(customerId, adGroupId);\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion()\n {\n // The resource name the ad group the listing group node will be attached to unit.\n // Note: Listing group units do not require temporary IDs if ad group resource name\n // and parentAdGroupCriterionResourceName are specified. To use temporary IDs for\n // unit criteria, use ResourceName property.\n AdGroup = adGroupResourceName,\n Status = AdGroupCriterionStatus.Enabled,\n ListingGroup = new ListingGroupInfo()\n {\n // Set the type as a UNIT, which will allow the group to be biddable\n Type = ListingGroupType.Unit,\n\n // Set the ad group criterion resource name for the parent listing group.\n // This can include a criterion ID if the parent criterion is not yet created.\n // Use StringValue to convert from a String to a compatible argument type.\n ParentAdGroupCriterion = parentAdGroupCriterionResourceName,\n\n // Case values contain the listing dimension used for the node.\n CaseValue = listingDimensionInfo\n },\n\n // Set the bid for this listing group unit.\n // This will be used as the CPC bid for items that are included in this\n // listing group\n CpcBidMicros = cpcBidMicros\n };\n return adGroupCriterion;\n }\n\n /// <summary>\n /// Creates a new criterion containing a subdivision listing group node.\n /// </summary>\n /// <param name=\"customerId\">The client customer ID.</param>\n /// <param name=\"adGroupId\">The ID of the ad group.</param>\n /// <param name=\"adGroupCriterionId\">The ID of the criterion. This value will used to\n /// construct the resource name. This can be a negative number if the criterion is yet to\n /// be created.</param>\n /// <param name=\"parentAdGroupCriterionResourceName\">The resource name of the parent of\n /// this criterion.</param>\n /// <param name=\"listingDimensionInfo\">The ListingDimensionInfo to be set for this listing\n /// group.</param>\n /// <returns>The ad group criterion object that contains the subdivision listing group\n /// node.</returns>\n private AdGroupCriterion CreateListingGroupSubdivision(long customerId, long adGroupId,\n long adGroupCriterionId, String parentAdGroupCriterionResourceName,\n ListingDimensionInfo listingDimensionInfo)\n {\n String adGroupCriterionResourceName = ResourceNames.AdGroupCriterion(\n customerId, adGroupId, adGroupCriterionId);\n\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion()\n {\n // The resource name the criterion will be created with. This will define the\n // ID for the ad group criterion.\n ResourceName = adGroupCriterionResourceName,\n Status = AdGroupCriterionStatus.Enabled,\n ListingGroup = new ListingGroupInfo()\n {\n Type = ListingGroupType.Subdivision,\n\n // Set the ad group criterion resource name for the parent listing group.\n // This can include a criterion ID if the parent criterion is not yet created.\n // Use StringValue to convert from a String to a compatible argument type.\n ParentAdGroupCriterion = parentAdGroupCriterionResourceName,\n\n // Case values contain the listing dimension used for the node.\n CaseValue = listingDimensionInfo\n }\n };\n\n return adGroupCriterion;\n }\n\n /// <summary>\n /// Creates a new criterion containing a root subdivision listing group node.\n /// </summary>\n /// <param name=\"customerId\">The client customer ID.</param>\n /// <param name=\"adGroupId\">The ID of the ad group.</param>\n /// <param name=\"adGroupCriterionId\">The ID of the criterion. This value will used to\n /// construct the resource name. This can be a negative number if the criterion is yet\n /// to be created.</param>\n /// <returns>The ad group criterion object that contains the listing group root node.\n /// </returns>\n private AdGroupCriterion CreateListingGroupSubdivisionRoot(long customerId, long adGroupId,\n long adGroupCriterionId)\n {\n String adGroupCriterionResourceName = ResourceNames.AdGroupCriterion(customerId,\n adGroupId, adGroupCriterionId);\n\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion()\n {\n // The resource name the criterion will be created with. This will define the ID\n // for the ad group criterion.\n ResourceName = adGroupCriterionResourceName,\n Status = AdGroupCriterionStatus.Enabled,\n ListingGroup = new ListingGroupInfo()\n {\n // Set the type as a SUBDIVISION, which will allow the node to be the parent of\n // another sub-tree.\n Type = ListingGroupType.Subdivision\n }\n };\n return adGroupCriterion;\n }\n }\n}\nAddShoppingProductListingGroupTree.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2018 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\ShoppingAds;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ProductBrandInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ListingDimensionInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ListingGroupInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ProductConditionInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdGroupCriterionStatusEnum\\AdGroupCriterionStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\ListingGroupTypeEnum\\ListingGroupType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\ProductConditionEnum\\ProductCondition;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AdGroupCriterion;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AdGroupCriterionOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\GoogleAdsRow;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAdGroupCriteriaRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\SearchGoogleAdsRequest;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * This example shows how to add a shopping listing group tree to a shopping ad group. The example\n * will optionally clear an existing listing group tree and rebuild it to include the following tree\n * structure:\n *\n * <pre>\n * ProductCanonicalCondition NEW $0.20\n * ProductCanonicalCondition USED $0.10\n * ProductCanonicalCondition null (everything else)\n * ProductBrand CoolBrand $0.90\n * ProductBrand CheapBrand $0.01\n * ProductBrand null (everything else) $0.50\n * </pre>\n */\nclass AddShoppingProductListingGroupTree\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const AD_GROUP_ID = 'INSERT_AD_GROUP_ID_HERE';\n private const REPLACE_EXISTING_TREE = 'INSERT_BOOLEAN_TRUE_OR_FALSE_HERE';\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::AD_GROUP_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::REPLACE_EXISTING_TREE => GetOpt::REQUIRED_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::AD_GROUP_ID]\n ?: self::AD_GROUP_ID,\n $options[ArgumentNames::REPLACE_EXISTING_TREE]\n ?: self::REPLACE_EXISTING_TREE\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $adGroupId the ad group ID\n * @param bool $replaceExistingTree true if it should replace the existing listing group\n * tree on the ad group, if it already exists. The example will throw a\n * 'LISTING_GROUP_ALREADY_EXISTS' error if listing group tree already exists and this option\n * is not set to true\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n bool $replaceExistingTree\n ) {\n // 1) Optional: Remove the existing listing group tree, if it already exists on the ad\n // group.\n if ($replaceExistingTree === 'true') {\n self::removeListingGroupTree($googleAdsClient, $customerId, $adGroupId);\n }\n // Create a list of ad group criteria to add.\n $operations = [];\n\n // 2) Construct the listing group tree \"root\" node.\n\n // Subdivision node: (Root node)\n $adGroupCriterionRoot = self::createListingGroupSubdivision($customerId, $adGroupId);\n // Get the resource name that will be used for the root node.\n // This resource has not been created yet and will include the temporary ID as part of the\n // criterion ID.\n $adGroupCriterionResourceNameRoot = $adGroupCriterionRoot->getResourceName();\n $operations[] = new AdGroupCriterionOperation(['create' => $adGroupCriterionRoot]);\n\n // 3) Construct the listing group unit nodes for NEW, USED and other.\n\n // Biddable Unit node: (Condition NEW node)\n // * Product Condition: NEW\n // * CPC bid: $0.20\n $adGroupCriterionConditionNew = self::createListingGroupUnitBiddable(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo([\n 'product_condition' => new ProductConditionInfo(\n ['condition' => ProductCondition::PBNEW]\n )\n ]),\n 200000\n );\n $operations[] = new AdGroupCriterionOperation(['create' => $adGroupCriterionConditionNew]);\n\n // Biddable Unit node: (Condition USED node)\n // * Product Condition: USED\n // * CPC bid: $0.10\n $adGroupCriterionConditionUsed = self::createListingGroupUnitBiddable(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo([\n 'product_condition' => new ProductConditionInfo(\n ['condition' => ProductCondition::USED]\n )\n ]),\n 100000\n );\n $operations[] = new AdGroupCriterionOperation(['create' => $adGroupCriterionConditionUsed]);\n\n // Sub-division node: (Condition \"other\" node)\n // * Product Condition: (not specified)\n $adGroupCriterionConditionOther = self::createListingGroupSubdivision(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameRoot,\n new ListingDimensionInfo([\n // All sibling nodes must have the same dimension type, even if they don't contain a\n // bid.\n 'product_condition' => new ProductConditionInfo()\n ])\n );\n $operations[] =\n new AdGroupCriterionOperation(['create' => $adGroupCriterionConditionOther]);\n\n // Get the resource name that will be used for the condition other node.\n // This resource has not been created yet and will include the temporary ID as part of the\n // criterion ID.\n $adGroupCriterionResourceNameConditionOther =\n $adGroupCriterionConditionOther->getResourceName();\n\n // 4) Construct the listing group unit nodes for CoolBrand, CheapBrand and other.\n\n // Biddable Unit node: (Brand CoolBrand node)\n // * Brand: CoolBrand\n // * CPC bid: $0.90\n $adGroupCriterionBrandCoolBrand = self::createListingGroupUnitBiddable(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo([\n 'product_brand' => new ProductBrandInfo(['value' => 'CoolBrand'])\n ]),\n 900000\n );\n $operations[] =\n new AdGroupCriterionOperation(['create' => $adGroupCriterionBrandCoolBrand]);\n\n // Biddable Unit node: (Brand CheapBrand node)\n // * Brand: CheapBrand\n // * CPC bid: $0.01\n $adGroupCriterionBrandCheapBrand = self::createListingGroupUnitBiddable(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo([\n 'product_brand' => new ProductBrandInfo(['value' => 'CheapBrand'])\n ]),\n 10000\n );\n $operations[] =\n new AdGroupCriterionOperation(['create' => $adGroupCriterionBrandCheapBrand]);\n\n // Biddable Unit node: (Brand other node)\n // * CPC bid: $0.05\n $adGroupCriterionBrandOtherBrand = self::createListingGroupUnitBiddable(\n $customerId,\n $adGroupId,\n $adGroupCriterionResourceNameConditionOther,\n new ListingDimensionInfo([\n 'product_brand' => new ProductBrandInfo()\n ]),\n 50000\n );\n $operations[] =\n new AdGroupCriterionOperation(['create' => $adGroupCriterionBrandOtherBrand]);\n\n // Issues a mutate request.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, $operations)\n );\n printf(\n 'Added %d ad group criteria for listing group tree with the following resource '\n . 'names:%s',\n $response->getResults()->count(),\n PHP_EOL\n );\n foreach ($response->getResults() as $addedAdGroupCriterion) {\n /** @var AdGroupCriterion $addedAdGroupCriterion */\n print $addedAdGroupCriterion->getResourceName() . PHP_EOL;\n }\n }\n\n /**\n * Removes all the ad group criteria that define the existing listing group tree for an ad\n * group.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $adGroupId the ID of ad group that the existing listing group tree will be\n * removed from\n */\n private static function removeListingGroupTree(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId\n ) {\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n // Creates a query that retrieves a listing group tree.\n $query = 'SELECT ad_group_criterion.resource_name '\n . 'FROM ad_group_criterion '\n . 'WHERE ad_group_criterion.type = LISTING_GROUP '\n . 'AND ad_group_criterion.listing_group.parent_ad_group_criterion IS NULL '\n . 'AND ad_group.id = ' . $adGroupId;\n\n // Issues a search request.\n $response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n $operations = [];\n // Iterates over all rows in all pages and prints the requested field values for\n // the listing group tree in each row.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $adGroupCriterion = $googleAdsRow->getAdGroupCriterion();\n printf(\n \"Found an ad group criterion with the resource name: '%s'.%s\",\n $adGroupCriterion->getResourceName(),\n PHP_EOL\n );\n\n // Creates an ad group criterion operation.\n $adGroupCriterionOperation = new AdGroupCriterionOperation();\n $adGroupCriterionOperation->setRemove($adGroupCriterion->getResourceName());\n $operations[] = $adGroupCriterionOperation;\n }\n if (count($operations) > 0) {\n // Issues a mutate request.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, $operations)\n );\n printf(\"Removed %d ad group criteria.%s\", $response->getResults()->count(), PHP_EOL);\n }\n }\n\n /**\n * Creates a new criterion containing a subdivision listing group node. If the parent ad group\n * criterion resource name is not specified, this method creates a root node.\n *\n * @param int $customerId the customer ID\n * @param int $adGroupId the ad group ID\n * @param string|null $parentAdGroupCriterionResourceName the resource name of the parent of\n * this criterion. If null, this method will create a root of the tree\n * @param ListingDimensionInfo|null $listingDimensionInfo the listing dimension info to be set\n * for this listing group. This is required for non-root subdivisions\n * @return AdGroupCriterion the ad group criterion that contains the listing group root node\n */\n private static function createListingGroupSubdivision(\n int $customerId,\n int $adGroupId,\n string $parentAdGroupCriterionResourceName = null,\n ListingDimensionInfo $listingDimensionInfo = null\n ) {\n static $tempId = 0;\n $listingGroupInfo = new ListingGroupInfo([\n // Set the type as a SUBDIVISION, which will allow the node to be the parent of\n // another sub-tree.\n 'type' => ListingGroupType::SUBDIVISION\n ]);\n // If $parentAdGroupCriterionResourceName and $listingDimensionInfo are not null, create\n // a non-root division by setting its parent and case value.\n if (!is_null($parentAdGroupCriterionResourceName) && !is_null($listingDimensionInfo)) {\n // Set the ad group criterion resource name for the parent listing group.\n // This can include a temporary ID if the parent criterion is not yet created.\n $listingGroupInfo->setParentAdGroupCriterion($parentAdGroupCriterionResourceName);\n // Case values contain the listing dimension used for the node.\n $listingGroupInfo->setCaseValue($listingDimensionInfo);\n }\n\n $adGroupCriterion = new AdGroupCriterion([\n // The resource name the criterion will be created with. This will define the ID for the\n // ad group criterion.\n 'resource_name' => ResourceNames::forAdGroupCriterion(\n $customerId,\n $adGroupId,\n // Specify a decreasing negative number as a temporary ad group criterion ID. The\n // ad group criterion will get the real ID when created on the server.\n --$tempId\n ),\n 'status' => AdGroupCriterionStatus::ENABLED,\n 'listing_group' => $listingGroupInfo\n ]);\n\n return $adGroupCriterion;\n }\n\n /**\n * Creates a new criterion containing a biddable unit listing group node.\n *\n * @param int $customerId the customer ID\n * @param int $adGroupId the ad group ID\n * @param string $parentAdGroupCriterionResourceName the resource name of the parent of this\n * criterion\n * @param ListingDimensionInfo $listingDimensionInfo the listing dimension info to be set for\n * this listing group\n * @param int $cpcBidMicros the CPC bid for items in this listing group. This value should be\n * specified\n * @return AdGroupCriterion the ad group criterion that contains the biddable unit listing\n * group node\n */\n private static function createListingGroupUnitBiddable(\n int $customerId,\n int $adGroupId,\n string $parentAdGroupCriterionResourceName,\n ListingDimensionInfo $listingDimensionInfo,\n int $cpcBidMicros\n ) {\n // Note: There are two approaches for creating new unit nodes:\n // (1) Set the ad group resource name on the criterion (no temporary ID required).\n // (2) Use a temporary ID to construct the criterion resource name and set it using\n // setResourceName.\n // In both cases you must set the parentAdGroupCriterionResourceName on the listing\n // group for non-root nodes.\n // This example demonstrates method (1).\n $adGroupCriterion = new AdGroupCriterion([\n // The ad group the listing group will be attached to.\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'status' => AdGroupCriterionStatus::ENABLED,\n 'listing_group' => new ListingGroupInfo([\n // Set the type as a UNIT, which will allow the group to be biddable.\n 'type' => ListingGroupType::UNIT,\n // Set the ad group criterion resource name for the parent listing group.\n // This can include a temporary ID if the parent criterion is not yet created.\n 'parent_ad_group_criterion' => $parentAdGroupCriterionResourceName,\n // Case values contain the listing dimension used for the node.\n 'case_value' => $listingDimensionInfo\n ]),\n // Set the bid for this listing group unit.\n // This will be used as the CPC bid for items that are included in this listing group.\n 'cpc_bid_micros' => $cpcBidMicros\n ]);\n\n return $adGroupCriterion;\n }\n}\n\nAddShoppingProductListingGroupTree::main();\nAddShoppingProductListingGroupTree.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2020 Google LLC\n#\n# 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.\n\"\"\"Adds a shopping listing group tree to a shopping ad group.\n\nThe example will clear an existing listing group tree and rebuild it include the\nfollowing tree structure:\n\nProductCanonicalCondition NEW $0.20\nProductCanonicalCondition USED $0.10\nProductCanonicalCondition null (everything else)\n ProductBrand CoolBrand $0.90\n ProductBrand CheapBrand $0.01\n ProductBrand null (everything else) $0.50\n\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import List, Optional\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.common.types.criteria import (\n ListingDimensionInfo,\n ListingGroupInfo,\n)\nfrom google.ads.googleads.v24.enums.types.product_condition import (\n ProductConditionEnum,\n)\nfrom google.ads.googleads.v24.resources.types.ad_group_criterion import (\n AdGroupCriterion,\n)\nfrom google.ads.googleads.v24.services.services.ad_group_criterion_service import (\n AdGroupCriterionServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.ad_group_service import (\n AdGroupServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.google_ads_service import (\n GoogleAdsServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_criterion_service import (\n AdGroupCriterionOperation,\n)\nfrom google.ads.googleads.v24.services.types.google_ads_service import (\n MutateGoogleAdsResponse,\n SearchGoogleAdsResponse,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\nlast_criterion_id: int = 0\n\n\ndef next_id() -> str:\n \"\"\"Returns a decreasing negative number for temporary ad group criteria IDs.\n\n The ad group criteria will get real IDs when created on the server.\n Returns -1, -2, -3, etc. on subsequent calls.\n\n Returns:\n The string representation of a negative integer.\n \"\"\"\n global last_criterion_id\n last_criterion_id -= 1\n return str(last_criterion_id)\n\n\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n replace_existing_tree: bool,\n) -> None:\n \"\"\"Adds a shopping listing group tree to a shopping ad group.\n\n Args:\n client: An initialized Google Ads client.\n customer_id: The Google Ads customer ID.\n ad_group_id: The ad group ID to which the node will be added.\n replace_existing_tree: Boolean, whether to replace the existing listing\n group tree on the ad group. Defaults to false.\n \"\"\"\n # Get the AdGroupCriterionService client.\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n\n # Optional: Remove the existing listing group tree, if it already exists\n # on the ad group. The example will throw a LISTING_GROUP_ALREADY_EXISTS\n # error if a listing group tree already exists and this option is not\n # set to true.\n if replace_existing_tree:\n remove_listing_group_tree(client, customer_id, ad_group_id)\n\n # Create a list of ad group criteria operations.\n operations: List[AdGroupCriterionOperation] = []\n\n # Construct the listing group tree \"root\" node.\n # Subdivision node: (Root node)\n ad_group_criterion_root_operation: AdGroupCriterionOperation = (\n create_listing_group_subdivision(client, customer_id, ad_group_id)\n )\n\n # Get the resource name that will be used for the root node.\n # This resource has not been created yet and will include the temporary\n # ID as part of the criterion ID.\n ad_group_criterion_root_resource_name: str = (\n ad_group_criterion_root_operation.create.resource_name\n )\n operations.append(ad_group_criterion_root_operation)\n\n # Construct the listing group unit nodes for NEW, USED, and other.\n product_condition_enum: ProductConditionEnum = (\n client.enums.ProductConditionEnum\n )\n condition_dimension_info: ListingDimensionInfo = client.get_type(\n \"ListingDimensionInfo\"\n )\n\n # Biddable Unit node: (Condition NEW node)\n # * Product Condition: NEW\n # * CPC bid: $0.20\n condition_dimension_info.product_condition.condition = (\n product_condition_enum.NEW\n )\n operations.append(\n create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n condition_dimension_info,\n 200_000,\n )\n )\n\n # Biddable Unit node: (Condition USED node)\n # * Product Condition: USED\n # * CPC bid: $0.10\n condition_dimension_info.product_condition.condition = (\n product_condition_enum.USED\n )\n operations.append(\n create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n condition_dimension_info,\n 100_000,\n )\n )\n\n # Sub-division node: (Condition \"other\" node)\n # * Product Condition: (not specified)\n # Note that all sibling nodes must have the same dimension type, even if\n # they don't contain a bid.\n client.copy_from(\n condition_dimension_info.product_condition,\n client.get_type(\"ProductConditionInfo\"),\n )\n ad_group_criterion_other_operation: AdGroupCriterionOperation = (\n create_listing_group_subdivision(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n condition_dimension_info,\n )\n )\n # Get the resource name that will be used for the condition other node.\n # This resource has not been created yet and will include the temporary\n # ID as part of the criterion ID.\n ad_group_criterion_other_resource_name: str = (\n ad_group_criterion_other_operation.create.resource_name\n )\n operations.append(ad_group_criterion_other_operation)\n\n # Build the listing group nodes for CoolBrand, CheapBrand, and other.\n brand_dimension_info: ListingDimensionInfo = client.get_type(\n \"ListingDimensionInfo\"\n )\n\n # Biddable Unit node: (Brand CoolBrand node)\n # * Brand: CoolBrand\n # * CPC bid: $0.90\n brand_dimension_info.product_brand.value = \"CoolBrand\"\n operations.append(\n create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_other_resource_name,\n brand_dimension_info,\n 900_000,\n )\n )\n\n # Biddable Unit node: (Brand CheapBrand node)\n # * Brand: CheapBrand\n # * CPC bid: $0.01\n brand_dimension_info.product_brand.value = \"CheapBrand\"\n operations.append(\n create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_other_resource_name,\n brand_dimension_info,\n 10_000,\n )\n )\n\n # Biddable Unit node: (Brand other node)\n # * CPC bid: $0.05\n client.copy_from(\n brand_dimension_info.product_brand,\n client.get_type(\"ProductBrandInfo\"),\n )\n operations.append(\n create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_other_resource_name,\n brand_dimension_info,\n 50_000,\n )\n )\n\n # Add the ad group criteria.\n mutate_ad_group_criteria_response: MutateGoogleAdsResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=operations\n )\n )\n\n # Print the results of the successful mutates.\n print(\n \"Added ad group criteria for the listing group tree with the \"\n \"following resource names:\"\n )\n for result in mutate_ad_group_criteria_response.results:\n print(f\"\\t{result.resource_name}\")\n\n print(f\"{len(mutate_ad_group_criteria_response.results)} criteria added.\")\n\n\ndef remove_listing_group_tree(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n) -> None:\n \"\"\"Removes ad group criteria for an ad group's existing listing group tree.\n\n Args:\n client: An initialized Google Ads client.\n customer_id: The Google Ads customer ID.\n ad_group_id: The ad group ID from which to remove the listing group\n tree.\n \"\"\"\n # Get the GoogleAdsService client.\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n print(\"Removing existing listing group tree...\")\n # Create a search Google Ads request that will retrieve all listing groups\n # where the parent ad group criterion is NULL (and hence the root node in\n # the tree) for a given ad group id.\n # Note: ad_group_id is used as an int in the query.\n query: str = f\"\"\"\n SELECT ad_group_criterion.resource_name\n FROM ad_group_criterion\n WHERE\n ad_group_criterion.type = LISTING_GROUP\n AND ad_group_criterion.listing_group.parent_ad_group_criterion IS NULL\n AND ad_group.id = {ad_group_id}\"\"\"\n\n results: SearchGoogleAdsResponse = googleads_service.search(\n customer_id=customer_id, query=query\n )\n ad_group_criterion_operations: List[AdGroupCriterionOperation] = []\n\n # Iterate over all rows to find the ad group criteria to remove.\n for row in results:\n criterion: AdGroupCriterion = row.ad_group_criterion\n print(\n \"Found an ad group criterion with resource name: \"\n f\"'{criterion.resource_name}'.\"\n )\n ad_group_criterion_operation: AdGroupCriterionOperation = (\n client.get_type(\"AdGroupCriterionOperation\")\n )\n ad_group_criterion_operation.remove = criterion.resource_name\n ad_group_criterion_operations.append(ad_group_criterion_operation)\n\n if ad_group_criterion_operations:\n # Remove the ad group criteria that define the listing group tree.\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n response: MutateGoogleAdsResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id,\n operations=ad_group_criterion_operations,\n )\n )\n print(f\"Removed {len(response.results)} ad group criteria.\")\n\n\ndef create_listing_group_subdivision(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n parent_ad_group_criterion_resource_name: Optional[str] = None,\n listing_dimension_info: Optional[ListingDimensionInfo] = None,\n) -> AdGroupCriterionOperation:\n \"\"\"Creates a new criterion containing a subdivision listing group node.\n\n If the parent ad group criterion resource name or listing dimension info are\n not specified, this method creates a root node.\n\n Args:\n client: An initialized Google Ads client.\n customer_id: The Google Ads customer ID.\n ad_group_id: The ad group ID to which the node will be added.\n parent_ad_group_criterion_resource_name: The string resource name of the\n parent node to which this listing will be attached.\n listing_dimension_info: A ListingDimensionInfo object containing details\n for this listing.\n\n Returns:\n An AdGroupCriterionOperation containing a populated ad group criterion.\n \"\"\"\n # Create an ad group criterion operation and populate the criterion.\n operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n ad_group_criterion: AdGroupCriterion = operation.create\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n # The resource name the criterion will be created with. This will define\n # the ID for the ad group criterion.\n ad_group_criterion.resource_name = (\n ad_group_criterion_service.ad_group_criterion_path(\n customer_id, ad_group_id, next_id()\n )\n )\n ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED\n\n listing_group_info: ListingGroupInfo = ad_group_criterion.listing_group\n # Set the type as a SUBDIVISION, which will allow the node to be the\n # parent of another sub-tree.\n listing_group_info.type_ = client.enums.ListingGroupTypeEnum.SUBDIVISION\n # If parent_ad_group_criterion_resource_name and listing_dimension_info\n # are not null, create a non-root division by setting its parent and case\n # value.\n if (\n parent_ad_group_criterion_resource_name\n and listing_dimension_info is not None\n ):\n # Set the ad group criterion resource name for the parent listing group.\n # This can include a temporary ID if the parent criterion is not yet\n # created.\n listing_group_info.parent_ad_group_criterion = (\n parent_ad_group_criterion_resource_name\n )\n\n # Case values contain the listing dimension used for the node.\n client.copy_from(listing_group_info.case_value, listing_dimension_info)\n\n return operation\n\n\ndef create_listing_group_unit_biddable(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n parent_ad_group_criterion_resource_name: str,\n listing_dimension_info: ListingDimensionInfo,\n cpc_bid_micros: Optional[int] = None,\n) -> AdGroupCriterionOperation:\n \"\"\"Creates a new criterion containing a biddable unit listing group node.\n\n Args:\n client: An initialized Google Ads client.\n customer_id: The Google Ads customer ID.\n ad_group_id: The ad group ID to which the node will be added.\n parent_ad_group_criterion_resource_name: The string resource name of the\n parent node to which this listing will be attached.\n listing_dimension_info: A ListingDimensionInfo object containing details\n for this listing.\n cpc_bid_micros: The cost-per-click bid for this listing in micros.\n\n Returns:\n An AdGroupCriterionOperation with a populated create field.\n \"\"\"\n # Note: There are two approaches for creating new unit nodes:\n # (1) Set the ad group resource name on the criterion (no temporary ID\n # required).\n # (2) Use a temporary ID to construct the criterion resource name and set\n # it to the 'resourceName' attribute.\n # In both cases you must set the parent ad group criterion's resource name\n # on the listing group for non-root nodes.\n # This example demonstrates method (1).\n operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n\n criterion: AdGroupCriterion = operation.create\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n criterion.ad_group = ad_group_service.ad_group_path(\n customer_id, ad_group_id\n )\n criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED\n # Set the bid for this listing group unit.\n # This will be used as the CPC bid for items that are included in this\n # listing group.\n if cpc_bid_micros:\n criterion.cpc_bid_micros = cpc_bid_micros\n\n listing_group: ListingGroupInfo = criterion.listing_group\n # Set the type as a UNIT, which will allow the group to be biddable.\n listing_group.type_ = client.enums.ListingGroupTypeEnum.UNIT\n # Set the ad group criterion resource name for the parent listing group.\n # This can have a temporary ID if the parent criterion is not yet created.\n listing_group.parent_ad_group_criterion = (\n parent_ad_group_criterion_resource_name\n )\n # Case values contain the listing dimension used for the node.\n if listing_dimension_info is not None:\n client.copy_from(listing_group.case_value, listing_dimension_info)\n\n return operation\n\n\nif __name__ == \"__main__\":\n parser = argparse.ArgumentParser(\n description=\"Add shopping product listing group tree to a shopping ad \"\n \"group.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-a\",\n \"--ad_group_id\",\n type=str,\n required=True,\n help=\"The ID of the ad group that will receive the listing group tree.\",\n )\n parser.add_argument(\n \"-r\",\n \"--replace_existing_tree\",\n action=\"store_true\",\n required=False,\n default=False,\n help=\"Optional, whether to replace the existing listing group tree on \"\n \"the ad group if one already exists. Defaults to false.\",\n )\n\n args: argparse.Namespace = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(\n googleads_client,\n args.customer_id,\n args.ad_group_id,\n args.replace_existing_tree,\n )\n except GoogleAdsException as ex:\n print(\n f\"Request with ID '{ex.request_id}' failed with status \"\n f\"'{ex.error.code().name}' and includes the following errors:\"\n )\n for error in ex.failure.errors:\n print(f\"\\tError with message '{error.message}'.\")\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nadd_shopping_product_listing_group_tree.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2019 Google LLC\n#\n# 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.\n#\n# This example shows how to add a shopping listing group tree to a shopping ad\n# group. The example will optionally clear an existing listing group tree and\n# rebuild it to include the following tree structure:\n#\n# ProductCanonicalCondition NEW $0.20\n# ProductCanonicalCondition USED $0.10\n# ProductCanonicalCondition null (everything else)\n# ProductBrand CoolBrand $0.90\n# ProductBrand CheapBrand $0.01\n# ProductBrand null (everything else) $0.50\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\nrequire 'date'\n\ndef add_shopping_product_listing_group_tree(\n customer_id,\n ad_group_id,\n should_replace_existing_tree\n)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # 1) Optional: Remove the existing listing group tree, if it already exists\n # on the ad group.\n if should_replace_existing_tree\n remove_listing_group_tree(client, customer_id, ad_group_id)\n end\n\n # 2) Construct the listing group tree \"root\" node.\n\n # Subdivision node: (Root node)\n ad_group_criterion_root = create_listing_group_subdivision(\n client,\n customer_id,\n ad_group_id,\n )\n # This resource has not been created yet and will include the temporary ID as\n # part of the criterion ID.\n ad_group_criterion_root_resource_name = ad_group_criterion_root.resource_name\n operations = [client.operation.create_resource.ad_group_criterion(ad_group_criterion_root)]\n\n # 3) Construct the listing group unit nodes for NEW, USED, and other.\n\n # Biddable Unit node: (Condition NEW node)\n # * Product Condition: NEW\n # * CPC bid: $0.20\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_condition = client.resource.product_condition_info do |pci|\n pci.condition = :NEW\n end\n end\n\n ad_group_criterion_condition_new = create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n listing_dimension_info,\n 200_000,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_condition_new\n )\n operations << operation\n\n # Biddable Unit node: (Condition USED node)\n # * Product Condition: USED\n # * CPC bid: $0.10\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_condition = client.resource.product_condition_info do |pci|\n pci.condition = :USED\n end\n end\n ad_group_criterion_condition_used = create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n listing_dimension_info,\n 100_000,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_condition_used\n )\n operations << operation\n\n # Sub-division node: (Condition \"other\" node)\n # * Product Condition: (not specified)\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_condition = client.resource.product_condition_info\n end\n ad_group_criterion_condition_other = create_listing_group_subdivision(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_root_resource_name,\n listing_dimension_info,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_condition_other\n )\n operations << operation\n\n ad_group_criterion_condition_other_resource_name =\n ad_group_criterion_condition_other.resource_name\n\n # 4) Construct the listing group unit nodes for CoolBrand, CheapBrand, and\n # other.\n\n # Biddable Unit node: (Brand CoolBrand node)\n # * Brand: CoolBrand\n # * CPC bid: $0.90\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_brand = client.resource.product_brand_info do |pbi|\n pbi.value = \"CoolBrand\"\n end\n end\n\n ad_group_criterion_brand_cool_brand = create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_condition_other_resource_name,\n listing_dimension_info,\n 900_000,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_brand_cool_brand\n )\n operations << operation\n\n # Biddable Unit node: (Brand CheapBrand node)\n # * Brand: CheapBrand\n # * CPC bid: $0.01\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_brand = client.resource.product_brand_info do |pbi|\n pbi.value = \"CheapBrand\"\n end\n end\n ad_group_criterion_brand_cheap_brand = create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_condition_other_resource_name,\n listing_dimension_info,\n 10_000,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_brand_cheap_brand\n )\n operations << operation\n\n # Biddable Unit node: (Brand other node)\n # * CPC bid: $0.05\n listing_dimension_info = client.resource.listing_dimension_info do |ldi|\n ldi.product_brand = client.resource.product_brand_info\n end\n ad_group_criterion_brand_other_brand = create_listing_group_unit_biddable(\n client,\n customer_id,\n ad_group_id,\n ad_group_criterion_condition_other_resource_name,\n listing_dimension_info,\n 50_000,\n )\n operation = client.operation.create_resource.ad_group_criterion(\n ad_group_criterion_brand_other_brand\n )\n operations << operation\n\n # Issue the mutate request.\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: operations,\n )\n\n total_count = 0\n response.results.each do |added_criterion|\n puts \"Added ad group criterion with name: #{added_criterion.resource_name}\"\n total_count += 1\n end\n puts \"#{total_count} criteria added in total.\"\nend\n\ndef remove_listing_group_tree(client, customer_id, ad_group_id)\n ga_service = client.service.google_ads\n\n query = <<~QUERY\n SELECT\n ad_group_criterion.resource_name\n FROM\n ad_group_criterion\n WHERE\n ad_group_criterion.type = LISTING_GROUP\n AND\n ad_group_criterion.listing_group.parent_ad_group_criterion IS NULL\n AND\n ad_group.id = #{ad_group_id}\n QUERY\n\n response = ga_service.search(customer_id: customer_id, query: query)\n\n operations = response.map do |row|\n criterion = row.ad_group_criterion\n puts \"Found an ad group criterion with resource name: #{criterion.resource_name}\"\n\n client.operation.remove_resource.ad_group_criterion(criterion.resource_name)\n end\n\n if operations.any?\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: operations,\n )\n puts \"Removed #{response.results.count} ad group criteria.\"\n end\nend\n\n# Specify a decreasing negative number for temporary ad group criteria IDs. The\n# ad group criteria will get real IDs when created on the server.\n# Returns -1, -2, -3, etc. on subsequent calls.\ndef next_id\n @id ||= 0\n @id -= 1\nend\n\ndef create_listing_group_subdivision(\n client,\n customer_id,\n ad_group_id,\n parent_ad_group_criterion_name = nil,\n listing_dimension_info = nil\n)\n client.resource.ad_group_criterion do |criterion|\n criterion.resource_name = client.path.ad_group_criterion(\n customer_id,\n ad_group_id,\n next_id,\n )\n\n criterion.status = :ENABLED\n criterion.listing_group = client.resource.listing_group_info do |listing_group_info|\n listing_group_info.type = :SUBDIVISION\n\n if parent_ad_group_criterion_name && listing_dimension_info\n listing_group_info.parent_ad_group_criterion = parent_ad_group_criterion_name\n listing_group_info.case_value = listing_dimension_info\n end\n end\n end\nend\n\ndef create_listing_group_unit_biddable(client, customer_id, ad_group_id,\n parent_ad_group_criterion_name, listing_dimension_info, cpc_bid_micros)\n # Note: There are two approaches for creating new unit nodes:\n # (1) Set the ad group resource name on the criterion (no temporary ID\n # required).\n # (2) Use a temporary ID to construct the criterion resource name and set it\n # using the client.path utility.\n # In both cases you must set the parent ad group criterion's resource name on\n # the listing group for non-root nodes.\n # This example demonstrates method (1).\n client.resource.ad_group_criterion do |criterion|\n criterion.ad_group = client.path.ad_group(customer_id, ad_group_id)\n criterion.status = :ENABLED\n criterion.cpc_bid_micros = cpc_bid_micros\n\n criterion.listing_group = client.resource.listing_group_info do |listing_group|\n # The type UNIT allows the group to be biddable.\n listing_group.type = :UNIT\n listing_group.parent_ad_group_criterion = parent_ad_group_criterion_name\n listing_group.case_value = listing_dimension_info\n end\n end\nend\n\nif __FILE__ == $0\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:ad_group_id] = 'INSERT_AD_GROUP_ID_HERE'\n # Specifying any value for this field on the command line will override this\n # to true.\n options[:should_replace_existing_tree] = false\n\n OptionParser.new do |opts|\n opts.banner = sprintf(\"Usage: #{File.basename(__FILE__)} [options]\")\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-A', '--ad-group-id AD-GROUP-ID', String, 'Ad Group ID') do |v|\n options[:ad_group_id] = v\n end\n\n opts.on('-r', '--replace-existing-tree REPLACE-EXISTING-TREE',\n TrueClass, 'Create Default Listing Group') do |v|\n options[:should_replace_existing_tree] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n add_shopping_product_listing_group_tree(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options.fetch(:ad_group_id),\n options.fetch(:should_replace_existing_tree),\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\nadd_shopping_product_listing_group_tree.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2019, Google LLC\n#\n# 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# http://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.\n#\n# This example shows how to add a shopping listing group tree to a shopping ad\n# group. The example will optionally clear an existing listing group tree and\n# rebuild it to include the following tree structure:\n#\n# ProductCanonicalCondition NEW $0.20\n# ProductCanonicalCondition USED $0.10\n# ProductCanonicalCondition null (everything else)\n# ProductBrand CoolBrand $0.90\n# ProductBrand CheapBrand $0.01\n# ProductBrand null (everything else) $0.50\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::Utils::SearchGoogleAdsIterator;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion;\nuse Google::Ads::GoogleAds::V25::Common::ListingGroupInfo;\nuse Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo;\nuse Google::Ads::GoogleAds::V25::Common::ProductConditionInfo;\nuse Google::Ads::GoogleAds::V25::Common::ProductBrandInfo;\nuse Google::Ads::GoogleAds::V25::Enums::ListingGroupTypeEnum\n qw(SUBDIVISION UNIT);\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupCriterionStatusEnum qw(ENABLED);\nuse Google::Ads::GoogleAds::V25::Enums::ProductConditionEnum qw(NEW USED);\nuse\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsRequest;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\nmy $ad_group_id = \"INSERT_AD_GROUP_ID_HERE\";\nmy $replace_existing_tree = undef;\n\nsub add_shopping_product_listing_group_tree {\n my ($api_client, $customer_id, $ad_group_id, $replace_existing_tree) = @_;\n\n # 1) Optional: Remove the existing listing group tree, if it already exists\n # on the ad group.\n if ($replace_existing_tree) {\n remove_listing_group_tree($api_client, $customer_id, $ad_group_id);\n }\n\n # Create a list of ad group criteria operations to add.\n my $operations = [];\n\n # 2) Construct the listing group tree \"root\" node.\n\n # Subdivision node: (Root node)\n my $ad_group_criterion_root =\n create_listing_group_subdivision($customer_id, $ad_group_id);\n # Get the resource name that will be used for the root node.\n # This resource has not been created yet and will include the temporary ID as\n # part of the criterion ID.\n my $ad_group_criterion_root_resource_name =\n $ad_group_criterion_root->{resourceName};\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_root\n });\n\n # 3) Construct the listing group unit nodes for NEW, USED, and other.\n\n # Biddable Unit node: (Condition NEW node)\n # * Product Condition: NEW\n # * CPC bid: $0.20\n my $ad_group_criterion_condition_new = create_listing_group_unit_biddable(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_root_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n productCondition =>\n Google::Ads::GoogleAds::V25::Common::ProductConditionInfo->new({\n condition => NEW\n })}\n ),\n 200000\n );\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_condition_new\n });\n\n # Biddable Unit node: (Condition USED node)\n # * Product Condition: USED\n # * CPC bid: $0.10\n my $ad_group_criterion_condition_used = create_listing_group_unit_biddable(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_root_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n productCondition =>\n Google::Ads::GoogleAds::V25::Common::ProductConditionInfo->new({\n condition => USED\n })}\n ),\n 100000\n );\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_condition_used\n });\n\n # Sub-division node: (Condition \"other\" node)\n # * Product Condition: (not specified)\n my $ad_group_criterion_condition_other = create_listing_group_subdivision(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_root_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n # All sibling nodes must have the same dimension type, even if they\n # don't contain a bid.\n productCondition =>\n Google::Ads::GoogleAds::V25::Common::ProductConditionInfo->new()}));\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_condition_other\n });\n\n # Get the resource name that will be used for the condition other node.\n # This resource has not been created yet and will include the temporary ID as\n # part of the criterion ID.\n my $ad_group_criterion_condition_other_resource_name =\n $ad_group_criterion_condition_other->{resourceName};\n\n # 4) Construct the listing group unit nodes for CoolBrand, CheapBrand, and\n # other.\n\n # Biddable Unit node: (Brand CoolBrand node)\n # * Brand: CoolBrand\n # * CPC bid: $0.90\n my $ad_group_criterion_brand_cool_brand = create_listing_group_unit_biddable(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_condition_other_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Common::ProductBrandInfo->new(\n {value => \"CoolBrand\"})}\n ),\n 900000\n );\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_brand_cool_brand\n });\n\n # Biddable Unit node: (Brand CheapBrand node)\n # * Brand: CheapBrand\n # * CPC bid: $0.01\n my $ad_group_criterion_brand_cheap_brand = create_listing_group_unit_biddable(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_condition_other_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Common::ProductBrandInfo->new(\n {value => \"CheapBrand\"})}\n ),\n 10000\n );\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_brand_cheap_brand\n });\n\n # Biddable Unit node: (Brand other node)\n # * CPC bid: $0.05\n my $ad_group_criterion_brand_other_brand = create_listing_group_unit_biddable(\n $customer_id,\n $ad_group_id,\n $ad_group_criterion_condition_other_resource_name,\n Google::Ads::GoogleAds::V25::Common::ListingDimensionInfo->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Common::ProductBrandInfo->new()}\n ),\n 50000\n );\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion_brand_other_brand\n });\n\n # Add the ad group criterion.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => $operations\n });\n\n printf \"Added %d ad group criteria for listing group tree with the \" .\n \"following resource names:\\n\",\n scalar @{$ad_group_criteria_response->{results}};\n\n foreach my $result (@{$ad_group_criteria_response->{results}}) {\n print $result->{resourceName}, \"\\n\";\n }\n\n return 1;\n}\n\n# Removes all the ad group criteria that define the existing listing group\n# tree for an ad group.\nsub remove_listing_group_tree {\n my ($api_client, $customer_id, $ad_group_id) = @_;\n\n my $search_query =\n \"SELECT ad_group_criterion.resource_name \" .\n \"FROM ad_group_criterion WHERE ad_group_criterion.type = LISTING_GROUP \" .\n \"AND ad_group_criterion.listing_group.parent_ad_group_criterion IS NULL \" .\n \"AND ad_group.id = $ad_group_id\";\n\n # Create a search Google Ads request that will retrieve all listing groups\n # where the parent ad group criterion is NULL (and hence the root node in\n # the tree) for a given ad group id.\n my $search_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsRequest\n ->new({\n customerId => $customer_id,\n query => $search_query\n });\n\n # Get the GoogleAdsService.\n my $google_ads_service = $api_client->GoogleAdsService();\n\n my $iterator = Google::Ads::GoogleAds::Utils::SearchGoogleAdsIterator->new({\n service => $google_ads_service,\n request => $search_request\n });\n\n my $operations = [];\n # Iterate over all rows in all pages to find the ad group criterion to remove.\n while ($iterator->has_next) {\n my $google_ads_row = $iterator->next;\n my $ad_group_criterion = $google_ads_row->{adGroupCriterion};\n printf \"Found an ad group criterion with the resource name: '%s'.\\n\",\n $ad_group_criterion->{resourceName};\n\n # Create an ad group criterion operation.\n my $ad_group_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n remove => $ad_group_criterion->{resourceName}});\n\n push @$operations, $ad_group_criterion_operation;\n }\n\n if (scalar @$operations) {\n # Remove the ad group criterion that define the listing group tree.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => $operations\n });\n\n printf \"Removed %d ad group criteria.\\n\",\n scalar @{$ad_group_criteria_response->{results}};\n }\n}\n\n# Creates a new criterion containing a subdivision listing group node. If\n# the parent ad group criterion resource name is not specified, this method\n# creates a root node.\nsub create_listing_group_subdivision {\n my ($customer_id, $ad_group_id, $parent_ad_group_criterion_resource_name,\n $listing_dimension_info)\n = @_;\n\n my $listing_group_info =\n Google::Ads::GoogleAds::V25::Common::ListingGroupInfo->new({\n # Set the type as a SUBDIVISION, which will allow the node to be the\n # parent of another sub-tree.\n 'type' => SUBDIVISION\n });\n\n # If $parent_ad_group_criterion_resource_name and $listing_dimension_info\n # are not null, create a non-root division by setting its parent and case value.\n if ($parent_ad_group_criterion_resource_name and $listing_dimension_info) {\n # Set the ad group criterion resource name for the parent listing group.\n # This can include a temporary ID if the parent criterion is not yet created.\n $listing_group_info->{parentAdGroupCriterion} =\n $parent_ad_group_criterion_resource_name;\n\n # Case values contain the listing dimension used for the node.\n $listing_group_info->{caseValue} = $listing_dimension_info;\n }\n\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n # The resource name the criterion will be created with. This will define\n # the ID for the ad group criterion.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group_criterion(\n $customer_id, $ad_group_id, next_id()\n ),\n status => ENABLED,\n listingGroup => $listing_group_info\n });\n\n return $ad_group_criterion;\n}\n\n# Creates a new criterion containing a biddable unit listing group node.\nsub create_listing_group_unit_biddable {\n my ($customer_id, $ad_group_id, $parent_ad_group_criterion_resource_name,\n $listing_dimension_info, $cpc_bid_micros)\n = @_;\n\n # Note: There are two approaches for creating new unit nodes:\n # (1) Set the ad group resource name on the criterion (no temporary ID\n # required).\n # (2) Use a temporary ID to construct the criterion resource name and set it\n # to the 'resourceName' attribute.\n # In both cases you must set the parent ad group criterion's resource name on\n # the listing group for non-root nodes.\n # This example demonstrates method (1).\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n status => ENABLED,\n listingGroup =>\n Google::Ads::GoogleAds::V25::Common::ListingGroupInfo->new({\n # Set the type as a UNIT, which will allow the group to be biddable.\n type => UNIT,\n # Set the ad group criterion resource name for the parent listing group.\n # This can include a temporary ID if the parent criterion is not yet created.\n parentAdGroupCriterion => $parent_ad_group_criterion_resource_name,\n # Case values contain the listing dimension used for the node.\n caseValue => $listing_dimension_info\n }\n ),\n # Set the bid for this listing group unit.\n # This will be used as the CPC bid for items that are included in this\n # listing group.\n cpcBidMicros => $cpc_bid_micros\n });\n\n return $ad_group_criterion;\n}\n\n# Specifies a decreasing negative number for temporary ad group criteria IDs.\n# The ad group criteria will get real IDs when created on the server.\n# Returns -1, -2, -3, etc. on subsequent calls.\nsub next_id {\n our $id ||= 0;\n $id -= 1;\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"ad_group_id=i\" => \\$ad_group_id,\n \"replace_existing_tree=s\" => \\$replace_existing_tree\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id, $ad_group_id);\n\n# Call the example.\nadd_shopping_product_listing_group_tree($api_client, $customer_id =~ s/-//gr,\n $ad_group_id, $replace_existing_tree);\n\n=pod\n\n=head1 NAME\n\nadd_shopping_product_listing_group_tree\n\n=head1 DESCRIPTION\n\nThis example shows how to add a shopping listing group tree to a shopping ad group.\nThe example will optionally clear an existing listing group tree and rebuild it to\ninclude the following tree structure:\n\nProductCanonicalCondition NEW $0.20\nProductCanonicalCondition USED $0.10\nProductCanonicalCondition null (everything else)\n ProductBrand CoolBrand $0.90\n ProductBrand CheapBrand $0.01\n ProductBrand null (everything else) $0.50\n\n=head1 SYNOPSIS\n\nadd_shopping_product_listing_group_tree.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -ad_group_id The ad group ID.\n -replace_existing_tree [optional] Replace the existing listing group tree\n on the ad group, if it already exists.\n\n=cut\nadd_shopping_product_listing_group_tree.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.338Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":3734,"estimatedTokens":36313}}143{"id":"doc-asset_group_level_performance_google_ads_api_goo-a9d5c8af","source":"documentation","title":"Asset Group Level Performance | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/asset-group-reporting","text":"Example:\n```text\nSELECT\n asset_group.id,\n asset_group.ad_strength,\n asset_group.asset_coverage\nFROM asset_group\nWHERE asset_group.status = 'ENABLED'\n```\n\nExample:\n```text\nSELECT\n asset_group.id,\n asset_group.name,\n asset_group.primary_status,\n metrics.conversions,\n metrics.conversions_value,\n metrics.cost_micros,\n metrics.clicks,\n metrics.impressions\nFROM asset_group\nWHERE campaign.id = CAMPAIGN_ID\n AND segments.date DURING LAST_7_DAYS\n```\n\nExample:\n```text\nSELECT\n asset_group.id,\n asset_group.name,\n segments.ad_network_type,\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.cost_micros\nFROM asset_group\nWHERE campaign.id = CAMPAIGN_ID\n AND segments.date DURING LAST_7_DAYS\n```\n\nExample:\n```text\nSELECT\n asset_group.id,\n asset_group.name,\n metrics.all_conversions,\n segments.external_conversion_source\nFROM asset_group\nWHERE segments.external_conversion_source = 'STORE_VISITS'\n```\n\nExample:\n```text\nSELECT\n asset_group.id,\n asset_group.name,\n metrics.view_through_conversions,\n segments.external_conversion_source\nFROM asset_group\nWHERE\n segments.external_conversion_source = 'STORE_VISITS'\n```\n\nExample:\n```text\nSELECT\n asset_group.id,\n asset_group.name,\n metrics.all_conversions_value,\n segments.external_conversion_source\nFROM asset_group\nWHERE segments.external_conversion_source = 'STORE_VISITS'\n```\n\nExample:\n```text\nSELECT\n asset_group.id,\n asset_group.name,\n metrics.conversions,\n segments.new_versus_returning_customers,\n segments.conversion_action_category\nFROM asset_group\nWHERE\n segments.new_versus_returning_customers = 'NEW'\n AND segments.conversion_action_category = 'PURCHASE'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.341Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":90,"estimatedTokens":420}}144{"id":"doc-assets_in_a_performance_max_campaign_google_ads_-f7966cf5","source":"documentation","title":"Assets in a Performance Max Campaign | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/assets","text":"Example:\n```text\n/** Creates multiple text assets and returns the list of resource names. */\nprivate List<String> createMultipleTextAssets(\n GoogleAdsClient googleAdsClient, long customerId, List<String> texts) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n for (String text : texts) {\n Asset asset = Asset.newBuilder().setTextAsset(TextAsset.newBuilder().setText(text)).build();\n AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build();\n mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build());\n }\n\n List<String> assetResourceNames = new ArrayList<>();\n // Creates the service client.\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Sends the operations in a single Mutate request.\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);\n for (MutateOperationResponse result : response.getMutateOperationResponsesList()) {\n if (result.hasAssetResult()) {\n assetResourceNames.add(result.getAssetResult().getResourceName());\n }\n }\n printResponseDetails(response);\n }\n return assetResourceNames;\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates multiple text assets and returns the list of resource names.\n/// </summary>\n/// <param name=\"client\">The Google Ads Client.</param>\n/// <param name=\"customerId\">The customer's ID.</param>\n/// <param name=\"texts\">The texts to add.</param>\n/// <returns>A list of asset resource names.</returns>\nprivate List<string> CreateMultipleTextAssets(\n GoogleAdsClient client,\n long customerId,\n string[] texts)\n{\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n MutateGoogleAdsRequest request = new MutateGoogleAdsRequest()\n {\n CustomerId = customerId.ToString()\n };\n\n foreach (string text in texts)\n {\n request.MutateOperations.Add(\n new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n TextAsset = new TextAsset()\n {\n Text = text\n }\n }\n }\n }\n );\n }\n\n // Send the operations in a single Mutate request.\n MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);\n\n List<string> assetResourceNames = new List<string>();\n\n foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)\n {\n MutateAssetResult assetResult = operationResponse.AssetResult;\n assetResourceNames.Add(assetResult.ResourceName);\n }\n\n PrintResponseDetails(response);\n\n return assetResourceNames;\n}\nAddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createMultipleTextAssets(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n array $texts\n): array {\n // Here again, we use the GoogleAdService to create multiple text assets in a single\n // request.\n $operations = [];\n foreach ($texts as $text) {\n // Creates a mutate operation for a text asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset(['text_asset' => new TextAsset(['text' => $text])])\n ])\n ]);\n }\n\n // Issues a mutate request to add all assets.\n $googleAdsService = $googleAdsClient->getGoogleAdsServiceClient();\n /** @var MutateGoogleAdsResponse $mutateGoogleAdsResponse */\n $mutateGoogleAdsResponse =\n $googleAdsService->mutate(MutateGoogleAdsRequest::build($customerId, $operations));\n\n $assetResourceNames = [];\n foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {\n /** @var MutateOperationResponse $response */\n $assetResourceNames[] = $response->getAssetResult()->getResourceName();\n }\n self::printResponseDetails($mutateGoogleAdsResponse);\n\n return $assetResourceNames;\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\ndef create_multiple_text_assets(\n client: GoogleAdsClient, customer_id: str, texts: List[str]\n) -> List[str]:\n \"\"\"Creates multiple text assets and returns the list of resource names.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n texts: a list of strings, each of which will be used to create a text\n asset.\n\n Returns:\n asset_resource_names: a list of asset resource names.\n \"\"\"\n # Here again we use the GoogleAdService to create multiple text\n # assets in a single request.\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n operations: List[MutateOperation] = []\n for text in texts:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = mutate_operation.asset_operation.create\n asset.text_asset.text = text\n operations.append(mutate_operation)\n\n # Send the operations in a single Mutate request.\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id,\n mutate_operations=operations,\n )\n asset_resource_names: List[str] = []\n for result in response.mutate_operation_responses:\n if result._pb.HasField(\"asset_result\"):\n asset_resource_names.append(result.asset_result.resource_name)\n print_response_details(response)\n return asset_resource_namesadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates multiple text assets and returns the list of resource names.\ndef create_multiple_text_assets(client, customer_id, texts)\n operations = texts.map do |text|\n client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |asset|\n asset.text_asset = client.resource.text_asset do |text_asset|\n text_asset.text = text\n end\n end\n end\n end\n\n # Send the operations in a single Mutate request.\n response = client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: operations,\n )\n\n asset_resource_names = []\n response.mutate_operation_responses.each do |result|\n if result.asset_result\n asset_resource_names.append(result.asset_result.resource_name)\n end\n end\n print_response_details(response)\n asset_resource_names\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_multiple_text_assets {\n my ($api_client, $customer_id, $texts) = @_;\n\n # Here again we use the GoogleAdService to create multiple text assets in a\n # single request.\n my $operations = [];\n foreach my $text (@$texts) {\n # Create a mutate operation for a text asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n textAsset =>\n Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $text\n })})})});\n }\n\n # Issue a mutate request to add all assets.\n my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $operations\n });\n\n my $asset_resource_names = [];\n foreach\n my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})\n {\n push @$asset_resource_names, $response->{assetResult}{resourceName};\n }\n print_response_details($mutate_google_ads_response);\n\n return $asset_resource_names;\n}add_performance_max_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.343Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":242,"estimatedTokens":2016}}145{"id":"doc-campaign_level_conversion_goals_google_ads_api_g-c7d31c7c","source":"documentation","title":"Campaign-level Conversion Goals | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/conversion-goals","text":"Example:\n```text\nSELECT\n customer_conversion_goal.category,\n customer_conversion_goal.origin\nFROM customer_conversion_goal\n```\n\nExample:\n```text\n/** Retrieves the list of customer conversion goals. */\nprivate static List<CustomerConversionGoal> getCustomerConversionGoals(\n GoogleAdsClient googleAdsClient, long customerId) {\n String query =\n \"SELECT customer_conversion_goal.category, customer_conversion_goal.origin \"\n + \"FROM customer_conversion_goal\";\n\n List<CustomerConversionGoal> customerConversionGoals = new ArrayList<>();\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // The number of conversion goals is typically less than 50, so we use\n // GoogleAdsService.search instead of search_stream.\n SearchPagedResponse response =\n googleAdsServiceClient.search(Long.toString(customerId), query);\n for (GoogleAdsRow googleAdsRow : response.iterateAll()) {\n customerConversionGoals.add(googleAdsRow.getCustomerConversionGoal());\n }\n }\n\n return customerConversionGoals;\n}\n\n/** Creates a list of MutateOperations that override customer conversion goals. */\nprivate static List<MutateOperation> createConversionGoalOperations(\n long customerId, List<CustomerConversionGoal> customerConversionGoals) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n // To override the customer conversion goals, we will change the\n // biddability of each of the customer conversion goals so that only\n // the desired conversion goal is biddable in this campaign.\n for (CustomerConversionGoal customerConversionGoal : customerConversionGoals) {\n ConversionActionCategory category = customerConversionGoal.getCategory();\n ConversionOrigin origin = customerConversionGoal.getOrigin();\n String campaignConversionGoalResourceName =\n ResourceNames.campaignConversionGoal(\n customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID, category, origin);\n CampaignConversionGoal.Builder campaignConversionGoalBuilder =\n CampaignConversionGoal.newBuilder().setResourceName(campaignConversionGoalResourceName);\n // Change the biddability for the campaign conversion goal.\n // Set biddability to True for the desired (category, origin).\n // Set biddability to False for all other conversion goals.\n // Note:\n // 1- It is assumed that this Conversion Action\n // (category=PURCHASE, origin=WEBSITE) exists in this account.\n // 2- More than one goal can be biddable if desired. This example\n // shows only one.\n if (category == ConversionActionCategory.PURCHASE && origin == ConversionOrigin.WEBSITE) {\n campaignConversionGoalBuilder.setBiddable(true);\n } else {\n campaignConversionGoalBuilder.setBiddable(false);\n }\n CampaignConversionGoal campaignConversionGoal = campaignConversionGoalBuilder.build();\n CampaignConversionGoalOperation campaignConversionGoalOperation =\n CampaignConversionGoalOperation.newBuilder()\n .setUpdate(campaignConversionGoal)\n .setUpdateMask(FieldMasks.allSetFieldsOf(campaignConversionGoal))\n .build();\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setCampaignConversionGoalOperation(campaignConversionGoalOperation)\n .build());\n }\n return mutateOperations;\n}\nAddPerformanceMaxRetailCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a MutateOperation that links an asset to an asset group.\n/// </summary>\n/// <param name=\"fieldType\">The field type of the asset to be linked.</param>\n/// <param name=\"linkedEntityResourceName\">The resource name of the entity (asset group or\n/// campaign) to link the asset to.</param>\n/// <param name=\"assetResourceName\">The resource name of the text asset to be\n/// linked.</param>\n/// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n/// <returns>A MutateOperation that links an asset to an asset group.</returns>\nprivate MutateOperation CreateLinkAssetOperation(\n AssetFieldType fieldType,\n string linkedEntityResourceName,\n string assetResourceName,\n bool brandGuidelinesEnabled = false)\n{ if (brandGuidelinesEnabled)\n {\n return new MutateOperation()\n {\n CampaignAssetOperation = new CampaignAssetOperation()\n {\n Create = new CampaignAsset()\n {\n FieldType = fieldType,\n Campaign = linkedEntityResourceName,\n Asset = assetResourceName\n }\n }\n };\n } else\n { return new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = fieldType,\n AssetGroup = linkedEntityResourceName,\n Asset = assetResourceName\n }\n }\n };\n }\n}\nAddPerformanceMaxRetailCampaign.cs\n```\n\nExample:\n```text\nprivate static function getCustomerConversionGoals(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n): array {\n $customerConversionGoals = [];\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n // Creates a query that retrieves all customer conversion goals.\n $query = 'SELECT customer_conversion_goal.category, customer_conversion_goal.origin ' .\n 'FROM customer_conversion_goal';\n // The number of conversion goals is typically less than 50 so we use a search request\n // instead of search stream.\n $response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n // Iterates over all rows in all pages and builds the list of conversion goals.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $customerConversionGoals[] = [\n 'category' => $googleAdsRow->getCustomerConversionGoal()->getCategory(),\n 'origin' => $googleAdsRow->getCustomerConversionGoal()->getOrigin()\n ];\n }\n\n return $customerConversionGoals;\n}\n\n/**\n * Creates a list of MutateOperations that override customer conversion goals.\n *\n * @param int $customerId the customer ID\n * @param array $customerConversionGoals the list of customer conversion goals that will be\n * overridden\n * @return MutateOperation[] a list of MutateOperations that update campaign conversion goals\n */\nprivate static function createConversionGoalOperations(\n int $customerId,\n array $customerConversionGoals\n): array {\n $operations = [];\n\n // To override the customer conversion goals, we will change the biddability of each of the\n // customer conversion goals so that only the desired conversion goal is biddable in this\n // campaign.\n foreach ($customerConversionGoals as $customerConversionGoal) {\n $campaignConversionGoal = new CampaignConversionGoal([\n 'resource_name' => ResourceNames::forCampaignConversionGoal(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n ConversionActionCategory::name($customerConversionGoal['category']),\n ConversionOrigin::name($customerConversionGoal['origin'])\n )\n ]);\n // Changes the biddability for the campaign conversion goal.\n // Sets biddability to true for the desired (category, origin).\n // Sets biddability to false for all other conversion goals.\n // Note:\n // 1- It is assumed that this Conversion Action\n // (category=PURCHASE, origin=WEBSITE) exists in this account.\n // 2- More than one goal can be biddable if desired. This example\n // shows only one.\n if (\n $customerConversionGoal[\"category\"] === ConversionActionCategory::PURCHASE\n && $customerConversionGoal[\"origin\"] === ConversionOrigin::WEBSITE\n ) {\n $campaignConversionGoal->setBiddable(true);\n } else {\n $campaignConversionGoal->setBiddable(false);\n }\n\n $operations[] = new MutateOperation([\n 'campaign_conversion_goal_operation' => new CampaignConversionGoalOperation([\n 'update' => $campaignConversionGoal,\n // Sets the update mask on the operation. Here the update mask will be a list\n // of all the fields that were set on the update object.\n 'update_mask' => FieldMasks::allSetFieldsOf($campaignConversionGoal)\n ])\n ]);\n }\n\n return $operations;\n}AddPerformanceMaxRetailCampaign.php\n```\n\nExample:\n```text\ndef get_customer_conversion_goals(\n client: GoogleAdsClient, customer_id: str\n) -> List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n]:\n \"\"\"Retrieves the list of customer conversion goals.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a list of dicts containing the category and origin of customer\n conversion goals.\n \"\"\"\n ga_service: GoogleAdsServiceClient = client.get_service(\"GoogleAdsService\")\n customer_conversion_goals: List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n ] = []\n query: str = \"\"\"\n SELECT\n customer_conversion_goal.category,\n customer_conversion_goal.origin\n FROM customer_conversion_goal\n \"\"\"\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService.search instead of search_stream.\n search_request: SearchGoogleAdsRequest = client.get_type(\n \"SearchGoogleAdsRequest\"\n )\n search_request.customer_id = customer_id\n search_request.query = query\n results: SearchGoogleAdsResponse = ga_service.search(request=search_request)\n\n # Iterate over the results and build the list of conversion goals.\n for row in results:\n customer_conversion_goals.append(\n {\n \"category\": row.customer_conversion_goal.category,\n \"origin\": row.customer_conversion_goal.origin,\n }\n )\n return customer_conversion_goals\n\n\ndef create_conversion_goal_operations(\n client: GoogleAdsClient,\n customer_id: str,\n customer_conversion_goals: List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n ],\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that override customer conversion goals.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n customer_conversion_goals: the list of customer conversion goals that\n will be overridden.\n\n Returns:\n MutateOperations that update campaign conversion goals.\n \"\"\"\n campaign_conversion_goal_service: CampaignConversionGoalServiceClient = (\n client.get_service(\"CampaignConversionGoalService\")\n )\n operations: List[MutateOperation] = []\n\n # To override the customer conversion goals, we will change the\n # biddability of each of the customer conversion goals so that only\n # the desired conversion goal is biddable in this campaign.\n for customer_goal_dict in customer_conversion_goals:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_conversion_goal: CampaignConversionGoal = (\n mutate_operation.campaign_conversion_goal_operation.update\n )\n\n category_enum_value: (\n ConversionActionCategoryEnum.ConversionActionCategory\n ) = customer_goal_dict[\"category\"]\n origin_enum_value: ConversionOriginEnum.ConversionOrigin = (\n customer_goal_dict[\"origin\"]\n )\n\n campaign_conversion_goal.resource_name = (\n campaign_conversion_goal_service.campaign_conversion_goal_path(\n customer_id,\n _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n category_enum_value.name,\n origin_enum_value.name,\n )\n )\n # Change the biddability for the campaign conversion goal.\n # Set biddability to True for the desired (category, origin).\n # Set biddability to False for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n if (\n category_enum_value\n == client.enums.ConversionActionCategoryEnum.PURCHASE\n and origin_enum_value == client.enums.ConversionOriginEnum.WEBSITE\n ):\n biddable = True\n else:\n biddable = False\n campaign_conversion_goal.biddable = biddable\n field_mask = protobuf_helpers.field_mask(\n None, campaign_conversion_goal._pb\n )\n client.copy_from(\n mutate_operation.campaign_conversion_goal_operation.update_mask,\n field_mask,\n )\n operations.append(mutate_operation)\n\n return operationsadd_performance_max_retail_campaign.py\n```\n\nExample:\n```text\ndef _get_customer_conversion_goals(client, customer_id)\n query = <<~EOD\n SELECT\n customer_conversion_goal.category,\n customer_conversion_goal.origin\n FROM customer_conversion_goal\n EOD\n\n customer_conversion_goals = []\n\n ga_service = client.service.google_ads\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService.search instead of search_stream.\n response = ga_service.search(\n customer_id: customer_id,\n query: query,\n )\n\n # Iterate over the results and build the list of conversion goals.\n response.each do |row|\n customer_conversion_goals << {\n \"category\" => row.customer_conversion_goal.category,\n \"origin\" => row.customer_conversion_goal.origin\n }\n end\n\n customer_conversion_goals\nend\n\ndef create_conversion_goal_operations(client, customer_id, customer_conversion_goals)\n campaign_conversion_goal_service = client.service.campaign_conversion_goal\n\n operations = []\n\n # To override the customer conversion goals, we will change the\n # biddability of each of the customer conversion goals so that only\n # the desired conversion goal is biddable in this campaign.\n customer_conversion_goals.each do |customer_conversion_goal|\n operations << client.operation.mutate do |m|\n m.campaign_conversion_goal_operation = client.operation.campaign_conversion_goal do |op|\n op.update = client.resource.campaign_conversion_goal do |ccg|\n ccg.resource_name = client.path.campaign_conversion_goal(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n customer_conversion_goal[\"category\"].to_s,\n customer_conversion_goal[\"origin\"].to_s)\n # Change the biddability for the campaign conversion goal.\n # Set biddability to True for the desired (category, origin).\n # Set biddability to False for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n ccg.biddable = (customer_conversion_goal[\"category\"] == :PURCHASE &&\n customer_conversion_goal[\"origin\"] == :WEBSITE)\n end\n op.update_mask = Google::Ads::GoogleAds::FieldMaskUtil.all_set_fields_of(op.update)\n end\n end\n end\n\n operations\nendadd_performance_max_retail_campaign.rb\n```\n\nExample:\n```text\nsub get_customer_conversion_goals {\n my ($api_client, $customer_id) = @_;\n\n my $customer_conversion_goals = [];\n # Create a query that retrieves all customer conversion goals.\n my $query =\n \"SELECT customer_conversion_goal.category, customer_conversion_goal.origin \"\n . \"FROM customer_conversion_goal\";\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService->search() method instead of search_stream().\n my $search_response = $api_client->GoogleAdsService()->search({\n customerId => $customer_id,\n query => $query\n });\n\n # Iterate over the results and build the list of conversion goals.\n foreach my $google_ads_row (@{$search_response->{results}}) {\n push @$customer_conversion_goals,\n {\n category => $google_ads_row->{customerConversionGoal}{category},\n origin => $google_ads_row->{customerConversionGoal}{origin}};\n }\n\n return $customer_conversion_goals;\n}\n\n# Creates a list of MutateOperations that override customer conversion goals.\nsub create_conversion_goal_operations {\n my ($customer_id, $customer_conversion_goals) = @_;\n\n my $operations = [];\n # To override the customer conversion goals, we will change the biddability of\n # each of the customer conversion goals so that only the desired conversion goal\n # is biddable in this campaign.\n foreach my $customer_conversion_goal (@$customer_conversion_goals) {\n my $campaign_conversion_goal =\n Google::Ads::GoogleAds::V25::Resources::CampaignConversionGoal->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_conversion_goal(\n $customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n $customer_conversion_goal->{category},\n $customer_conversion_goal->{origin})});\n # Change the biddability for the campaign conversion goal.\n # Set biddability to true for the desired (category, origin).\n # Set biddability to false for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n if ( $customer_conversion_goal->{category} eq PURCHASE\n && $customer_conversion_goal->{origin} eq WEBSITE)\n {\n $campaign_conversion_goal->{biddable} = \"true\";\n } else {\n $campaign_conversion_goal->{biddable} = \"false\";\n }\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignConversionGoalOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignConversionGoalService::CampaignConversionGoalOperation\n ->new({\n update => $campaign_conversion_goal,\n # Set the update mask on the operation. Here the update mask will be\n # a list of all the fields that were set on the update object.\n updateMask => all_set_fields_of($campaign_conversion_goal)})});\n }\n\n return $operations;\n}add_performance_max_retail_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.347Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":497,"estimatedTokens":4857}}146{"id":"doc-performance_max_for_online_sales_with_a_product_-72f2e463","source":"documentation","title":"Performance Max for online sales with a product feed (retail) | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/retail","text":"Example:\n```text\n/** Retrieves the list of customer conversion goals. */\nprivate static List<CustomerConversionGoal> getCustomerConversionGoals(\n GoogleAdsClient googleAdsClient, long customerId) {\n String query =\n \"SELECT customer_conversion_goal.category, customer_conversion_goal.origin \"\n + \"FROM customer_conversion_goal\";\n\n List<CustomerConversionGoal> customerConversionGoals = new ArrayList<>();\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // The number of conversion goals is typically less than 50, so we use\n // GoogleAdsService.search instead of search_stream.\n SearchPagedResponse response =\n googleAdsServiceClient.search(Long.toString(customerId), query);\n for (GoogleAdsRow googleAdsRow : response.iterateAll()) {\n customerConversionGoals.add(googleAdsRow.getCustomerConversionGoal());\n }\n }\n\n return customerConversionGoals;\n}\n\n/** Creates a list of MutateOperations that override customer conversion goals. */\nprivate static List<MutateOperation> createConversionGoalOperations(\n long customerId, List<CustomerConversionGoal> customerConversionGoals) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n // To override the customer conversion goals, we will change the\n // biddability of each of the customer conversion goals so that only\n // the desired conversion goal is biddable in this campaign.\n for (CustomerConversionGoal customerConversionGoal : customerConversionGoals) {\n ConversionActionCategory category = customerConversionGoal.getCategory();\n ConversionOrigin origin = customerConversionGoal.getOrigin();\n String campaignConversionGoalResourceName =\n ResourceNames.campaignConversionGoal(\n customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID, category, origin);\n CampaignConversionGoal.Builder campaignConversionGoalBuilder =\n CampaignConversionGoal.newBuilder().setResourceName(campaignConversionGoalResourceName);\n // Change the biddability for the campaign conversion goal.\n // Set biddability to True for the desired (category, origin).\n // Set biddability to False for all other conversion goals.\n // Note:\n // 1- It is assumed that this Conversion Action\n // (category=PURCHASE, origin=WEBSITE) exists in this account.\n // 2- More than one goal can be biddable if desired. This example\n // shows only one.\n if (category == ConversionActionCategory.PURCHASE && origin == ConversionOrigin.WEBSITE) {\n campaignConversionGoalBuilder.setBiddable(true);\n } else {\n campaignConversionGoalBuilder.setBiddable(false);\n }\n CampaignConversionGoal campaignConversionGoal = campaignConversionGoalBuilder.build();\n CampaignConversionGoalOperation campaignConversionGoalOperation =\n CampaignConversionGoalOperation.newBuilder()\n .setUpdate(campaignConversionGoal)\n .setUpdateMask(FieldMasks.allSetFieldsOf(campaignConversionGoal))\n .build();\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setCampaignConversionGoalOperation(campaignConversionGoalOperation)\n .build());\n }\n return mutateOperations;\n}\nAddPerformanceMaxRetailCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a MutateOperation that links an asset to an asset group.\n/// </summary>\n/// <param name=\"fieldType\">The field type of the asset to be linked.</param>\n/// <param name=\"linkedEntityResourceName\">The resource name of the entity (asset group or\n/// campaign) to link the asset to.</param>\n/// <param name=\"assetResourceName\">The resource name of the text asset to be\n/// linked.</param>\n/// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n/// <returns>A MutateOperation that links an asset to an asset group.</returns>\nprivate MutateOperation CreateLinkAssetOperation(\n AssetFieldType fieldType,\n string linkedEntityResourceName,\n string assetResourceName,\n bool brandGuidelinesEnabled = false)\n{ if (brandGuidelinesEnabled)\n {\n return new MutateOperation()\n {\n CampaignAssetOperation = new CampaignAssetOperation()\n {\n Create = new CampaignAsset()\n {\n FieldType = fieldType,\n Campaign = linkedEntityResourceName,\n Asset = assetResourceName\n }\n }\n };\n } else\n { return new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = fieldType,\n AssetGroup = linkedEntityResourceName,\n Asset = assetResourceName\n }\n }\n };\n }\n}\nAddPerformanceMaxRetailCampaign.cs\n```\n\nExample:\n```text\nprivate static function getCustomerConversionGoals(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n): array {\n $customerConversionGoals = [];\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n // Creates a query that retrieves all customer conversion goals.\n $query = 'SELECT customer_conversion_goal.category, customer_conversion_goal.origin ' .\n 'FROM customer_conversion_goal';\n // The number of conversion goals is typically less than 50 so we use a search request\n // instead of search stream.\n $response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n // Iterates over all rows in all pages and builds the list of conversion goals.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $customerConversionGoals[] = [\n 'category' => $googleAdsRow->getCustomerConversionGoal()->getCategory(),\n 'origin' => $googleAdsRow->getCustomerConversionGoal()->getOrigin()\n ];\n }\n\n return $customerConversionGoals;\n}\n\n/**\n * Creates a list of MutateOperations that override customer conversion goals.\n *\n * @param int $customerId the customer ID\n * @param array $customerConversionGoals the list of customer conversion goals that will be\n * overridden\n * @return MutateOperation[] a list of MutateOperations that update campaign conversion goals\n */\nprivate static function createConversionGoalOperations(\n int $customerId,\n array $customerConversionGoals\n): array {\n $operations = [];\n\n // To override the customer conversion goals, we will change the biddability of each of the\n // customer conversion goals so that only the desired conversion goal is biddable in this\n // campaign.\n foreach ($customerConversionGoals as $customerConversionGoal) {\n $campaignConversionGoal = new CampaignConversionGoal([\n 'resource_name' => ResourceNames::forCampaignConversionGoal(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n ConversionActionCategory::name($customerConversionGoal['category']),\n ConversionOrigin::name($customerConversionGoal['origin'])\n )\n ]);\n // Changes the biddability for the campaign conversion goal.\n // Sets biddability to true for the desired (category, origin).\n // Sets biddability to false for all other conversion goals.\n // Note:\n // 1- It is assumed that this Conversion Action\n // (category=PURCHASE, origin=WEBSITE) exists in this account.\n // 2- More than one goal can be biddable if desired. This example\n // shows only one.\n if (\n $customerConversionGoal[\"category\"] === ConversionActionCategory::PURCHASE\n && $customerConversionGoal[\"origin\"] === ConversionOrigin::WEBSITE\n ) {\n $campaignConversionGoal->setBiddable(true);\n } else {\n $campaignConversionGoal->setBiddable(false);\n }\n\n $operations[] = new MutateOperation([\n 'campaign_conversion_goal_operation' => new CampaignConversionGoalOperation([\n 'update' => $campaignConversionGoal,\n // Sets the update mask on the operation. Here the update mask will be a list\n // of all the fields that were set on the update object.\n 'update_mask' => FieldMasks::allSetFieldsOf($campaignConversionGoal)\n ])\n ]);\n }\n\n return $operations;\n}AddPerformanceMaxRetailCampaign.php\n```\n\nExample:\n```text\ndef get_customer_conversion_goals(\n client: GoogleAdsClient, customer_id: str\n) -> List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n]:\n \"\"\"Retrieves the list of customer conversion goals.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a list of dicts containing the category and origin of customer\n conversion goals.\n \"\"\"\n ga_service: GoogleAdsServiceClient = client.get_service(\"GoogleAdsService\")\n customer_conversion_goals: List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n ] = []\n query: str = \"\"\"\n SELECT\n customer_conversion_goal.category,\n customer_conversion_goal.origin\n FROM customer_conversion_goal\n \"\"\"\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService.search instead of search_stream.\n search_request: SearchGoogleAdsRequest = client.get_type(\n \"SearchGoogleAdsRequest\"\n )\n search_request.customer_id = customer_id\n search_request.query = query\n results: SearchGoogleAdsResponse = ga_service.search(request=search_request)\n\n # Iterate over the results and build the list of conversion goals.\n for row in results:\n customer_conversion_goals.append(\n {\n \"category\": row.customer_conversion_goal.category,\n \"origin\": row.customer_conversion_goal.origin,\n }\n )\n return customer_conversion_goals\n\n\ndef create_conversion_goal_operations(\n client: GoogleAdsClient,\n customer_id: str,\n customer_conversion_goals: List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n ],\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that override customer conversion goals.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n customer_conversion_goals: the list of customer conversion goals that\n will be overridden.\n\n Returns:\n MutateOperations that update campaign conversion goals.\n \"\"\"\n campaign_conversion_goal_service: CampaignConversionGoalServiceClient = (\n client.get_service(\"CampaignConversionGoalService\")\n )\n operations: List[MutateOperation] = []\n\n # To override the customer conversion goals, we will change the\n # biddability of each of the customer conversion goals so that only\n # the desired conversion goal is biddable in this campaign.\n for customer_goal_dict in customer_conversion_goals:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_conversion_goal: CampaignConversionGoal = (\n mutate_operation.campaign_conversion_goal_operation.update\n )\n\n category_enum_value: (\n ConversionActionCategoryEnum.ConversionActionCategory\n ) = customer_goal_dict[\"category\"]\n origin_enum_value: ConversionOriginEnum.ConversionOrigin = (\n customer_goal_dict[\"origin\"]\n )\n\n campaign_conversion_goal.resource_name = (\n campaign_conversion_goal_service.campaign_conversion_goal_path(\n customer_id,\n _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n category_enum_value.name,\n origin_enum_value.name,\n )\n )\n # Change the biddability for the campaign conversion goal.\n # Set biddability to True for the desired (category, origin).\n # Set biddability to False for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n if (\n category_enum_value\n == client.enums.ConversionActionCategoryEnum.PURCHASE\n and origin_enum_value == client.enums.ConversionOriginEnum.WEBSITE\n ):\n biddable = True\n else:\n biddable = False\n campaign_conversion_goal.biddable = biddable\n field_mask = protobuf_helpers.field_mask(\n None, campaign_conversion_goal._pb\n )\n client.copy_from(\n mutate_operation.campaign_conversion_goal_operation.update_mask,\n field_mask,\n )\n operations.append(mutate_operation)\n\n return operationsadd_performance_max_retail_campaign.py\n```\n\nExample:\n```text\ndef _get_customer_conversion_goals(client, customer_id)\n query = <<~EOD\n SELECT\n customer_conversion_goal.category,\n customer_conversion_goal.origin\n FROM customer_conversion_goal\n EOD\n\n customer_conversion_goals = []\n\n ga_service = client.service.google_ads\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService.search instead of search_stream.\n response = ga_service.search(\n customer_id: customer_id,\n query: query,\n )\n\n # Iterate over the results and build the list of conversion goals.\n response.each do |row|\n customer_conversion_goals << {\n \"category\" => row.customer_conversion_goal.category,\n \"origin\" => row.customer_conversion_goal.origin\n }\n end\n\n customer_conversion_goals\nend\n\ndef create_conversion_goal_operations(client, customer_id, customer_conversion_goals)\n campaign_conversion_goal_service = client.service.campaign_conversion_goal\n\n operations = []\n\n # To override the customer conversion goals, we will change the\n # biddability of each of the customer conversion goals so that only\n # the desired conversion goal is biddable in this campaign.\n customer_conversion_goals.each do |customer_conversion_goal|\n operations << client.operation.mutate do |m|\n m.campaign_conversion_goal_operation = client.operation.campaign_conversion_goal do |op|\n op.update = client.resource.campaign_conversion_goal do |ccg|\n ccg.resource_name = client.path.campaign_conversion_goal(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n customer_conversion_goal[\"category\"].to_s,\n customer_conversion_goal[\"origin\"].to_s)\n # Change the biddability for the campaign conversion goal.\n # Set biddability to True for the desired (category, origin).\n # Set biddability to False for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n ccg.biddable = (customer_conversion_goal[\"category\"] == :PURCHASE &&\n customer_conversion_goal[\"origin\"] == :WEBSITE)\n end\n op.update_mask = Google::Ads::GoogleAds::FieldMaskUtil.all_set_fields_of(op.update)\n end\n end\n end\n\n operations\nendadd_performance_max_retail_campaign.rb\n```\n\nExample:\n```text\nsub get_customer_conversion_goals {\n my ($api_client, $customer_id) = @_;\n\n my $customer_conversion_goals = [];\n # Create a query that retrieves all customer conversion goals.\n my $query =\n \"SELECT customer_conversion_goal.category, customer_conversion_goal.origin \"\n . \"FROM customer_conversion_goal\";\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService->search() method instead of search_stream().\n my $search_response = $api_client->GoogleAdsService()->search({\n customerId => $customer_id,\n query => $query\n });\n\n # Iterate over the results and build the list of conversion goals.\n foreach my $google_ads_row (@{$search_response->{results}}) {\n push @$customer_conversion_goals,\n {\n category => $google_ads_row->{customerConversionGoal}{category},\n origin => $google_ads_row->{customerConversionGoal}{origin}};\n }\n\n return $customer_conversion_goals;\n}\n\n# Creates a list of MutateOperations that override customer conversion goals.\nsub create_conversion_goal_operations {\n my ($customer_id, $customer_conversion_goals) = @_;\n\n my $operations = [];\n # To override the customer conversion goals, we will change the biddability of\n # each of the customer conversion goals so that only the desired conversion goal\n # is biddable in this campaign.\n foreach my $customer_conversion_goal (@$customer_conversion_goals) {\n my $campaign_conversion_goal =\n Google::Ads::GoogleAds::V25::Resources::CampaignConversionGoal->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_conversion_goal(\n $customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n $customer_conversion_goal->{category},\n $customer_conversion_goal->{origin})});\n # Change the biddability for the campaign conversion goal.\n # Set biddability to true for the desired (category, origin).\n # Set biddability to false for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n if ( $customer_conversion_goal->{category} eq PURCHASE\n && $customer_conversion_goal->{origin} eq WEBSITE)\n {\n $campaign_conversion_goal->{biddable} = \"true\";\n } else {\n $campaign_conversion_goal->{biddable} = \"false\";\n }\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignConversionGoalOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignConversionGoalService::CampaignConversionGoalOperation\n ->new({\n update => $campaign_conversion_goal,\n # Set the update mask on the operation. Here the update mask will be\n # a list of all the fields that were set on the update object.\n updateMask => all_set_fields_of($campaign_conversion_goal)})});\n }\n\n return $operations;\n}add_performance_max_retail_campaign.pl\n```\n\nExample:\n```text\nSELECT\n segments.product_item_id,\n metrics.clicks,\n metrics.cost_micros,\n metrics.impressions,\n metrics.search_budget_lost_impression_share,\n metrics.search_rank_lost_impression_share,\n metrics.search_budget_lost_absolute_top_impression_share,\n metrics.search_rank_lost_absolute_top_impression_share,\n metrics.conversions,\n metrics.all_conversions,\n campaign.advertising_channel_type\nFROM shopping_performance_view\nWHERE\n campaign.advertising_channel_type = 'PERFORMANCE_MAX'\n AND segments.date DURING LAST_30_DAYS\n AND metrics.clicks > 0\nORDER BY\n metrics.all_conversions DESC,\n metrics.conversions DESC,\n metrics.clicks DESC,\n metrics.cost_micros DESC,\n metrics.impressions DESC\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.349Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":516,"estimatedTokens":5005}}147{"id":"doc-performance_max_for_travel_goals_google_ads_api_-80d41cce","source":"documentation","title":"Performance Max for travel goals | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/travel-goals","text":"Example:\n```text\nprivate String createHotelAssetSet(GoogleAdsClient googleAdsClient, long customerId) {\n // Creates an asset set operation for a hotel property asset set.\n AssetSetOperation assetSetOperation =\n AssetSetOperation.newBuilder()\n .setCreate(\n AssetSet.newBuilder()\n .setName(\n \"My Hotel propery asset set #\" + CodeSampleHelper.getPrintableDateTime())\n .setType(AssetSetType.HOTEL_PROPERTY))\n .build();\n try (AssetSetServiceClient assetSetServiceClient =\n googleAdsClient.getLatestVersion().createAssetSetServiceClient()) {\n MutateAssetSetsResponse mutateAssetSetsResponse =\n assetSetServiceClient.mutateAssetSets(\n Long.toString(customerId), ImmutableList.of(assetSetOperation));\n String assetSetResourceName = mutateAssetSetsResponse.getResults(0).getResourceName();\n System.out.printf(\"Created an asset set with resource name: '%s'%n\", assetSetResourceName);\n return assetSetResourceName;\n }\n}AddPerformanceMaxForTravelGoalsCampaign.java\n```\n\nExample:\n```text\nprivate string CreateHotelAssetSet(GoogleAdsClient client, long customerId)\n{\n AssetSetOperation operation = new AssetSetOperation()\n {\n Create = new AssetSet {\n Name = \"My Hotel property asset set #\" + ExampleUtilities.GetRandomString(),\n Type = AssetSetType.HotelProperty\n }\n };\n\n AssetSetServiceClient assetSetService = client.GetService(Services.V25.AssetSetService);\n\n MutateAssetSetsResponse response = assetSetService.MutateAssetSets(\n customerId.ToString(),\n new List<AssetSetOperation> { operation }\n );\n\n string assetResourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Created an asset set with resource name: {assetResourceName}\");\n return assetResourceName;\n}AddPerformanceMaxForTravelGoalsCampaign.cs\n```\n\nExample:\n```text\nprivate static function createHotelAssetSet(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n): string {\n // Creates an asset set operation for a hotel property asset set.\n $assetSetOperation = new AssetSetOperation([\n // Creates a hotel property asset set.\n 'create' => new AssetSet([\n 'name' => 'My Hotel propery asset set #' . Helper::getPrintableDatetime(),\n 'type' => AssetSetType::HOTEL_PROPERTY\n ])\n ]);\n\n // Issues a mutate request to add a hotel asset set and prints its information.\n $assetSetServiceClient = $googleAdsClient->getAssetSetServiceClient();\n $response = $assetSetServiceClient->mutateAssetSets(\n MutateAssetSetsRequest::build($customerId, [$assetSetOperation])\n );\n $assetSetResourceName = $response->getResults()[0]->getResourceName();\n printf(\"Created an asset set with resource name: '%s'.%s\", $assetSetResourceName, PHP_EOL);\n return $assetSetResourceName;\n}AddPerformanceMaxForTravelGoalsCampaign.php\n```\n\nExample:\n```text\ndef create_hotel_asset_set(client: GoogleAdsClient, customer_id: str) -> str:\n \"\"\"Creates a hotel property asset set.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n the created hotel property asset set's resource name.\n \"\"\"\n # Creates an asset set operation for a hotel property asset set.\n operation: AssetSetOperation = client.get_type(\"AssetSetOperation\")\n # Creates a hotel property asset set.\n asset_set: AssetSet = operation.create\n asset_set.name = f\"My hotel property asset set #{get_printable_datetime()}\"\n asset_set.type_ = client.enums.AssetSetTypeEnum.HOTEL_PROPERTY\n\n # Issues a mutate request to add a hotel asset set.\n asset_set_service: AssetSetServiceClient = client.get_service(\n \"AssetSetService\"\n )\n response: MutateAssetSetsResponse = asset_set_service.mutate_asset_sets(\n customer_id=customer_id, operations=[operation]\n )\n resource_name: str = response.results[0].resource_name\n print(f\"Created an asset set with resource name: '{resource_name}'\")\n\n return resource_nameadd_performance_max_for_travel_goals_campaign.py\n```\n\nExample:\n```text\n# Creates a hotel property asset set.\ndef create_hotel_asset_set(client, customer_id)\n operation =\n client.operation.create_resource.asset_set do |asset_set|\n asset_set.name = \"My Hotel propery asset set #{Time.now}\"\n asset_set.type = :HOTEL_PROPERTY\n end\n\n # Sends the mutate request.\n response =\n client.service.asset_set.mutate_asset_sets(\n customer_id: customer_id,\n operations: [operation]\n )\n\n # Prints some information about the response.\n response.results.first.resource_name\nendadd_performance_max_for_travel_goals_campaign.rb\n```\n\nExample:\n```text\nsub create_hotel_asset_set {\n my ($api_client, $customer_id) = @_;\n\n my $asset_set_operation =\n Google::Ads::GoogleAds::V25::Services::AssetSetService::AssetSetOperation->\n new({\n # Creates a hotel property asset set.\n create => Google::Ads::GoogleAds::V25::Resources::AssetSet->new({\n name => 'My Hotel propery asset set #' . uniqid(),\n type => HOTEL_PROPERTY\n })});\n # Issues a mutate request to add a hotel asset set and prints its information.\n my $response = $api_client->AssetSetService()->mutate({\n customerId => $customer_id,\n operations => [$asset_set_operation]});\n\n my $asset_set_resource_name = $response->{results}[0]{resourceName};\n printf \"Created an asset set with resource name: '%s'.\\n\",\n $asset_set_resource_name;\n\n return $asset_set_resource_name;\n}add_performance_max_for_travel_goals_campaign.pl\n```\n\nExample:\n```text\nprivate String createHotelAsset(\n GoogleAdsClient googleAdsClient,\n long customerId,\n String placeId,\n String hotelPropertyAssetSetResourceName) {\n // Uses the GoogleAdService to create an asset and asset set asset in a single request.\n List<MutateOperation> mutateOperations = new ArrayList<>();\n String assetResourceName = ResourceNames.asset(customerId, ASSET_TEMPORARY_ID);\n // Creates a mutate operation for a hotel property asset.\n Asset hotelPropertyAsset =\n Asset.newBuilder()\n .setResourceName(assetResourceName)\n .setHotelPropertyAsset(HotelPropertyAsset.newBuilder().setPlaceId(placeId))\n .build();\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetOperation(AssetOperation.newBuilder().setCreate(hotelPropertyAsset))\n .build());\n\n // Creates a mutate operation for an asset set asset.\n AssetSetAsset assetSetAsset =\n AssetSetAsset.newBuilder()\n .setAsset(assetResourceName)\n .setAssetSet(hotelPropertyAssetSetResourceName)\n .build();\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetSetAssetOperation(AssetSetAssetOperation.newBuilder().setCreate(assetSetAsset))\n .build());\n // Issues a mutate request to create all entities.\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n MutateGoogleAdsResponse mutateGoogleAdsResponse =\n googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);\n System.out.println(\"Created the following entities for the hotel asset:\");\n printResponseDetails(mutateGoogleAdsResponse);\n // Returns the created asset resource name, which will be used later to create an asset\n // group. Other resource names are not used later.\n return mutateGoogleAdsResponse\n .getMutateOperationResponses(0)\n .getAssetResult()\n .getResourceName();\n }\n}AddPerformanceMaxForTravelGoalsCampaign.java\n```\n\nExample:\n```text\nprivate string CreateHotelAsset(\n GoogleAdsClient client, long customerId, string placeId,\n string hotelPropertyAssetSetResourceName)\n{\n // Uses the GoogleAdService to create an asset and asset set asset in a single request.\n List<MutateOperation> mutateOperations = new List<MutateOperation>();\n string assetResourceName = ResourceNames.Asset(customerId, ASSET_TEMPORARY_ID);\n\n // Creates a mutate operation for a hotel property asset.\n Asset hotelPropertyAsset = new Asset()\n {\n ResourceName = assetResourceName,\n HotelPropertyAsset = new HotelPropertyAsset\n {\n PlaceId = placeId\n }\n };\n mutateOperations.Add(new MutateOperation\n {\n AssetOperation = new AssetOperation\n {\n Create = hotelPropertyAsset\n }\n });\n\n // Creates a mutate operation for an asset set asset.\n AssetSetAsset assetSetAsset = new AssetSetAsset\n {\n Asset = assetResourceName,\n AssetSet = hotelPropertyAssetSetResourceName\n };\n mutateOperations.Add(new MutateOperation\n {\n AssetSetAssetOperation = new AssetSetAssetOperation\n {\n Create = assetSetAsset\n }\n });\n\n // Issues a mutate request to create all entities.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.Mutate(customerId.ToString(), mutateOperations);\n Console.WriteLine(\"Created the following entities for the hotel asset:\");\n PrintResponseDetails(response);\n\n return response.MutateOperationResponses[0].AssetResult.ResourceName;\n}AddPerformanceMaxForTravelGoalsCampaign.cs\n```\n\nExample:\n```text\nprivate static function createHotelAsset(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $placeId,\n string $assetSetResourceName\n): string {\n // We use the GoogleAdService to create an asset and asset set asset in a single\n // request.\n $operations = [];\n $assetResourceName =\n ResourceNames::forAsset($customerId, self::ASSET_TEMPORARY_ID);\n // Creates a mutate operation for a hotel property asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n // Creates a hotel property asset.\n 'create' => new Asset([\n 'resource_name' => $assetResourceName,\n // Creates a hotel property asset for the place ID.\n 'hotel_property_asset' => new HotelPropertyAsset(['place_id' => $placeId]),\n ])\n ])\n ]);\n // Creates a mutate operation for an asset set asset.\n $operations[] = new MutateOperation([\n 'asset_set_asset_operation' => new AssetSetAssetOperation([\n // Creates an asset set asset.\n 'create' => new AssetSetAsset([\n 'asset' => $assetResourceName,\n 'asset_set' => $assetSetResourceName\n ])\n ])\n ]);\n\n // Issues a mutate request to create all entities.\n $googleAdsService = $googleAdsClient->getGoogleAdsServiceClient();\n /** @var MutateGoogleAdsResponse $mutateGoogleAdsResponse */\n $mutateGoogleAdsResponse =\n $googleAdsService->mutate(MutateGoogleAdsRequest::build($customerId, $operations));\n print \"Created the following entities for the hotel asset:\" . PHP_EOL;\n self::printResponseDetails($mutateGoogleAdsResponse);\n\n // Returns the created asset resource name, which will be used later to create an asset\n // group. Other resource names are not used later.\n return $mutateGoogleAdsResponse->getMutateOperationResponses()[0]->getAssetResult()\n ->getResourceName();\n}AddPerformanceMaxForTravelGoalsCampaign.php\n```\n\nExample:\n```text\ndef create_hotel_asset(\n client: GoogleAdsClient,\n customer_id: str,\n place_id: str,\n asset_set_resource_name: str,\n) -> str:\n \"\"\"Creates a hotel property asset using the specified place ID.\n\n The place ID must belong to a hotel property. Then, links it to the\n specified asset set.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n place_id: a place ID identifying a place in the Google Places database.\n asset_set_resource_name: an asset set resource name\n\n Returns:\n the created hotel property asset's resource name.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n # We use the GoogleAdService to create an asset and asset set asset in a\n # single request.\n\n asset_resource_name: str = googleads_service.asset_path(\n customer_id, ASSET_TEMPORARY_ID\n )\n\n # Creates a mutate operation for a hotel property asset.\n asset_mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n # Creates a hotel property asset.\n asset: Asset = asset_mutate_operation.asset_operation.create\n asset.resource_name = asset_resource_name\n # Creates a hotel property asset for the place ID.\n asset.hotel_property_asset.place_id = place_id\n\n # Creates a mutate operation for an asset set asset.\n asset_set_asset_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n # Creates an asset set asset.\n\n asset_set_asset: AssetSetAsset = (\n asset_set_asset_mutate_operation.asset_set_asset_operation.create\n )\n asset_set_asset.asset = asset_resource_name\n asset_set_asset.asset_set = asset_set_resource_name\n\n # Issues a mutate request to create all entities.\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id,\n mutate_operations=[\n asset_mutate_operation,\n asset_set_asset_mutate_operation,\n ],\n )\n print(\"Created the following entities for the hotel asset:\")\n print_response_details(response)\n\n return response.mutate_operation_responses[0].asset_result.resource_nameadd_performance_max_for_travel_goals_campaign.py\n```\n\nExample:\n```text\n# Creates a hotel property asset using the specified place ID.\n# The place ID must belong to a hotel property. Then, links it to the\n# specified asset set.\n# See https://developers.google.com/places/web-service/place-id to search for a\n# hotel place ID.\ndef create_hotel_asset(\n client,\n customer_id,\n place_id,\n hotel_property_asset_set_resource_name\n)\n asset_operation =\n client.operation.create_resource.asset do |asset|\n asset.name = 'Ad Media Bundle'\n asset.hotel_property_asset =\n client.resource.hotel_property_asset do |hotel_asset|\n hotel_asset.place_id = place_id\n end\n end\n\n # Send the mutate request.\n response =\n client.service.asset.mutate_assets(\n customer_id: customer_id,\n operations: [asset_operation]\n )\n\n asset_resource_name = response.results.first.resource_name\n\n # Creates a mutate operation for an asset set asset.\n asset_set_asset_operation =\n client.operation.create_resource.asset_set_asset do |asa|\n asa.asset = asset_resource_name\n asa.asset_set = hotel_property_asset_set_resource_name\n end\n\n # Sends the mutate request.\n response =\n client.service.asset_set_asset.mutate_asset_set_assets(\n customer_id: customer_id,\n operations: [asset_set_asset_operation]\n )\n\n asset_resource_name\nendadd_performance_max_for_travel_goals_campaign.rb\n```\n\nExample:\n```text\nsub create_hotel_asset {\n my ($api_client, $customer_id, $place_id, $asset_set_resource_name) = @_;\n\n # We use the GoogleAdService to create an asset and asset set asset in a single request.\n my $operations = [];\n my $asset_resource_name =\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset($customer_id,\n ASSET_TEMPORARY_ID);\n\n # Create a mutate operation for a hotel property asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName => $asset_resource_name,\n hotelPropertyAsset =>\n Google::Ads::GoogleAds::V25::Common::HotelPropertyAsset->new({\n placeId => $place_id\n })})})});\n\n # Create a mutate operation for an asset set asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetSetAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetSetAssetService::AssetSetAssetOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::AssetSetAsset->new({\n asset => $asset_resource_name,\n assetSet => $asset_set_resource_name\n })})});\n\n # Issue a mutate request to create all entities.\n my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $operations\n });\n\n printf \"Created the following entities for the hotel asset:\\n\";\n print_response_details($mutate_google_ads_response);\n\n # Return the created asset resource name, which will be used later to create an asset\n # group. Other resource names are not used later.\n return $mutate_google_ads_response->{mutateOperationResponses}[0]\n {assetResult}{resourceName};\n}add_performance_max_for_travel_goals_campaign.pl\n```\n\nExample:\n```text\nprivate HotelAssetSuggestion getHotelAssetSuggestion(\n GoogleAdsClient googleAdsClient, long customerId, String placeId) {\n\n try (TravelAssetSuggestionServiceClient travelAssetSuggestionServiceClient =\n googleAdsClient.getLatestVersion().createTravelAssetSuggestionServiceClient()) {\n // Sends a request to suggest assets to be created as an asset group for the Performance Max\n // for travel goals campaign.\n SuggestTravelAssetsResponse suggestTravelAssetsResponse =\n travelAssetSuggestionServiceClient.suggestTravelAssets(\n SuggestTravelAssetsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n // Uses 'en-US' as an example. It can be any language specifications in BCP 47\n // format.\n .setLanguageOption(\"en-US\")\n // The service accepts several place IDs. We use only one here for demonstration.\n .addPlaceIds(placeId)\n .build());\n System.out.printf(\"Fetched a hotel asset suggestion for the place ID '%s'.%n\", placeId);\n return suggestTravelAssetsResponse.getHotelAssetSuggestions(0);\n }\n}AddPerformanceMaxForTravelGoalsCampaign.java\n```\n\nExample:\n```text\nprivate HotelAssetSuggestion GetHotelAssetSuggestion(GoogleAdsClient client,\n long customerId, string placeId)\n{\n // Get the TravelAssetSuggestionService client.\n TravelAssetSuggestionServiceClient travelAssetSuggestionService =\n client.GetService(Services.V25.TravelAssetSuggestionService);\n\n SuggestTravelAssetsRequest request = new SuggestTravelAssetsRequest\n {\n CustomerId = customerId.ToString(),\n LanguageOption = \"en-US\",\n };\n\n request.PlaceIds.Add(placeId);\n\n SuggestTravelAssetsResponse response = travelAssetSuggestionService.SuggestTravelAssets(\n request\n );\n\n Console.WriteLine($\"Fetched a hotel asset suggestion for the place ID {placeId}\");\n return response.HotelAssetSuggestions[0];\n}AddPerformanceMaxForTravelGoalsCampaign.cs\n```\n\nExample:\n```text\nprivate static function getHotelAssetSuggestion(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $placeId\n): HotelAssetSuggestion {\n // Send a request to suggest assets to be created as an asset group for the Performance Max\n // for travel goals campaign.\n $travelAssetSuggestionServiceClient =\n $googleAdsClient->getTravelAssetSuggestionServiceClient();\n // Uses 'en-US' as an example. It can be any language specifications in BCP 47 format.\n $request = SuggestTravelAssetsRequest::build($customerId, 'en-US');\n // The service accepts several place IDs. We use only one here for demonstration.\n $request->setPlaceIds([$placeId]);\n $response = $travelAssetSuggestionServiceClient->suggestTravelAssets($request);\n printf(\"Fetched a hotel asset suggestion for the place ID '%s'.%s\", $placeId, PHP_EOL);\n return $response->getHotelAssetSuggestions()[0];\n}AddPerformanceMaxForTravelGoalsCampaign.php\n```\n\nExample:\n```text\ndef get_hotel_asset_suggestion(\n client: GoogleAdsClient, customer_id: str, place_id: str\n) -> HotelAssetSuggestion:\n \"\"\"Returns hotel asset suggestion from TravelAssetsSuggestionService.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n place_id: a place ID identifying a place in the Google Places database.\n\n Returns:\n A HotelAssetSuggestion instance.\n \"\"\"\n request: SuggestTravelAssetsRequest = client.get_type(\n \"SuggestTravelAssetsRequest\"\n )\n request.customer_id = customer_id\n # Uses 'en-US' as an example. It can be any language specifications in\n # BCP 47 format.\n request.language_option = \"en-US\"\n # In this example we only use a single place ID for the purpose of\n # demonstration, but it's possible to append more than one here if needed.\n request.place_ids.append(place_id)\n travel_asset_suggestion_service: TravelAssetSuggestionServiceClient = (\n client.get_service(\"TravelAssetSuggestionService\")\n )\n response: SuggestTravelAssetsResponse = (\n travel_asset_suggestion_service.suggest_travel_assets(request=request)\n )\n print(f\"Fetched a hotel asset suggestion for the place ID: '{place_id}'.\")\n\n # Since we sent a single operation in the request, it's guaranteed that\n # there will only be a single item in the response.\n return response.hotel_asset_suggestions[0]add_performance_max_for_travel_goals_campaign.py\n```\n\nExample:\n```text\ndef get_hotel_asset_suggestion(client, customer_id, place_id)\n response =\n client.service.travel_asset_suggestion.suggest_travel_assets(\n customer_id: customer_id,\n language_option: 'en-US',\n place_ids: [place_id]\n )\n\n response.hotel_asset_suggestions.first\nendadd_performance_max_for_travel_goals_campaign.rb\n```\n\nExample:\n```text\nsub get_hotel_asset_suggestion {\n my ($api_client, $customer_id, $place_id) = @_;\n\n # Send a request to suggest assets to be created as an asset group for the Performance Max\n # for travel goals campaign.\n my $suggest_travel_assets_response =\n $api_client->TravelAssetSuggestionService()->suggest_travel_assets({\n customerId => $customer_id,\n # Uses 'en-US' as an example. It can be any language specifications in BCP 47 format.\n languageOption => 'en-US',\n # The service accepts several place IDs. We use only one here for demonstration.\n placeIds => [$place_id],\n });\n\n printf \"Fetched a hotel asset suggestion for the place ID '%s'.\\n\", $place_id;\n return $suggest_travel_assets_response->{hotelAssetSuggestions}[0];\n}add_performance_max_for_travel_goals_campaign.pl\n```\n\nExample:\n```text\nprivate MutateOperation createCampaignOperation(\n long customerId, String hotelPropertyAssetSetResourceName) {\n Campaign performanceMaxCampaign =\n Campaign.newBuilder()\n .setName(\"Performance Max for travel goals campaign #\" + getPrintableDateTime())\n // Sets the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n .setStatus(CampaignStatus.PAUSED)\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n .setAdvertisingChannelType(AdvertisingChannelType.PERFORMANCE_MAX)\n // To create a Performance Max for travel goals campaign, you need to set\n // `hotel_property_asset_set`.\n .setHotelPropertyAssetSet(hotelPropertyAssetSetResourceName)\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n .setMaximizeConversionValue(\n MaximizeConversionValue.newBuilder().setTargetRoas(3.5).build())\n // Assigns the resource name with a temporary ID.\n .setResourceName(ResourceNames.campaign(customerId, CAMPAIGN_TEMPORARY_ID))\n // Sets the budget using the given budget resource name.\n .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n // Optional fields.\n .setStartDateTime(new DateTime().plusDays(1).toString(\"yyyy-MM-dd 00:00:00\"))\n .setEndDateTime(new DateTime().plusDays(365).toString(\"yyyy-MM-dd 23:59:59\"))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignOperation(\n CampaignOperation.newBuilder().setCreate(performanceMaxCampaign).build())\n .build();\n}AddPerformanceMaxForTravelGoalsCampaign.java\n```\n\nExample:\n```text\nprivate MutateOperation CreateCampaignOperation(long customerId,\n string hotelPropertyAssetSetResourceName)\n{\n Campaign performanceMaxCampaign = new Campaign\n {\n Name = \"Performance Max for travel goals campaign #\" +\n ExampleUtilities.GetRandomString(),\n // Sets the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n Status = CampaignStatus.Paused,\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n AdvertisingChannelType = AdvertisingChannelType.PerformanceMax,\n // To create a Performance Max for travel goals campaign, you need to set\n // `hotel_property_asset_set`.\n HotelPropertyAssetSet = hotelPropertyAssetSetResourceName,\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n MaximizeConversionValue = new MaximizeConversionValue\n {\n TargetRoas = 3.5\n },\n // Assigns the resource name with a temporary ID.\n ResourceName = ResourceNames.Campaign(customerId, CAMPAIGN_TEMPORARY_ID),\n // Sets the budget using the given budget resource name.\n CampaignBudget = ResourceNames.CampaignBudget(customerId, BUDGET_TEMPORARY_ID),\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n };\n\n return new MutateOperation\n {\n CampaignOperation = new CampaignOperation\n {\n Create = performanceMaxCampaign\n }\n };\n}AddPerformanceMaxForTravelGoalsCampaign.cs\n```\n\nExample:\n```text\nprivate static function createCampaignOperation(\n int $customerId,\n string $hotelPropertyAssetSetResourceName\n): MutateOperation {\n // Creates a mutate operation that creates a campaign.\n return new MutateOperation([\n 'campaign_operation' => new CampaignOperation([\n 'create' => new Campaign([\n 'name' => 'Performance Max for travel goals campaign #'\n . Helper::getPrintableDatetime(),\n // Assigns the resource name with a temporary ID.\n 'resource_name' => ResourceNames::forCampaign(\n $customerId,\n self::CAMPAIGN_TEMPORARY_ID\n ),\n // Sets the budget using the given budget resource name.\n 'campaign_budget' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n // The campaign is the only entity in the mutate request that should have its\n // status set.\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving.\n 'status' => CampaignStatus::PAUSED,\n // Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n 'advertising_channel_type' => AdvertisingChannelType::PERFORMANCE_MAX,\n\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n\n // To create a Performance Max for travel goals campaign, you need to set\n // `hotel_property_asset_set`.\n 'hotel_property_asset_set' => $hotelPropertyAssetSetResourceName,\n\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: https://support.google.com/google-ads/answer/7684216.\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n 'maximize_conversion_value' => new MaximizeConversionValue([\n 'target_roas' => 3.5\n ])\n ])\n ])\n ]);\n}AddPerformanceMaxForTravelGoalsCampaign.php\n```\n\nExample:\n```text\ndef create_campaign_operation(\n client: GoogleAdsClient,\n customer_id: str,\n hotel_property_asset_set_resource_name: str,\n) -> MutateOperation:\n \"\"\"Creates a mutate operation that creates a new Performance Max for travel\n goals campaign.\n\n Links the specified hotel property asset set to this campaign.\n\n A temporary ID will be assigned to this campaign budget so that it can be\n referenced by other objects being created in the same mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n hotel_property_asset_set_resource_name: the resource name for a hotel\n property asset set.\n\n Returns:\n a MutateOperation message that creates a new Performance Max campaign.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n # Creates a mutate operation that creates a campaign.\n operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign: Campaign = operation.campaign_operation.create\n campaign.name = (\n \"Performance Max for travel goals campaign \"\n f\"#{get_printable_datetime()}\"\n )\n # Assigns the resource name with a temporary ID.\n campaign.resource_name = googleads_service.campaign_path(\n customer_id, CAMPAIGN_TEMPORARY_ID\n )\n # Sets the budget using the given budget resource name.\n campaign.campaign_budget = googleads_service.campaign_budget_path(\n customer_id, BUDGET_TEMPORARY_ID\n )\n # The campaign is the only entity in the mutate request that should have its\n # status set.\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n # Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.PERFORMANCE_MAX\n )\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n # To create a Performance Max for travel goals campaign, you need to set\n # the `hotel_property_asset_set` field.\n campaign.hotel_property_asset_set = hotel_property_asset_set_resource_name\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: https://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n campaign.maximize_conversion_value.target_roas = 3.5\n\n return operationadd_performance_max_for_travel_goals_campaign.py\n```\n\nExample:\n```text\n# Creates a MutateOperation that creates a new Performance Max campaign.\n#\n# A temporary ID will be assigned to this campaign so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_performance_max_campaign_operation(\n client,\n customer_id,\n hotel_property_asset_set_resource_name\n)\n client.operation.mutate do |m|\n m.campaign_operation =\n client.operation.create_resource.campaign do |c|\n c.name = \"Performance Max for Travel Goals #{SecureRandom.uuid}\"\n\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n c.status = :PAUSED\n\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n c.advertising_channel_type = :PERFORMANCE_MAX\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n # To create a Performance Max for travel goals campaign, you need to set hotel_property_asset_set\n c.hotel_property_asset_set = hotel_property_asset_set_resource_name\n\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio\n # in the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n c.bidding_strategy_type = :MAXIMIZE_CONVERSION_VALUE\n c.maximize_conversion_value =\n client.resource.maximize_conversion_value do |mcv|\n mcv.target_roas = 3.5\n end\n\n # Assign the resource name with a temporary ID.\n c.resource_name =\n client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n\n # Set the budget using the given budget resource name.\n c.campaign_budget =\n client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n end\n end\nendadd_performance_max_for_travel_goals_campaign.rb\n```\n\nExample:\n```text\nsub create_campaign_operation {\n my ($customer_id, $hotel_property_asset_set_resource_name) = @_;\n\n # Create a mutate operation that creates a campaign operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n # Assign the resource name with a temporary ID.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, CAMPAIGN_TEMPORARY_ID\n ),\n name => \"Performance Max for travel goals campaign #'\" . uniqid(),\n # Set the budget using the given budget resource name.\n campaignBudget =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n status =>\n Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n # All Performance Max campaigns have an advertisingChannelType of\n # PERFORMANCE_MAX. The advertisingChannelSubType should not be set.\n advertisingChannelType => PERFORMANCE_MAX,\n\n # To create a Performance Max for travel goals campaign, you need to set\n # `hotelPropertyAssetSet`.\n hotelPropertyAssetSet => $hotel_property_asset_set_resource_name,\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Max Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximizeConversionValue. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Max Conversion Value, see the support article:\n # http://support.google.com/google-ads/answer/7684216.\n # A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n maximizeConversionValue =>\n Google::Ads::GoogleAds::V25::Common::MaximizeConversionValue->\n new({\n targetRoas => 3.5\n })})})});\n}add_performance_max_for_travel_goals_campaign.pl\n```\n\nExample:\n```text\n// Link the previously created hotel property asset to the asset group. In the real-world\n// scenario, you'd need to do this step several times for each hotel property asset.\nAssetGroupAsset hotelProperyAssetGroupAsset =\n AssetGroupAsset.newBuilder()\n .setAsset(hotelPropertyAssetResourceName)\n .setAssetGroup(assetGroupResourceName)\n .setFieldType(AssetFieldType.HOTEL_PROPERTY)\n .build();\n// Adds an operation to link the hotel property asset to the asset group.\nmutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupAssetOperation(\n AssetGroupAssetOperation.newBuilder().setCreate(hotelProperyAssetGroupAsset))\n .build());AddPerformanceMaxForTravelGoalsCampaign.java\n```\n\nExample:\n```text\n// Link the previously created hotel property asset to the asset group. In the\n// real-world scenario, you'd need to do this step several times for each hotel property\n// asset.\nAssetGroupAsset hotelPropertyAssetGroupAsset = new AssetGroupAsset\n{\n Asset = hotelPropertyAssetResourceName,\n AssetGroup = assetGroupResourceName,\n FieldType = AssetFieldType.HotelProperty\n};\n\n// Adds an operation to link the hotel property asset to the asset group.\nmutateOperations.Add(new MutateOperation\n{\n AssetGroupAssetOperation = new AssetGroupAssetOperation\n {\n Create = hotelPropertyAssetGroupAsset\n }\n});AddPerformanceMaxForTravelGoalsCampaign.cs\n```\n\nExample:\n```text\n// Link the previously created hotel property asset to the asset group. In the real-world\n// scenario, you'd need to do this step several times for each hotel property asset.\n$operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $hotelPropertyAssetResourceName,\n 'asset_group' => $assetGroupResourceName,\n 'field_type' => AssetFieldType::HOTEL_PROPERTY\n ])\n ])\n]);AddPerformanceMaxForTravelGoalsCampaign.php\n```\n\nExample:\n```text\n# Link the previously created hotel property asset to the asset group. If\n# there are multiple assets, these steps to create a new operation need to\n# be performed for each asset.\nasset_group_asset_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n)\nasset_group_asset_hotel: AssetGroupAsset = (\n asset_group_asset_mutate_operation.asset_group_asset_operation.create\n)\nasset_group_asset_hotel.asset = hotel_property_asset_resource_name\nasset_group_asset_hotel.asset_group = asset_group_resource_name\nasset_group_asset_hotel.field_type = (\n client.enums.AssetFieldTypeEnum.HOTEL_PROPERTY\n)\noperations.append(asset_group_asset_mutate_operation)add_performance_max_for_travel_goals_campaign.py\n```\n\nExample:\n```text\n# Link the previously created hotel property asset to the asset group.\n# In the real-world scenario, you'd need to do this step several times for\n# each hotel property asset.\noperations << client.operation.mutate do |m|\n m.asset_group_asset_operation =\n client.operation.create_resource.asset_group_asset do |aga|\n aga.field_type = :HOTEL_PROPERTY\n aga.asset_group =\n client.path.asset_group(customer_id, ASSET_GROUP_TEMPORARY_ID)\n aga.asset = hotel_property_asset_resource_name\n end\nendadd_performance_max_for_travel_goals_campaign.rb\n```\n\nExample:\n```text\n# Link the previously created hotel property asset to the asset group. In the real-world\n# scenario, you'd need to do this step several times for each hotel property asset.\npush @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $hotel_property_asset_resource_name,\n assetGroup => $asset_group_resource_name,\n fieldType => HOTEL_PROPERTY\n })})});add_performance_max_for_travel_goals_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.352Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":30,"totalLines":1084,"estimatedTokens":11170}}148{"id":"doc-create_a_performance_max_campaign_google_ads_api-83ff3fdc","source":"documentation","title":"Create a Performance Max Campaign | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/create-campaign","text":"Example:\n```text\n/** Creates a MutateOperation that creates a new Performance Max campaign. */\nprivate MutateOperation createPerformanceMaxCampaignOperation(\n long customerId, boolean brandGuidelinesEnabled) {\n TextGuidelines textGuidelines =\n TextGuidelines.newBuilder()\n // Specifies a list of terms that should not be used in any auto-generated\n // text assets.\n .addAllTermExclusions(ImmutableList.of(\"cheap\", \"free\"))\n // Specifies freeform messaging restriction prompts that will apply to all\n // auto-generated text assets.\n .addMessagingRestrictions(\n MessagingRestriction.newBuilder()\n .setRestrictionText(\"Don't mention competitor names\")\n .setRestrictionType(\n MessagingRestrictionType.RESTRICTION_BASED_EXCLUSION)\n .build())\n .build();\n Campaign performanceMaxCampaign =\n Campaign.newBuilder()\n .setName(\"Performance Max campaign #\" + getPrintableDateTime())\n // Sets the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n .setStatus(CampaignStatus.PAUSED)\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n .setAdvertisingChannelType(AdvertisingChannelType.PERFORMANCE_MAX)\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n .setMaximizeConversionValue(\n MaximizeConversionValue.newBuilder().setTargetRoas(3.5).build())\n // Sets if the campaign is enabled for brand guidelines. For more information on brand\n // guidelines, see https://support.google.com/google-ads/answer/14934472.\n .setBrandGuidelinesEnabled(brandGuidelinesEnabled)\n // Sets the text guidelines.\n .setTextGuidelines(textGuidelines)\n // Assigns the resource name with a temporary ID.\n .setResourceName(\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID))\n // Sets the budget using the given budget resource name.\n .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n // Optional fields.\n .setStartDateTime(new DateTime().plusDays(1).toString(\"yyyy-MM-dd 00:00:00\"))\n .setEndDateTime(new DateTime().plusDays(365).toString(\"yyyy-MM-dd 23:59:59\"))\n // Configures the optional opt-in/out status for asset automation settings.\n .addAllAssetAutomationSettings(ImmutableList.of(\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_IMAGE_EXTRACTION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(\n AssetAutomationType.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_ENHANCED_YOUTUBE_VIDEOS)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_IMAGE_ENHANCEMENT)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build()))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignOperation(\n CampaignOperation.newBuilder().setCreate(performanceMaxCampaign).build())\n .build();\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n/// Creates a MutateOperation that creates a new Performance Max campaign.\n/// <param name=\"campaignResourceName\">The campaign resource name.</param>\n/// <param name=\"campaignBudgetResourceName\">The campaign budget resource name.</param>\n/// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n/// <returns>A MutateOperations that will create this new campaign.</returns>\nprivate MutateOperation CreatePerformanceMaxCampaignOperation(\n string campaignResourceName,\n string campaignBudgetResourceName,\n bool brandGuidelinesEnabled)\n{\n Campaign.Types.TextGuidelines textGuidelines =\n new Campaign.Types.TextGuidelines();\n textGuidelines.TermExclusions.AddRange([\"cheap\", \"free\"]);\n textGuidelines.MessagingRestrictions.Add(\n new Campaign.Types.MessagingRestriction()\n {\n RestrictionText = \"Don't mention competitor names\",\n RestrictionType = MessagingRestrictionType.RestrictionBasedExclusion\n }\n );\n\n Campaign campaign = new Campaign()\n {\n Name = \"Performance Max campaign #\" + ExampleUtilities.GetRandomString(),\n\n // Set the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n Status = CampaignStatus.Paused,\n\n // All Performance Max campaigns have an AdvertisingChannelType of\n // PerformanceMax. The AdvertisingChannelSubType should not be set.\n AdvertisingChannelType = AdvertisingChannelType.PerformanceMax,\n\n // Bidding strategy must be set directly on the campaign. Setting a\n // portfolio bidding strategy by resource name is not supported. Max\n // Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns. BiddingStrategyType is\n // read-only and cannot be set by the API. An optional ROAS (Return on\n // Advertising Spend) can be set to enable the MaximizeConversionValue\n // bidding strategy. The ROAS value must be specified as a ratio in the API.\n // It is calculated by dividing \"total value\" by \"total spend\".\n //\n // For more information on Maximize Conversion Value, see the support\n // article:\n // http://support.google.com/google-ads/answer/7684216.\n //\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n MaximizeConversionValue = new MaximizeConversionValue()\n {\n TargetRoas = 3.5\n },\n\n // Use the temporary resource name created earlier\n ResourceName = campaignResourceName,\n\n // Set the budget using the given budget resource name.\n CampaignBudget = campaignBudgetResourceName,\n\n // Set if the campaign is enabled for brand guidelines. For more information\n // on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n BrandGuidelinesEnabled = brandGuidelinesEnabled,\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n\n TextGuidelines = textGuidelines,\n\n // Optional fields\n StartDateTime = DateTime.Now.AddDays(1).ToString(\"yyyyMMdd 00:00:00\"),\n EndDateTime = DateTime.Now.AddDays(365).ToString(\"yyyyMMdd 23:59:59\")\n };\n\n campaign.AssetAutomationSettings.AddRange(new[]{\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateImageExtraction,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.FinalUrlExpansionTextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.TextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateEnhancedYoutubeVideos,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateImageEnhancement,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n });\n\n MutateOperation operation = new MutateOperation()\n {\n CampaignOperation = new CampaignOperation()\n {\n Create = campaign\n }\n };\n\n return operation;\n}\nAddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createPerformanceMaxCampaignOperation(\n int $customerId,\n bool $brandGuidelinesEnabled\n): MutateOperation {\n // Creates a mutate operation that creates a campaign operation.\n return new MutateOperation([\n 'campaign_operation' => new CampaignOperation([\n 'create' => new Campaign([\n 'name' => 'Performance Max campaign #' . Helper::getPrintableDatetime(),\n // Assigns the resource name with a temporary ID.\n 'resource_name' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n // Sets the budget using the given budget resource name.\n 'campaign_budget' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n // The campaign is the only entity in the mutate request that should have its\n // status set.\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving.\n 'status' => CampaignStatus::PAUSED,\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n 'advertising_channel_type' => AdvertisingChannelType::PERFORMANCE_MAX,\n\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n 'maximize_conversion_value' => new MaximizeConversionValue([\n 'target_roas' => 3.5\n ]),\n\n 'asset_automation_settings' => [\n new AssetAutomationSetting([\n 'asset_automation_type' => AssetAutomationType::TEXT_ASSET_AUTOMATION,\n 'asset_automation_status' => AssetAutomationStatus::OPTED_IN\n ]),\n new AssetAutomationSetting([\n 'asset_automation_type' => AssetAutomationType::URL_EXPANSION,\n 'asset_automation_status' => AssetAutomationStatus::OPTED_IN\n ])\n ],\n\n\n // Sets if the campaign is enabled for brand guidelines. For more information\n // on brand guidelines, see\n // https://support.google.com/google-ads/answer/14934472.\n 'brand_guidelines_enabled' => $brandGuidelinesEnabled,\n\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n\n // Optional fields.\n 'start_date_time' => date('Y-m-d 00:00:00', strtotime('+1 day')),\n 'end_date_time' => date('Y-m-d 23:59:59', strtotime('+365 days'))\n ])\n ])\n ]);\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\ndef create_performance_max_campaign_operation(\n client: GoogleAdsClient,\n customer_id: str,\n brand_guidelines_enabled: bool,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new Performance Max campaign.\n\n A temporary ID will be assigned to this campaign so that it can\n be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n\n Returns:\n a MutateOperation that creates a campaign.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign: Campaign = mutate_operation.campaign_operation.create\n campaign.name = f\"Performance Max campaign #{uuid4()}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.PERFORMANCE_MAX\n )\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n campaign.bidding_strategy_type = (\n client.enums.BiddingStrategyTypeEnum.MAXIMIZE_CONVERSION_VALUE\n )\n campaign.maximize_conversion_value.target_roas = 3.5\n\n # Set if the campaign is enabled for brand guidelines. For more information\n # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n campaign.brand_guidelines_enabled = brand_guidelines_enabled\n\n # Assign the resource name with a temporary ID.\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n campaign.resource_name = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n # Set the budget using the given budget resource name.\n campaign.campaign_budget = campaign_service.campaign_budget_path(\n customer_id, _BUDGET_TEMPORARY_ID\n )\n\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n # Optional fields\n campaign.start_date_time = (datetime.now() + timedelta(1)).strftime(\n \"%Y%m%d 00:00:00\"\n )\n campaign.end_date_time = (datetime.now() + timedelta(365)).strftime(\n \"%Y%m%d 23:59:59\"\n )\n\n campaign.text_guidelines.term_exclusions = [\"cheap\", \"free\"]\n messaging_restriction = campaign.MessagingRestriction()\n messaging_restriction.restriction_text = \"Don't mention competitor names\"\n messaging_restriction.restriction_type = (\n client.enums.MessagingRestrictionTypeEnum.RESTRICTION_BASED_EXCLUSION\n )\n campaign.text_guidelines.messaging_restrictions.append(\n messaging_restriction\n )\n\n # Configures the optional opt-in/out status for asset automation settings.\n for asset_automation_type_enum in [\n client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_EXTRACTION,\n client.enums.AssetAutomationTypeEnum.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,\n client.enums.AssetAutomationTypeEnum.TEXT_ASSET_AUTOMATION,\n client.enums.AssetAutomationTypeEnum.GENERATE_ENHANCED_YOUTUBE_VIDEOS,\n client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_ENHANCEMENT,\n ]:\n asset_automattion_setting: Campaign.AssetAutomationSetting = (\n client.get_type(\"Campaign\").AssetAutomationSetting()\n )\n asset_automattion_setting.asset_automation_type = (\n asset_automation_type_enum\n )\n asset_automattion_setting.asset_automation_status = (\n client.enums.AssetAutomationStatusEnum.OPTED_IN\n )\n campaign.asset_automation_settings.append(asset_automattion_setting)\n\n return mutate_operationadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates a MutateOperation that creates a new Performance Max campaign.\n#\n# A temporary ID will be assigned to this campaign so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_performance_max_campaign_operation(\n client,\n customer_id,\n brand_guidelines_enabled)\n client.operation.mutate do |m|\n m.campaign_operation = client.operation.create_resource.campaign do |c|\n c.name = \"Performance Max campaign #{SecureRandom.uuid}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n c.status = :PAUSED\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n c.advertising_channel_type = :PERFORMANCE_MAX\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n c.bidding_strategy_type = :MAXIMIZE_CONVERSION_VALUE\n c.maximize_conversion_value = client.resource.maximize_conversion_value do |mcv|\n mcv.target_roas = 3.5\n end\n\n # Configures the optional opt-in/out status for asset automation settings.\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_IMAGE_EXTRACTION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :TEXT_ASSET_AUTOMATION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_ENHANCED_YOUTUBE_VIDEOS\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_IMAGE_ENHANCEMENT\n aas.asset_automation_status = :OPTED_IN\n end\n\n # Set if the campaign is enabled for brand guidelines. For more\n # information on brand guidelines, see\n # https://support.google.com/google-ads/answer/14934472.\n c.brand_guidelines_enabled = brand_guidelines_enabled\n\n # Assign the resource name with a temporary ID.\n c.resource_name = client.path.campaign(customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n # Set the budget using the given budget resource name.\n c.campaign_budget = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n # Optional fields\n c.start_date_time = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d %H:%M:%S')\n c.end_date_time = DateTime.parse(Date.today.next_year.to_s).strftime('%Y%m%d %H:%M:%S')\n end\n end\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_performance_max_campaign_operation {\n my ($customer_id, $brand_guidelines_enabled) = @_;\n # Configures the optional opt-in/out status for asset automation settings.\n # When we create the campaign object, we set campaign->{assetAutomationSettings}\n # equal to $asset_automation_settings.\n my $asset_automation_settings = [];\n my $asset_automation_types = [\n GENERATE_IMAGE_EXTRACTION, FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,\n TEXT_ASSET_AUTOMATION, GENERATE_ENHANCED_YOUTUBE_VIDEOS,\n GENERATE_IMAGE_ENHANCEMENT\n ];\n foreach my $asset_automation_type (@$asset_automation_types) {\n push @$asset_automation_settings,\n Google::Ads::GoogleAds::V25::Resources::AssetAutomationSetting->new({\n assetAutomationStatus => OPTED_IN,\n assetAutomationType => $asset_automation_type\n });\n }\n\n my $text_guidelines =\n Google::Ads::GoogleAds::V25::Resources::TextGuidelines->new({\n termExclusions => [\"cheap\", \"free\"],\n messagingRestrictions => [\n Google::Ads::GoogleAds::V25::Resources::MessagingRestriction->new({\n restrictionText => \"Don't mention competitor names\",\n restrictionType => RESTRICTION_BASED_EXCLUSION\n })]});\n\n # Create a mutate operation that creates a campaign operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n # Assign the resource name with a temporary ID.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n name => \"Performance Max campaign #\" . uniqid(),\n # Set the budget using the given budget resource name.\n campaignBudget =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n status =>\n Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n # All Performance Max campaigns have an advertisingChannelType of\n # PERFORMANCE_MAX. The advertisingChannelSubType should not be set.\n advertisingChannelType => PERFORMANCE_MAX,\n\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximizeConversionValue. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n maximizeConversionValue =>\n Google::Ads::GoogleAds::V25::Common::MaximizeConversionValue->\n new({\n targetRoas => 3.5\n }\n ),\n\n # Set if the campaign is enabled for brand guidelines. For more information\n # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n brandGuidelinesEnabled => $brand_guidelines_enabled,\n\n # Configures the optional opt-in/out status for asset automation settings.\n assetAutomationSettings => $asset_automation_settings,\n\n # Set the text guidelines.\n textGuidelines => $text_guidelines,\n\n # Optional fields.\n startDateTime =>\n strftime(\"%Y%m%d 00:00:00\", localtime(time + 60 * 60 * 24)),\n endDateTime => strftime(\n \"%Y%m%d 23:59:59\",\n localtime(time + 60 * 60 * 24 * 365)\n ),\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n })})});\n}add_performance_max_campaign.pl\n```\n\nExample:\n```text\n/** Creates a list of MutateOperations that create linked brand assets. */\nList<MutateOperation> createAndLinkBrandAssets(\n long customerId,\n boolean brandGuidelinesEnabled,\n String businessName,\n String logoUrl,\n String logoName)\n throws IOException {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n\n // Creates the brand name text asset.\n String businessNameAssetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());\n Asset businessNameAsset =\n Asset.newBuilder()\n .setResourceName(businessNameAssetResourceName)\n .setTextAsset(TextAsset.newBuilder().setText(businessName).build())\n .build();\n AssetOperation businessNameAssetOperation =\n AssetOperation.newBuilder().setCreate(businessNameAsset).build();\n mutateOperations.add(\n MutateOperation.newBuilder().setAssetOperation(businessNameAssetOperation).build());\n\n // Creates the logo image asset.\n String logoAssetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());\n // Creates a media file.\n byte[] logoBytes = ByteStreams.toByteArray(new URL(logoUrl).openStream());\n Asset logoAsset =\n Asset.newBuilder()\n .setResourceName(logoAssetResourceName)\n .setImageAsset(ImageAsset.newBuilder().setData(ByteString.copyFrom(logoBytes)).build())\n // Provides a unique friendly name to identify your asset. When there is an existing\n // image asset with the same content but a different name, the new name will be dropped\n // silently.\n .setName(logoName)\n .build();\n AssetOperation logoImageAssetOperation =\n AssetOperation.newBuilder().setCreate(logoAsset).build();\n mutateOperations.add(\n MutateOperation.newBuilder().setAssetOperation(logoImageAssetOperation).build());\n\n if (brandGuidelinesEnabled) {\n // Creates CampaignAsset resources to link the Asset resources to the Campaign.\n mutateOperations.add(\n createCampaignAssetMutateOperation(\n customerId, AssetFieldType.BUSINESS_NAME, businessNameAssetResourceName));\n mutateOperations.add(\n createCampaignAssetMutateOperation(\n customerId, AssetFieldType.LOGO, logoAssetResourceName));\n } else {\n // Creates an AssetGroupAsset to link the Asset to the AssetGroup.\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.BUSINESS_NAME,\n businessNameAssetResourceName,\n ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.LOGO,\n logoAssetResourceName,\n ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));\n }\n\n return mutateOperations;\n}\n\n/** Creates a MutateOperation to add an AssetGroupAsset. */\nMutateOperation createAssetGroupAssetMutateOperation(\n AssetFieldType fieldType, String assetResourceName, String assetGroupResourceName) {\n AssetGroupAsset assetGroupAsset =\n AssetGroupAsset.newBuilder()\n .setFieldType(fieldType)\n .setAssetGroup(assetGroupResourceName)\n .setAsset(assetResourceName)\n .build();\n AssetGroupAssetOperation assetGroupAssetOperation =\n AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build();\n return MutateOperation.newBuilder()\n .setAssetGroupAssetOperation(assetGroupAssetOperation)\n .build();\n}\n\n/** Creates a MutateOperation to add a CampaignAsset. */\nMutateOperation createCampaignAssetMutateOperation(\n long customerId, AssetFieldType fieldType, String assetResourceName) {\n CampaignAsset campaignAsset =\n CampaignAsset.newBuilder()\n .setFieldType(fieldType)\n .setCampaign(ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID))\n .setAsset(assetResourceName)\n .build();\n CampaignAssetOperation campaignAssetOperation =\n CampaignAssetOperation.newBuilder().setCreate(campaignAsset).build();\n return MutateOperation.newBuilder().setCampaignAssetOperation(campaignAssetOperation).build();\n}AddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\nprivate List<MutateOperation> CreateAndLinkBrandAssets(\n string assetGroupResourceName,\n string campaignResourceName,\n AssetTemporaryResourceNameGenerator assetResourceNameGenerator,\n string businessName,\n string logoUrl,\n string logoName,\n GoogleAdsConfig config,\n bool brandGuidelinesEnabled)\n{\n List<MutateOperation> operations = new List<MutateOperation>();\n\n string logoAssetResourceName = assetResourceNameGenerator.Next();\n string businessNameAssetResourceName = assetResourceNameGenerator.Next();\n\n // Create the Image Asset.\n operations.Add(\n new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n ResourceName = logoAssetResourceName,\n ImageAsset = new ImageAsset()\n {\n Data =\n ByteString.CopyFrom(\n MediaUtilities.GetAssetDataFromUrl(logoUrl, config)\n )\n },\n // Provide a unique friendly name to identify your asset.\n // When there is an existing image asset with the same content but a\n // different name, the new name will be dropped silently.\n Name = logoName\n }\n }\n }\n );\n\n // Create the business name asset.\n operations.Add(\n new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n ResourceName = businessNameAssetResourceName,\n TextAsset = new TextAsset()\n {\n Text = businessName,\n }\n }\n }\n }\n );\n\n if (brandGuidelinesEnabled)\n {\n // Create CampaignAssets to link the Assets to the Campaign.\n operations.Add(\n new MutateOperation()\n {\n CampaignAssetOperation = new CampaignAssetOperation()\n {\n Create = new CampaignAsset()\n {\n FieldType = AssetFieldType.Logo,\n Campaign = campaignResourceName,\n Asset = logoAssetResourceName\n }\n }\n }\n );\n\n operations.Add(\n new MutateOperation()\n {\n CampaignAssetOperation = new CampaignAssetOperation()\n {\n Create = new CampaignAsset()\n {\n FieldType = AssetFieldType.BusinessName,\n Campaign = campaignResourceName,\n Asset = businessNameAssetResourceName\n }\n }\n }\n );\n } else {\n // Create AssetGroupAssets to link the Assets to the AssetGroup.\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.Logo,\n AssetGroup = assetGroupResourceName,\n Asset = logoAssetResourceName\n }\n }\n }\n );\n\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.BusinessName,\n AssetGroup = assetGroupResourceName,\n Asset = businessNameAssetResourceName\n }\n }\n }\n );\n\n }\n\n\n return operations;\n}AddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nThis example is not yet available in PHP; you can take a look at the other languages.\n```\n\nExample:\n```text\ndef create_and_link_brand_assets(\n client: GoogleAdsClient,\n customer_id: str,\n brand_guidelines_enabled: bool,\n business_name: str,\n logo_url: str,\n logo_name: str,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create linked brand assets.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n business_name: the business name text to be put into an asset.\n logo_url: the url of the logo to be retrieved and put into an asset.\n logo_name: the asset name of the logo.\n\n Returns:\n MutateOperations that create linked brand assets.\n \"\"\"\n global next_temp_id\n operations: List[MutateOperation] = []\n asset_service: AssetServiceClient = client.get_service(\"AssetService\")\n\n # Create the Text Asset.\n text_asset_temp_id = next_temp_id\n next_temp_id -= 1\n\n text_mutate_operation = client.get_type(\"MutateOperation\")\n text_asset: Asset = text_mutate_operation.asset_operation.create\n text_asset.resource_name = asset_service.asset_path(\n customer_id, text_asset_temp_id\n )\n text_asset.text_asset.text = business_name\n operations.append(text_mutate_operation)\n\n # Create the Image Asset.\n image_asset_temp_id = next_temp_id\n next_temp_id -= 1\n\n image_mutate_operation = client.get_type(\"MutateOperation\")\n image_asset: Asset = image_mutate_operation.asset_operation.create\n image_asset.resource_name = asset_service.asset_path(\n customer_id, image_asset_temp_id\n )\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n image_asset.name = logo_name\n image_asset.type_ = client.enums.AssetTypeEnum.IMAGE\n image_asset.image_asset.data = get_image_bytes_from_url(logo_url)\n operations.append(image_mutate_operation)\n\n if brand_guidelines_enabled:\n # Create CampaignAsset resources to link the Asset resources to the Campaign.\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n business_name_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n business_name_campaign_asset: CampaignAsset = (\n business_name_mutate_operation.campaign_asset_operation.create\n )\n business_name_campaign_asset.field_type = (\n client.enums.AssetFieldTypeEnum.BUSINESS_NAME\n )\n business_name_campaign_asset.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n business_name_campaign_asset.asset = asset_service.asset_path(\n customer_id, text_asset_temp_id\n )\n operations.append(business_name_mutate_operation)\n\n logo_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n logo_campaign_asset: CampaignAsset = (\n logo_mutate_operation.campaign_asset_operation.create\n )\n logo_campaign_asset.field_type = client.enums.AssetFieldTypeEnum.LOGO\n logo_campaign_asset.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n logo_campaign_asset.asset = asset_service.asset_path(\n customer_id, image_asset_temp_id\n )\n operations.append(logo_mutate_operation)\n\n else:\n # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n\n business_name_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n business_name_asset_group_asset: AssetGroupAsset = (\n business_name_mutate_operation.asset_group_asset_operation.create\n )\n business_name_asset_group_asset.field_type = (\n client.enums.AssetFieldTypeEnum.BUSINESS_NAME\n )\n business_name_asset_group_asset.asset_group = (\n asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n )\n business_name_asset_group_asset.asset = asset_service.asset_path(\n customer_id, text_asset_temp_id\n )\n operations.append(business_name_mutate_operation)\n\n logo_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n logo_asset_group_asset: AssetGroupAsset = (\n logo_mutate_operation.asset_group_asset_operation.create\n )\n logo_asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.LOGO\n logo_asset_group_asset.asset_group = (\n asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n )\n logo_asset_group_asset.asset = asset_service.asset_path(\n customer_id, image_asset_temp_id\n )\n operations.append(logo_mutate_operation)\n\n return operationsadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates a list of MutateOperations that create linked brand assets.\ndef create_and_link_brand_assets(\n client,\n customer_id,\n brand_guidelines_enabled,\n business_name,\n logo_url,\n logo_name)\n operations = []\n\n # Create the Text Asset.\n text_asset_temp_id = next_temp_id\n operations << client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, text_asset_temp_id)\n a.text_asset = client.resource.text_asset do |text_asset|\n text_asset.text = business_name\n end\n end\n end\n\n # Create the Image Asset.\n image_asset_temp_id = next_temp_id\n operations << client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, image_asset_temp_id)\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n a.name = logo_name\n a.type = :IMAGE\n a.image_asset = client.resource.image_asset do |image_asset|\n image_asset.data = get_image_bytes(logo_url)\n end\n end\n end\n\n if brand_guidelines_enabled\n # Create CampaignAsset resources to link the Asset resources to the Campaign.\n operations << client.operation.mutate do |m|\n m.campaign_asset_operation = client.operation.create_resource.\n campaign_asset do |ca|\n ca.field_type = :BUSINESS_NAME\n ca.campaign = client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n )\n ca.asset = client.path.asset(customer_id, text_asset_temp_id)\n end\n end\n\n operations << client.operation.mutate do |m|\n m.campaign_asset_operation = client.operation.create_resource.\n campaign_asset do |ca|\n ca.field_type = :LOGO\n ca.campaign = client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n )\n ca.asset = client.path.asset(customer_id, image_asset_temp_id)\n end\n end\n else\n # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource.\n asset_group_asset do |aga|\n aga.field_type = :BUSINESS_NAME\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n aga.asset = client.path.asset(customer_id, text_asset_temp_id)\n end\n end\n\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource.\n asset_group_asset do |aga|\n aga.field_type = :LOGO\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n aga.asset = client.path.asset(customer_id, image_asset_temp_id)\n end\n end\n end\n\n operations\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\n# Creates a list of MutateOperations that create linked brand assets.\nsub create_and_link_brand_assets {\n my ($customer_id, $brand_guidelines_enabled, $business_name, $logo_url,\n $logo_name)\n = @_;\n\n my $operations = [];\n\n # Create the text asset.\n my $text_asset_temp_id = $next_temp_id--;\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $text_asset_temp_id\n ),\n textAsset => Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $business_name\n })})})});\n\n # Create the image asset.\n my $image_asset_temp_id = $next_temp_id--;\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $image_asset_temp_id\n ),\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n name => $logo_name,\n imageAsset =>\n Google::Ads::GoogleAds::V25::Common::ImageAsset->new({\n data => get_base64_data_from_url($logo_url)})})})});\n\n if ($brand_guidelines_enabled) {\n # Create CampaignAsset resources to link the Asset resources to the Campaign.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignAssetService::CampaignAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignAsset->new({\n fieldType => BUSINESS_NAME,\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $text_asset_temp_id\n )})})});\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignAssetService::CampaignAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignAsset->new({\n fieldType => LOGO,\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $image_asset_temp_id\n )})})});\n } else {\n # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $text_asset_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => BUSINESS_NAME\n })})});\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $image_asset_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => LOGO\n })})});\n }\n\n return $operations;\n}add_performance_max_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.355Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":1165,"estimatedTokens":12428}}149{"id":"doc-listing_groups_for_retail_google_ads_api_google_-94f18da7","source":"documentation","title":"Listing Groups for Retail | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/listing-groups","text":"Example:\n```text\n/**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param assetGroupId the asset group id for the Performance Max campaign.\n * @param replaceExistingTree option to remove existing product tree from the passed in asset\n * group.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\nprivate void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n long assetGroupId,\n boolean replaceExistingTree)\n throws Exception {\n String assetGroupResourceName = ResourceNames.assetGroup(customerId, assetGroupId);\n\n List<MutateOperation> operations = new ArrayList<>();\n\n if (replaceExistingTree) {\n List<AssetGroupListingGroupFilter> existingListingGroupFilters =\n getAllExistingListingGroupFilterAssetsInAssetGroup(\n googleAdsClient, customerId, assetGroupResourceName);\n\n if (!existingListingGroupFilters.isEmpty()) {\n // A special factory object that ensures the creation of remove operations in the\n // correct order (child listing group filters must be removed before their parents).\n AssetGroupListingGroupFilterRemoveOperationFactory removeOperationFactory =\n new AssetGroupListingGroupFilterRemoveOperationFactory(existingListingGroupFilters);\n\n operations.addAll(removeOperationFactory.removeAll());\n }\n }\n\n // Uses a factory to create all the MutateOperations that manipulate a specific\n // AssetGroup for a specific customer. The operations returned by the factory's methods\n // are used to construct a new tree of filters. These filters can have parent-child\n // relationships, and also include a special root that includes all children.\n //\n // When creating these filters, temporary IDs are used to create the hierarchy between\n // each of the nodes in the tree, beginning with the root listing group filter.\n //\n // The factory created below is specific to a customerId and assetGroupId.\n AssetGroupListingGroupFilterCreateOperationFactory createOperationFactory =\n new AssetGroupListingGroupFilterCreateOperationFactory(\n customerId, assetGroupId, TEMPORARY_ID_LISTING_GROUP_ROOT);\n\n // Creates the operation to add the root node of the tree.\n operations.add(createOperationFactory.createRoot());\n\n // Creates an operation to add a leaf node for new products.\n ListingGroupFilterDimension newProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductCondition(\n ProductCondition.newBuilder()\n .setCondition(ListingGroupFilterProductCondition.NEW)\n .build())\n .build();\n operations.add(\n createOperationFactory.createUnit(\n TEMPORARY_ID_LISTING_GROUP_ROOT, createOperationFactory.nextId(), newProductDimension));\n\n // Creates an operation to add a leaf node for used products.\n ListingGroupFilterDimension usedProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductCondition(\n ProductCondition.newBuilder()\n .setCondition(ListingGroupFilterProductCondition.USED)\n .build())\n .build();\n operations.add(\n createOperationFactory.createUnit(\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n createOperationFactory.nextId(),\n usedProductDimension));\n\n // This represents the ID of the \"other\" category in the ProductCondition subdivision. This ID\n // is saved because the node with this ID will be further partitioned, and this ID will serve as\n // the parent ID for subsequent child nodes of the \"other\" category.\n long otherSubdivisionId = createOperationFactory.nextId();\n\n // Creates an operation to add a subdivision node for other products in the ProductCondition\n // subdivision.\n ListingGroupFilterDimension otherProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductCondition(ProductCondition.newBuilder().build())\n .build();\n operations.add(\n // Calls createSubdivision because this listing group will have children.\n createOperationFactory.createSubdivision(\n TEMPORARY_ID_LISTING_GROUP_ROOT, otherSubdivisionId, otherProductDimension));\n\n // Creates an operation to add a leaf node for products with the brand \"CoolBrand\".\n ListingGroupFilterDimension coolBrandProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductBrand(ProductBrand.newBuilder().setValue(\"CoolBrand\").build())\n .build();\n operations.add(\n createOperationFactory.createUnit(\n otherSubdivisionId, createOperationFactory.nextId(), coolBrandProductDimension));\n\n // Creates an operation to add a leaf node for products with the brand \"CheapBrand\".\n ListingGroupFilterDimension cheapBrandProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductBrand(ProductBrand.newBuilder().setValue(\"CheapBrand\").build())\n .build();\n operations.add(\n createOperationFactory.createUnit(\n otherSubdivisionId, createOperationFactory.nextId(), cheapBrandProductDimension));\n\n // Creates an operation to add a leaf node for other products in the ProductBrand subdivision.\n ListingGroupFilterDimension otherBrandProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductBrand(ProductBrand.newBuilder().build())\n .build();\n operations.add(\n createOperationFactory.createUnit(\n otherSubdivisionId, createOperationFactory.nextId(), otherBrandProductDimension));\n\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n MutateGoogleAdsRequest request =\n MutateGoogleAdsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .addAllMutateOperations(operations)\n .build();\n MutateGoogleAdsResponse response = googleAdsServiceClient.mutate(request);\n printResponseDetails(request, response);\n }\n}\nAddPerformanceMaxProductListingGroupTree.java\n```\n\nExample:\n```text\n/// <summary>\n/// Runs the code example.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <param name=\"assetGroupId\">The asset group id for the Performance Max campaign.</param>\n/// <param name=\"replaceExistingTree\">Option to remove existing product tree\n/// from the passed in asset group.</param>\npublic void Run(\n GoogleAdsClient client,\n long customerId,\n long assetGroupId,\n bool replaceExistingTree)\n{\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n string assetGroupResourceName = ResourceNames.AssetGroup(customerId, assetGroupId);\n\n // We use a factory to create all the MutateOperations that manipulate a specific\n // AssetGroup for a specific customer. The operations returned by the factory's methods\n // are used to optionally remove all AssetGroupListingGroupFilters from the tree, and\n // then to construct a new tree of filters. These filters can have a parent-child\n // relationship, and also include a special root that includes all children.\n //\n // When creating these filters, we use temporary IDs to create the hierarchy between\n // the root listing group filter, and the subdivisions and leave nodes beneath that.\n //\n // The factory specific to a customerId and assetGroupId is created below.\n AssetGroupListingGroupFilterCreateOperationFactory createOperationFactory =\n new AssetGroupListingGroupFilterCreateOperationFactory(\n customerId,\n assetGroupId,\n TEMPORARY_ID_LISTING_GROUP_ROOT\n );\n\n MutateGoogleAdsRequest request = new MutateGoogleAdsRequest\n {\n CustomerId = customerId.ToString()\n };\n\n if (replaceExistingTree)\n {\n List<AssetGroupListingGroupFilter> existingListingGroupFilters =\n GetAllExistingListingGroupFilterAssetsInAssetGroup(\n client,\n customerId,\n assetGroupResourceName\n );\n\n if (existingListingGroupFilters.Count > 0)\n {\n // A special factory object that ensures the creation of remove operations in the\n // correct order (child listing group filters must be removed before their parents).\n AssetGroupListingGroupFilterRemoveOperationFactory removeOperationFactory =\n new AssetGroupListingGroupFilterRemoveOperationFactory(\n existingListingGroupFilters\n );\n\n request.MutateOperations.AddRange(removeOperationFactory.RemoveAll());\n }\n }\n\n request.MutateOperations.Add(createOperationFactory.CreateRoot());\n\n request.MutateOperations.Add(\n createOperationFactory.CreateUnit(\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n createOperationFactory.NextId(),\n new ListingGroupFilterDimension()\n {\n ProductCondition = new ListingGroupFilterDimension.Types.ProductCondition()\n {\n Condition = ListingGroupFilterProductCondition.New\n }\n }\n )\n );\n\n request.MutateOperations.Add(\n createOperationFactory.CreateUnit(\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n createOperationFactory.NextId(),\n new ListingGroupFilterDimension()\n {\n ProductCondition = new ListingGroupFilterDimension.Types.ProductCondition()\n {\n Condition = ListingGroupFilterProductCondition.Used\n }\n }\n )\n );\n\n // We save this ID because create child nodes underneath it.\n long subdivisionIdConditionOther = createOperationFactory.NextId();\n\n request.MutateOperations.Add(\n // We're calling CreateSubdivision because this listing group will have children.\n createOperationFactory.CreateSubdivision(\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n subdivisionIdConditionOther,\n new ListingGroupFilterDimension()\n {\n // All sibling nodes must have the same dimension type. We use an empty\n // ProductCondition to indicate that this is an \"Other\" partition.\n ProductCondition = new ListingGroupFilterDimension.Types.ProductCondition()\n }\n )\n );\n\n request.MutateOperations.Add(\n createOperationFactory.CreateUnit(\n subdivisionIdConditionOther,\n createOperationFactory.NextId(),\n new ListingGroupFilterDimension()\n {\n ProductBrand = new ProductBrand()\n {\n Value = \"CoolBrand\"\n }\n }\n )\n );\n\n request.MutateOperations.Add(\n createOperationFactory.CreateUnit(\n subdivisionIdConditionOther,\n createOperationFactory.NextId(),\n new ListingGroupFilterDimension()\n {\n ProductBrand = new ProductBrand()\n {\n Value = \"CheapBrand\"\n }\n }\n )\n );\n\n request.MutateOperations.Add(\n createOperationFactory.CreateUnit(\n subdivisionIdConditionOther,\n createOperationFactory.NextId(),\n new ListingGroupFilterDimension()\n {\n ProductBrand = new ProductBrand()\n }\n )\n );\n\n MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);\n\n PrintResponseDetails(request, response);\n}\nAddPerformanceMaxProductListingGroupTree.cs\n```\n\nExample:\n```text\n/**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $assetGroupId the asset group ID\n * @param bool $replaceExistingTree true if it should replace the existing listing group\n * tree on the asset group\n */\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $assetGroupId,\n bool $replaceExistingTree\n) {\n // We create all the mutate operations that manipulate a specific asset group for a specific\n // customer. The operations are used to optionally remove all asset group listing group\n // filters from the tree, and then to construct a new tree of filters. These filters can\n // have a parent-child relationship, and also include a special root that includes all\n // children.\n //\n // When creating these filters, we use temporary IDs to create the hierarchy between\n // the root listing group filter, and the subdivisions and leave nodes beneath that.\n $mutateOperations = [];\n if ($replaceExistingTree === true) {\n $existingListingGroupFilters = self::getAllExistingListingGroupFilterAssetsInAssetGroup(\n $googleAdsClient,\n $customerId,\n ResourceNames::forAssetGroup($customerId, $assetGroupId)\n );\n if (count($existingListingGroupFilters) > 0) {\n $mutateOperations = array_merge(\n $mutateOperations,\n // Ensures the creation of remove operations in the correct order (child listing\n // group filters must be removed before their parents).\n self::createMutateOperationsForRemovingListingGroupFiltersTree(\n $existingListingGroupFilters\n )\n );\n }\n }\n\n $mutateOperations[] = self::createMutateOperationForRoot(\n $customerId,\n $assetGroupId,\n self::LISTING_GROUP_ROOT_TEMPORARY_ID\n );\n\n // The temporary ID to be used for creating subdivisions and units.\n static $tempId = self::LISTING_GROUP_ROOT_TEMPORARY_ID - 1;\n\n $mutateOperations[] = self::createMutateOperationForUnit(\n $customerId,\n $assetGroupId,\n $tempId--,\n self::LISTING_GROUP_ROOT_TEMPORARY_ID,\n new ListingGroupFilterDimension([\n 'product_condition' => new ProductCondition([\n 'condition' => ListingGroupFilterProductCondition::PBNEW\n ])\n ])\n );\n\n $mutateOperations[] = self::createMutateOperationForUnit(\n $customerId,\n $assetGroupId,\n $tempId--,\n self::LISTING_GROUP_ROOT_TEMPORARY_ID,\n new ListingGroupFilterDimension([\n 'product_condition' => new ProductCondition([\n 'condition' => ListingGroupFilterProductCondition::USED\n ])\n ])\n );\n\n // We save this ID to create child nodes underneath it.\n $conditionOtherSubdivisionId = $tempId--;\n\n // We're calling createMutateOperationForSubdivision() because this listing group will\n // have children.\n $mutateOperations[] = self::createMutateOperationForSubdivision(\n $customerId,\n $assetGroupId,\n $conditionOtherSubdivisionId,\n self::LISTING_GROUP_ROOT_TEMPORARY_ID,\n new ListingGroupFilterDimension([\n // All sibling nodes must have the same dimension type. We use an empty\n // ProductCondition to indicate that this is an \"Other\" partition.\n 'product_condition' => new ProductCondition()\n ])\n );\n\n $mutateOperations[] = self::createMutateOperationForUnit(\n $customerId,\n $assetGroupId,\n $tempId--,\n $conditionOtherSubdivisionId,\n new ListingGroupFilterDimension(\n ['product_brand' => new ProductBrand(['value' => 'CoolBrand'])]\n )\n );\n\n $mutateOperations[] = self::createMutateOperationForUnit(\n $customerId,\n $assetGroupId,\n $tempId--,\n $conditionOtherSubdivisionId,\n new ListingGroupFilterDimension([\n 'product_brand' => new ProductBrand(['value' => 'CheapBrand'])\n ])\n );\n\n $mutateOperations[] = self::createMutateOperationForUnit(\n $customerId,\n $assetGroupId,\n $tempId--,\n $conditionOtherSubdivisionId,\n // All other product brands.\n new ListingGroupFilterDimension(['product_brand' => new ProductBrand()])\n );\n\n // Issues a mutate request to create everything and prints its information.\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n $response = $googleAdsServiceClient->mutate(\n MutateGoogleAdsRequest::build($customerId, $mutateOperations)\n );\n\n self::printResponseDetails($mutateOperations, $response);\n}AddPerformanceMaxProductListingGroupTree.php\n```\n\nExample:\n```text\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n asset_group_id: int, # Will be str for path construction\n replace_existing_tree: bool,\n) -> None:\n \"\"\"The main method that creates all necessary entities for the example.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n asset_group_id: the asset group id for the Performance Max campaign.\n replace_existing_tree: option to remove existing product tree from the\n passed in asset group.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n # asset_group_id is used as a string in path construction.\n asset_group_resource_name: str = googleads_service.asset_group_path(\n customer_id, str(asset_group_id)\n )\n operations: List[MutateOperation] = []\n\n if replace_existing_tree:\n # Retrieve a list of existing AssetGroupListingGroupFilters\n existing_listing_group_filters: List[AssetGroupListingGroupFilter] = (\n get_all_existing_listing_group_filter_assets_in_asset_group(\n client, customer_id, asset_group_resource_name\n )\n )\n\n # If present, create MutateOperations to remove each\n # AssetGroupListingGroupFilter and add them to the list of operations.\n if existing_listing_group_filters:\n remove_operation_factory = (\n AssetGroupListingGroupFilterRemoveOperationFactory(\n client, existing_listing_group_filters\n )\n )\n operations.extend(remove_operation_factory.remove_all())\n\n create_operation_factory = (\n AssetGroupListingGroupFilterCreateOperationFactory(\n client,\n customer_id,\n asset_group_id, # Pass as int, will be converted to str in __init__\n _TEMPORARY_ID_LISTING_GROUP_ROOT,\n )\n )\n\n operations.append(create_operation_factory.create_root())\n\n new_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n new_dimension.product_condition.condition = (\n client.enums.ListingGroupFilterProductConditionEnum.NEW\n )\n operations.append(\n create_operation_factory.create_unit(\n _TEMPORARY_ID_LISTING_GROUP_ROOT,\n create_operation_factory.next_id(),\n new_dimension,\n )\n )\n\n used_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n used_dimension.product_condition.condition = (\n client.enums.ListingGroupFilterProductConditionEnum.USED\n )\n operations.append(\n create_operation_factory.create_unit(\n _TEMPORARY_ID_LISTING_GROUP_ROOT,\n create_operation_factory.next_id(),\n used_dimension,\n )\n )\n\n # We save this ID because create child nodes underneath it.\n subdivision_id_condition_other: int = create_operation_factory.next_id()\n\n # All sibling nodes must have the same dimension type. We use an empty\n # product_condition to indicate that this is an \"Other\" partition.\n other_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n # This triggers the presence of the product_condition field without\n # specifying any field values. This is important in order to tell the API\n # that this is an \"other\" node.\n other_dimension.product_condition._pb.SetInParent()\n # We're calling create_subdivision because this listing group will have\n # children.\n operations.append(\n create_operation_factory.create_subdivision(\n _TEMPORARY_ID_LISTING_GROUP_ROOT,\n subdivision_id_condition_other,\n other_dimension,\n )\n )\n\n cool_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n cool_dimension.product_brand.value = \"CoolBrand\"\n operations.append(\n create_operation_factory.create_unit(\n subdivision_id_condition_other,\n create_operation_factory.next_id(),\n cool_dimension,\n )\n )\n\n cheap_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n cheap_dimension.product_brand.value = \"CheapBrand\"\n operations.append(\n create_operation_factory.create_unit(\n subdivision_id_condition_other,\n create_operation_factory.next_id(),\n cheap_dimension,\n )\n )\n\n empty_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n # This triggers the presence of the product_brand field without specifying\n # any field values. This is important in order to tell the API\n # that this is an \"other\" node.\n empty_dimension.product_brand._pb.SetInParent()\n operations.append(\n create_operation_factory.create_unit(\n subdivision_id_condition_other,\n create_operation_factory.next_id(),\n empty_dimension,\n )\n )\n\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id, mutate_operations=operations\n )\n\n print_response_details(operations, response)add_performance_max_product_listing_group_tree.py\n```\n\nExample:\n```text\ndef add_performance_max_product_listing_group_tree(\n customer_id,\n asset_group_id,\n replace_existing_tree)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n asset_group_resource_name = client.path.asset_group(\n customer_id,\n asset_group_id,\n )\n\n # We use a factory to create all the MutateOperations that manipulate a\n # specific AssetGroup for a specific customer. The operations returned by the\n # factory's methods are used to optionally remove all\n # AssetGroupListingGroupFilters from the tree, and then to construct a new\n # tree of filters. These filters can have a parent-child relationship, and\n # also include a special root that includes all children.\n #\n # When creating these filters, we use temporary IDs to create the hierarchy\n # between the root listing group filter, and the subdivisions and leave nodes\n # beneath that.\n #\n # The factory specific to a customerId and assetGroupId is created below.\n create_operation_factory = AssetGroupListingGroupFilterCreateOperationFactory.new(\n customer_id,\n asset_group_id,\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n )\n\n operations = []\n\n if replace_existing_tree\n existing_listing_group_filters = get_existing_listing_group_filters_in_asset_group(\n client,\n customer_id,\n asset_group_resource_name,\n )\n\n if existing_listing_group_filters.length > 0\n # A special factory object that ensures the creation of remove operations\n # in the correct order (child listing group filters must be removed\n # before their parents).\n remove_operation_factory = AssetGroupListingGroupFilterRemoveOperationFactory.new(\n existing_listing_group_filters\n )\n\n operations += remove_operation_factory.remove_all(client)\n end\n end\n\n operations << create_operation_factory.create_root(client)\n\n operations << create_operation_factory.create_unit(\n client,\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n create_operation_factory.next_id,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_condition = client.resource.product_condition do |condition|\n condition.condition = :NEW\n end\n end,\n )\n operations << create_operation_factory.create_unit(\n client,\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n create_operation_factory.next_id,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_condition = client.resource.product_condition do |condition|\n condition.condition = :USED\n end\n end,\n )\n\n # We save this ID because we create child nodes underneath it.\n subdivision_id_condition_other = create_operation_factory.next_id\n\n operations << create_operation_factory.create_subdivision(\n client,\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n subdivision_id_condition_other,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_condition = client.resource.product_condition do |condition|\n # All sibling nodes must have the same dimension type. We use an empty\n # ProductCondition to indicate that this is an \"Other\" partition.\n end\n end,\n )\n\n operations << create_operation_factory.create_unit(\n client,\n subdivision_id_condition_other,\n create_operation_factory.next_id,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_brand = client.resource.product_brand do |brand|\n brand.value = 'CoolBrand'\n end\n end,\n )\n operations << create_operation_factory.create_unit(\n client,\n subdivision_id_condition_other,\n create_operation_factory.next_id,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_brand = client.resource.product_brand do |brand|\n brand.value = 'CheapBrand'\n end\n end,\n )\n operations << create_operation_factory.create_unit(\n client,\n subdivision_id_condition_other,\n create_operation_factory.next_id,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_brand = client.resource.product_brand do |brand|\n end\n end,\n )\n\n response = client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: operations,\n )\n\n print_response_details(operations, response)\nendadd_performance_max_product_listing_group_tree.rb\n```\n\nExample:\n```text\nsub add_performance_max_product_listing_group_tree {\n my ($api_client, $customer_id, $asset_group_id, $replace_existing_tree) = @_;\n\n # We create all the mutate operations that manipulate a specific asset group for\n # a specific customer. The operations are used to optionally remove all asset\n # group listing group filters from the tree, and then to construct a new tree\n # of filters. These filters can have a parent-child relationship, and also include\n # a special root that includes all children.\n #\n # When creating these filters, we use temporary IDs to create the hierarchy between\n # the root listing group filter, and the subdivisions and leave nodes beneath that.\n my $mutate_operations = [];\n if (defined $replace_existing_tree) {\n my $existing_listing_group_filters =\n get_all_existing_listing_group_filter_assets_in_asset_group(\n $api_client,\n $customer_id,\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, $asset_group_id\n ));\n\n if (scalar @$existing_listing_group_filters > 0) {\n push @$mutate_operations,\n # Ensure the creation of remove operations in the correct order (child\n # listing group filters must be removed before their parents).\n @{\n create_mutate_operations_for_removing_listing_group_filters_tree(\n $existing_listing_group_filters)};\n }\n }\n\n push @$mutate_operations,\n create_mutate_operation_for_root($customer_id, $asset_group_id,\n LISTING_GROUP_ROOT_TEMPORARY_ID);\n\n # The temporary ID to be used for creating subdivisions and units.\n my $temp_id = LISTING_GROUP_ROOT_TEMPORARY_ID - 1;\n\n push @$mutate_operations,\n create_mutate_operation_for_unit(\n $customer_id,\n $asset_group_id,\n $temp_id--,\n LISTING_GROUP_ROOT_TEMPORARY_ID,\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n productCondition =>\n Google::Ads::GoogleAds::V25::Resources::ProductCondition->new({\n condition => NEW\n })}));\n\n push @$mutate_operations,\n create_mutate_operation_for_unit(\n $customer_id,\n $asset_group_id,\n $temp_id--,\n LISTING_GROUP_ROOT_TEMPORARY_ID,\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n productCondition =>\n Google::Ads::GoogleAds::V25::Resources::ProductCondition->new({\n condition => USED\n })}));\n\n # We save this ID to create child nodes underneath it.\n my $condition_other_subdivision_id = $temp_id--;\n\n # We're calling create_mutate_operation_for_subdivision() because this listing\n # group will have children.\n push @$mutate_operations, create_mutate_operation_for_subdivision(\n $customer_id,\n $asset_group_id,\n $condition_other_subdivision_id,\n LISTING_GROUP_ROOT_TEMPORARY_ID,\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n # All sibling nodes must have the same dimension type. We use an empty\n # ProductCondition to indicate that this is an \"Other\" partition.\n productCondition =>\n Google::Ads::GoogleAds::V25::Resources::ProductCondition->new({})}));\n\n push @$mutate_operations,\n create_mutate_operation_for_unit(\n $customer_id,\n $asset_group_id,\n $temp_id--,\n $condition_other_subdivision_id,\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Resources::ProductBrand->new({\n value => \"CoolBrand\"\n })}));\n\n push @$mutate_operations,\n create_mutate_operation_for_unit(\n $customer_id,\n $asset_group_id,\n $temp_id--,\n $condition_other_subdivision_id,\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Resources::ProductBrand->new({\n value => \"CheapBrand\"\n })}));\n\n push @$mutate_operations, create_mutate_operation_for_unit(\n $customer_id,\n $asset_group_id,\n $temp_id--,\n $condition_other_subdivision_id,\n # All other product brands.\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Resources::ProductBrand->new({})}));\n\n # Issue a mutate request to create everything and print its information.\n my $response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $mutate_operations\n });\n\n print_response_details($mutate_operations, $response);\n\n return 1;\n}add_performance_max_product_listing_group_tree.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.358Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":836,"estimatedTokens":7802}}150{"id":"doc-asset_group_signals_google_ads_api_google_for_de-a4ffaebe","source":"documentation","title":"Asset Group Signals | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/asset-group-signals","text":"Example:\n```text\nAssetGroupSignal audienceSignal =\n AssetGroupSignal.newBuilder()\n .setAssetGroup(assetGroupResourceName)\n .setAudience(\n AudienceInfo.newBuilder()\n .setAudience(ResourceNames.audience(customerId, audienceId)))\n .build();\n\nmutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupSignalOperation(\n AssetGroupSignalOperation.newBuilder().setCreate(audienceSignal))\n .build());AddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\noperations.Add(\n new MutateOperation()\n {\n AssetGroupSignalOperation = new AssetGroupSignalOperation()\n {\n Create = new AssetGroupSignal()\n {\n AssetGroup = assetGroupResourceName,\n Audience = new AudienceInfo()\n {\n Audience = ResourceNames.Audience(customerId, audienceId.Value)\n }\n }\n }\n }\n);AddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createAssetGroupSignalOperations(\n int $customerId,\n string $assetGroupResourceName,\n ?int $audienceId\n): array {\n $operations = [];\n if (is_null($audienceId)) {\n return $operations;\n }\n\n $operations[] = new MutateOperation([\n 'asset_group_signal_operation' => new AssetGroupSignalOperation([\n // To learn more about Audience Signals, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals.\n 'create' => new AssetGroupSignal([\n 'asset_group' => $assetGroupResourceName,\n 'audience' => new AudienceInfo([\n 'audience' => ResourceNames::forAudience($customerId, $audienceId)\n ])\n ])\n ])\n ]);\n\n return $operations;\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\nmutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\noperation: AssetGroupSignal = (\n mutate_operation.asset_group_signal_operation.create\n)\noperation.asset_group = asset_group_resource_name\noperation.audience.audience = googleads_service.audience_path(\n customer_id, audience_id\n)\noperations.append(mutate_operation)add_performance_max_campaign.py\n```\n\nExample:\n```text\n# Create a list of MutateOperations that create AssetGroupSignals.\ndef create_asset_group_signal_operations(client, customer_id, audience_id)\n operations = []\n return operations if audience_id.nil?\n\n operations << client.operation.mutate do |m|\n m.asset_group_signal_operation = client.operation.create_resource.\n asset_group_signal do |ags|\n ags.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n ags.audience = client.resource.audience_info do |ai|\n ai.audience = client.path.audience(customer_id, audience_id)\n end\n end\n end\n\n operations\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_asset_group_signal_operations {\n my ($customer_id, $audience_id) = @_;\n\n my $operations = [];\n return $operations if not defined $audience_id;\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupSignalOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupSignalService::AssetGroupSignalOperation\n ->new({\n # To learn more about Audience Signals, see:\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupSignal->new({\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n audience =>\n Google::Ads::GoogleAds::V25::Common::AudienceInfo->new({\n audience =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::audience(\n $customer_id, $audience_id\n )})})})});\n return $operations;\n}add_performance_max_campaign.pl\n```\n\nExample:\n```text\nAssetGroupSignal searchThemeSignal =\n AssetGroupSignal.newBuilder()\n .setAssetGroup(assetGroupResourceName)\n .setSearchTheme(SearchThemeInfo.newBuilder().setText(\"travel\").build())\n .build();\n\nmutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupSignalOperation(\n AssetGroupSignalOperation.newBuilder().setCreate(searchThemeSignal))\n .build());AddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\nThis example is not yet available in C#; you can take a look at the other languages.\n```\n\nExample:\n```text\nThis example is not yet available in PHP; you can take a look at the other languages.\n```\n\nExample:\n```text\nmutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\noperation: AssetGroupSignal = (\n mutate_operation.asset_group_signal_operation.create\n)\noperation.asset_group = asset_group_resource_name\noperation.search_theme.text = \"travel\"\noperations.append(mutate_operation)add_performance_max_campaign.py\n```\n\nExample:\n```text\nThis example is not yet available in Ruby; you can take a look at the other languages.\n```\n\nExample:\n```text\nThis example is not yet available in Perl; you can take a look at the other languages.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.359Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":182,"estimatedTokens":1363}}151{"id":"doc-asset_reporting_google_ads_api_google_for_develo-4d6e9f30","source":"documentation","title":"Asset reporting | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/asset-reporting","text":"Example:\n```text\nSELECT\n campaign.id,\n asset_group.id,\n asset_group_asset.asset,\n asset_group_asset.field_type,\n asset_group_asset.primary_status,\n asset_group_asset.primary_status_details,\n asset_group_asset.primary_status_reasons,\n asset_group_asset.policy_summary.policy_topic_entries,\n asset_group_asset.status,\n metrics.impressions,\n metrics.clicks,\n metrics.ctr,\n metrics.conversions,\n metrics.conversions_value,\n metrics.cost_micros\nFROM asset_group_asset\nWHERE asset_group.id = ASSET_GROUP_ID\n AND asset_group_asset.status != 'REMOVED'\n AND campaign.id = CAMPAIGN_ID\n```\n\nExample:\n```text\nSELECT\n asset_group_asset.asset,\n asset_group_asset.asset_group,\n asset_group_asset.field_type,\n asset_group_asset.status,\n segments.ad_network_type,\n metrics.conversions,\n metrics.conversions_value,\n metrics.cost_micros,\n metrics.impressions,\n metrics.clicks\nFROM asset_group_asset\nWHERE\n segments.date DURING LAST_30_DAYS\n AND asset_group_asset.status = 'ENABLED'\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n asset_group.id,\n asset_group_top_combination_view.asset_group_top_combinations\nFROM asset_group_top_combination_view\nWHERE asset_group.id = ASSET_GROUP_ID\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n asset_group_top_combination_view.asset_group_top_combinations,\n asset_group.ad_strength,\n asset_group.id\nFROM asset_group_top_combination_view\nWHERE asset_group.ad_strength IN ('GOOD', 'EXCELLENT')\n AND campaign.id = CAMPAIGN_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.361Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":66,"estimatedTokens":373}}152{"id":"doc-performance_max_optimizations_google_ads_api_goo-a618f3ff","source":"documentation","title":"Performance Max Optimizations | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/optimizations","text":"Example:\n```text\nSELECT\n asset_group.ad_strength,\n asset_group.asset_coverage\nFROM asset_group\nWHERE asset_group.resource_name = \"customers/CUSTOMER_ID/assetGroups/ASSET_GROUP_ID\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.362Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":50}}153{"id":"doc-create_the_campaign_criteria_google_ads_api_goog-3562ce87","source":"documentation","title":"Create the campaign criteria | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/create-campaign-criteria","text":"Example:\n```text\n/** Creates a list of MutateOperations that create new campaign criteria. */\nprivate List<MutateOperation> createCampaignCriterionOperations(long customerId) {\n String campaignResourceName =\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID);\n List<CampaignCriterion> campaignCriteria = new ArrayList<>();\n // Sets the LOCATION campaign criteria.\n // Targets all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n //\n // We will add one positive location target for New York City (ID=1023191)\n // and one negative location target for Brooklyn (ID=1022762).\n // First, adds the positive (negative = False) for New York City.\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n .setLocation(\n LocationInfo.newBuilder()\n .setGeoTargetConstant(ResourceNames.geoTargetConstant(1023191))\n .build())\n .setNegative(false)\n .build());\n // Next adds the negative target for Brooklyn.\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n .setLocation(\n LocationInfo.newBuilder()\n .setGeoTargetConstant(ResourceNames.geoTargetConstant(1022762))\n .build())\n .setNegative(true)\n .build());\n // Sets the LANGUAGE campaign criterion.\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n // Sets the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n .setLanguage(\n LanguageInfo.newBuilder()\n .setLanguageConstant(ResourceNames.languageConstant(1000)) // English\n .build())\n .build());\n // Returns a list of mutate operations with one operation per criterion.\n return campaignCriteria.stream()\n .map(\n criterion ->\n MutateOperation.newBuilder()\n .setCampaignCriterionOperation(\n CampaignCriterionOperation.newBuilder().setCreate(criterion).build())\n .build())\n .collect(Collectors.toList());\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a list of MutateOperations that create new campaign criteria.\n/// </summary>\n/// <param name=\"campaignResourceName\">The campaign resource name.</param>\n/// <returns>A list of MutateOperations that create new campaign criteria.</returns>\nprivate List<MutateOperation> CreateCampaignCriterionOperations(\n string campaignResourceName)\n{\n List<MutateOperation> operations = new List<MutateOperation>();\n\n // Set the LOCATION campaign criteria.\n // Target all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n //\n // We will add one positive location target for New York City (ID=1023191)\n // and one negative location target for Brooklyn (ID=1022762).\n // First, add the positive (negative = False) for New York City.\n MutateOperation operation1 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n Location = new LocationInfo()\n {\n GeoTargetConstant = ResourceNames.GeoTargetConstant(1023191)\n },\n\n Negative = false\n }\n }\n };\n\n operations.Add(operation1);\n\n // Next add the negative target for Brooklyn.\n MutateOperation operation2 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n Location = new LocationInfo()\n {\n GeoTargetConstant = ResourceNames.GeoTargetConstant(1022762)\n },\n\n Negative = true\n }\n }\n };\n\n operations.Add(operation2);\n\n // Set the LANGUAGE campaign criterion.\n MutateOperation operation3 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n\n // Set the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n Language = new LanguageInfo()\n {\n LanguageConstant = ResourceNames.LanguageConstant(1000) // English\n },\n }\n }\n };\n\n operations.Add(operation3);\n\n return operations;\n}\nAddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createCampaignCriterionOperations(int $customerId): array\n{\n $operations = [];\n // Set the LOCATION campaign criteria.\n // Target all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n //\n // We will add one positive location target for New York City (ID=1023191)\n // and one negative location target for Brooklyn (ID=1022762).\n // First, adds the positive (negative = false) for New York City.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'location' => new LocationInfo([\n 'geo_target_constant' => ResourceNames::forGeoTargetConstant(1023191)\n ]),\n 'negative' => false\n ])\n ])\n ]);\n\n // Next adds the negative target for Brooklyn.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'location' => new LocationInfo([\n 'geo_target_constant' => ResourceNames::forGeoTargetConstant(1022762)\n ]),\n 'negative' => true\n ])\n ])\n ]);\n\n // Sets the LANGUAGE campaign criterion.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n // Set the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n 'language' => new LanguageInfo([\n 'language_constant' => ResourceNames::forLanguageConstant(1000) // English\n ])\n ])\n ])\n ]);\n\n return $operations;\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\ndef create_campaign_criterion_operations(\n client: GoogleAdsClient,\n customer_id: str,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create new campaign criteria.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a list of MutateOperations that create new campaign criteria.\n \"\"\"\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n geo_target_constant_service: GeoTargetConstantServiceClient = (\n client.get_service(\"GeoTargetConstantService\")\n )\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n operations: List[MutateOperation] = []\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n #\n # We will add one positive location target for New York City (ID=1023191)\n # and one negative location target for Brooklyn (ID=1022762).\n # First, add the positive (negative = False) for New York City.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion: CampaignCriterion = (\n mutate_operation.campaign_criterion_operation.create\n )\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n campaign_criterion.location.geo_target_constant = (\n geo_target_constant_service.geo_target_constant_path(\"1023191\")\n )\n campaign_criterion.negative = False\n operations.append(mutate_operation)\n\n # Next add the negative target for Brooklyn.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion: CampaignCriterion = (\n mutate_operation.campaign_criterion_operation.create\n )\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n campaign_criterion.location.geo_target_constant = (\n geo_target_constant_service.geo_target_constant_path(\"1022762\")\n )\n campaign_criterion.negative = True\n operations.append(mutate_operation)\n\n # Set the LANGUAGE campaign criterion.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion: CampaignCriterion = (\n mutate_operation.campaign_criterion_operation.create\n )\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n campaign_criterion.language.language_constant = (\n googleads_service.language_constant_path(\"1000\")\n ) # English\n operations.append(mutate_operation)\n\n return operationsadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates a list of MutateOperations that create new campaign criteria.\ndef create_campaign_criterion_operations(client, customer_id)\n operations = []\n\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n #\n # We will add one positive location target for New York City (ID=1023191)\n # and one negative location target for Brooklyn (ID=1022762).\n # First, add the positive (negative = false) for New York City.\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n cc.location = client.resource.location_info do |li|\n li.geo_target_constant = client.path.geo_target_constant(\"1023191\")\n end\n cc.negative = false\n end\n end\n\n # Next add the negative target for Brooklyn.\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n cc.location = client.resource.location_info do |li|\n li.geo_target_constant = client.path.geo_target_constant(\"1022762\")\n end\n cc.negative = true\n end\n end\n\n # Set the LANGUAGE campaign criterion.\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n cc.language = client.resource.language_info do |li|\n li.language_constant = client.path.language_constant(\"1000\") # English\n end\n end\n end\n\n operations\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_campaign_criterion_operations {\n my ($customer_id) = @_;\n\n my $operations = [];\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting.\n #\n # We will add one positive location target for New York City (ID=1023191)\n # and one negative location target for Brooklyn (ID=1022762).\n # First, add the positive (negative = false) for New York City.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n location =>\n Google::Ads::GoogleAds::V25::Common::LocationInfo->new({\n geoTargetConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n 1023191)}\n ),\n negative => \"false\"\n })})});\n\n # Next add the negative target for Brooklyn.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n location =>\n Google::Ads::GoogleAds::V25::Common::LocationInfo->new({\n geoTargetConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n 1022762)}\n ),\n negative => \"true\"\n })})});\n\n # Set the LANGUAGE campaign criterion.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7.\n language =>\n Google::Ads::GoogleAds::V25::Common::LanguageInfo->new({\n languageConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::language_constant(\n 1000) # English\n })})})});\n\n return $operations;\n}add_performance_max_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.366Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":446,"estimatedTokens":4387}}154{"id":"doc-campaign_criterion_performance_google_ads_api_go-bcba1389","source":"documentation","title":"Campaign criterion performance | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/campaign-criterion-reporting","text":"Example:\n```text\nSELECT\n campaign.id,\n campaign.name,\n metrics.clicks,\n metrics.impressions,\n campaign_criterion.location.geo_target_constant\nFROM location_view\nWHERE campaign.status != 'REMOVED'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.367Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":55}}155{"id":"doc-retail_campaign_performance_google_ads_api_googl-f1db801d","source":"documentation","title":"Retail Campaign Performance | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/retail-reporting","text":"Example:\n```text\nSELECT\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.cost_micros\nFROM campaign\nWHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'\n AND campaign.shopping_setting.merchant_id IS NOT NULL\n AND segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.cost_micros\nFROM campaign\nWHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'\n AND campaign.shopping_setting.merchant_id IS NOT NULL\n AND campaign.shopping_setting.feed_label = 'WINTER-PRODUCTS'\n AND segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n segments.ad_network_type,\n segments.ad_using_product_data,\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.cost_micros\nFROM campaign\nWHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'\n AND campaign.shopping_setting.merchant_id IS NOT NULL\n AND segments.date DURING LAST_30_DAYS\n AND segments.ad_using_product_data = true\n```\n\nExample:\n```text\nSELECT\n segments.product_item_id,\n metrics.clicks,\n metrics.cost_micros,\n metrics.impressions,\n metrics.search_budget_lost_impression_share,\n metrics.search_rank_lost_impression_share,\n metrics.search_budget_lost_absolute_top_impression_share,\n metrics.search_rank_lost_absolute_top_impression_share,\n metrics.conversions,\n metrics.all_conversions,\n campaign.advertising_channel_type\nFROM shopping_performance_view\nWHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'\n AND segments.date DURING LAST_30_DAYS\n AND metrics.clicks > 0\nORDER BY\n metrics.all_conversions DESC,\n metrics.conversions DESC,\n metrics.clicks DESC,\n metrics.cost_micros DESC,\n metrics.impressions DESC\n```\n\nExample:\n```text\nSELECT\n segments.product_item_id,\n segments.product_title,\n metrics.average_cart_size,\n metrics.average_order_value_micros,\n metrics.conversions,\n metrics.conversions_value,\n metrics.gross_profit_micros,\n metrics.gross_profit_margin,\n metrics.revenue_micros,\n metrics.units_sold,\n campaign.advertising_channel_type\nFROM shopping_performance_view\nWHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'\n AND segments.date DURING LAST_30_DAYS\n AND metrics.conversions > 0\nORDER BY\n metrics.gross_profit_margin DESC,\n metrics.revenue_micros DESC,\n metrics.conversions_value DESC\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n campaign.advertising_channel_type,\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.cost_micros,\n metrics.average_order_value_micros,\n metrics.gross_profit_micros,\n metrics.gross_profit_margin\nFROM campaign\nWHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'\n AND campaign.shopping_setting.merchant_id IS NOT NULL\n AND segments.date DURING LAST_30_DAYS\nORDER BY\n metrics.gross_profit_margin DESC,\n metrics.average_order_value_micros DESC,\n metrics.cost_micros DESC,\n metrics.conversions DESC,\n metrics.clicks DESC,\n metrics.impressions DESC\n```\n\nExample:\n```text\nSELECT\n asset_group.id,\n asset_group_listing_group_filter.id,\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.cost_micros\nFROM asset_group_product_group_view\nWHERE campaign.id = CAMPAIGN_ID\n AND segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n asset_group_listing_group_filter.id,\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.cost_micros\nFROM asset_group_product_group_view\nWHERE asset_group.id = ASSET_GROUP_ID\n AND segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n asset_group_listing_group_filter.case_value.product_brand.value,\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.cost_micros\nFROM asset_group_product_group_view\nWHERE asset_group.id = ASSET_GROUP_ID\n AND segments.date DURING LAST_30_DAYS\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.370Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":161,"estimatedTokens":971}}156{"id":"doc-app_campaigns_google_ads_api_google_for_develope-1739092e","source":"documentation","title":"App campaigns | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/app-campaigns/overview","text":"Example:\n```text\nSELECT\n campaign.id,\n campaign.name,\n campaign.app_campaign_setting.app_id,\n campaign.app_campaign_setting.app_store\nFROM campaign\nWHERE campaign.advertising_channel_type = 'MULTI_CHANNEL'\n AND campaign.app_campaign_setting.app_id IS NOT NULL\n AND campaign.status != 'REMOVED'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.371Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":14,"estimatedTokens":80}}157{"id":"doc-performance_max_troubleshooting_google_ads_api_g-b4cba8c3","source":"documentation","title":"Performance Max troubleshooting | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/troubleshooting","text":"Example:\n```text\nSELECT\n asset_group.resource_name,\n asset_group.primary_status,\n asset_group.primary_status_reasons\nFROM asset_group\nWHERE asset_group.resource_name = \"customers/CUSTOMER_ID/assetGroups/ASSET_GROUP_ID\"\n```\n\nExample:\n```text\nSELECT\n asset_group_asset.resource_name,\n asset_group_asset.primary_status,\n asset_group_asset.primary_status_reasons,\n asset_group_asset.primary_status_details\nFROM asset_group_asset\nWHERE asset_group_asset.resource_name = \"customers/CUSTOMER/assetGroupAssets/ASSET_GROUP_ID~ASSET_ID~FIELD_TYPE\"\n```\n\nExample:\n```text\nSELECT\n asset.id,\n asset.name,\n asset_group.id,\n asset_group_asset.source\nFROM asset_group_asset\nWHERE campaign.id = CAMPAIGN_ID\n```\n\nExample:\n```text\nnonNewCustomerAcquisitionConversionValueTotal = 0;\n// For each campaign that has that conversion...\nfor (campaign in campaigns) {\n // If the new customer acquisition value is 'Bid higher', then subtract.\n if (bidHigher == true) {\n nonNewCustomerAcquisitionConversionValueTotal +=\n campaign.allConversionsValue - campaign.allNewCustomerLifetimeValue;\n }\n // If the new customer acquisition value is 'Only bid' or not set, then don't subtract.\n else {\n nonNewCustomerAcquisitionConversionValueTotal += campaign.allConversionsValue;\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.372Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":50,"estimatedTokens":324}}158{"id":"doc-report_and_optimize_google_ads_api_google_for_de-a4b18556","source":"documentation","title":"Report and optimize | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/demand-gen/reporting","text":"Example:\n```text\nSELECT\n campaign.id,\n campaign.status,\n campaign.bidding_strategy_type\nFROM campaign\nWHERE campaign.advertising_channel_type = DEMAND_GEN\n```\n\nExample:\n```text\nSELECT\n ad_group_ad.ad.id,\n ad_group_ad.ad.type,\n ad_group_ad.ad.demand_gen_multi_asset_ad.marketing_images,\n ad_group_ad.ad.demand_gen_multi_asset_ad.square_marketing_images,\n ad_group_ad.ad.demand_gen_multi_asset_ad.portrait_marketing_images,\n ad_group_ad.ad.demand_gen_multi_asset_ad.classic_display_images,\n ad_group_ad.ad.demand_gen_multi_asset_ad.logo_images,\n ad_group_ad.ad.demand_gen_multi_asset_ad.headlines,\n ad_group_ad.ad.demand_gen_multi_asset_ad.descriptions,\n ad_group_ad.ad.demand_gen_multi_asset_ad.business_name,\n ad_group_ad.ad.demand_gen_multi_asset_ad.call_to_action_text\nFROM ad_group_ad\nWHERE ad_group_ad.ad.type = DEMAND_GEN_MULTI_ASSET_AD\n```\n\nExample:\n```text\nSELECT\n ad_group_ad.ad.id,\n ad_group_ad.ad.type,\n ad_group_ad.ad.demand_gen_carousel_ad.business_name,\n ad_group_ad.ad.demand_gen_carousel_ad.logo_image,\n ad_group_ad.ad.demand_gen_carousel_ad.headline,\n ad_group_ad.ad.demand_gen_carousel_ad.description,\n ad_group_ad.ad.demand_gen_carousel_ad.call_to_action_text,\n ad_group_ad.ad.demand_gen_carousel_ad.carousel_cards\nFROM ad_group_ad\nWHERE ad_group_ad.ad.type = DEMAND_GEN_CAROUSEL_AD\n```\n\nExample:\n```text\nSELECT\n ad_group_ad.ad.id,\n ad_group_ad.ad.type,\n ad_group_ad.ad.demand_gen_video_responsive_ad.breadcrumb1,\n ad_group_ad.ad.demand_gen_video_responsive_ad.breadcrumb2,\n ad_group_ad.ad.demand_gen_video_responsive_ad.business_name,\n ad_group_ad.ad.demand_gen_video_responsive_ad.call_to_actions,\n ad_group_ad.ad.demand_gen_video_responsive_ad.descriptions,\n ad_group_ad.ad.demand_gen_video_responsive_ad.headlines,\n ad_group_ad.ad.demand_gen_video_responsive_ad.logo_images,\n ad_group_ad.ad.demand_gen_video_responsive_ad.long_headlines,\n ad_group_ad.ad.demand_gen_video_responsive_ad.videos\nFROM ad_group_ad\nWHERE ad_group_ad.ad.type = DEMAND_GEN_VIDEO_RESPONSIVE_AD\n```\n\nExample:\n```text\nSELECT\n asset.id,\n asset.demand_gen_carousel_card_asset.marketing_image_asset,\n asset.demand_gen_carousel_card_asset.square_marketing_image_asset,\n asset.demand_gen_carousel_card_asset.portrait_marketing_image_asset,\n asset.demand_gen_carousel_card_asset.headline,\n asset.demand_gen_carousel_card_asset.call_to_action_text\nFROM asset\nWHERE asset.type = DEMAND_GEN_CAROUSEL_CARD\n```\n\nExample:\n```text\nSELECT\n asset.id,\n asset.name,\n asset.type,\n metrics.impressions\nFROM ad_group_ad_asset_view\nWHERE ad_group_ad_asset_view.field_type = DEMAND_GEN_CAROUSEL_CARD\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.373Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":86,"estimatedTokens":658}}159{"id":"doc-smart_campaigns_google_ads_api_google_for_develo-f8ed7878","source":"documentation","title":"Smart Campaigns | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/smart-campaigns/overview","text":"Example:\n```text\nlocations/locationId\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.374Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":14}}160{"id":"doc-channel_controls_google_ads_api_google_for_devel-09fbba62","source":"documentation","title":"Channel controls | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/demand-gen/channel-controls","text":"Example:\n```text\n\"demand_gen_ad_group_settings\": {\n \"channel_controls\": {\n \"channel_strategy\": \"ALL_CHANNELS\"\n }\n}\n```\n\nExample:\n```text\n\"demand_gen_ad_group_settings\": {\n \"channel_controls\": {\n \"channel_strategy\": \"ALL_OWNED_AND_OPERATED_CHANNELS\"\n }\n}\n```\n\nExample:\n```text\n\"demand_gen_ad_group_settings\": {\n \"channel_controls\": {\n \"selected_channels\": {\n \"youtube_in_stream\": false,\n \"youtube_in_feed\": false,\n \"youtube_shorts\": true,\n \"discover\": false,\n \"gmail\": false,\n \"display\": false,\n \"maps\": false,\n }\n }\n}\n```\n\nExample:\n```text\nSELECT\n ad_group.id,\n ad_group.demand_gen_ad_group_settings.channel_controls.channel_config,\n ad_group.demand_gen_ad_group_settings.channel_controls.channel_strategy,\n ad_group.demand_gen_ad_group_settings.channel_controls.selected_channels.youtube_in_feed,\n ad_group.demand_gen_ad_group_settings.channel_controls.selected_channels.youtube_in_stream,\n ad_group.demand_gen_ad_group_settings.channel_controls.selected_channels.youtube_shorts,\n ad_group.demand_gen_ad_group_settings.channel_controls.selected_channels.discover,\n ad_group.demand_gen_ad_group_settings.channel_controls.selected_channels.display,\n ad_group.demand_gen_ad_group_settings.channel_controls.selected_channels.gmail,\n ad_group.demand_gen_ad_group_settings.channel_controls.selected_channels.maps\nFROM ad_group\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.376Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":52,"estimatedTokens":367}}161{"id":"doc-reporting_google_ads_api_google_for_developers-deb27b6a","source":"documentation","title":"Reporting | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/app-campaigns/reporting","text":"Example:\n```text\nSELECT\n campaign.id,\n campaign.name,\n campaign.advertising_channel_type,\n campaign.advertising_channel_sub_type,\n campaign.app_campaign_setting.app_id,\n campaign.app_campaign_setting.app_store,\n campaign.app_campaign_setting.bidding_strategy_goal_type,\n segments.date,\n metrics.impressions,\n metrics.clicks\nFROM campaign\nWHERE\n campaign.advertising_channel_type = 'MULTI_CHANNEL'\n AND campaign.advertising_channel_sub_type IN\n ('APP_CAMPAIGN', 'APP_CAMPAIGN_FOR_ENGAGEMENT')\n AND segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n ad_group.name,\n campaign.app_campaign_setting.app_id,\n campaign.app_campaign_setting.bidding_strategy_goal_type,\n campaign.app_campaign_setting.app_store\nFROM ad_group\nWHERE\n campaign.advertising_channel_type = 'MULTI_CHANNEL'\n AND campaign.advertising_channel_sub_type = 'APP_CAMPAIGN'\nORDER BY ad_group.name ASC\nLIMIT 100\n```\n\nExample:\n```text\nSELECT\n campaign.app_campaign_setting.app_id,\n metrics.all_conversions,\n metrics.view_through_conversions\nFROM campaign\nWHERE campaign.app_campaign_setting.app_id LIKE 'com.google.android.apps.%'\nORDER BY metrics.all_conversions DESC\nLIMIT 10\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n metrics.conversions,\n segments.conversion_action\nFROM campaign\nWHERE campaign.advertising_channel_type = 'MULTI_CHANNEL'\n AND campaign.advertising_channel_sub_type = 'APP_CAMPAIGN'\n```\n\nExample:\n```text\nSELECT\n ad_group.name,\n ad_group_ad_asset_view.asset,\n asset.name,\n metrics.clicks,\n metrics.impressions\nFROM ad_group_ad_asset_view\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.378Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":72,"estimatedTokens":400}}162{"id":"doc-local_services_campaigns_google_ads_api_google_f-ef4e7f9f","source":"documentation","title":"Local Services campaigns | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/local-service-campaigns","text":"Example:\n```text\nSELECT\n campaign.id,\n campaign.status,\n campaign_budget.id,\n campaign_budget.period,\n campaign_budget.amount_micros,\n campaign_budget.type\nFROM campaign\nWHERE campaign.advertising_channel_type = 'LOCAL_SERVICES'\n```\n\nExample:\n```text\nSELECT campaign.id\nFROM campaign\nWHERE campaign.advertising_channel_type = 'LOCAL_SERVICES'\n```\n\nExample:\n```text\nSELECT\n local_services_lead.lead_type,\n local_services_lead.category_id,\n local_services_lead.service_id,\n local_services_lead.contact_details,\n local_services_lead.lead_status,\n local_services_lead.creation_date_time,\n local_services_lead.locale,\n local_services_lead.lead_charged,\n local_services_lead.credit_details.credit_state,\n local_services_lead.credit_details.credit_state_last_update_date_time\nFROM local_services_lead\n```\n\nExample:\n```text\nSELECT\n local_services_lead_conversation.id,\n local_services_lead_conversation.conversation_channel,\n local_services_lead_conversation.participant_type,\n local_services_lead_conversation.lead,\n local_services_lead_conversation.event_date_time,\n local_services_lead_conversation.phone_call_details.call_duration_millis,\n local_services_lead_conversation.phone_call_details.call_recording_url,\n local_services_lead_conversation.message_details.text,\n local_services_lead_conversation.message_details.attachment_urls\nFROM local_services_lead_conversation\nWHERE local_services_lead_conversation.conversation_channel = 'PHONE_CALL'\n```\n\nExample:\n```text\nSELECT\nlocal_services_lead_conversation.id,\nlocal_services_lead_conversation.event_date_time,\nlocal_services_lead_conversation.message_details.text\nFROM local_services_lead_conversation\nWHERE local_services_lead.id = LEAD_ID\n```\n\nExample:\n```text\nSELECT\n local_services_verification_artifact.id,\n local_services_verification_artifact.creation_date_time,\n local_services_verification_artifact.status,\n local_services_verification_artifact.artifact_type,\n local_services_verification_artifact.license_verification_artifact.license_type,\n local_services_verification_artifact.license_verification_artifact.license_number,\n local_services_verification_artifact.license_verification_artifact.licensee_first_name,\n local_services_verification_artifact.license_verification_artifact.licensee_last_name,\n local_services_verification_artifact.license_verification_artifact.rejection_reason\nFROM local_services_verification_artifact\nWHERE local_services_verification_artifact.artifact_type = 'LICENSE'\n```\n\nExample:\n```text\nSELECT\n customer.local_services_settings.granular_license_statuses,\n customer.local_services_settings.granular_insurance_statuses\nFROM customer\n```\n\nExample:\n```text\nSELECT\n local_services_employee.status,\n local_services_employee.type,\n local_services_employee.university_degrees,\n local_services_employee.residencies,\n local_services_employee.fellowships,\n local_services_employee.job_title,\n local_services_employee.year_started_practicing,\n local_services_employee.languages_spoken,\n local_services_employee.first_name,\n local_services_employee.middle_name,\n local_services_employee.last_name\nFROM local_services_employee\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.379Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":104,"estimatedTokens":793}}163{"id":"doc-send_a_mutate_request_google_ads_api_google_for_-d9b3f700","source":"documentation","title":"Send a mutate request | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/smart-campaigns/mutate-request","text":"Example:\n```text\n/**\n * Sends a mutate request with a group of mutate operations.\n *\n * <p>The {@link GoogleAdsServiceClient} allows batching together a list of operations. These are\n * executed sequentially, and later operations my refer to previous operations via temporary IDs.\n * For more detail on this, please refer to\n * https://developers.google.com/google-ads/api/docs/batch-processing/temporary-ids.\n */\nprivate void sendMutateRequest(\n GoogleAdsClient googleAdsClient, long customerId, List<MutateOperation> operations) {\n try (GoogleAdsServiceClient client =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n MutateGoogleAdsResponse outerResponse = client.mutate(String.valueOf(customerId), operations);\n for (MutateOperationResponse innerResponse :\n outerResponse.getMutateOperationResponsesList()) {\n OneofDescriptor oneofDescriptor =\n innerResponse.getDescriptorForType().getOneofs().stream()\n .filter(o -> o.getName().equals(\"response\"))\n .findFirst()\n .get();\n Message createdEntity =\n (Message)\n innerResponse.getField(innerResponse.getOneofFieldDescriptor(oneofDescriptor));\n String resourceName =\n (String)\n createdEntity.getField(\n createdEntity.getDescriptorForType().findFieldByName(\"resource_name\"));\n System.out.printf(\n \"Created a(n) %s with resource name: '%s'.%n\",\n createdEntity.getClass().getSimpleName(), resourceName);\n }\n }\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n// The below methods create and return MutateOperations that we later provide to\n// the GoogleAdsService.Mutate method in order to create the entities in a single\n// request. Since the entities for a Smart campaign are closely tied to one-another\n// it's considered a best practice to create them in a single Mutate request; the\n// entities will either all complete successfully or fail entirely, leaving no\n// orphaned entities. See:\n// https://developers.google.com/google-ads/api/docs/mutating/overview\nMutateOperation campaignBudgetOperation =\n CreateCampaignBudgetOperation(customerId, suggestedBudgetAmount);\nMutateOperation smartCampaignOperation =\n CreateSmartCampaignOperation(customerId);\nMutateOperation smartCampaignSettingOperation =\n CreateSmartCampaignSettingOperation(customerId, businessProfileLocation,\n businessName);\nIEnumerable<MutateOperation> campaignCriterionOperations =\n CreateCampaignCriterionOperations(customerId, keywordThemeInfos,\n suggestionInfo);\nMutateOperation adGroupOperation = CreateAdGroupOperation(customerId);\nMutateOperation adGroupAdOperation = CreateAdGroupAdOperation(customerId,\n adSuggestions);\n\n// Send the operations in a single mutate request.\nMutateGoogleAdsRequest mutateGoogleAdsRequest = new MutateGoogleAdsRequest\n{\n CustomerId = customerId.ToString()\n};\n// It's important to create these entities in this order because they depend on\n// each other, for example the SmartCampaignSetting and ad group depend on the\n// campaign, and the ad group ad depends on the ad group.\nmutateGoogleAdsRequest.MutateOperations.Add(campaignBudgetOperation);\nmutateGoogleAdsRequest.MutateOperations.Add(smartCampaignOperation);\nmutateGoogleAdsRequest.MutateOperations.Add(smartCampaignSettingOperation);\nmutateGoogleAdsRequest.MutateOperations.Add(campaignCriterionOperations);\nmutateGoogleAdsRequest.MutateOperations.Add(adGroupOperation);\nmutateGoogleAdsRequest.MutateOperations.Add(adGroupAdOperation);\n\nMutateGoogleAdsResponse response =\n googleAdsServiceClient.Mutate(mutateGoogleAdsRequest);\n\nPrintResponseDetails(response);AddSmartCampaign.cs\n```\n\nExample:\n```text\n// The below methods create and return MutateOperations that we later provide to the\n // GoogleAdsService.Mutate method in order to create the entities in a single request.\n // Since the entities for a Smart campaign are closely tied to one-another it's considered\n // a best practice to create them in a single Mutate request so they all complete\n // successfully or fail entirely, leaving no orphaned entities.\n // See: https://developers.google.com/google-ads/api/docs/mutating/overview.\n $campaignBudgetOperation = self::createCampaignBudgetOperation(\n $customerId,\n $suggestedBudgetAmount\n );\n $smartCampaignOperation = self::createSmartCampaignOperation($customerId);\n $smartCampaignSettingOperation = self::createSmartCampaignSettingOperation(\n $customerId,\n $businessProfileLocationResourceName,\n $businessName\n );\n $campaignCriterionOperations = self::createCampaignCriterionOperations(\n $customerId,\n $keywordThemeInfos,\n $suggestionInfo\n );\n $adGroupOperation = self::createAdGroupOperation($customerId);\n $adGroupAdOperation = self::createAdGroupAdOperation($customerId, $adSuggestions);\n\n // Issues a single mutate request to add the entities.\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n $response = $googleAdsServiceClient->mutate(MutateGoogleAdsRequest::build(\n $customerId,\n // It's important to create these entities in this order because they depend on\n // each other, for example the SmartCampaignSetting and ad group depend on the\n // campaign, and the ad group ad depends on the ad group.\n array_merge(\n [\n $campaignBudgetOperation,\n $smartCampaignOperation,\n $smartCampaignSettingOperation,\n ],\n $campaignCriterionOperations,\n [\n $adGroupOperation,\n $adGroupAdOperation\n ]\n )\n ));\n\n self::printResponseDetails($response);\n}AddSmartCampaign.php\n```\n\nExample:\n```text\n# The below methods create and return MutateOperations that we later\n# provide to the GoogleAdsService.Mutate method in order to create the\n# entities in a single request. Since the entities for a Smart campaign\n# are closely tied to one-another it's considered a best practice to\n# create them in a single Mutate request so they all complete successfully\n# or fail entirely, leaving no orphaned entities. See:\n# https://developers.google.com/google-ads/api/docs/mutating/overview\ncampaign_budget_operation: MutateOperation = (\n create_campaign_budget_operation(\n client, customer_id, suggested_budget_amount\n )\n)\nsmart_campaign_operation: MutateOperation = create_smart_campaign_operation(\n client, customer_id\n)\nsmart_campaign_setting_operation: MutateOperation = (\n create_smart_campaign_setting_operation(\n client, customer_id, business_profile_location, business_name\n )\n)\ncampaign_criterion_operations: List[MutateOperation] = (\n create_campaign_criterion_operations(\n client, customer_id, keyword_theme_infos, suggestion_info\n )\n)\nad_group_operation: MutateOperation = create_ad_group_operation(\n client, customer_id\n)\nad_group_ad_operation: MutateOperation = create_ad_group_ad_operation(\n client, customer_id, ad_suggestions\n)\n\ngoogleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n)\n\n# Send the operations into a single Mutate request.\nresponse: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id,\n mutate_operations=[\n # It's important to create these entities in this order because\n # they depend on each other, for example the SmartCampaignSetting\n # and ad group depend on the campaign, and the ad group ad depends\n # on the ad group.\n campaign_budget_operation,\n smart_campaign_operation,\n smart_campaign_setting_operation,\n # Expand the list of campaign criterion operations into the list of\n # other mutate operations\n *campaign_criterion_operations,\n ad_group_operation,\n ad_group_ad_operation,\n ],\n)\n\nprint_response_details(response)add_smart_campaign.py\n```\n\nExample:\n```text\n# The below methods create and return MutateOperations that we later\n# provide to the GoogleAdsService.Mutate method in order to create the\n# entities in a single request. Since the entities for a Smart campaign\n# are closely tied to one-another it's considered a best practice to\n# create them in a single Mutate request so they all complete successfully\n# or fail entirely, leaving no orphaned entities. See:\n# https://developers.google.com/google-ads/api/docs/mutating/overview\nmutate_operations = []\n\n# It's important to create these operations in this order because\n# they depend on each other, for example the SmartCampaignSetting\n# and ad group depend on the campaign, and the ad group ad depends\n# on the ad group.\nmutate_operations << create_campaign_budget_operation(\n client,\n customer_id,\n suggested_budget_amount,\n)\n\nmutate_operations << create_smart_campaign_operation(\n client,\n customer_id,\n)\n\nmutate_operations << create_smart_campaign_setting_operation(\n client,\n customer_id,\n business_profile_location,\n business_name,\n)\n\nmutate_operations += create_campaign_criterion_operations(\n client,\n customer_id,\n keyword_theme_infos,\n suggestion_info,\n)\n\nmutate_operations << create_ad_group_operation(client, customer_id)\n\nmutate_operations << create_ad_group_ad_operation(\n client,\n customer_id,\n ad_suggestions\n)\n\n# Sends the operations into a single Mutate request.\nresponse = client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: mutate_operations,\n)\n\nprint_response_details(response)add_smart_campaign.rb\n```\n\nExample:\n```text\n# The below methods create and return MutateOperations that we later provide to the\n# GoogleAdsService.Mutate method in order to create the entities in a single\n# request. Since the entities for a Smart campaign are closely tied to one-another\n# it's considered a best practice to create them in a single Mutate request; the\n# entities will either all complete successfully or fail entirely, leaving no\n# orphaned entities. See:\n# https://developers.google.com/google-ads/api/docs/mutating/overview\nmy $campaign_budget_operation =\n _create_campaign_budget_operation($customer_id, $suggested_budget_amount);\nmy $smart_campaign_operation = _create_smart_campaign_operation($customer_id);\nmy $smart_campaign_setting_operation =\n _create_smart_campaign_setting_operation($customer_id,\n $business_profile_location, $business_name);\nmy $campaign_criterion_operations =\n _create_campaign_criterion_operations($customer_id, $keyword_theme_infos,\n $suggestion_info);\nmy $ad_group_operation = _create_ad_group_operation($customer_id);\nmy $ad_group_ad_operation =\n _create_ad_group_ad_operation($customer_id, $ad_suggestions);\n\n# It's important to create these entities in this order because they depend on\n# each other. For example, the SmartCampaignSetting and ad group depend on the\n# campaign and the ad group ad depends on the ad group.\nmy $mutate_operations = [\n $campaign_budget_operation, $smart_campaign_operation,\n $smart_campaign_setting_operation,\n # Expand the list of campaign criterion operations into the list of\n # other mutate operations.\n @$campaign_criterion_operations,\n $ad_group_operation, $ad_group_ad_operation\n];\n\n# Send the operations in a single mutate request.\nmy $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $mutate_operations\n});\n\n_print_response_details($mutate_google_ads_response);add_smart_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.380Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":293,"estimatedTokens":2893}}164{"id":"doc-create_a_demand_gen_campaign_google_ads_api_goog-5637cc24","source":"documentation","title":"Create a Demand Gen Campaign | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/demand-gen/create-campaign","text":"Example:\n```text\n// The below methods create and return MutateOperations that we later provide to\n// the GoogleAdsService.Mutate method in order to create the entities in a single\n// request. Since the entities for a Demand Gen campaign are closely tied to one-another\n// it's considered a best practice to create them in a single Mutate request; the\n// entities will either all complete successfully or fail entirely, leaving no\n// orphaned entities. See:\n// https://developers.google.com/google-ads/api/docs/mutating/overview\nList<MutateOperation> operations = new ArrayList<>();\n// A utility to create temporary IDs for the resources.\nAtomicLong tempId = new AtomicLong(-1);\n\n// Creates a new campaign budget operation and adds it to the list of operations.\nString budgetResourceName = ResourceNames.campaignBudget(customerId, tempId.getAndDecrement());\noperations.add(\n MutateOperation.newBuilder()\n .setCampaignBudgetOperation(createCampaignBudgetOperation(budgetResourceName))\n .build());\n\n// Creates a new campaign operation and adds it to the list of operations.\nString campaignResourceName = ResourceNames.campaign(customerId, tempId.getAndDecrement());\noperations.add(\n MutateOperation.newBuilder()\n .setCampaignOperation(\n createDemandGenCampaignOperation(campaignResourceName, budgetResourceName))\n .build());\n\n// Creates a new ad group operation and adds it to the list of operations.\nString adGroupResourceName = ResourceNames.adGroup(customerId, tempId.getAndDecrement());\noperations.add(\n MutateOperation.newBuilder()\n .setAdGroupOperation(\n createDemandGenAdGroupOperation(adGroupResourceName, campaignResourceName))\n .build());\n\n// Creates the asset operations for the ad.\nMap<String, String> assetResourceNames = new HashMap<>();\noperations.addAll(\n createAssetOperations(customerId, youTubeVideoId, tempId, assetResourceNames));\n\n// Creates a new ad group ad operation and adds it to the list of operations.\noperations.add(\n MutateOperation.newBuilder()\n .setAdGroupAdOperation(\n createDemandGenAdGroupAdOperation(adGroupResourceName, assetResourceNames))\n .build());\n\n// Creates the service client.\ntry (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Sends the mutate request.\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.mutate(String.valueOf(customerId), operations);\n\n // Prints the results.\n System.out.printf(\n \"Created campaign with resource name: %s%n\",\n response\n .getMutateOperationResponses(1)\n .getCampaignResult()\n .getResourceName());\n System.out.printf(\n \"Created ad group with resource name: %s%n\",\n response\n .getMutateOperationResponses(2)\n .getAdGroupResult()\n .getResourceName());\n for (Map.Entry<String, String> entry : assetResourceNames.entrySet()) {\n System.out.printf(\n \"Created asset with temporary resource name '%s' and final resource name '%s'.%n\",\n entry.getValue(),\n response\n .getMutateOperationResponses(\n operations.indexOf(getOperationForAsset(operations, entry.getValue())))\n .getAssetResult()\n .getResourceName());\n }\n System.out.printf(\n \"Created ad group ad with resource name: %s%n\",\n response\n .getMutateOperationResponses(operations.size() - 1)\n .getAdGroupAdResult()\n .getResourceName());\n}AddDemandGenCampaign.java\n```\n\nExample:\n```text\n// The below methods create and return MutateOperations that we later provide to\n// the GoogleAdsService.Mutate method in order to create the entities in a single\n// request. Since the entities for a Demand Gen campaign are closely tied to one-another\n// it's considered a best practice to create them in a single Mutate request; the\n// entities will either all complete successfully or fail entirely, leaving no\n// orphaned entities. See:\n// https://developers.google.com/google-ads/api/docs/mutating/overview\nMutateOperation campaignBudgetOperation =\n CreateCampaignBudgetOperation(budgetResourceName);\nMutateOperation campaignOperation =\n CreateDemandGenCampaignOperation(campaignResourceName, budgetResourceName);\nMutateOperation adGroupOperation =\n CreateAdGroupOperation(adGroupResourceName, campaignResourceName);\n\n// Send the operations in a single mutate request.\nMutateGoogleAdsRequest mutateGoogleAdsRequest = new MutateGoogleAdsRequest\n{\n CustomerId = customerId.ToString()\n};\n// It's important to create these entities in this order because they depend on\n// each other, for example the ad group depends on the\n// campaign, and the ad group ad depends on the ad group.\nmutateGoogleAdsRequest.MutateOperations.Add(campaignBudgetOperation);\nmutateGoogleAdsRequest.MutateOperations.Add(campaignOperation);\nmutateGoogleAdsRequest.MutateOperations.Add(adGroupOperation);\n\nmutateGoogleAdsRequest.MutateOperations.AddRange(\n CreateAssetOperations(\n videoAssetResourceName,\n videoId,\n logoResourceName,\n client.Config\n )\n);\n\nmutateGoogleAdsRequest.MutateOperations.Add(\n CreateDemandGenAdOperation(\n adGroupResourceName,\n videoAssetResourceName,\n logoResourceName\n )\n);\n\nMutateGoogleAdsResponse response =\n googleAdsServiceClient.Mutate(mutateGoogleAdsRequest);AddDemandGenCampaign.cs\n```\n\nExample:\n```text\nThis example is not yet available in PHP; you can take a look at the other languages.\n```\n\nExample:\n```text\n# The below methods create and return MutateOperations that we later provide\n# to the GoogleAdsService.Mutate method in order to create the entities in a\n# single request. Since the entities for a Demand Gen campaign are closely\n# tied to one-another it's considered a best practice to create them in a\n# single Mutate request; the entities will either all complete successfully\n# or fail entirely, leaving no orphaned entities. See:\n# https://developers.google.com/google-ads/api/docs/mutating/overview\nmutate_operations: List[MutateOperation] = [\n # It's important to create these entities in this order because they\n # depend on each other, for example the ad group depends on the\n # campaign, and the ad group ad depends on the ad group.\n create_campaign_budget_operation(client, budget_resource_name),\n create_demand_gen_campaign_operation(\n client, campaign_resource_name, budget_resource_name\n ),\n create_ad_group_operation(\n client, ad_group_resource_name, campaign_resource_name\n ),\n *create_asset_operations( # Use iterable unpacking\n client,\n video_asset_resource_name,\n video_id,\n logo_asset_resource_name,\n ),\n create_demand_gen_ad_operation(\n client,\n ad_group_resource_name,\n video_asset_resource_name,\n logo_asset_resource_name,\n ),\n]\n\n# Send the operations in a single mutate request.\ngoogleads_service.mutate(\n customer_id=customer_id, mutate_operations=mutate_operations\n)add_demand_gen_campaign.py\n```\n\nExample:\n```text\noperations = []\n\noperations << client.operation.mutate do |m|\n m.campaign_budget_operation = create_campaign_budget_operation(client, budget_resource_name)\nend\n\noperations << client.operation.mutate do |m|\n m.campaign_operation = create_demand_gen_campaign_operation(client, campaign_resource_name, budget_resource_name)\nend\n\noperations << client.operation.mutate do |m|\n m.ad_group_operation = create_ad_group_operation(client, ad_group_resource_name, campaign_resource_name)\nend\n\noperations += create_asset_operations(client, video_asset_resource_name, video_id, logo_asset_resource_name).map do |asset_op|\n client.operation.mutate do |m|\n m.asset_operation = asset_op\n end\nend\n\noperations << client.operation.mutate do |m|\n m.ad_group_ad_operation = create_demand_gen_ad_operation(client, ad_group_resource_name, video_asset_resource_name, logo_asset_resource_name)\nend\n\nresponse = client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: operations,\n)add_demand_gen_campaign.rb\n```\n\nExample:\n```text\nThis example is not yet available in Perl; you can take a look at the other languages.\n```\n\nExample:\n```text\nprivate static String addCampaignBudget(GoogleAdsClient googleAdsClient, long customerId) {\n CampaignBudget budget =\n CampaignBudget.newBuilder()\n .setName(\"Interplanetary Cruise Budget #\" + getPrintableDateTime())\n .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)\n .setAmountMicros(500_000)\n .build();\n\n CampaignBudgetOperation op = CampaignBudgetOperation.newBuilder().setCreate(budget).build();\n\n try (CampaignBudgetServiceClient campaignBudgetServiceClient =\n googleAdsClient.getLatestVersion().createCampaignBudgetServiceClient()) {\n MutateCampaignBudgetsResponse response =\n campaignBudgetServiceClient.mutateCampaignBudgets(\n Long.toString(customerId), ImmutableList.of(op));\n String budgetResourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Added budget: %s%n\", budgetResourceName);\n return budgetResourceName;\n }\n}AddCampaigns.java\n```\n\nExample:\n```text\nprivate static string CreateBudget(GoogleAdsClient client, long customerId)\n{\n // Get the BudgetService.\n CampaignBudgetServiceClient budgetService = client.GetService(\n Services.V25.CampaignBudgetService);\n\n // Create the campaign budget.\n CampaignBudget budget = new CampaignBudget()\n {\n Name = \"Interplanetary Cruise Budget #\" + ExampleUtilities.GetRandomString(),\n DeliveryMethod = BudgetDeliveryMethod.Standard,\n AmountMicros = 500000\n };\n\n // Create the operation.\n CampaignBudgetOperation budgetOperation = new CampaignBudgetOperation()\n {\n Create = budget\n };\n\n // Create the campaign budget.\n MutateCampaignBudgetsResponse response = budgetService.MutateCampaignBudgets(\n customerId.ToString(), new CampaignBudgetOperation[] { budgetOperation });\n return response.Results[0].ResourceName;\n}AddCampaigns.cs\n```\n\nExample:\n```text\nprivate static function addCampaignBudget(GoogleAdsClient $googleAdsClient, int $customerId)\n{\n // Creates a campaign budget.\n $budget = new CampaignBudget([\n 'name' => 'Interplanetary Cruise Budget #' . Helper::getPrintableDatetime(),\n 'delivery_method' => BudgetDeliveryMethod::STANDARD,\n 'amount_micros' => 500000\n ]);\n\n // Creates a campaign budget operation.\n $campaignBudgetOperation = new CampaignBudgetOperation();\n $campaignBudgetOperation->setCreate($budget);\n\n // Issues a mutate request.\n $campaignBudgetServiceClient = $googleAdsClient->getCampaignBudgetServiceClient();\n $response = $campaignBudgetServiceClient->mutateCampaignBudgets(\n MutateCampaignBudgetsRequest::build($customerId, [$campaignBudgetOperation])\n );\n\n /** @var CampaignBudget $addedBudget */\n $addedBudget = $response->getResults()[0];\n printf(\"Added budget named '%s'%s\", $addedBudget->getResourceName(), PHP_EOL);\n\n return $addedBudget->getResourceName();\n}AddCampaigns.php\n```\n\nExample:\n```text\n# Create a budget, which can be shared by multiple campaigns.\ncampaign_budget_operation: CampaignBudgetOperation = client.get_type(\n \"CampaignBudgetOperation\"\n)\ncampaign_budget: CampaignBudget = campaign_budget_operation.create\ncampaign_budget.name = f\"Interplanetary Budget {uuid.uuid4()}\"\ncampaign_budget.delivery_method = (\n client.enums.BudgetDeliveryMethodEnum.STANDARD\n)\ncampaign_budget.amount_micros = 500000\n\n# Add budget.\ncampaign_budget_response: MutateCampaignBudgetsResponse\ntry:\n budget_operations: List[CampaignBudgetOperation] = [\n campaign_budget_operation\n ]\n campaign_budget_response = (\n campaign_budget_service.mutate_campaign_budgets(\n customer_id=customer_id,\n operations=budget_operations,\n )\n )\nexcept GoogleAdsException as ex:\n handle_googleads_exception(ex)add_campaigns.py\n```\n\nExample:\n```text\n# Create a budget, which can be shared by multiple campaigns.\ncampaign_budget = client.resource.campaign_budget do |cb|\n cb.name = \"Interplanetary Budget #{(Time.new.to_f * 1000).to_i}\"\n cb.delivery_method = :STANDARD\n cb.amount_micros = 500000\nend\n\noperation = client.operation.create_resource.campaign_budget(campaign_budget)\n\n# Add budget.\nreturn_budget = client.service.campaign_budget.mutate_campaign_budgets(\n customer_id: customer_id,\n operations: [operation],\n)add_campaigns.rb\n```\n\nExample:\n```text\n# Create a campaign budget, which can be shared by multiple campaigns.\nmy $campaign_budget =\n Google::Ads::GoogleAds::V25::Resources::CampaignBudget->new({\n name => \"Interplanetary budget #\" . uniqid(),\n deliveryMethod => STANDARD,\n amountMicros => 500000\n });\n\n# Create a campaign budget operation.\nmy $campaign_budget_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation\n ->new({create => $campaign_budget});\n\n# Add the campaign budget.\nmy $campaign_budgets_response = $api_client->CampaignBudgetService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_budget_operation]});add_campaigns.pl\n```\n\nExample:\n```text\n// Creates the campaign.\nCampaign campaign =\n Campaign.newBuilder()\n .setResourceName(campaignResourceName)\n .setName(\"Demand Gen campaign \" + System.currentTimeMillis())\n // Demand Gen campaigns are supported in the DEMAND_GEN channel.\n .setAdvertisingChannelType(AdvertisingChannelType.DEMAND_GEN)\n // Sets the campaign status to PAUSED. The campaign is enabled later.\n .setStatus(CampaignStatus.PAUSED)\n .setCampaignBudget(budgetResourceName)\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n // Sets the bidding strategy.\n .setTargetCpa(TargetCpa.newBuilder().setTargetCpaMicros(10_000_000L).build())\n .build();\n\n// Creates the operation.\nreturn CampaignOperation.newBuilder().setCreate(campaign).build();AddDemandGenCampaign.java\n```\n\nExample:\n```text\nprivate MutateOperation CreateDemandGenCampaignOperation(\n string campaignResourceName, string budgetResourceName)\n{\n return new MutateOperation\n {\n CampaignOperation = new CampaignOperation\n {\n Create = new Campaign\n {\n Name = $\"Demand Gen #{ExampleUtilities.GetRandomString()}\",\n // Set the campaign status as PAUSED.\n Status = CampaignStatus.Paused,\n\n // AdvertisingChannelType must be DEMAND_GEN.\n AdvertisingChannelType = AdvertisingChannelType.DemandGen,\n\n // Assign the resource name with a temporary ID.\n ResourceName = campaignResourceName,\n\n // Set the budget using the given budget resource name.\n CampaignBudget = budgetResourceName,\n\n // Use the Target CPA bidding strategy.\n TargetCpa = new TargetCpa()\n {\n TargetCpaMicros = 1_000_000,\n },\n\n ContainsEuPoliticalAdvertising =\n EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising\n }\n }\n };\n}AddDemandGenCampaign.cs\n```\n\nExample:\n```text\ndef create_demand_gen_campaign_operation(\n client: GoogleAdsClient,\n campaign_resource_name: str,\n budget_resource_name: str,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new Campaign.\n\n A temporary ID will be assigned to this campaign so that it can be\n referenced by other objects being created in the same Mutate request.\n\n Args:\n client: An initialized GoogleAdsClient instance.\n campaign_resource_name: The temporary resource name of the campaign.\n budget_resource_name: The resource name of the budget to assign.\n\n Returns:\n A MutateOperation for creating a Campaign.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_operation: CampaignOperation = mutate_operation.campaign_operation\n campaign: Campaign = campaign_operation.create\n campaign.name = f\"Demand Gen #{uuid4()}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in the\n # mutate request that should have its status set.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n # AdvertisingChannelType must be DEMAND_GEN.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.DEMAND_GEN\n )\n # Assign the resource name with a temporary ID.\n campaign.resource_name = campaign_resource_name\n # Set the budget using the given budget resource name.\n campaign.campaign_budget = budget_resource_name\n # Use the Target CPA bidding strategy.\n campaign.bidding_strategy_type = (\n client.enums.BiddingStrategyTypeEnum.TARGET_CPA\n )\n campaign.target_cpa.target_cpa_micros = 1_000_000\n return mutate_operationadd_demand_gen_campaign.py\n```\n\nExample:\n```text\ndef create_demand_gen_campaign_operation(client, campaign_resource_name, budget_resource_name)\n client.operation.create_resource.campaign do |c|\n c.name = \"Demand Gen ##{Time.now.to_f}\"\n\n # Recommendation: Set the campaign to PAUSED when creating it to\n # prevent the ads from immediately serving. Set to ENABLED once you've\n # added targeting and the ads are ready to serve.\n c.status = :PAUSED\n\n # AdvertisingChannelType must be DEMAND_GEN.\n c.advertising_channel_type = :DEMAND_GEN\n\n # Assign the resource name with a temporary ID.\n c.resource_name = campaign_resource_name\n\n # Set the budget using the given budget resource name.\n c.campaign_budget = budget_resource_name\n\n # Use the Target CPA bidding strategy.\n c.bidding_strategy_type = :TARGET_CPA\n c.target_cpa = client.resource.target_cpa do |tc|\n tc.target_cpa_micros = 1_000_000\n end\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n # Optional fields\n c.start_date_time = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d %H:%M:%S')\n c.end_date_time = DateTime.parse(Date.today.next_year.to_s).strftime('%Y%m%d %H:%M:%S')\n end\nendadd_demand_gen_campaign.rb\n```\n\nExample:\n```text\n// Creates the ad group.\nAdGroup adGroup =\n AdGroup.newBuilder()\n .setResourceName(adGroupResourceName)\n .setName(\"Demand Gen ad group \" + System.currentTimeMillis())\n .setCampaign(campaignResourceName)\n .setStatus(AdGroupStatus.ENABLED)\n // Selects the specific channels for the ad group.\n // For further information on Demand Gen channel controls, see\n // https://developers.google.com/google-ads/api/docs/demand-gen/channel-controls\n .setDemandGenAdGroupSettings(\n DemandGenAdGroupSettings.newBuilder()\n .setChannelControls(\n DemandGenChannelControls.newBuilder()\n .setSelectedChannels(DemandGenSelectedChannels.newBuilder()\n .setGmail(false)\n .setDiscover(false)\n .setDisplay(false)\n .setYoutubeInFeed(true)\n .setYoutubeInStream(true)\n .setYoutubeShorts(true)\n .build())\n .build())\n .build())\n .build();\n\n// Creates the operation.\nreturn AdGroupOperation.newBuilder().setCreate(adGroup).build();AddDemandGenCampaign.java\n```\n\nExample:\n```text\nprivate MutateOperation CreateAdGroupOperation(\n string adGroupResourceName,\n string campaignResourceName\n)\n{\n return new MutateOperation\n {\n AdGroupOperation = new AdGroupOperation\n {\n // Creates an ad group.\n Create = new AdGroup\n {\n ResourceName = adGroupResourceName,\n Name = $\"Earth to Mars Cruises #{ExampleUtilities.GetRandomString()}\",\n Status = AdGroupStatus.Enabled,\n Campaign = campaignResourceName,\n\n // Select the specific channels for the ad group.\n // For further information on Demand Gen channel controls, see\n // https://developers.google.com/google-ads/api/docs/demand-gen/channel-controls\n DemandGenAdGroupSettings = new DemandGenAdGroupSettings\n {\n ChannelControls = new DemandGenChannelControls\n {\n SelectedChannels = new DemandGenSelectedChannels\n {\n Gmail = false,\n Discover = false,\n Display = false,\n YoutubeInFeed = true,\n YoutubeInStream = true,\n YoutubeShorts = true,\n }\n }\n }\n }\n }\n };\n}AddDemandGenCampaign.cs\n```\n\nExample:\n```text\ndef create_ad_group_operation(\n client: GoogleAdsClient,\n ad_group_resource_name: str,\n campaign_resource_name: str,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new AdGroup.\n\n Args:\n client: An initialized GoogleAdsClient instance.\n ad_group_resource_name: The temporary resource name of the ad group.\n campaign_resource_name: The temporary resource name of the campaign the\n ad group will belong to.\n\n Returns:\n A MutateOperation for creating an AdGroup.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n ad_group_operation: AdGroupOperation = mutate_operation.ad_group_operation\n # Creates an ad group.\n ad_group: AdGroup = ad_group_operation.create\n ad_group.resource_name = ad_group_resource_name\n ad_group.name = f\"Earth to Mars Cruises #{uuid4()}\"\n ad_group.status = client.enums.AdGroupStatusEnum.ENABLED\n ad_group.campaign = campaign_resource_name\n\n # Select the specific channels for the ad group. For further information on\n # Demand Gen channel controls, see:\n # https://developers.google.com/google-ads/api/docs/demand-gen/channel-controls\n selected_channel_controls = (\n ad_group.demand_gen_ad_group_settings.channel_controls.selected_channels\n )\n selected_channel_controls.gmail = False\n selected_channel_controls.discover = False\n selected_channel_controls.display = False\n selected_channel_controls.youtube_in_feed = True\n selected_channel_controls.youtube_in_stream = True\n selected_channel_controls.youtube_shorts = True\n\n return mutate_operationadd_demand_gen_campaign.py\n```\n\nExample:\n```text\ndef create_ad_group_operation(client, ad_group_resource_name, campaign_resource_name)\n # Creates an ad group.\n client.operation.create_resource.ad_group do |ag|\n ag.resource_name = ad_group_resource_name\n ag.name = \"Earth to Mars Cruises ##{Time.now.to_f}\"\n ag.status = :ENABLED\n ag.campaign = campaign_resource_name\n\n # Select the specific channels for the ad group.\n # For further information on Demand Gen channel controls, see\n # https://developers.google.com/google-ads/api/docs/demand-gen/channel-controls\n ag.demand_gen_ad_group_settings = client.resource.demand_gen_ad_group_settings do |dgas|\n dgas.channel_controls = client.resource.demand_gen_channel_controls do |dcc|\n dcc.selected_channels = client.resource.demand_gen_selected_channels do |dsc|\n dsc.gmail = false\n dsc.discover = false\n dsc.display = false\n dsc.youtube_in_feed = true\n dsc.youtube_in_stream = true\n dsc.youtube_shorts = true\n end\n end\n end\n end\nendadd_demand_gen_campaign.rb\n```\n\nExample:\n```text\nDemandGenVideoResponsiveAdInfo.Builder videoResponsiveAdbuilder =\n DemandGenVideoResponsiveAdInfo.newBuilder()\n .setBusinessName(\n AdTextAsset.newBuilder()\n .setText(\"Interplanetary Cruises\")\n .build()\n )\n .addVideos(\n AdVideoAsset.newBuilder()\n .setAsset(assetResourceNames.get(\"Video\"))\n .build()\n )\n .addAllLongHeadlines(\n Arrays.asList(\"Long headline 1\").stream()\n .map(s -> AdTextAsset.newBuilder().setText(s).build())\n .collect(Collectors.toList())\n )\n .addAllHeadlines(\n Arrays.asList(\"Headline 1\", \"Headline 2\", \"Headline 3\").stream()\n .map(s -> AdTextAsset.newBuilder().setText(s).build())\n .collect(Collectors.toList()))\n .addAllDescriptions(\n Arrays.asList(\"Description 1\", \"Description 2\").stream()\n .map(s -> AdTextAsset.newBuilder().setText(s).build())\n .collect(Collectors.toList()))\n .addLogoImages(AdImageAsset.newBuilder().setAsset(assetResourceNames.get(\"LogoImage\")));\n\nAdGroupAd adGroupAd =\n AdGroupAd.newBuilder()\n .setAdGroup(adGroupResourceName)\n .setAd(\n Ad.newBuilder()\n .setName(\"Demand gen video responsive ad\")\n .addFinalUrls(\"https://www.example.com\")\n .setDemandGenVideoResponsiveAd(videoResponsiveAdbuilder.build())\n .build())\n .build();\n\nreturn AdGroupAdOperation.newBuilder().setCreate(adGroupAd).build();AddDemandGenCampaign.java\n```\n\nExample:\n```text\nprivate MutateOperation CreateDemandGenAdOperation(\n string adGroupResourceName,\n string videoAssetResourceName,\n string logoResourceName\n)\n{\n\n Ad ad = new Ad\n {\n Name = \"Demand gen video responsive ad\",\n FinalUrls = { \"http://example.com\" },\n DemandGenVideoResponsiveAd = new DemandGenVideoResponsiveAdInfo\n {\n BusinessName = new AdTextAsset\n {\n Text = \"Interplanetary Cruises\"\n },\n\n }\n };\n\n ad.DemandGenVideoResponsiveAd.Videos.Add(new AdVideoAsset\n {\n Asset = videoAssetResourceName\n });\n\n ad.DemandGenVideoResponsiveAd.LogoImages.Add(new AdImageAsset\n {\n Asset = logoResourceName\n });\n\n ad.DemandGenVideoResponsiveAd.Headlines.Add(new AdTextAsset\n {\n Text = \"Interplanetary cruises\"\n });\n\n ad.DemandGenVideoResponsiveAd.LongHeadlines.Add(new AdTextAsset\n {\n Text = \"Travel the World\"\n });\n\n ad.DemandGenVideoResponsiveAd.Descriptions.Add(new AdTextAsset\n {\n Text = \"Book now for an extra discount\"\n });\n\n\n return new MutateOperation\n {\n AdGroupAdOperation = new AdGroupAdOperation\n {\n Create = new AdGroupAd\n {\n AdGroup = adGroupResourceName,\n Ad = ad\n }\n }\n };\n}AddDemandGenCampaign.cs\n```\n\nExample:\n```text\ndef create_demand_gen_ad_operation(\n client: GoogleAdsClient,\n ad_group_resource_name: str,\n video_asset_resource_name: str,\n logo_asset_resource_name: str,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new Demand Gen Ad.\n\n Args:\n client: An initialized GoogleAdsClient instance.\n ad_group_resource_name: The ad group the ad will belong to.\n video_asset_resource_name: The video asset resource name.\n logo_asset_resource_name: The logo asset resource name.\n\n Returns:\n A MutateOperation for creating an AdGroupAd.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n ad_group_ad_operation: AdGroupAdOperation = (\n mutate_operation.ad_group_ad_operation\n )\n ad_group_ad: AdGroupAd = ad_group_ad_operation.create\n ad_group_ad.ad_group = ad_group_resource_name\n ad_group_ad.status = client.enums.AdGroupAdStatusEnum.ENABLED\n\n ad: Ad = ad_group_ad.ad\n ad.name = \"Demand gen multi asset ad\"\n ad.final_urls.append(DEFAULT_FINAL_URL)\n\n demand_gen_ad: DemandGenVideoResponsiveAdInfo = (\n ad.demand_gen_video_responsive_ad\n )\n # Ensure business_name is an AssetLink and assign text to its text_asset.text\n demand_gen_ad.business_name.text = \"Interplanetary Cruises\"\n # If it needs to be an AssetLink to a text asset,\n # that would require creating another asset.\n\n # Create AssetLink for video\n video_asset_link: AdVideoAsset = client.get_type(\"AdVideoAsset\")\n video_asset_link.asset = video_asset_resource_name\n demand_gen_ad.videos.append(video_asset_link)\n\n # Create AssetLink for logo\n logo_image_asset_link: AdImageAsset = client.get_type(\"AdImageAsset\")\n logo_image_asset_link.asset = logo_asset_resource_name\n demand_gen_ad.logo_images.append(logo_image_asset_link)\n\n # Create AssetLink for headline\n headline_asset_link: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline_asset_link.text = \"Interplanetary cruises\"\n demand_gen_ad.headlines.append(headline_asset_link)\n\n # Create AssetLink for long headline\n long_headline_asset_link: AdTextAsset = client.get_type(\"AdTextAsset\")\n long_headline_asset_link.text = \"Travel the World\"\n demand_gen_ad.long_headlines.append(long_headline_asset_link)\n\n # Create AssetLink for description\n description_asset_link: AdTextAsset = client.get_type(\"AdTextAsset\")\n description_asset_link.text = \"Book now for an extra discount\"\n demand_gen_ad.descriptions.append(description_asset_link)\n\n return mutate_operationadd_demand_gen_campaign.py\n```\n\nExample:\n```text\ndef create_demand_gen_ad_operation(client, ad_group_resource_name, video_asset_resource_name, logo_asset_resource_name)\n client.operation.create_resource.ad_group_ad do |aga|\n aga.ad_group = ad_group_resource_name\n aga.status = :ENABLED\n aga.ad = client.resource.ad do |ad|\n ad.name = \"Demand gen video responsive ad\"\n ad.final_urls << DEFAULT_FINAL_URL\n ad.demand_gen_video_responsive_ad = client.resource.demand_gen_video_responsive_ad_info do |dgv|\n dgv.business_name = client.resource.ad_text_asset do |ata|\n ata.text = \"Interplanetary Cruises\"\n end\n dgv.videos << client.resource.ad_video_asset do |ava|\n ava.asset = video_asset_resource_name\n end\n dgv.logo_images << client.resource.ad_image_asset do |aia|\n aia.asset = logo_asset_resource_name\n end\n dgv.headlines << client.resource.ad_text_asset do |ata|\n ata.text = \"Interplanetary cruises\"\n end\n dgv.long_headlines << client.resource.ad_text_asset do |ata|\n ata.text = \"Travel the World\"\n end\n dgv.descriptions << client.resource.ad_text_asset do |ata|\n ata.text = \"Book now for an extra discount\"\n end\n end\n end\n end\nendadd_demand_gen_campaign.rb\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.383Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":24,"totalLines":864,"estimatedTokens":7798}}165{"id":"doc-get_keyword_theme_budget_and_ad_text_asset_sugge-ee3aee35","source":"documentation","title":"Get keyword theme, budget, and ad text asset suggestions | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/smart-campaigns/get-suggestions","text":"Example:\n```text\nprivate SmartCampaignSuggestionInfo getSmartCampaignSuggestionInfo(\n GoogleAdsClient googleAdsClient, String businessProfileLocation, String businessName) {\n SmartCampaignSuggestionInfo.Builder suggestionInfoBuilder =\n SmartCampaignSuggestionInfo.newBuilder()\n // Adds the URL of the campaign's landing page.\n .setFinalUrl(LANDING_PAGE_URL)\n // Adds the language code for the campaign.\n .setLanguageCode(LANGUAGE_CODE)\n // Constructs location information using the given geo target constant. It's also\n // possible to provide a geographic proximity using the \"proximity\" field,\n // for example:\n // .setProximity(\n // ProximityInfo.newBuilder()\n // .setAddress(\n // AddressInfo.newBuilder()\n // .setPostalCode(INSERT_POSTAL_CODE)\n // .setProvinceCode(INSERT_PROVINCE_CODE)\n // .setCountryCode(INSERT_COUNTRY_CODE)\n // .setProvinceName(INSERT_PROVINCE_NAME)\n // .setStreetAddress(INSERT_STREET_ADDRESS)\n // .setStreetAddress2(INSERT_STREET_ADDRESS_2)\n // .setCityName(INSERT_CITY_NAME)\n // .build())\n // .setRadius(INSERT_RADIUS)\n // .setRadiusUnits(INSERT_RADIUS_UNITS)\n // .build())\n // For more information on proximities see:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo\n //\n // Adds LocationInfo objects to the list of locations. You have the option of\n // providing multiple locations when using location-based suggestions.\n .setLocationList(\n LocationList.newBuilder()\n // Sets one location to the resource name of the given geo target constant.\n .addLocations(\n LocationInfo.newBuilder()\n .setGeoTargetConstant(\n ResourceNames.geoTargetConstant(GEO_TARGET_CONSTANT))\n .build())\n .build())\n // Adds a schedule detailing which days of the week the business is open.\n // This schedule describes a schedule in which the business is open on\n // Mondays from 9am to 5pm.\n .addAdSchedules(\n AdScheduleInfo.newBuilder()\n // Sets the day of this schedule as Monday.\n .setDayOfWeek(DayOfWeek.MONDAY)\n // Sets the start hour to 9am.\n .setStartHour(9)\n // Sets the end hour to 5pm.\n .setEndHour(17)\n // Sets the start and end minute of zero, for example: 9:00 and 5:00.\n .setStartMinute(MinuteOfHour.ZERO)\n .setEndMinute(MinuteOfHour.ZERO)\n .build());\n\n // Sets either of the business_profile_location or business_name, depending on whichever is\n // provided.\n if (businessProfileLocation != null) {\n suggestionInfoBuilder.setBusinessProfileLocation(businessProfileLocation);\n } else {\n suggestionInfoBuilder.setBusinessContext(\n BusinessContext.newBuilder().setBusinessName(businessName).build());\n }\n return suggestionInfoBuilder.build();\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Builds a SmartCampaignSuggestionInfo object with business details.\n///\n/// The details are used by the SmartCampaignSuggestService to suggest a\n/// budget amount as well as creatives for the ad.\n///\n/// Note that when retrieving ad creative suggestions it's required that the\n/// \"final_url\", \"language_code\" and \"keyword_themes\" fields are set on the\n/// SmartCampaignSuggestionInfo instance.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"businessProfileLocation\">The identifier of a Business Profile location.\n/// </param>\n/// <param name=\"businessName\">The name of a Business Profile.</param>\n/// <returns>A SmartCampaignSuggestionInfo instance .</returns>\nprivate SmartCampaignSuggestionInfo GetSmartCampaignSuggestionInfo(GoogleAdsClient client,\n string businessProfileLocation, string businessName)\n{\n // Note: This is broken since businessLocationId is not yet renamed in\n // SmartCampaignSuggestionInfo. The use of dynamic temporarily fixes the broken build.\n // TODO(Anash): Revert the type change once this field is fixed.\n dynamic suggestionInfo = new SmartCampaignSuggestionInfo\n {\n // Add the URL of the campaign's landing page.\n FinalUrl = LANDING_PAGE_URL,\n LanguageCode = LANGUAGE_CODE,\n // Construct location information using the given geo target constant. It's\n // also possible to provide a geographic proximity using the \"proximity\"\n // field on suggestion_info, for example:\n // Proximity = new ProximityInfo\n // {\n // Address = new AddressInfo\n // {\n // PostalCode = \"INSERT_POSTAL_CODE\",\n // ProvinceCode = \"INSERT_PROVINCE_CODE\",\n // CountryCode = \"INSERT_COUNTRY_CODE\",\n // ProvinceName = \"INSERT_PROVINCE_NAME\",\n // StreetAddress = \"INSERT_STREET_ADDRESS\",\n // StreetAddress2 = \"INSERT_STREET_ADDRESS_2\",\n // CityName = \"INSERT_CITY_NAME\"\n // },\n // Radius = Double.Parse(\"INSERT_RADIUS\"),\n // RadiusUnits = ProximityRadiusUnits.Kilometers\n // }\n // For more information on proximities see:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo\n LocationList = new LocationList()\n {\n Locations =\n {\n new LocationInfo\n {\n // Set the location to the resource name of the given geo target\n // constant.\n GeoTargetConstant =\n ResourceNames.GeoTargetConstant(GEO_TARGET_CONSTANT)\n }\n }\n }\n };\n\n // Add the Business Profile location if provided.\n if (!string.IsNullOrEmpty(businessProfileLocation))\n {\n suggestionInfo.BusinessProfileLocation = businessProfileLocation;\n }\n else\n {\n suggestionInfo.BusinessContext = new BusinessContext\n {\n BusinessName = businessName,\n };\n }\n\n // Add a schedule detailing which days of the week the business is open. This schedule\n // describes a business that is open on Mondays from 9:00 AM to 5:00 PM.\n AdScheduleInfo adScheduleInfo = new AdScheduleInfo\n {\n // Set the day of this schedule as Monday.\n DayOfWeek = DayOfWeekEnum.Types.DayOfWeek.Monday,\n // Set the start hour to 9 AM.\n StartHour = 9,\n // Set the end hour to 5 PM.\n EndHour = 17,\n // Set the start and end minutes to zero.\n StartMinute = MinuteOfHourEnum.Types.MinuteOfHour.Zero,\n EndMinute = MinuteOfHourEnum.Types.MinuteOfHour.Zero\n };\n\n suggestionInfo.AdSchedules.Add(adScheduleInfo);\n\n return suggestionInfo;\n}AddSmartCampaign.cs\n```\n\nExample:\n```text\nprivate static function getSmartCampaignSuggestionInfo(\n ?string $businessProfileLocationResourceName,\n ?string $businessName\n): SmartCampaignSuggestionInfo {\n $suggestionInfo = new SmartCampaignSuggestionInfo([\n // Adds the URL of the campaign's landing page.\n 'final_url' => self::LANDING_PAGE_URL,\n\n // Adds the language code for the campaign.\n 'language_code' => self::LANGUAGE_CODE,\n\n // Constructs location information using the given geo target constant. It's also\n // possible to provide a geographic proximity using the \"proximity\" field,\n // for example:\n //\n // 'proximity' => new ProximityInfo([\n // 'address' => mew AddressInfo([\n // 'post_code' => INSERT_POSTAL_CODE,\n // 'province_code' => INSERT_PROVINCE_CODE,\n // 'country_code' => INSERT_COUNTRY_CODE,\n // 'province_name' => INSERT_PROVINCE_NAME,\n // 'street_address' => INSERT_STREET_ADDRESS,\n // 'street_address2' => INSERT_STREET_ADDRESS_2,\n // 'city_name' => INSERT_CITY_NAME\n // ]),\n // 'radius' => INSERT_RADIUS,\n // 'radius_units' => INSERT_RADIUS_UNITS\n // ])\n //\n // For more information on proximities see:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo\n\n // Adds LocationInfo objects to the list of locations. You have the option of\n // providing multiple locations when using location-based suggestions.\n 'location_list' => new LocationList([\n // Sets one location to the resource name of the given geo target constant.\n 'locations' => [new LocationInfo([\n 'geo_target_constant' => ResourceNames::forGeoTargetConstant(\n self::GEO_TARGET_CONSTANT\n )\n ])]\n ]),\n\n // Adds a schedule detailing which days of the week the business is open.\n // This schedule describes a schedule in which the business is open on\n // Mondays from 9am to 5pm.\n 'ad_schedules' => [new AdScheduleInfo([\n // Sets the day of this schedule as Monday.\n 'day_of_week' => DayOfWeek::MONDAY,\n // Sets the start hour to 9am.\n 'start_hour' => 9,\n // Sets the end hour to 5pm.\n 'end_hour' => 17,\n // Sets the start and end minute of zero, for example: 9:00 and 5:00.\n 'start_minute' => MinuteOfHour::ZERO,\n 'end_minute' => MinuteOfHour::ZERO\n ])]\n ]);\n\n // Sets either of the business_profile_location or business_name, depending on whichever is\n // provided.\n if ($businessProfileLocationResourceName) {\n $suggestionInfo->setBusinessProfileLocation($businessProfileLocationResourceName);\n } else {\n $suggestionInfo->setBusinessContext(new BusinessContext([\n 'business_name' => $businessName\n ]));\n }\n return $suggestionInfo;\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef get_smart_campaign_suggestion_info(\n client: GoogleAdsClient,\n business_profile_location: Optional[str],\n business_name: Optional[str],\n) -> SmartCampaignSuggestionInfo:\n \"\"\"Builds a SmartCampaignSuggestionInfo object with business details.\n\n The details are used by the SmartCampaignSuggestService to suggest a\n budget amount as well as creatives for the ad.\n\n Note that when retrieving ad creative suggestions it's required that the\n \"final_url\", \"language_code\" and \"keyword_themes\" fields are set on the\n SmartCampaignSuggestionInfo instance.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n business_profile_location: the resource name of a Business Profile\n location.\n business_name: the name of a Business Profile.\n\n Returns:\n A SmartCampaignSuggestionInfo instance.\n \"\"\"\n suggestion_info: SmartCampaignSuggestionInfo = client.get_type(\n \"SmartCampaignSuggestionInfo\"\n )\n\n # Add the URL of the campaign's landing page.\n suggestion_info.final_url = _LANDING_PAGE_URL\n\n # Add the language code for the campaign.\n suggestion_info.language_code = _LANGUAGE_CODE\n\n # Construct location information using the given geo target constant. It's\n # also possible to provide a geographic proximity using the \"proximity\"\n # field on suggestion_info, for example:\n #\n # suggestion_info.proximity.address.post_code = INSERT_POSTAL_CODE\n # suggestion_info.proximity.address.province_code = INSERT_PROVINCE_CODE\n # suggestion_info.proximity.address.country_code = INSERT_COUNTRY_CODE\n # suggestion_info.proximity.address.province_name = INSERT_PROVINCE_NAME\n # suggestion_info.proximity.address.street_address = INSERT_STREET_ADDRESS\n # suggestion_info.proximity.address.street_address2 = INSERT_STREET_ADDRESS_2\n # suggestion_info.proximity.address.city_name = INSERT_CITY_NAME\n # suggestion_info.proximity.radius = INSERT_RADIUS\n # suggestion_info.proximity.radius_units = RADIUS_UNITS\n #\n # For more information on proximities see:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo\n location: LocationInfo = client.get_type(\"LocationInfo\")\n # Set the location to the resource name of the given geo target constant.\n location.geo_target_constant = client.get_service(\n \"GeoTargetConstantService\"\n ).geo_target_constant_path(_GEO_TARGET_CONSTANT)\n # Add the LocationInfo object to the list of locations on the\n # suggestion_info object. You have the option of providing multiple\n # locations when using location-based suggestions.\n suggestion_info.location_list.locations.append(location)\n\n # Set either of the business_profile_location or business_name, depending on\n # whichever is provided.\n if business_profile_location:\n suggestion_info.business_profile_location = business_profile_location\n else:\n suggestion_info.business_context.business_name = business_name\n\n # Add a schedule detailing which days of the week the business is open.\n # This schedule describes a schedule in which the business is open on\n # Mondays from 9am to 5pm.\n ad_schedule_info: AdScheduleInfo = client.get_type(\"AdScheduleInfo\")\n # Set the day of this schedule as Monday.\n ad_schedule_info.day_of_week = client.enums.DayOfWeekEnum.MONDAY\n # Set the start hour to 9am.\n ad_schedule_info.start_hour = 9\n # Set the end hour to 5pm.\n ad_schedule_info.end_hour = 17\n # Set the start and end minute of zero, for example: 9:00 and 5:00.\n zero_minute_of_hour: MinuteOfHourEnum.MinuteOfHour = (\n client.enums.MinuteOfHourEnum.ZERO\n )\n ad_schedule_info.start_minute = zero_minute_of_hour\n ad_schedule_info.end_minute = zero_minute_of_hour\n suggestion_info.ad_schedules.append(ad_schedule_info)\n\n return suggestion_infoadd_smart_campaign.py\n```\n\nExample:\n```text\n# Builds a SmartCampaignSuggestionInfo object with business details.\n#\n# The details are used by the SmartCampaignSuggestService to suggest a\n# budget amount as well as creatives for the ad.\n#\n# Note that when retrieving ad creative suggestions it's required that the\n# \"final_url\", \"language_code\" and \"keyword_themes\" fields are set on the\n# SmartCampaignSuggestionInfo instance.\ndef get_smart_campaign_suggestion_info(\n client,\n business_profile_location,\n business_name)\n\n # Since these suggestions are for a new campaign, we're going to\n # use the suggestion_info field instead.\n suggestion_info = client.resource.smart_campaign_suggestion_info do |si|\n # Adds the URL of the campaign's landing page.\n si.final_url = LANDING_PAGE_URL\n # Add the language code for the campaign.\n si.language_code = LANGUAGE_CODE\n # Constructs location information using the given geo target constant. It's\n # also possible to provide a geographic proximity using the \"proximity\"\n # field on suggestion_info, for example:\n # si.proximity = client.resource.proximity_info do |proximity|\n # proximity.address = client.resource.address_info do |address|\n # address.post_code = INSERT_POSTAL_CODE\n # address.province_code = INSERT_PROVINCE_CODE\n # address.country_code = INSERT_COUNTRY_CODE\n # address.province_name = INSERT_PROVINCE_NAME\n # address.street_address = INSERT_STREET_ADDRESS\n # address.street_address2 = INSERT_STREET_ADDRESS_2\n # address.city_name = INSERT_CITY_NAME\n # end\n # proximity.radius = INSERT_RADIUS\n # proximity.radius_units = :INSERT_RADIUS_UNIT_ENUM\n # end\n #\n # For more information on proximities see:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo\n si.location_list = client.resource.location_list do |loc_list|\n # Adds the location_info object to the list of locations on the\n # suggestion_info object. You have the option of providing multiple\n # locations when using location-based suggestions.\n loc_list.locations << client.resource.location_info do |li|\n li.geo_target_constant = client.path.geo_target_constant(GEO_TARGET_CONSTANT)\n end\n end\n # Set either of the business_profile_location or business_name, depending on\n # whichever is provided.\n if business_profile_location\n si.business_profile_location = business_profile_location\n else\n si.business_context = client.resource.business_context do |bc|\n bc.business_name = business_name\n end\n end\n # Adds a schedule detailing which days of the week the business is open.\n # This schedule describes a schedule in which the business is open on\n # Mondays from 9am to 5pm.\n si.ad_schedules += [\n client.resource.ad_schedule_info do |as|\n # Sets the day of this schedule as Monday.\n as.day_of_week = :MONDAY\n # Sets the start hour to 9:00am.\n as.start_hour = 9\n as.start_minute = :ZERO\n # Sets the end hour to 5:00pm.\n as.end_hour = 17\n as.end_minute = :ZERO\n end\n ]\n end\n\n suggestion_info\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Builds a SmartCampaignSuggestionInfo object with business details.\n# The details are used by the SmartCampaignSuggestService to suggest a budget\n# amount as well as creatives for the ad.\n# Note that when retrieving ad creative suggestions you must set the\n# \"final_url\", \"language_code\" and \"keyword_themes\" fields on the\n# SmartCampaignSuggestionInfo instance.\nsub _get_smart_campaign_suggestion_info {\n my ($business_profile_location, $business_name) = @_;\n\n my $suggestion_info =\n Google::Ads::GoogleAds::V25::Services::SmartCampaignSuggestService::SmartCampaignSuggestionInfo\n ->new({\n # Add the URL of the campaign's landing page.\n finalUrl => LANDING_PAGE_URL,\n # Add the language code for the campaign.\n languageCode => LANGUAGE_CODE,\n # Construct location information using the given geo target constant.\n # It's also possible to provide a geographic proximity using the\n # \"proximity\" field on suggestion_info, for example:\n #\n # proximity => Google::Ads::GoogleAds::V25::Common::ProximityInfo->new({\n # address => Google::Ads::GoogleAds::V25::Common::AddressInfo->new({\n # postalCode => \"INSERT_POSTAL_CODE\",\n # provinceCode => \"INSERT_PROVINCE_CODE\",\n # countryCode => \"INSERT_COUNTRY_CODE\",\n # provinceName => \"INSERT_PROVINCE_NAME\",\n # streetAddress => \"INSERT_STREET_ADDRESS\",\n # streetAddress2 => \"INSERT_STREET_ADDRESS_2\",\n # cityName => \"INSERT_CITY_NAME\"\n # }\n # ),\n # radius => \"INSERT_RADIUS\",\n # radiusUnits => MILES\n # }\n # ),\n #\n # For more information on proximities see:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/ProximityInfo\n locationList =>\n Google::Ads::GoogleAds::V25::Services::SmartCampaignSuggestService::LocationList\n ->new(\n )});\n\n # Add the LocationInfo object to the list of locations on the SuggestionInfo\n # object. You have the option of providing multiple locations when using\n # location-based suggestions.\n push @{$suggestion_info->{locationList}{locations}},\n Google::Ads::GoogleAds::V25::Common::LocationInfo->new({\n # Set the location to the resource name of the given geo target constant.\n geoTargetConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n GEO_TARGET_CONSTANT)});\n\n # Set one of the business_profile_location or business_name, whichever is provided.\n if (defined $business_profile_location) {\n $suggestion_info->{businessProfileLocation} =\n _convert_business_profile_location($business_profile_location);\n } else {\n $suggestion_info->{businessContext} =\n Google::Ads::GoogleAds::V25::Services::SmartCampaignSuggestService::BusinessContext\n ->new({\n businessName => $business_name\n });\n }\n\n # Add a schedule detailing which days of the week the business is open. This\n # example schedule describes a business that is open on Mondays from 9:00 AM\n # to 5:00 PM.\n push @{$suggestion_info->{adSchedules}},\n Google::Ads::GoogleAds::V25::Common::AdScheduleInfo->new({\n # Set the day of this schedule as Monday.\n dayOfWeek => MONDAY,\n # Set the start hour to 9 AM.\n startHour => 9,\n # Set the end hour to 5 PM.\n endHour => 17,\n # Set the start and end minutes to zero.\n startMinute => ZERO,\n endMinute => ZERO\n });\n\n return $suggestion_info;\n}add_smart_campaign.pl\n```\n\nExample:\n```text\nprivate List<KeywordTheme> getKeywordThemeSuggestions(\n GoogleAdsClient googleAdsClient,\n long customerId,\n SmartCampaignSuggestionInfo suggestionInfo) {\n // Creates the service client.\n try (SmartCampaignSuggestServiceClient client =\n googleAdsClient.getLatestVersion().createSmartCampaignSuggestServiceClient()) {\n // Sends the request.\n SuggestKeywordThemesResponse response =\n client.suggestKeywordThemes(\n SuggestKeywordThemesRequest.newBuilder()\n .setSuggestionInfo(suggestionInfo)\n .setCustomerId(String.valueOf(customerId))\n .build());\n // Prints some information about the result.\n System.out.printf(\n \"Retrieved %d keyword theme suggestions from the SuggestKeywordThemes method.%n\",\n response.getKeywordThemesCount());\n return new ArrayList(response.getKeywordThemesList());\n }\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Retrieves KeywordThemeConstants suggestions with the SmartCampaignSuggestService.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <param name=\"suggestionInfo\">The suggestion information.</param>\n/// <returns>The suggestions.</returns>\nprivate List<KeywordThemeConstant> GetKeywordThemeSuggestions(\n GoogleAdsClient client, long customerId, SmartCampaignSuggestionInfo suggestionInfo)\n{\n SmartCampaignSuggestServiceClient smartCampaignSuggestService =\n client.GetService(Services.V25.SmartCampaignSuggestService);\n\n SuggestKeywordThemesRequest request = new SuggestKeywordThemesRequest()\n {\n SuggestionInfo = suggestionInfo,\n CustomerId = customerId.ToString()\n };\n\n SuggestKeywordThemesResponse response =\n smartCampaignSuggestService.SuggestKeywordThemes(request);\n\n // Prints some information about the result.\n Console.WriteLine($\"Retrieved {response.KeywordThemes.Count} keyword theme \" +\n $\"constant suggestions from the SuggestKeywordThemes method.\");\n return response.KeywordThemes.ToList().ConvertAll(x => x.KeywordThemeConstant);\n}AddSmartCampaign.cs\n```\n\nExample:\n```text\nprivate static function getKeywordThemeSuggestions(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n SmartCampaignSuggestionInfo $suggestionInfo\n): array {\n $smartCampaignSuggestServiceClient =\n $googleAdsClient->getSmartCampaignSuggestServiceClient();\n\n // Issues a request to retrieve the keyword themes.\n $response = $smartCampaignSuggestServiceClient->suggestKeywordThemes(\n (new SuggestKeywordThemesRequest())\n ->setCustomerId($customerId)\n ->setSuggestionInfo($suggestionInfo)\n );\n\n printf(\n \"Retrieved %d keyword theme suggestions from the SuggestKeywordThemes \"\n . \"method.%s\",\n $response->getKeywordThemes()->count(),\n PHP_EOL\n );\n return iterator_to_array($response->getKeywordThemes()->getIterator());\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef get_keyword_theme_suggestions(\n client: GoogleAdsClient,\n customer_id: str,\n suggestion_info: SmartCampaignSuggestionInfo,\n) -> List[SuggestKeywordThemesResponse.KeywordTheme]:\n \"\"\"Retrieves KeywordThemes using the given suggestion info.\n\n Here we use the SuggestKeywordThemes method, which uses all of the business\n details included in the given SmartCampaignSuggestionInfo instance to\n generate keyword theme suggestions. This is the recommended way to\n generate keyword themes because it uses detailed information about your\n business, its location, and website content to generate keyword themes.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n suggestion_info: a SmartCampaignSuggestionInfo instance with details\n about the business being advertised.\n\n Returns:\n a list of KeywordThemes.\n \"\"\"\n smart_campaign_suggest_service: SmartCampaignSuggestServiceClient = (\n client.get_service(\"SmartCampaignSuggestService\")\n )\n request: SuggestKeywordThemesRequest = client.get_type(\n \"SuggestKeywordThemesRequest\"\n )\n request.customer_id = customer_id\n request.suggestion_info = suggestion_info\n\n response: SuggestKeywordThemesResponse = (\n smart_campaign_suggest_service.suggest_keyword_themes(request=request)\n )\n\n print(\n f\"Retrieved {len(response.keyword_themes)} keyword theme suggestions \"\n \"from the SuggestKeywordThemes method.\"\n )\n return response.keyword_themesadd_smart_campaign.py\n```\n\nExample:\n```text\ndef get_keyword_theme_suggestions(client, customer_id, suggestion_info)\n response = client.service.smart_campaign_suggest.suggest_keyword_themes(\n customer_id: customer_id,\n suggestion_info: suggestion_info,\n )\n\n puts \"Retrieved #{response.keyword_themes.size} keyword theme\" \\\n \" suggestions from SuggestKeywordThemes service.\"\n return response.keyword_themes\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Retrieves KeywordThemes using the given suggestion info.\n# Here we use the SuggestKeywordThemes method, which uses all of the business\n# details included in the given SmartCampaignSuggestionInfo instance to generate\n# keyword theme suggestions. This is the recommended way to generate keyword themes\n# because it uses detailed information about your business, its location, and\n# website content to generate keyword themes.\nsub _get_keyword_theme_suggestions {\n my ($api_client, $customer_id, $suggestion_info) = @_;\n\n my $response =\n $api_client->SmartCampaignSuggestService()->suggest_keyword_themes(\n Google::Ads::GoogleAds::V25::Services::SmartCampaignSuggestService::SuggestKeywordThemesRequest\n ->new({\n customerId => $customer_id,\n suggestionInfo => $suggestion_info\n }));\n\n printf \"Retrieved %d keyword theme suggestions from the SuggestKeywordThemes\"\n . \"method.\\n\",\n scalar @{$response->{keywordThemes}};\n\n return $response->{keywordThemes};\n}add_smart_campaign.pl\n```\n\nExample:\n```text\nprivate List<KeywordTheme> getKeywordTextAutoCompletions(\n GoogleAdsClient googleAdsClient, String keywordText) {\n try (KeywordThemeConstantServiceClient client =\n googleAdsClient.getLatestVersion().createKeywordThemeConstantServiceClient()) {\n SuggestKeywordThemeConstantsRequest request =\n SuggestKeywordThemeConstantsRequest.newBuilder()\n .setQueryText(keywordText)\n .setCountryCode(COUNTRY_CODE)\n .setLanguageCode(LANGUAGE_CODE)\n .build();\n SuggestKeywordThemeConstantsResponse response = client.suggestKeywordThemeConstants(request);\n // Converts the keyword theme constants to KeywordTheme instances for consistency with the\n // response from SmartCampaignSuggestService.SuggestKeywordThemes.\n return response.getKeywordThemeConstantsList().stream()\n .map(\n keywordThemeConstant ->\n KeywordTheme.newBuilder().setKeywordThemeConstant(keywordThemeConstant).build())\n .collect(Collectors.toList());\n }\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Retrieves KeywordThemeConstants that are derived from autocomplete data for the\n/// given keyword text.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"keywordText\">A keyword used for generating keyword auto completions.\n/// </param>\n/// <returns>A list of KeywordThemeConstants.</returns>\nprivate IEnumerable<KeywordThemeConstant> GetKeywordTextAutoCompletions(\n GoogleAdsClient client, string keywordText)\n{\n KeywordThemeConstantServiceClient keywordThemeConstantServiceClient =\n client.GetService(Services.V25.KeywordThemeConstantService);\n\n SuggestKeywordThemeConstantsRequest request = new SuggestKeywordThemeConstantsRequest\n {\n QueryText = keywordText,\n CountryCode = COUNTRY_CODE,\n LanguageCode = LANGUAGE_CODE\n };\n\n SuggestKeywordThemeConstantsResponse response =\n keywordThemeConstantServiceClient.SuggestKeywordThemeConstants(request);\n\n Console.WriteLine($\"Retrieved {response.KeywordThemeConstants.Count} keyword theme \" +\n $\"constants using the keyword '{keywordText}'.\");\n return response.KeywordThemeConstants.ToList();\n}AddSmartCampaign.cs\n```\n\nExample:\n```text\nprivate static function getKeywordTextAutoCompletions(\n GoogleAdsClient $googleAdsClient,\n string $keywordText\n): array {\n $keywordThemeConstantService = $googleAdsClient->getKeywordThemeConstantServiceClient();\n\n // Issues a request to retrieve the keyword theme constants.\n $response = $keywordThemeConstantService->suggestKeywordThemeConstants(\n (new SuggestKeywordThemeConstantsRequest())\n ->setQueryText($keywordText)\n ->setCountryCode(self::COUNTRY_CODE)\n ->setLanguageCode(self::LANGUAGE_CODE)\n );\n\n printf(\n \"Retrieved %d keyword theme constants using the keyword: '%s'.%s\",\n $response->getKeywordThemeConstants()->count(),\n $keywordText,\n PHP_EOL\n );\n\n // Maps the keyword theme constants to KeywordTheme instances for consistency with the\n // response from SmartCampaignSuggestService.SuggestKeywordThemes.\n return array_map(function (KeywordThemeConstant $keywordThemeConstant) {\n return new KeywordTheme([\n 'keyword_theme_constant' => $keywordThemeConstant\n ]);\n }, iterator_to_array($response->getKeywordThemeConstants()->getIterator()));\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef get_keyword_text_auto_completions(\n client: GoogleAdsClient, keyword_text: str\n) -> List[SuggestKeywordThemesResponse.KeywordTheme]:\n \"\"\"Retrieves KeywordThemeConstants for the given keyword text.\n\n These KeywordThemeConstants are derived from autocomplete data for the\n given keyword text. They are mapped to KeywordThemes before being returned.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n keyword_text: a keyword used for generating keyword themes.\n\n Returns:\n a list of KeywordThemes.\n \"\"\"\n keyword_theme_constant_service: KeywordThemeConstantServiceClient = (\n client.get_service(\"KeywordThemeConstantService\")\n )\n request: SuggestKeywordThemeConstantsRequest = client.get_type(\n \"SuggestKeywordThemeConstantsRequest\"\n )\n request.query_text = keyword_text\n request.country_code = _COUNTRY_CODE\n request.language_code = _LANGUAGE_CODE\n\n response: SuggestKeywordThemeConstantsResponse = (\n keyword_theme_constant_service.suggest_keyword_theme_constants(\n request=request\n )\n )\n\n print(\n f\"Retrieved {len(response.keyword_theme_constants)} keyword theme \"\n f\"constants using the keyword: '{keyword_text}'\"\n )\n\n # Map the keyword theme constants to KeywordTheme instances for consistency\n # with the response from SmartCampaignSuggestService.SuggestKeywordThemes.\n keyword_themes: List[SuggestKeywordThemesResponse.KeywordTheme] = []\n keyword_theme_constant: KeywordThemeConstant\n for keyword_theme_constant in response.keyword_theme_constants:\n # Note that the SuggestKeywordThemesResponse.KeywordTheme is a nested\n # type and not the same as the top-level KeywordTheme message.\n keyword_theme: SuggestKeywordThemesResponse.KeywordTheme = (\n client.get_type(\"SuggestKeywordThemesResponse\").KeywordTheme()\n )\n keyword_theme.keyword_theme_constant = keyword_theme_constant\n keyword_themes.append(keyword_theme)\n\n return keyword_themesadd_smart_campaign.py\n```\n\nExample:\n```text\n# Retrieves keyword_theme_constants for the given criteria.\n# These KeywordThemeConstants are derived from autocomplete data for the given\n# keyword text. They are mapped to KeywordThemes before being returned.\ndef get_keyword_text_auto_completions(client, keyword_text)\n response = client.service.keyword_theme_constant.suggest_keyword_theme_constants(\n query_text: keyword_text,\n country_code: COUNTRY_CODE,\n language_code: LANGUAGE_CODE,\n )\n\n puts \"Retrieved #{response.keyword_theme_constants.size} keyword theme\" \\\n \"constants using the keyword: '#{keyword_text}'\"\n\n response.keyword_theme_constants.map do |ktc|\n client.resource.keyword_theme do |kt|\n kt.keyword_theme_constant = ktc\n end\n end\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Retrieves KeywordThemeConstants for the given keyword text.\n# These KeywordThemeConstants are derived from autocomplete data for the given\n# keyword text. They are mapped to KeywordThemes before being returned.\nsub _get_keyword_text_auto_completions {\n my ($api_client, $keyword_text) = @_;\n\n my $response = $api_client->KeywordThemeConstantService()->suggest(\n Google::Ads::GoogleAds::V25::Services::KeywordThemeConstantService::SuggestKeywordThemeConstantsRequest\n ->new({\n queryText => $keyword_text,\n countryCode => COUNTRY_CODE,\n languageCode => LANGUAGE_CODE\n }));\n\n printf \"Retrieved %d keyword theme constants using the keyword '%s'.\\n\",\n scalar @{$response->{keywordThemeConstants}}, $keyword_text;\n\n # Map the keyword theme constants to KeywordTheme instances for consistency\n # with the response from SmartCampaignSuggestService.SuggestKeywordThemes.\n my $keyword_themes = [];\n foreach my $keyword_theme_constant (@{$response->{keywordThemeConstants}}) {\n push @$keyword_themes,\n Google::Ads::GoogleAds::V25::Services::SmartCampaignSuggestService::KeywordTheme\n ->new({\n keywordThemeConstant => $keyword_theme_constant\n });\n }\n\n return $keyword_themes;\n}add_smart_campaign.pl\n```\n\nExample:\n```text\nif (freeFormKeywordText != null) {\n keywordThemeInfos.add(\n KeywordThemeInfo.newBuilder().setFreeFormKeywordTheme(freeFormKeywordText).build());\n}AddSmartCampaign.java\n```\n\nExample:\n```text\nsuggestionInfo.KeywordThemes.Add(keywordThemeInfos);AddSmartCampaign.cs\n```\n\nExample:\n```text\n// Optionally includes any free-form keywords in verbatim.\nif (!empty($freeFormKeywordText)) {\n $keywordThemeInfos[] =\n new KeywordThemeInfo(['free_form_keyword_theme' => $freeFormKeywordText]);\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef get_free_form_keyword_theme_info(\n client: GoogleAdsClient, free_form_keyword_text: str\n) -> KeywordThemeInfo:\n \"\"\"Creates a KeywordThemeInfo using the given free-form keyword text.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n free_form_keyword_text: a keyword used to create a free-form keyword\n theme.\n\n Returns:\n a KeywordThemeInfo instance.\n \"\"\"\n info: KeywordThemeInfo = client.get_type(\"KeywordThemeInfo\")\n info.free_form_keyword_theme = free_form_keyword_text\n return infoadd_smart_campaign.py\n```\n\nExample:\n```text\ndef get_freeform_keyword_theme_info(client, free_form_keyword_text)\n client.resource.keyword_theme_info do |kti|\n kti.free_form_keyword_theme = free_form_keyword_text\n end\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Creates a KeywordInfo instance using the given free-form keyword text.\nsub _get_free_form_keyword_theme_info {\n my ($free_form_keyword_text) = @_;\n\n return Google::Ads::GoogleAds::V25::Common::KeywordThemeInfo->new({\n freeFormKeywordTheme => $free_form_keyword_text\n });\n}add_smart_campaign.pl\n```\n\nExample:\n```text\n// Gets the SmartCampaignSuggestionInfo object which acts as the basis for many of the\n// entities necessary to create a Smart campaign. It will be reused a number of times to\n// retrieve suggestions for keyword themes, budget amount, ad creatives, and campaign criteria.\nSmartCampaignSuggestionInfo suggestionInfo =\n getSmartCampaignSuggestionInfo(googleAdsClient, businessProfileLocation, businessName);\n\n// Generates a list of keyword themes using the SuggestKeywordThemes method on the\n// SmartCampaignSuggestService. It is strongly recommended that you use this strategy for\n// generating keyword themes.\nList<KeywordTheme> keywordThemes =\n getKeywordThemeSuggestions(googleAdsClient, customerId, suggestionInfo);\n\n// If a keyword text is given, retrieves keyword theme constant suggestions from the\n// KeywordThemeConstantService, maps them to KeywordThemes, and appends them to the existing\n// list.\n// This logic should ideally only be used if the suggestions from the\n// getKeywordThemeSuggestions function are insufficient.\nif (keywordText != null) {\n keywordThemes.addAll(getKeywordTextAutoCompletions(googleAdsClient, keywordText));\n}\n\n// Converts the list of KeywordThemes to a list of KeywordThemes objects.\nList<KeywordThemeInfo> keywordThemeInfos = getKeywordThemeInfos(keywordThemes);\n\n// Optionally includes any freeForm keywords in verbatim.\nif (freeFormKeywordText != null) {\n keywordThemeInfos.add(\n KeywordThemeInfo.newBuilder().setFreeFormKeywordTheme(freeFormKeywordText).build());\n}\n\n// Includes the keyword suggestions in the overall SuggestionInfo object.\nsuggestionInfo = suggestionInfo.toBuilder().addAllKeywordThemes(keywordThemeInfos).build();AddSmartCampaign.java\n```\n\nExample:\n```text\n// Gets the SmartCampaignSuggestionInfo object which acts as the basis for many\n// of the entities necessary to create a Smart campaign. It will be reused a number\n// of times to retrieve suggestions for keyword themes, budget amount, ad\n//creatives, and campaign criteria.\nSmartCampaignSuggestionInfo suggestionInfo =\n GetSmartCampaignSuggestionInfo(client, businessProfileLocation, businessName);\n\n// Generates a list of keyword themes using the SuggestKeywordThemes method on the\n// SmartCampaignSuggestService. It is strongly recommended that you use this\n// strategy for generating keyword themes.\nList<KeywordThemeConstant> keywordThemeConstants =\n GetKeywordThemeSuggestions(client, customerId, suggestionInfo);\n\n// Optionally retrieves auto-complete suggestions for the given keyword text and\n// adds them to the list of keyWordThemeConstants.\nif (keywordText != null)\n{\n keywordThemeConstants.AddRange(GetKeywordTextAutoCompletions(\n client, keywordText));\n}\n\n// Converts the KeywordThemeConstants to KeywordThemeInfos.\nList<KeywordThemeInfo> keywordThemeInfos = keywordThemeConstants.Select(\n constant =>\n new KeywordThemeInfo { KeywordThemeConstant = constant.ResourceName })\n .ToList();\n\n// Optionally includes any freeform keywords verbatim.\nif (freeFormKeywordText != null)\n{\n keywordThemeInfos.Add(new KeywordThemeInfo()\n {\n FreeFormKeywordTheme = freeFormKeywordText\n });\n}\n\n// Includes the keyword suggestions in the overall SuggestionInfo object.\nsuggestionInfo.KeywordThemes.Add(keywordThemeInfos);AddSmartCampaign.cs\n```\n\nExample:\n```text\n// Gets the SmartCampaignSuggestionInfo object which acts as the basis for many of the\n// entities necessary to create a Smart campaign. It will be reused a number of times to\n// retrieve suggestions for keyword themes, budget amount, ads, and campaign criteria.\n$suggestionInfo = self::getSmartCampaignSuggestionInfo(\n $businessProfileLocationResourceName,\n $businessName\n);\n\n// Generates a list of keyword themes using the SuggestKeywordThemes method on the\n// SmartCampaignSuggestService. It is strongly recommended that you use this strategy for\n// generating keyword themes.\n$keywordThemes =\n self::getKeywordThemeSuggestions($googleAdsClient, $customerId, $suggestionInfo);\n\n// Optionally retrieves auto-complete suggestions for the given keyword text and adds them\n// to the list of keyword themes.\nif (!empty($keywordText)) {\n $keywordThemes = array_merge(\n $keywordThemes,\n self::getKeywordTextAutoCompletions($googleAdsClient, $keywordText)\n );\n}\n\n// Maps the list of KeywordThemes to KeywordThemeInfos.\n$keywordThemeInfos = array_map(function (KeywordTheme $keywordTheme) {\n if ($keywordTheme->getKeywordThemeConstant()) {\n return new KeywordThemeInfo([\n 'keyword_theme_constant' => $keywordTheme->getKeywordThemeConstant()\n ->getResourceName()\n ]);\n } elseif ($keywordTheme->getFreeFormKeywordTheme()) {\n return new KeywordThemeInfo([\n 'free_form_keyword_theme' => $keywordTheme->getFreeFormKeywordTheme()\n ]);\n } else {\n throw new \\UnexpectedValueException(\n 'A malformed KeywordTheme was encountered: ' . $keywordTheme->getKeywordTheme()\n );\n }\n}, $keywordThemes);\n\n// Optionally includes any free-form keywords in verbatim.\nif (!empty($freeFormKeywordText)) {\n $keywordThemeInfos[] =\n new KeywordThemeInfo(['free_form_keyword_theme' => $freeFormKeywordText]);\n}\n// Includes the keyword suggestions in the overall SuggestionInfo object.\n$suggestionInfo = $suggestionInfo->setKeywordThemes($keywordThemeInfos);AddSmartCampaign.php\n```\n\nExample:\n```text\n# The SmartCampaignSuggestionInfo object acts as the basis for many of the\n# entities necessary to create a Smart campaign. It will be reused a number\n# of times to retrieve suggestions for keyword themes, budget amount,\n# ad creatives, and campaign criteria.\nsuggestion_info: SmartCampaignSuggestionInfo = (\n get_smart_campaign_suggestion_info(\n client, business_profile_location, business_name\n )\n)\n\n# After creating a SmartCampaignSuggestionInfo object we first use it to\n# generate a list of keyword themes using the SuggestKeywordThemes method\n# on the SmartCampaignSuggestService. It is strongly recommended that you\n# use this strategy for generating keyword themes.\nkeyword_themes: List[SuggestKeywordThemesResponse.KeywordTheme] = (\n get_keyword_theme_suggestions(client, customer_id, suggestion_info)\n)\n\n# If a keyword text is given, retrieve keyword theme constant suggestions\n# from the KeywordThemeConstantService, map them to KeywordThemes, and\n# append them to the existing list. This logic should ideally only be used\n# if the suggestions from the get_keyword_theme_suggestions function are\n# insufficient.\nif keyword_text:\n keyword_themes.extend(\n get_keyword_text_auto_completions(client, keyword_text)\n )\n\n# Map the KeywordThemes retrieved by the previous two steps to\n# KeywordThemeInfo instances.\nkeyword_theme_infos: List[KeywordThemeInfo] = (\n map_keyword_themes_to_keyword_infos(client, keyword_themes)\n)\n\n# If a free-form keyword text is given we create a KeywordThemeInfo instance\n# from it and add it to the existing list.\nif free_form_keyword_text:\n keyword_theme_infos.append(\n get_free_form_keyword_theme_info(client, free_form_keyword_text)\n )\n\n# Now add the generated keyword themes to the suggestion info instance.\nsuggestion_info.keyword_themes.extend(keyword_theme_infos)add_smart_campaign.py\n```\n\nExample:\n```text\n# The SmartCampaignSuggestionInfo object acts as the basis for many of the\n# entities necessary to create a Smart campaign. It will be reused a number\n# of times to retrieve suggestions for keyword themes, budget amount,\n# ad creatives, and campaign criteria.\nsuggestion_info = get_smart_campaign_suggestion_info(\n client,\n business_profile_location,\n business_name,\n)\n\n# After creating a SmartCampaignSuggestionInfo object we first use it to\n# generate a list of keyword themes using the SuggestKeywordThemes method\n# on the SmartCampaignSuggestService. It is strongly recommended that you\n# use this strategy for generating keyword themes.\nkeyword_themes = get_keyword_theme_suggestions(\n client,\n customer_id,\n suggestion_info,\n)\n\n# If a keyword text is given, retrieve keyword theme constant suggestions\n# from the KeywordThemeConstantService, map them to KeywordThemes, and append\n# them to the existing list. This logic should ideally only be used if the\n# suggestions from the get_keyword_theme_suggestions function are\n# insufficient.\nif keyword_text\n keyword_themes += get_keyword_text_auto_completions(\n client,\n keyword_text,\n )\nend\n\n# Map the KeywordThemeConstants retrieved by the previous two steps to\n# KeywordThemeInfo instances.\nkeyword_theme_infos = map_keyword_themes_to_keyword_infos(\n client,\n keyword_themes,\n)\n\n# If a free-form keyword text is given we create a KeywordThemeInfo instance\n# from it and add it to the existing list.\nif free_form_keyword_text\n keyword_theme_infos << get_freeform_keyword_theme_info(\n client,\n free_form_keyword_text,\n )\nend\n\n# Now add the generated keyword themes to the suggestion info instance.\nsuggestion_info.keyword_themes += keyword_theme_infosadd_smart_campaign.rb\n```\n\nExample:\n```text\n# The SmartCampaignSuggestionInfo object acts as the basis for many of the\n# entities necessary to create a Smart campaign. It will be reused a number\n# of times to retrieve suggestions for keyword themes, budget amount,\n# ad creatives, and campaign criteria.\nmy $suggestion_info =\n _get_smart_campaign_suggestion_info($business_profile_location,\n $business_name);\n\n# After creating a SmartCampaignSuggestionInfo object we first use it to\n# generate a list of keyword themes using the SuggestKeywordThemes method\n# on the SmartCampaignSuggestService. It is strongly recommended that you\n# use this strategy for generating keyword themes.\nmy $keyword_themes =\n _get_keyword_theme_suggestions($api_client, $customer_id, $suggestion_info);\n\n# If a keyword text is given, retrieve keyword theme constant suggestions\n# from the KeywordThemeConstantService, map them to KeywordThemes, and\n# append them to the existing list. This logic should ideally only be used\n# if the suggestions from the get_keyword_theme_suggestions funtion are\n# insufficient.\nif (defined $keyword_text) {\n push @$keyword_themes,\n @{_get_keyword_text_auto_completions($api_client, $keyword_text)};\n}\n\n# Map the KeywordThemeConstants retrieved by the previous two steps to\n# KeywordThemeInfo instances.\nmy $keyword_theme_infos =\n _map_keyword_themes_to_keyword_infos($keyword_themes);\n\n# If a free-form keyword text is given we create a KeywordThemeInfo instance\n# from it and add it to the existing list.\nif (defined $free_form_keyword_text) {\n push @$keyword_theme_infos,\n _get_free_form_keyword_theme_info($free_form_keyword_text);\n}\n\n# Now add the generated keyword themes to the suggestion info instance.\n$suggestion_info->{keywordThemes} = $keyword_theme_infos;add_smart_campaign.pl\n```\n\nExample:\n```text\nprivate long getBudgetSuggestions(\n GoogleAdsClient googleAdsClient,\n long customerId,\n SmartCampaignSuggestionInfo suggestionInfo) {\n SuggestSmartCampaignBudgetOptionsRequest.Builder request =\n SuggestSmartCampaignBudgetOptionsRequest.newBuilder()\n .setCustomerId(String.valueOf(customerId));\n\n // You can retrieve suggestions for an existing campaign by setting the\n // \"campaign\" field of the request equal to the resource name of a campaign\n // and leaving the rest of the request fields below unset:\n // request.setCampaign(\"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\");\n\n // Uses the suggestion_info field instead, since these suggestions are for a new campaign.\n request.setSuggestionInfo(suggestionInfo);\n\n // Issues a request to retrieve a budget suggestion.\n try (SmartCampaignSuggestServiceClient client =\n googleAdsClient.getLatestVersion().createSmartCampaignSuggestServiceClient()) {\n SuggestSmartCampaignBudgetOptionsResponse response =\n client.suggestSmartCampaignBudgetOptions(request.build());\n BudgetOption recommendation = response.getRecommended();\n System.out.printf(\n \"A daily budget amount of %d micros was suggested, garnering an estimated minimum of %d\"\n + \" clicks and an estimated maximum of %d per day.%n\",\n recommendation.getDailyAmountMicros(),\n recommendation.getMetrics().getMinDailyClicks(),\n recommendation.getMetrics().getMaxDailyClicks());\n return recommendation.getDailyAmountMicros();\n }\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Retrieves a suggested budget amount for a new budget.\n/// Using the SmartCampaignSuggestService to determine a daily budget for new and existing\n/// Smart campaigns is highly recommended because it helps the campaigns achieve optimal\n/// performance.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <param name=\"suggestionInfo\"></param>\n/// <returns>A daily budget amount in micros.</returns>\nprivate long GetBudgetSuggestion(GoogleAdsClient client, long customerId,\n SmartCampaignSuggestionInfo suggestionInfo)\n{\n SmartCampaignSuggestServiceClient smartCampaignSuggestServiceClient = client.GetService\n (Services.V25.SmartCampaignSuggestService);\n\n SuggestSmartCampaignBudgetOptionsRequest request =\n new SuggestSmartCampaignBudgetOptionsRequest\n {\n CustomerId = customerId.ToString(),\n // You can retrieve suggestions for an existing campaign by setting the\n // \"Campaign\" field of the request to the resource name of a campaign and\n // leaving the rest of the request fields below unset:\n // Campaign = \"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\",\n\n // Since these suggestions are for a new campaign, we're going to use the\n // SuggestionInfo field instead.\n SuggestionInfo = suggestionInfo,\n };\n\n LocationInfo locationInfo = new LocationInfo\n {\n // Set the location to the resource name of the given geo target constant.\n GeoTargetConstant = ResourceNames.GeoTargetConstant(GEO_TARGET_CONSTANT)\n };\n\n // Issue a request to retrieve a budget suggestion.\n SuggestSmartCampaignBudgetOptionsResponse response =\n smartCampaignSuggestServiceClient.SuggestSmartCampaignBudgetOptions(request);\n\n // Three tiers of options will be returned: \"low\", \"high\", and \"recommended\".\n // Here we will use the \"recommended\" option. The amount is specified in micros, where\n // one million is equivalent to one currency unit.\n Console.WriteLine($\"A daily budget amount of \" +\n $\"{response.Recommended.DailyAmountMicros}\" +\n $\" was suggested, garnering an estimated minimum of \" +\n $\"{response.Recommended.Metrics.MinDailyClicks} clicks and an estimated \" +\n $\"maximum of {response.Recommended.Metrics.MaxDailyClicks} clicks per day.\");\n\n return response.Recommended.DailyAmountMicros;\n}AddSmartCampaign.cs\n```\n\nExample:\n```text\nprivate static function getBudgetSuggestion(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n SmartCampaignSuggestionInfo $suggestionInfo\n): int {\n\n\n\n // Issues a request to retrieve a budget suggestion.\n $smartCampaignSuggestService = $googleAdsClient->getSmartCampaignSuggestServiceClient();\n $response = $smartCampaignSuggestService->suggestSmartCampaignBudgetOptions(\n (new SuggestSmartCampaignBudgetOptionsRequest())\n ->setCustomerId($customerId)\n // You can retrieve suggestions for an existing campaign by setting the \"campaign\"\n // field equal to the resource name of a campaign:\n // ->setCampaign('INSERT_CAMPAIGN_RESOURCE_NAME_HERE');\n // Since these suggestions are for a new campaign, we're going to use the\n // suggestion_info field instead.\n ->setSuggestionInfo($suggestionInfo)\n );\n\n // Three tiers of options will be returned, a \"low\", \"high\" and \"recommended\". Here we will\n // use the \"recommended\" option. The amount is specified in micros, where one million is\n // equivalent to one currency unit.\n $recommendation = $response->getRecommended();\n printf(\n \"A daily budget amount of %d micros was suggested, garnering an estimated minimum of \"\n . \"%d clicks and an estimated maximum of %d per day.%s\",\n $recommendation->getDailyAmountMicros(),\n $recommendation->getMetrics()->getMinDailyClicks(),\n $recommendation->getMetrics()->getMaxDailyClicks(),\n PHP_EOL\n );\n\n return $recommendation->getDailyAmountMicros();\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef get_budget_suggestion(\n client: GoogleAdsClient,\n customer_id: str,\n suggestion_info: SmartCampaignSuggestionInfo,\n) -> int:\n \"\"\"Retrieves a suggested budget amount for a new budget.\n\n Using the SmartCampaignSuggestService to determine a daily budget for new\n and existing Smart campaigns is highly recommended because it helps the\n campaigns achieve optimal performance.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n suggestion_info: a SmartCampaignSuggestionInfo instance with details\n about the business being advertised.\n\n Returns:\n a daily budget amount in micros.\n \"\"\"\n sc_suggest_service: SmartCampaignSuggestServiceClient = client.get_service(\n \"SmartCampaignSuggestService\"\n )\n request: SuggestSmartCampaignBudgetOptionsRequest = client.get_type(\n \"SuggestSmartCampaignBudgetOptionsRequest\"\n )\n request.customer_id = customer_id\n # You can retrieve suggestions for an existing campaign by setting the\n # \"campaign\" field of the request equal to the resource name of a campaign\n # and leaving the rest of the request fields below unset:\n # request.campaign = INSERT_CAMPAIGN_RESOURCE_NAME_HERE\n\n # Since these suggestions are for a new campaign, we're going to\n # use the suggestion_info field instead.\n request.suggestion_info = suggestion_info\n\n # Issue a request to retrieve a budget suggestion.\n response: SuggestSmartCampaignBudgetOptionsResponse = (\n sc_suggest_service.suggest_smart_campaign_budget_options(\n request=request\n )\n )\n\n # Three tiers of options will be returned, a \"low\", \"high\" and\n # \"recommended\". Here we will use the \"recommended\" option. The amount is\n # specified in micros, where one million is equivalent to one currency unit.\n recommendation: SuggestSmartCampaignBudgetOptionsResponse.BudgetOption = (\n response.recommended\n )\n print(\n f\"A daily budget amount of {recommendation.daily_amount_micros} micros \"\n \"was suggested, garnering an estimated minimum of \"\n f\"{recommendation.metrics.min_daily_clicks} clicks and an estimated \"\n f\"maximum of {recommendation.metrics.max_daily_clicks} per day.\"\n )\n\n return recommendation.daily_amount_microsadd_smart_campaign.py\n```\n\nExample:\n```text\n# Retrieves a suggested budget amount for a new budget.\n#\n# Using the SmartCampaignSuggestService to determine a daily budget for new\n# and existing Smart campaigns is highly recommended because it helps the\n# campaigns achieve optimal performance.\ndef get_budget_suggestion(client, customer_id, suggestion_info)\n # Issues a request to retrieve a budget suggestion.\n response = client.service.smart_campaign_suggest.suggest_smart_campaign_budget_options(\n customer_id: customer_id,\n # You can retrieve suggestions for an existing campaign by setting the\n # \"campaign\" field of the request equal to the resource name of a campaign\n # and leaving the rest of the request fields below unset:\n # campaign: INSERT_CAMPAIGN_RESOURCE_NAME_HERE,\n # Since these suggestions are for a new campaign, we're going to\n # use the suggestion_info field instead.\n suggestion_info: suggestion_info,\n )\n\n # Three tiers of options will be returned, a \"low\", \"high\" and\n # \"recommended\". Here we will use the \"recommended\" option. The amount is\n # specified in micros, where one million is equivalent to one currency unit.\n recommendation = response.recommended\n puts \"A daily budget amount of #{recommendation.daily_amount_micros} micros\" \\\n \" was suggested, garnering an estimated minimum of\" \\\n \" #{recommendation.metrics.min_daily_clicks} clicks and an estimated\" \\\n \" maximum of #{recommendation.metrics.max_daily_clicks} per day.\"\n\n recommendation.daily_amount_micros\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Retrieves a suggested budget amount for a new budget.\n# Using the SmartCampaignSuggestService to determine a daily budget for new and\n# existing Smart campaigns is highly recommended because it helps the campaigns\n# achieve optimal performance.\nsub _get_budget_suggestion {\n my ($api_client, $customer_id, $suggestion_info) = @_;\n\n my $request =\n Google::Ads::GoogleAds::V25::Services::SmartCampaignSuggestService::SuggestSmartCampaignBudgetOptionsRequest\n ->new({\n customerId => $customer_id,\n # You can retrieve suggestions for an existing campaign by setting the\n # \"campaign\" field of the request to the resource name of a campaign and\n # leaving the rest of the request fields below unset:\n # campaign => \"INSERT_CAMPAIGN_RESOURCE_NAME_HERE\",\n #\n # Since these suggestions are for a new campaign, we're going to use the\n # \"suggestion_info\" field instead.\n suggestionInfo => $suggestion_info\n });\n\n # Issue a request to retrieve a budget suggestion.\n my $response = $api_client->SmartCampaignSuggestService()\n ->suggest_smart_campaign_budget_options($request);\n\n # Three tiers of options will be returned: \"low\", \"high\", and \"recommended\".\n # Here we will use the \"recommended\" option. The amount is specified in micros,\n # where one million is equivalent to one currency unit.\n printf \"A daily budget amount of %d was suggested, garnering an estimated \" .\n \"minimum of %d clicks and an estimated maximum of %d clicks per day.\\n\",\n $response->{recommended}{dailyAmountMicros},\n $response->{recommended}{metrics}{minDailyClicks},\n $response->{recommended}{metrics}{maxDailyClicks};\n\n return $response->{recommended}{dailyAmountMicros};\n}add_smart_campaign.pl\n```\n\nExample:\n```text\nprivate SmartCampaignAdInfo getAdSuggestions(\n GoogleAdsClient googleAdsClient,\n long customerId,\n SmartCampaignSuggestionInfo suggestionInfo) {\n // Unlike the SuggestSmartCampaignBudgetOptions method, it's only possible to use\n // suggestion_info to retrieve ad creative suggestions.\n\n // Issues a request to retrieve ad creative suggestions.\n try (SmartCampaignSuggestServiceClient smartCampaignSuggestService =\n googleAdsClient.getLatestVersion().createSmartCampaignSuggestServiceClient()) {\n SuggestSmartCampaignAdResponse response =\n smartCampaignSuggestService.suggestSmartCampaignAd(\n SuggestSmartCampaignAdRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setSuggestionInfo(suggestionInfo)\n .build());\n\n // The SmartCampaignAdInfo object in the response contains a list of up to three headlines\n // and two descriptions. Note that some of the suggestions may have empty strings as text.\n // Before setting these on the ad you should review them and filter out any empty values.\n SmartCampaignAdInfo adSuggestions = response.getAdInfo();\n for (AdTextAsset headline : adSuggestions.getHeadlinesList()) {\n System.out.println(!headline.getText().isEmpty() ? headline.getText() : \"None\");\n }\n for (AdTextAsset description : adSuggestions.getDescriptionsList()) {\n System.out.println(!description.getText().isEmpty() ? description.getText() : \"None\");\n }\n return adSuggestions;\n }\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Retrieves creative suggestions for a Smart campaign ad.\n///\n/// Using the SmartCampaignSuggestService to suggest creatives for new\n/// and existing Smart campaigns is highly recommended because it helps\n/// the campaigns achieve optimal performance.\n/// </summary>\n/// <param name=\"client\"></param>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <param name=\"suggestionInfo\">a SmartCampaignSuggestionInfo instance\n/// with details about the business being advertised.</param>\n/// <returns>A SmartCampaignAdInfo instance with suggested headlines and\n/// descriptions.</returns>\nprivate SmartCampaignAdInfo GetAdSuggestions(GoogleAdsClient client,\n long customerId, SmartCampaignSuggestionInfo suggestionInfo)\n{\n SmartCampaignSuggestServiceClient smartCampaignSuggestService =\n client.GetService(Services.V25.SmartCampaignSuggestService);\n\n SuggestSmartCampaignAdRequest request = new SuggestSmartCampaignAdRequest\n {\n CustomerId = customerId.ToString(),\n // Unlike the SuggestSmartCampaignBudgetOptions method, it's only possible to\n // use suggestion_info to retrieve ad creative suggestions.\n SuggestionInfo = suggestionInfo\n };\n\n // Issue a request to retrieve ad creative suggestions.\n SuggestSmartCampaignAdResponse response =\n smartCampaignSuggestService.SuggestSmartCampaignAd(request);\n\n // The SmartCampaignAdInfo object in the response contains a list of up to\n // three headlines and two descriptions. Note that some of the suggestions\n // may have empty strings as text. Before setting these on the ad you should\n // review them and filter out any empty values.\n SmartCampaignAdInfo adSuggestions = response.AdInfo;\n\n if (adSuggestions != null)\n {\n Console.WriteLine($\"The following headlines were suggested:\");\n foreach (AdTextAsset headline in adSuggestions.Headlines)\n {\n Console.WriteLine($\"\\t{headline.Text}\");\n }\n\n Console.WriteLine($\"And the following descriptions were suggested:\");\n foreach (AdTextAsset description in adSuggestions.Descriptions)\n {\n Console.WriteLine($\"\\t{description.Text}\");\n }\n }\n else\n {\n Console.WriteLine(\"No ad suggestions were found.\");\n adSuggestions = new SmartCampaignAdInfo();\n }\n\n return adSuggestions;\n}AddSmartCampaign.cs\n```\n\nExample:\n```text\nprivate static function getAdSuggestions(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n SmartCampaignSuggestionInfo $suggestionInfo\n) {\n // Unlike the SuggestSmartCampaignBudgetOptions method, it's only possible to use\n // suggestion_info to retrieve ad creative suggestions.\n\n // Issues a request to retrieve ad creative suggestions.\n $smartCampaignSuggestService = $googleAdsClient->getSmartCampaignSuggestServiceClient();\n $response = $smartCampaignSuggestService->suggestSmartCampaignAd(\n (new SuggestSmartCampaignAdRequest())\n ->setCustomerId($customerId)\n ->setSuggestionInfo($suggestionInfo)\n );\n\n // The SmartCampaignAdInfo object in the response contains a list of up to three headlines\n // and two descriptions. Note that some of the suggestions may have empty strings as text.\n // Before setting these on the ad you should review them and filter out any empty values.\n $adSuggestions = $response->getAdInfo();\n if (is_null($adSuggestions)) {\n return null;\n }\n print 'The following headlines were suggested:' . PHP_EOL;\n foreach ($adSuggestions->getHeadlines() as $headline) {\n print \"\\t\" . ($headline->getText() ?: 'None') . PHP_EOL;\n }\n print 'And the following descriptions were suggested:' . PHP_EOL;\n foreach ($adSuggestions->getDescriptions() as $description) {\n print \"\\t\" . ($description->getText() ?: 'None') . PHP_EOL;\n }\n return $adSuggestions;\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef get_ad_suggestions(\n client: GoogleAdsClient,\n customer_id: str,\n suggestion_info: SmartCampaignSuggestionInfo,\n) -> SmartCampaignAdInfo:\n \"\"\"Retrieves creative suggestions for a Smart campaign ad.\n\n Using the SmartCampaignSuggestService to suggest creatives for new and\n existing Smart campaigns is highly recommended because it helps the\n campaigns achieve optimal performance.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n suggestion_info: a SmartCampaignSuggestionInfo instance with details\n about the business being advertised.\n\n Returns:\n a SmartCampaignAdInfo instance with suggested headlines and\n descriptions.\n \"\"\"\n sc_suggest_service: SmartCampaignSuggestServiceClient = client.get_service(\n \"SmartCampaignSuggestService\"\n )\n request: SuggestSmartCampaignAdRequest = client.get_type(\n \"SuggestSmartCampaignAdRequest\"\n )\n request.customer_id = customer_id\n\n # Unlike the SuggestSmartCampaignBudgetOptions method, it's only possible\n # to use suggestion_info to retrieve ad creative suggestions.\n request.suggestion_info = suggestion_info\n\n # Issue a request to retrieve ad creative suggestions.\n response: SuggestSmartCampaignAdResponse = (\n sc_suggest_service.suggest_smart_campaign_ad(request=request)\n )\n\n # The SmartCampaignAdInfo object in the response contains a list of up to\n # three headlines and two descriptions. Note that some of the suggestions\n # may have empty strings as text. Before setting these on the ad you should\n # review them and filter out any empty values.\n ad_suggestions: SmartCampaignAdInfo = response.ad_info\n\n print(\"The following headlines were suggested:\")\n headline: AdTextAsset\n for headline in ad_suggestions.headlines:\n print(f\"\\t{headline.text or '<None>'}\")\n\n print(\"And the following descriptions were suggested:\")\n description: AdTextAsset\n for description in ad_suggestions.descriptions:\n print(f\"\\t{description.text or '<None>'}\")\n\n return ad_suggestionsadd_smart_campaign.py\n```\n\nExample:\n```text\n# Retrieves creative suggestions for a Smart campaign ad.\n#\n# Using the SmartCampaignSuggestService to suggest creatives for new and\n# existing Smart campaigns is highly recommended because it helps the\n# campaigns achieve optimal performance.\ndef get_ad_suggestions(client, customer_id, suggestion_info)\n # Issue a request to retrieve ad creative suggestions.\n response = client.service.smart_campaign_suggest.suggest_smart_campaign_ad(\n customer_id: customer_id,\n # Unlike the SuggestSmartCampaignBudgetOptions method, it's only possible\n # to use suggestion_info to retrieve ad creative suggestions.\n suggestion_info: suggestion_info,\n )\n\n # The SmartCampaignAdInfo object in the response contains a list of up to\n # three headlines and two descriptions. Note that some of the suggestions\n # may have empty strings as text. Before setting these on the ad you should\n # review them and filter out any empty values.\n ad_suggestions = response.ad_info\n\n # If there are no suggestions, the response will be blank.\n return nil if ad_suggestions.nil?\n\n puts 'The following headlines were suggested:'\n ad_suggestions.headlines.each do |headline|\n puts \"\\t#{headline.text || '<None>'}\"\n end\n\n puts 'And the following descriptions were suggested:'\n ad_suggestions.descriptions.each do |description|\n puts \"\\t#{description.text || '<None>'}\"\n end\n\n ad_suggestions\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Retrieves creative suggestions for a Smart campaign ad.\n# Using the SmartCampaignSuggestService to suggest creatives for new and\n# existing Smart campaigns is highly recommended because it helps the campaigns\n# achieve optimal performance.\nsub _get_ad_suggestions {\n my ($api_client, $customer_id, $suggestion_info) = @_;\n\n # Issue a request to retrieve ad creative suggestions.\n my $response =\n $api_client->SmartCampaignSuggestService()->suggest_smart_campaign_ad(\n Google::Ads::GoogleAds::V25::Services::SmartCampaignSuggestService::SuggestSmartCampaignAdRequest\n ->new({\n customerId => $customer_id,\n # Unlike the SuggestSmartCampaignBudgetOptions method, it's only\n # possible to use suggestion_info to retrieve ad creative suggestions.\n suggestionInfo => $suggestion_info\n }));\n\n # The SmartCampaignAdInfo object in the response contains a list of up to\n # three headlines and two descriptions. Note that some of the suggestions\n # may have empty strings as text. Before setting these on the ad you should\n # review them and filter out any empty values.\n my $ad_suggestions = $response->{adInfo};\n printf \"The following headlines were suggested:\\n\";\n foreach my $headline (@{$ad_suggestions->{headlines}}) {\n printf \"\\t%s\\n\", defined $headline->{text} ? $headline->{text} : \"<None>\";\n }\n printf \"And the following descriptions were suggested:\\n\";\n foreach my $description (@{$ad_suggestions->{descriptions}}) {\n printf \"\\t%s\\n\",\n defined $description->{text} ? $description->{text} : \"<None>\";\n }\n\n return $ad_suggestions;\n}add_smart_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.389Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":42,"totalLines":1737,"estimatedTokens":17431}}166{"id":"doc-add_performance_max_product_listing_group_tree_g-c84df615","source":"documentation","title":"Add Performance Max Product Listing Group Tree | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/samples/add-performance-max-product-listing-group-tree","text":"Example:\n```text\n// Copyright 2022 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.shoppingads;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.enums.ListingGroupFilterListingSourceEnum.ListingGroupFilterListingSource;\nimport com.google.ads.googleads.v25.enums.ListingGroupFilterProductConditionEnum.ListingGroupFilterProductCondition;\nimport com.google.ads.googleads.v25.enums.ListingGroupFilterTypeEnum.ListingGroupFilterType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.AssetGroupListingGroupFilter;\nimport com.google.ads.googleads.v25.resources.ListingGroupFilterDimension;\nimport com.google.ads.googleads.v25.resources.ListingGroupFilterDimension.ProductBrand;\nimport com.google.ads.googleads.v25.resources.ListingGroupFilterDimension.ProductCondition;\nimport com.google.ads.googleads.v25.services.AssetGroupListingGroupFilterOperation;\nimport com.google.ads.googleads.v25.services.GoogleAdsRow;\nimport com.google.ads.googleads.v25.services.GoogleAdsServiceClient;\nimport com.google.ads.googleads.v25.services.GoogleAdsServiceClient.SearchPagedResponse;\nimport com.google.ads.googleads.v25.services.MutateGoogleAdsRequest;\nimport com.google.ads.googleads.v25.services.MutateGoogleAdsResponse;\nimport com.google.ads.googleads.v25.services.MutateOperation;\nimport com.google.ads.googleads.v25.services.MutateOperationResponse;\nimport com.google.ads.googleads.v25.services.MutateOperationResponse.ResponseCase;\nimport com.google.ads.googleads.v25.services.SearchGoogleAdsRequest;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.LongStream;\n\n/**\n * This example shows how to add product partitions to a Performance Max retail campaign.\n *\n * <p>For Performance Max campaigns, product partitions are represented using the\n * AssetGroupListingGroupFilter resource. This resource can be combined with itself to form a\n * hierarchy that creates a product partition tree.\n *\n * <p>For more information about Performance Max retail campaigns, see the {@link\n * AddPerformanceMaxRetailCampaign} example.\n */\npublic class AddPerformanceMaxProductListingGroupTree {\n\n private final int TEMPORARY_ID_LISTING_GROUP_ROOT = -1;\n\n private static class AddPerformanceMaxProductListingGroupTreeParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(names = ArgumentNames.ASSET_GROUP_ID, required = true)\n private Long assetGroupId;\n\n @Parameter(names = ArgumentNames.REPLACE_EXISTING_TREE, required = true, arity = 1)\n private Boolean replaceExistingTree;\n }\n\n public static void main(String[] args) throws Exception {\n AddPerformanceMaxProductListingGroupTreeParams params =\n new AddPerformanceMaxProductListingGroupTreeParams();\n if (!params.parseArguments(args)) {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.assetGroupId = Long.parseLong(\"INSERT_AD_GROUP_ID_HERE\");\n // Optional: To replace the existing listing group tree from the asset group set this\n // parameter to true.\n // If the current AssetGroup already has a tree of ListingGroupFilters, attempting to add a\n // new set of ListingGroupFilters including a root filter will result in an\n // 'ASSET_GROUP_LISTING_GROUP_FILTER_ERROR_MULTIPLE_ROOTS' error. Setting this option to true\n // will remove the existing tree and prevent this error.\n params.replaceExistingTree = Boolean.parseBoolean(\"INSERT_REPLACE_EXISTING_TREE_HERE\");\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new AddPerformanceMaxProductListingGroupTree()\n .runExample(\n googleAdsClient, params.customerId, params.assetGroupId, params.replaceExistingTree);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * A factory that creates MutateOperations for removing an existing tree of\n * AssetGroupListingGroupFilters.\n *\n * <p>AssetGroupListingGroupFilters must be removed in a specific order: all of the children of a\n * filter must be removed before the filter itself, otherwise the API will return an error.\n *\n * <p>This object is intended to be used with an array of MutateOperations to perform a series of\n * related updates to an AssetGroup.\n */\n private static class AssetGroupListingGroupFilterRemoveOperationFactory {\n private String rootResourceName = \"\";\n private final Map<String, Set<String>> parentsToChildren = new HashMap<>();\n\n private AssetGroupListingGroupFilterRemoveOperationFactory(\n List<AssetGroupListingGroupFilter> resources) throws Exception {\n if (resources.isEmpty()) {\n throw new Exception(\"No listing group filters to remove\");\n }\n\n for (AssetGroupListingGroupFilter filter : resources) {\n if (filter.getParentListingGroupFilter().isEmpty() && !this.rootResourceName.isEmpty()) {\n // A node with no parent is the root node, but there can only be a single root node.\n throw new IllegalStateException(\"More than one root node\");\n } else if (filter.getParentListingGroupFilter().isEmpty()) {\n // Sets the root node.\n this.rootResourceName = filter.getResourceName();\n } else {\n // Adds an entry to the parentsToChildren map for each non-root node.\n String parentResourceName = filter.getParentListingGroupFilter();\n // Checks to see if a sibling in this group has already been visited, and fetches or\n // creates a new set as required.\n Set<String> siblings =\n this.parentsToChildren.computeIfAbsent(parentResourceName, p -> new HashSet<>());\n siblings.add(filter.getResourceName());\n }\n }\n }\n\n /**\n * Creates a list of MutateOperations that remove all of the resources in the tree originally\n * used to create this factory object.\n */\n private List<MutateOperation> removeAll() {\n return removeDescendantsAndFilter(rootResourceName);\n }\n\n\n /**\n * Creates a list of MutateOperations that remove all the descendants of the specified\n * AssetGroupListingGroupFilter resource name. The order of removal is post-order, where all the\n * children (and their children, recursively) are removed first. Then, the node itself is\n * removed.\n */\n private List<MutateOperation> removeDescendantsAndFilter(String resourceName) {\n List<MutateOperation> operations = new ArrayList<>();\n\n if (this.parentsToChildren.containsKey(resourceName)) {\n Set<String> children = parentsToChildren.get(resourceName);\n for (String child : children) {\n // Recursively adds operations to the return value that remove each of the child nodes of\n // the current node from the tree.\n operations.addAll(removeDescendantsAndFilter(child));\n }\n }\n\n // Creates and adds an operation to the return value that will remove the current node from\n // the tree.\n AssetGroupListingGroupFilterOperation operation =\n AssetGroupListingGroupFilterOperation.newBuilder().setRemove(resourceName).build();\n operations.add(\n MutateOperation.newBuilder().setAssetGroupListingGroupFilterOperation(operation).build());\n return operations;\n }\n }\n\n\n /**\n * A factory that creates MutateOperations wrapping AssetGroupListingGroupFilterMutateOperations\n * for a specific customerId and assetGroupId.\n *\n * <p>This object is intended to be used with an array of MutateOperations to perform an atomic\n * update to an AssetGroup.\n */\n private static class AssetGroupListingGroupFilterCreateOperationFactory {\n private final long customerId;\n private final long assetGroupId;\n private final long rootListingGroupId;\n private static Iterator<Long> idGenerator;\n\n private AssetGroupListingGroupFilterCreateOperationFactory(\n long customerId, long assetGroupId, long rootListingGroupId) {\n this.customerId = customerId;\n this.assetGroupId = assetGroupId;\n this.rootListingGroupId = rootListingGroupId;\n // Generates a new temporary ID to be used for a resource name in a MutateOperation. See\n // https://developers.google.com/google-ads/api/docs/mutating/best-practices#temporary_resource_names\n // for details about temporary IDs.\n idGenerator = LongStream.iterate(rootListingGroupId - 1, prev -> prev - 1).iterator();\n }\n\n private Long nextId() {\n return idGenerator.next();\n }\n\n /**\n * Creates a MutateOperation that creates a root AssetGroupListingGroupFilter for the factory's\n * AssetGroup.\n *\n * <p>The root node or partition is the default, which is displayed as \"All Products\".\n */\n private MutateOperation createRoot() {\n AssetGroupListingGroupFilter listingGroupFilter =\n AssetGroupListingGroupFilter.newBuilder()\n .setResourceName(\n ResourceNames.assetGroupListingGroupFilter(\n customerId, assetGroupId, rootListingGroupId))\n .setAssetGroup(ResourceNames.assetGroup(customerId, assetGroupId))\n // Since this is the root node, do not set the ParentListingGroupFilter. For all other\n // nodes, this would refer to the parent listing group filter resource name.\n // .setParentListingGroupFilter(\"PARENT_FILTER_NAME\")\n //\n // Unlike AddPerformanceMaxRetailCampaign, the type for the root node here must be\n // SUBDIVISION because it will have child partitions under it.\n .setType(ListingGroupFilterType.SUBDIVISION)\n // Specifies that this uses the shopping listing source because it is a Performance\n // Max campaign for retail.\n .setListingSource(ListingGroupFilterListingSource.SHOPPING)\n // Note the case_value is not set because it should be undefined for the root node.\n .build();\n AssetGroupListingGroupFilterOperation operation =\n AssetGroupListingGroupFilterOperation.newBuilder().setCreate(listingGroupFilter).build();\n return MutateOperation.newBuilder()\n .setAssetGroupListingGroupFilterOperation(operation)\n .build();\n }\n\n\n /**\n * Creates a MutateOperation that creates an intermediate AssetGroupListingGroupFilter for the\n * factory's AssetGroup.\n *\n * <p>Use this method if the filter will have child filters. Otherwise, use the {@link\n * #createUnit(long, long, ListingGroupFilterDimension), createUnit} method.\n *\n * @param parent the ID of the parent AssetGroupListingGroupFilter.\n * @param id the ID of AssetGroupListingGroupFilter that will be created.\n * @param dimension the dimension to associate with the AssetGroupListingGroupFilter.\n */\n private MutateOperation createSubdivision(\n long parent, long id, ListingGroupFilterDimension dimension) {\n AssetGroupListingGroupFilter listingGroupFilter =\n AssetGroupListingGroupFilter.newBuilder()\n .setResourceName(\n ResourceNames.assetGroupListingGroupFilter(customerId, assetGroupId, id))\n .setAssetGroup(ResourceNames.assetGroup(customerId, assetGroupId))\n .setParentListingGroupFilter(\n ResourceNames.assetGroupListingGroupFilter(customerId, assetGroupId, parent))\n // Uses the SUBDIVISION type to indicate that the AssetGroupListingGroupFilter\n // will have children.\n .setType(ListingGroupFilterType.SUBDIVISION)\n // Specifies that this uses the shopping listing source because it is a Performance\n // Max campaign for retail.\n .setListingSource(ListingGroupFilterListingSource.SHOPPING)\n .setCaseValue(dimension)\n .build();\n AssetGroupListingGroupFilterOperation filterOperation =\n AssetGroupListingGroupFilterOperation.newBuilder().setCreate(listingGroupFilter).build();\n return MutateOperation.newBuilder()\n .setAssetGroupListingGroupFilterOperation(filterOperation)\n .build();\n }\n\n\n /**\n * Creates a MutateOperation that creates a child AssetGroupListingGroupFilter for the factory's\n * AssetGroup.\n *\n * <p>Use this method if the filter won't have child filters. Otherwise, use the {@link\n * #createSubdivision(long, long, ListingGroupFilterDimension), createSubdivision} method.\n *\n * @param parent the ID of the parent AssetGroupListingGroupFilter.\n * @param id the ID of AssetGroupListingGroupFilter that will be created.\n * @param dimension the dimension to associate with the AssetGroupListingGroupFilter.\n */\n private MutateOperation createUnit(\n long parent, long id, ListingGroupFilterDimension dimension) {\n AssetGroupListingGroupFilter listingGroupFilter =\n AssetGroupListingGroupFilter.newBuilder()\n .setResourceName(\n ResourceNames.assetGroupListingGroupFilter(customerId, assetGroupId, id))\n .setAssetGroup(ResourceNames.assetGroup(customerId, assetGroupId))\n .setParentListingGroupFilter(\n ResourceNames.assetGroupListingGroupFilter(customerId, assetGroupId, parent))\n // Uses the UNIT_INCLUDED type to indicate that the AssetGroupListingGroupFilter\n // won't have children.\n .setType(ListingGroupFilterType.UNIT_INCLUDED)\n // Specifies that this uses the shopping listing source because it is a Performance\n // Max campaign for retail.\n .setListingSource(ListingGroupFilterListingSource.SHOPPING)\n .setCaseValue(dimension)\n .build();\n AssetGroupListingGroupFilterOperation filterOperation =\n AssetGroupListingGroupFilterOperation.newBuilder().setCreate(listingGroupFilter).build();\n return MutateOperation.newBuilder()\n .setAssetGroupListingGroupFilterOperation(filterOperation)\n .build();\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param assetGroupId the asset group id for the Performance Max campaign.\n * @param replaceExistingTree option to remove existing product tree from the passed in asset\n * group.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n long assetGroupId,\n boolean replaceExistingTree)\n throws Exception {\n String assetGroupResourceName = ResourceNames.assetGroup(customerId, assetGroupId);\n\n List<MutateOperation> operations = new ArrayList<>();\n\n if (replaceExistingTree) {\n List<AssetGroupListingGroupFilter> existingListingGroupFilters =\n getAllExistingListingGroupFilterAssetsInAssetGroup(\n googleAdsClient, customerId, assetGroupResourceName);\n\n if (!existingListingGroupFilters.isEmpty()) {\n // A special factory object that ensures the creation of remove operations in the\n // correct order (child listing group filters must be removed before their parents).\n AssetGroupListingGroupFilterRemoveOperationFactory removeOperationFactory =\n new AssetGroupListingGroupFilterRemoveOperationFactory(existingListingGroupFilters);\n\n operations.addAll(removeOperationFactory.removeAll());\n }\n }\n\n // Uses a factory to create all the MutateOperations that manipulate a specific\n // AssetGroup for a specific customer. The operations returned by the factory's methods\n // are used to construct a new tree of filters. These filters can have parent-child\n // relationships, and also include a special root that includes all children.\n //\n // When creating these filters, temporary IDs are used to create the hierarchy between\n // each of the nodes in the tree, beginning with the root listing group filter.\n //\n // The factory created below is specific to a customerId and assetGroupId.\n AssetGroupListingGroupFilterCreateOperationFactory createOperationFactory =\n new AssetGroupListingGroupFilterCreateOperationFactory(\n customerId, assetGroupId, TEMPORARY_ID_LISTING_GROUP_ROOT);\n\n // Creates the operation to add the root node of the tree.\n operations.add(createOperationFactory.createRoot());\n\n // Creates an operation to add a leaf node for new products.\n ListingGroupFilterDimension newProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductCondition(\n ProductCondition.newBuilder()\n .setCondition(ListingGroupFilterProductCondition.NEW)\n .build())\n .build();\n operations.add(\n createOperationFactory.createUnit(\n TEMPORARY_ID_LISTING_GROUP_ROOT, createOperationFactory.nextId(), newProductDimension));\n\n // Creates an operation to add a leaf node for used products.\n ListingGroupFilterDimension usedProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductCondition(\n ProductCondition.newBuilder()\n .setCondition(ListingGroupFilterProductCondition.USED)\n .build())\n .build();\n operations.add(\n createOperationFactory.createUnit(\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n createOperationFactory.nextId(),\n usedProductDimension));\n\n // This represents the ID of the \"other\" category in the ProductCondition subdivision. This ID\n // is saved because the node with this ID will be further partitioned, and this ID will serve as\n // the parent ID for subsequent child nodes of the \"other\" category.\n long otherSubdivisionId = createOperationFactory.nextId();\n\n // Creates an operation to add a subdivision node for other products in the ProductCondition\n // subdivision.\n ListingGroupFilterDimension otherProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductCondition(ProductCondition.newBuilder().build())\n .build();\n operations.add(\n // Calls createSubdivision because this listing group will have children.\n createOperationFactory.createSubdivision(\n TEMPORARY_ID_LISTING_GROUP_ROOT, otherSubdivisionId, otherProductDimension));\n\n // Creates an operation to add a leaf node for products with the brand \"CoolBrand\".\n ListingGroupFilterDimension coolBrandProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductBrand(ProductBrand.newBuilder().setValue(\"CoolBrand\").build())\n .build();\n operations.add(\n createOperationFactory.createUnit(\n otherSubdivisionId, createOperationFactory.nextId(), coolBrandProductDimension));\n\n // Creates an operation to add a leaf node for products with the brand \"CheapBrand\".\n ListingGroupFilterDimension cheapBrandProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductBrand(ProductBrand.newBuilder().setValue(\"CheapBrand\").build())\n .build();\n operations.add(\n createOperationFactory.createUnit(\n otherSubdivisionId, createOperationFactory.nextId(), cheapBrandProductDimension));\n\n // Creates an operation to add a leaf node for other products in the ProductBrand subdivision.\n ListingGroupFilterDimension otherBrandProductDimension =\n ListingGroupFilterDimension.newBuilder()\n .setProductBrand(ProductBrand.newBuilder().build())\n .build();\n operations.add(\n createOperationFactory.createUnit(\n otherSubdivisionId, createOperationFactory.nextId(), otherBrandProductDimension));\n\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n MutateGoogleAdsRequest request =\n MutateGoogleAdsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .addAllMutateOperations(operations)\n .build();\n MutateGoogleAdsResponse response = googleAdsServiceClient.mutate(request);\n printResponseDetails(request, response);\n }\n }\n\n\n /**\n * Fetches all of the listing group filters in an asset group.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param assetGroupResourceName the resource name of the asset group.\n */\n private List<AssetGroupListingGroupFilter> getAllExistingListingGroupFilterAssetsInAssetGroup(\n GoogleAdsClient googleAdsClient, long customerId, String assetGroupResourceName) {\n String query =\n \"SELECT \"\n + \"asset_group_listing_group_filter.resource_name, \"\n + \"asset_group_listing_group_filter.parent_listing_group_filter \"\n + \"FROM asset_group_listing_group_filter \"\n + \"WHERE \"\n + \"asset_group_listing_group_filter.asset_group = '\"\n + assetGroupResourceName\n + \"'\";\n SearchGoogleAdsRequest request =\n SearchGoogleAdsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setQuery(query)\n .build();\n\n List<AssetGroupListingGroupFilter> resources = new ArrayList<>();\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n SearchPagedResponse searchPagedResponse = googleAdsServiceClient.search(request);\n for (GoogleAdsRow googleAdsRow : searchPagedResponse.iterateAll()) {\n resources.add(googleAdsRow.getAssetGroupListingGroupFilter());\n }\n }\n return resources;\n }\n\n\n /**\n * Prints the details of a MutateGoogleAdsResponse.\n *\n * @param request a MutateGoogleAdsRequest instance.\n * @param response a MutateGoogleAdsResponse instance.\n */\n private void printResponseDetails(\n MutateGoogleAdsRequest request, MutateGoogleAdsResponse response) {\n // Parse the Mutate response to print details about the entities that were removed and/or\n // created in the request.\n for (int i = 0; i < response.getMutateOperationResponsesCount(); i++) {\n MutateOperation operationRequest = request.getMutateOperations(i);\n MutateOperationResponse operationResponse = response.getMutateOperationResponses(i);\n\n if (operationResponse.getResponseCase()\n != ResponseCase.ASSET_GROUP_LISTING_GROUP_FILTER_RESULT) {\n String entityName = operationResponse.getResponseCase().toString();\n // Trim the substring \"_RESULT\" from the end of the entity name.\n entityName = entityName.substring(0, entityName.lastIndexOf(\"_RESULT\"));\n System.out.printf(\"Unsupported entity type: %s%n\", entityName);\n }\n\n String resourceName =\n operationResponse.getAssetGroupListingGroupFilterResult().getResourceName();\n AssetGroupListingGroupFilterOperation assetOperation =\n operationRequest.getAssetGroupListingGroupFilterOperation();\n\n // Converts the type of operation (for example, \"CREATE\") to title case.\n String operationTypeString = assetOperation.getOperationCase().toString();\n String operationTypeTitleCase =\n String.format(\n \"%S%s\",\n operationTypeString.substring(0, 1), operationTypeString.substring(1).toLowerCase());\n\n // Prints information about the completed operation.\n System.out.printf(\n \"%sd a(n) AssetGroupListingGroupFilter with resource name: '%s'%n\",\n operationTypeTitleCase, resourceName);\n }\n }\n}\nAddPerformanceMaxProductListingGroupTree.java\n```\n\nExample:\n```text\n// Copyright 2022 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing Google.Api.Gax;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing static Google.Ads.GoogleAds.V25.Enums.ListingGroupFilterListingSourceEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.ListingGroupFilterProductConditionEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.ListingGroupFilterTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Resources.ListingGroupFilterDimension.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This example shows how to add product partitions to a Performance Max retail campaign.\n ///\n /// For Performance Max campaigns, product partitions are represented using the\n /// AssetGroupListingGroupFilter resource. This resource can be combined with itself to form a\n /// hierarchy that creates a product partition tree.\n ///\n /// For more information about Performance Max retail campaigns, see the\n /// <see cref=\"AddPerformanceMaxRetailCampaign\"/> example.\n /// </summary>\n public class AddPerformanceMaxProductListingGroupTree : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see\n /// cref=\"AddPerformanceMaxProductListingGroupTree\"/>\n /// example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// The Asset Group ID.\n /// </summary>\n [Option(\"assetGroupId\", Required = true, HelpText =\n \"The Asset Group ID.\")]\n public long AssetGroupId { get; set; }\n\n /// <summary>\n /// An option to remove the listing group tree from the asset group when this example is\n /// run.\n /// </summary>\n [Option(\"replaceExistingTree\", Required = false, HelpText =\n \"An option that removes the existing listing group tree from the asset group.\")]\n public bool ReplaceExistingTree { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n AddPerformanceMaxProductListingGroupTree codeExample =\n new AddPerformanceMaxProductListingGroupTree();\n\n Console.WriteLine(codeExample.Description);\n\n codeExample.Run(\n new GoogleAdsClient(),\n options.CustomerId,\n options.AssetGroupId,\n options.ReplaceExistingTree\n );\n }\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This example shows how to add a product listing group tree to a \" +\n \"Performance Max retail campaign.\";\n\n /// <summary>\n /// A factory that creates MutateOperations for removing an existing tree of\n /// AssetGroupListingGroupFilters.\n ///\n /// AssetGroupListingGroupFilters must be removed in a specific order: all of the children\n /// of a filter must be removed before the filter itself, otherwise the API will return an\n /// error.\n ///\n /// This object is intended to be used with an array of MutateOperations to perform a series\n /// of related updates to an AssetGroup.\n /// </summary>\n private class AssetGroupListingGroupFilterRemoveOperationFactory\n {\n private string rootResourceName;\n private Dictionary<string, AssetGroupListingGroupFilter> resources;\n private Dictionary<string, HashSet<string>> parentsToChildren;\n\n public AssetGroupListingGroupFilterRemoveOperationFactory(\n List<AssetGroupListingGroupFilter> resources)\n {\n if (resources.Count == 0)\n {\n throw new InvalidOperationException(\"No listing group filters to remove\");\n }\n\n this.resources = new Dictionary<string, AssetGroupListingGroupFilter>();\n this.parentsToChildren = new Dictionary<string, HashSet<string>>();\n\n foreach (AssetGroupListingGroupFilter filter in resources)\n {\n this.resources[filter.ResourceName] = filter;\n\n // When the node has no parent, it means it's the root node, which is treated\n // differently.\n if (string.IsNullOrEmpty(filter.ParentListingGroupFilter))\n {\n if (!string.IsNullOrEmpty(this.rootResourceName))\n {\n throw new InvalidOperationException(\"More than one root node\");\n }\n\n this.rootResourceName = filter.ResourceName;\n continue;\n }\n\n string parentResourceName = filter.ParentListingGroupFilter;\n\n HashSet<string> siblings;\n\n // Check to see if we've already visited a sibling in this group, and fetch or\n // create a new set as required.\n if (this.parentsToChildren.ContainsKey(parentResourceName))\n {\n siblings = this.parentsToChildren[parentResourceName];\n }\n else\n {\n siblings = new HashSet<string>();\n }\n\n siblings.Add(filter.ResourceName);\n this.parentsToChildren[parentResourceName] = siblings;\n }\n }\n\n /// <summary>\n /// Creates a list of MutateOperations that remove all of the resources in the tree\n /// originally used to create this factory object.\n /// </summary>\n /// <returns>A list of MutateOperations</returns>\n public List<MutateOperation> RemoveAll()\n {\n return this.RemoveDescendentsAndFilter(this.rootResourceName);\n }\n\n\n /// <summary>\n /// Creates a list of MutateOperations that remove all the descendents of the specified\n /// AssetGroupListingGroupFilter resource name. The order of removal is post-order,\n /// where all the children (and their children, recursively) are removed first. Then,\n /// the node itself is removed.\n /// </summary>\n /// <returns>A list of MutateOperations</returns>\n public List<MutateOperation> RemoveDescendentsAndFilter(string resourceName)\n {\n List<MutateOperation> operations = new List<MutateOperation>();\n\n if (this.parentsToChildren.ContainsKey(resourceName))\n {\n HashSet<string> children = this.parentsToChildren[resourceName];\n\n foreach (string child in children)\n {\n operations.AddRange(this.RemoveDescendentsAndFilter(child));\n }\n }\n\n AssetGroupListingGroupFilterOperation operation =\n new AssetGroupListingGroupFilterOperation()\n {\n Remove = resourceName\n };\n\n operations.Add(\n new MutateOperation()\n {\n AssetGroupListingGroupFilterOperation = operation\n }\n );\n\n return operations;\n }\n\n }\n\n private const int TEMPORARY_ID_LISTING_GROUP_ROOT = -1;\n\n /// <summary>\n /// A factory that creates MutateOperations wrapping\n /// AssetGroupListingGroupFilterMutateOperations for a specific customerId and\n /// assetGroupId.\n ///\n /// This object is intended to be used with an array of MutateOperations to perform an\n /// atomic update to an AssetGroup.\n /// </summary>\n private class AssetGroupListingGroupFilterCreateOperationFactory\n {\n private long customerId;\n private long assetGroupId;\n private long rootListingGroupId;\n private long nextId;\n\n public AssetGroupListingGroupFilterCreateOperationFactory(\n long customerId,\n long assetGroupId,\n long rootListingGroupId)\n {\n this.customerId = customerId;\n this.assetGroupId = assetGroupId;\n this.rootListingGroupId = rootListingGroupId;\n this.nextId = this.rootListingGroupId - 1;\n }\n\n /// <summary>\n /// Returns a new temporary ID to be used for a resource name in a MutateOperation. See\n /// https://developers.google.com/google-ads/api/docs/mutating/best-practices#temporary_resource_names\n /// for details about temporary IDs.\n /// </summary>\n /// <returns>A new temporary ID.</returns>\n public long NextId()\n {\n long i = nextId;\n Interlocked.Decrement(ref nextId);\n return i;\n }\n\n /// <summary>\n /// Creates a MutateOperation that creates a root AssetGroupListingGroupFilter for the\n /// factory's AssetGroup.\n ///\n /// The root node or partition is the default, which is displayed as \"All Products\".\n /// </summary>\n /// <returns>A MutateOperation</returns>\n public MutateOperation CreateRoot()\n {\n AssetGroupListingGroupFilter listingGroupFilter = new AssetGroupListingGroupFilter()\n {\n ResourceName = ResourceNames.AssetGroupListingGroupFilter(\n this.customerId,\n this.assetGroupId,\n this.rootListingGroupId\n ),\n\n AssetGroup = ResourceNames.AssetGroup(\n this.customerId,\n this.assetGroupId\n ),\n\n // Since this is the root node, do not set the ParentListingGroupFilter. For all\n // other nodes, this would refer to the parent listing group filter resource\n // name.\n // ParentListingGroupFilter = \"<PARENT FILTER NAME>\"\n\n // Unlike AddPerformanceMaxRetailCampaign, the type for the root node here must\n // be Subdivision because we add child partitions under it.\n Type = ListingGroupFilterType.Subdivision,\n\n // Because this is a Performance Max campaign for retail, we need to specify\n // that this is in the shopping listing source.\n ListingSource = ListingGroupFilterListingSource.Shopping\n };\n\n AssetGroupListingGroupFilterOperation operation =\n new AssetGroupListingGroupFilterOperation()\n {\n Create = listingGroupFilter\n };\n\n return new MutateOperation()\n {\n AssetGroupListingGroupFilterOperation = operation\n };\n }\n\n\n /// <summary>\n /// Creates a MutateOperation that creates a intermediate AssetGroupListingGroupFilter\n /// for the factory's AssetGroup.\n ///\n /// Use this method if the filter will have child filters. Otherwise, use the\n /// CreateUnit method.\n /// </summary>\n /// <param name=\"parent\">The ID of the parent AssetGroupListingGroupFilter.</param>\n /// <param name=\"id\">The ID of AssetGroupListingGroupFilter that will be\n /// created.</param>\n /// <param name=\"dimension\">The dimension to associate with the\n /// AssetGroupListingGroupFilter.</param>\n /// <returns>A MutateOperation</returns>\n public MutateOperation CreateSubdivision(\n long parent,\n long id,\n ListingGroupFilterDimension dimension)\n {\n AssetGroupListingGroupFilter listingGroupFilter = new AssetGroupListingGroupFilter()\n {\n ResourceName = ResourceNames.AssetGroupListingGroupFilter(\n this.customerId,\n this.assetGroupId,\n id\n ),\n\n AssetGroup = ResourceNames.AssetGroup(\n this.customerId,\n this.assetGroupId\n ),\n\n ParentListingGroupFilter = ResourceNames.AssetGroupListingGroupFilter(\n this.customerId,\n this.assetGroupId,\n parent\n ),\n\n // We must use the Subdivision type to indicate that the\n // AssetGroupListingGroupFilter will have children.\n Type = ListingGroupFilterType.Subdivision,\n\n // Because this is a Performance Max campaign for retail, we need to specify\n // that this is in the shopping listing source.\n ListingSource = ListingGroupFilterListingSource.Shopping,\n\n CaseValue = dimension\n };\n\n AssetGroupListingGroupFilterOperation filterOperation =\n new AssetGroupListingGroupFilterOperation()\n {\n Create = listingGroupFilter\n };\n\n return new MutateOperation()\n {\n AssetGroupListingGroupFilterOperation = filterOperation\n };\n }\n\n\n /// <summary>\n /// Creates a MutateOperation that creates a child AssetGroupListingGroupFilter\n /// for the factory's AssetGroup.\n ///\n /// Use this method if the filter won't have child filters. Otherwise, use the\n /// CreateSubdivision method.\n /// </summary>\n /// <param name=\"parent\">The ID of the parent AssetGroupListingGroupFilter.</param>\n /// <param name=\"id\">The ID of AssetGroupListingGroupFilter that will be\n /// created.</param>\n /// <param name=\"dimension\">The dimension to associate with the\n /// AssetGroupListingGroupFilter.</param>\n /// <returns>A MutateOperation</returns>\n public MutateOperation CreateUnit(\n long parent,\n long id,\n ListingGroupFilterDimension dimension)\n {\n AssetGroupListingGroupFilter listingGroupFilter = new AssetGroupListingGroupFilter()\n {\n ResourceName = ResourceNames.AssetGroupListingGroupFilter(\n this.customerId,\n this.assetGroupId,\n id\n ),\n\n AssetGroup = ResourceNames.AssetGroup(\n this.customerId,\n this.assetGroupId\n ),\n\n ParentListingGroupFilter = ResourceNames.AssetGroupListingGroupFilter(\n this.customerId,\n this.assetGroupId,\n parent\n ),\n\n // We must use the UnitIncluded type to indicate that the\n // AssetGroupListingGroupFilter won't have children.\n Type = ListingGroupFilterType.UnitIncluded,\n\n // Because this is a Performance Max campaign for retail, we need to specify\n // that this is in the shopping listing source.\n ListingSource = ListingGroupFilterListingSource.Shopping,\n\n CaseValue = dimension\n };\n\n AssetGroupListingGroupFilterOperation filterOperation =\n new AssetGroupListingGroupFilterOperation()\n {\n Create = listingGroupFilter\n };\n\n return new MutateOperation()\n {\n AssetGroupListingGroupFilterOperation = filterOperation\n };\n }\n\n }\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID.</param>\n /// <param name=\"assetGroupId\">The asset group id for the Performance Max campaign.</param>\n /// <param name=\"replaceExistingTree\">Option to remove existing product tree\n /// from the passed in asset group.</param>\n public void Run(\n GoogleAdsClient client,\n long customerId,\n long assetGroupId,\n bool replaceExistingTree)\n {\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n string assetGroupResourceName = ResourceNames.AssetGroup(customerId, assetGroupId);\n\n // We use a factory to create all the MutateOperations that manipulate a specific\n // AssetGroup for a specific customer. The operations returned by the factory's methods\n // are used to optionally remove all AssetGroupListingGroupFilters from the tree, and\n // then to construct a new tree of filters. These filters can have a parent-child\n // relationship, and also include a special root that includes all children.\n //\n // When creating these filters, we use temporary IDs to create the hierarchy between\n // the root listing group filter, and the subdivisions and leave nodes beneath that.\n //\n // The factory specific to a customerId and assetGroupId is created below.\n AssetGroupListingGroupFilterCreateOperationFactory createOperationFactory =\n new AssetGroupListingGroupFilterCreateOperationFactory(\n customerId,\n assetGroupId,\n TEMPORARY_ID_LISTING_GROUP_ROOT\n );\n\n MutateGoogleAdsRequest request = new MutateGoogleAdsRequest\n {\n CustomerId = customerId.ToString()\n };\n\n if (replaceExistingTree)\n {\n List<AssetGroupListingGroupFilter> existingListingGroupFilters =\n GetAllExistingListingGroupFilterAssetsInAssetGroup(\n client,\n customerId,\n assetGroupResourceName\n );\n\n if (existingListingGroupFilters.Count > 0)\n {\n // A special factory object that ensures the creation of remove operations in the\n // correct order (child listing group filters must be removed before their parents).\n AssetGroupListingGroupFilterRemoveOperationFactory removeOperationFactory =\n new AssetGroupListingGroupFilterRemoveOperationFactory(\n existingListingGroupFilters\n );\n\n request.MutateOperations.AddRange(removeOperationFactory.RemoveAll());\n }\n }\n\n request.MutateOperations.Add(createOperationFactory.CreateRoot());\n\n request.MutateOperations.Add(\n createOperationFactory.CreateUnit(\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n createOperationFactory.NextId(),\n new ListingGroupFilterDimension()\n {\n ProductCondition = new ListingGroupFilterDimension.Types.ProductCondition()\n {\n Condition = ListingGroupFilterProductCondition.New\n }\n }\n )\n );\n\n request.MutateOperations.Add(\n createOperationFactory.CreateUnit(\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n createOperationFactory.NextId(),\n new ListingGroupFilterDimension()\n {\n ProductCondition = new ListingGroupFilterDimension.Types.ProductCondition()\n {\n Condition = ListingGroupFilterProductCondition.Used\n }\n }\n )\n );\n\n // We save this ID because create child nodes underneath it.\n long subdivisionIdConditionOther = createOperationFactory.NextId();\n\n request.MutateOperations.Add(\n // We're calling CreateSubdivision because this listing group will have children.\n createOperationFactory.CreateSubdivision(\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n subdivisionIdConditionOther,\n new ListingGroupFilterDimension()\n {\n // All sibling nodes must have the same dimension type. We use an empty\n // ProductCondition to indicate that this is an \"Other\" partition.\n ProductCondition = new ListingGroupFilterDimension.Types.ProductCondition()\n }\n )\n );\n\n request.MutateOperations.Add(\n createOperationFactory.CreateUnit(\n subdivisionIdConditionOther,\n createOperationFactory.NextId(),\n new ListingGroupFilterDimension()\n {\n ProductBrand = new ProductBrand()\n {\n Value = \"CoolBrand\"\n }\n }\n )\n );\n\n request.MutateOperations.Add(\n createOperationFactory.CreateUnit(\n subdivisionIdConditionOther,\n createOperationFactory.NextId(),\n new ListingGroupFilterDimension()\n {\n ProductBrand = new ProductBrand()\n {\n Value = \"CheapBrand\"\n }\n }\n )\n );\n\n request.MutateOperations.Add(\n createOperationFactory.CreateUnit(\n subdivisionIdConditionOther,\n createOperationFactory.NextId(),\n new ListingGroupFilterDimension()\n {\n ProductBrand = new ProductBrand()\n }\n )\n );\n\n MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);\n\n PrintResponseDetails(request, response);\n }\n\n\n /// <summary>\n /// Fetches all of the listing group filters in an asset group.\n /// </summary>\n /// <param name=\"client\">The Google Ads Client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID.</param>\n /// <param name=\"assetGroupResourceName\">The resource name of the asset group.</param>\n /// <returns>A list of asset group listing filter resources.</returns>\n private List<AssetGroupListingGroupFilter>\n GetAllExistingListingGroupFilterAssetsInAssetGroup(\n GoogleAdsClient client,\n long customerId,\n string assetGroupResourceName)\n {\n List<AssetGroupListingGroupFilter> resources = new List<AssetGroupListingGroupFilter>();\n\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsService = client.GetService(\n Services.V25.GoogleAdsService);\n\n SearchGoogleAdsRequest request = new SearchGoogleAdsRequest()\n {\n CustomerId = customerId.ToString(),\n Query =\n $@\"\n SELECT\n asset_group_listing_group_filter.resource_name,\n asset_group_listing_group_filter.parent_listing_group_filter\n FROM asset_group_listing_group_filter\n WHERE\n asset_group_listing_group_filter.asset_group = '{assetGroupResourceName}'\n \"\n };\n\n // The below enumerable will automatically iterate through the pages of the search\n // request. The limit to the number of listing group filters permitted in a Performance\n // Max campaign can be found here:\n // https://developers.google.com/google-ads/api/docs/best-practices/system-limits\n PagedEnumerable<SearchGoogleAdsResponse, GoogleAdsRow> searchPagedResponse =\n googleAdsService.Search(request);\n\n foreach (GoogleAdsRow row in searchPagedResponse)\n {\n resources.Add(row.AssetGroupListingGroupFilter);\n }\n\n return resources;\n }\n\n\n /// <summary>\n /// Prints the details of a MutateGoogleAdsResponse. Parses the \"response\" oneof field name\n /// and uses it to extract the new entity's name and resource name.\n /// </summary>\n /// <param name=\"request\">A MutateGoogleAdsRequest instance.</param>\n /// <param name=\"response\">A MutateGoogleAdsResponse instance.</param>\n private void PrintResponseDetails(MutateGoogleAdsRequest request, MutateGoogleAdsResponse response)\n {\n // Parse the Mutate response to print details about the entities that were created\n // in the request.\n for (int i = 0; i < response.MutateOperationResponses.Count; i++)\n {\n MutateOperation operationRequest = request.MutateOperations[i];\n MutateOperationResponse operationResponse = response.MutateOperationResponses[i];\n\n if (operationResponse.ResponseCase != MutateOperationResponse.ResponseOneofCase.AssetGroupListingGroupFilterResult)\n {\n string entityName = operationResponse.ResponseCase.ToString();\n // Trim the substring \"Result\" from the end of the entity name.\n entityName = entityName.Remove(entityName.Length - 6);\n\n Console.WriteLine($\"Unsupported entity type: {entityName}\");\n }\n\n string resourceName = operationResponse.AssetGroupListingGroupFilterResult.ResourceName;\n AssetGroupListingGroupFilterOperation assetOperation = operationRequest.AssetGroupListingGroupFilterOperation;\n\n switch (assetOperation.OperationCase)\n {\n case AssetGroupListingGroupFilterOperation.OperationOneofCase.Create:\n Console.WriteLine(\n $\"Created a(n) AssetGroupListingGroupFilter with resource name: '{resourceName}'.\");\n break;\n\n case AssetGroupListingGroupFilterOperation.OperationOneofCase.Remove:\n Console.WriteLine(\n $\"Removed a(n) AssetGroupListingGroupFilter with resource name: '{resourceName}'.\");\n break;\n\n default:\n Console.WriteLine($\"Unsupported operation type: {assetOperation.OperationCase.ToString()}\");\n continue;\n }\n }\n }\n }\n}\nAddPerformanceMaxProductListingGroupTree.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2022 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\ShoppingAds;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\ListingGroupFilterListingSourceEnum\\ListingGroupFilterListingSource;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\ListingGroupFilterProductConditionEnum\\ListingGroupFilterProductCondition;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\ListingGroupFilterTypeEnum\\ListingGroupFilterType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AssetGroupListingGroupFilter;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\ListingGroupFilterDimension;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\ListingGroupFilterDimension\\ProductBrand;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\ListingGroupFilterDimension\\ProductCondition;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetGroupListingGroupFilterOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\GoogleAdsRow;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateGoogleAdsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateGoogleAdsResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateOperationResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\SearchGoogleAdsRequest;\nuse Google\\ApiCore\\ApiException;\nuse Google\\ApiCore\\Serializer;\n\n/**\n * This example shows how to add product partitions to a Performance Max retail campaign.\n *\n * For Performance Max campaigns, product partitions are represented using the\n * AssetGroupListingGroupFilter resource. This resource can be combined with itself to form a\n * hierarchy that creates a product partition tree.\n *\n * For more information about Performance Max retail campaigns, see the\n * AddPerformanceMaxRetailCampaign example.\n */\nclass AddPerformanceMaxProductListingGroupTree\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const ASSET_GROUP_ID = 'INSERT_ASSET_GROUP_ID_HERE';\n // Optional: Removes the existing listing group tree from the asset group or not.\n //\n // If the current asset group already has a tree of listing group filters, and you\n // try to add a new set of listing group filters including a root filter, you'll\n // receive a 'ASSET_GROUP_LISTING_GROUP_FILTER_ERROR_MULTIPLE_ROOTS' error.\n //\n // Setting this option to true will remove the existing tree and prevent this error.\n private const REPLACE_EXISTING_TREE = false;\n\n // We specify temporary IDs that are specific to a single mutate request.\n // Temporary IDs are always negative and unique within one mutate request.\n private const LISTING_GROUP_ROOT_TEMPORARY_ID = -1;\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::ASSET_GROUP_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::REPLACE_EXISTING_TREE => GetOpt::OPTIONAL_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::ASSET_GROUP_ID] ?: self::ASSET_GROUP_ID,\n filter_var(\n $options[ArgumentNames::REPLACE_EXISTING_TREE]\n ?: self::REPLACE_EXISTING_TREE,\n FILTER_VALIDATE_BOOLEAN\n )\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $assetGroupId the asset group ID\n * @param bool $replaceExistingTree true if it should replace the existing listing group\n * tree on the asset group\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $assetGroupId,\n bool $replaceExistingTree\n ) {\n // We create all the mutate operations that manipulate a specific asset group for a specific\n // customer. The operations are used to optionally remove all asset group listing group\n // filters from the tree, and then to construct a new tree of filters. These filters can\n // have a parent-child relationship, and also include a special root that includes all\n // children.\n //\n // When creating these filters, we use temporary IDs to create the hierarchy between\n // the root listing group filter, and the subdivisions and leave nodes beneath that.\n $mutateOperations = [];\n if ($replaceExistingTree === true) {\n $existingListingGroupFilters = self::getAllExistingListingGroupFilterAssetsInAssetGroup(\n $googleAdsClient,\n $customerId,\n ResourceNames::forAssetGroup($customerId, $assetGroupId)\n );\n if (count($existingListingGroupFilters) > 0) {\n $mutateOperations = array_merge(\n $mutateOperations,\n // Ensures the creation of remove operations in the correct order (child listing\n // group filters must be removed before their parents).\n self::createMutateOperationsForRemovingListingGroupFiltersTree(\n $existingListingGroupFilters\n )\n );\n }\n }\n\n $mutateOperations[] = self::createMutateOperationForRoot(\n $customerId,\n $assetGroupId,\n self::LISTING_GROUP_ROOT_TEMPORARY_ID\n );\n\n // The temporary ID to be used for creating subdivisions and units.\n static $tempId = self::LISTING_GROUP_ROOT_TEMPORARY_ID - 1;\n\n $mutateOperations[] = self::createMutateOperationForUnit(\n $customerId,\n $assetGroupId,\n $tempId--,\n self::LISTING_GROUP_ROOT_TEMPORARY_ID,\n new ListingGroupFilterDimension([\n 'product_condition' => new ProductCondition([\n 'condition' => ListingGroupFilterProductCondition::PBNEW\n ])\n ])\n );\n\n $mutateOperations[] = self::createMutateOperationForUnit(\n $customerId,\n $assetGroupId,\n $tempId--,\n self::LISTING_GROUP_ROOT_TEMPORARY_ID,\n new ListingGroupFilterDimension([\n 'product_condition' => new ProductCondition([\n 'condition' => ListingGroupFilterProductCondition::USED\n ])\n ])\n );\n\n // We save this ID to create child nodes underneath it.\n $conditionOtherSubdivisionId = $tempId--;\n\n // We're calling createMutateOperationForSubdivision() because this listing group will\n // have children.\n $mutateOperations[] = self::createMutateOperationForSubdivision(\n $customerId,\n $assetGroupId,\n $conditionOtherSubdivisionId,\n self::LISTING_GROUP_ROOT_TEMPORARY_ID,\n new ListingGroupFilterDimension([\n // All sibling nodes must have the same dimension type. We use an empty\n // ProductCondition to indicate that this is an \"Other\" partition.\n 'product_condition' => new ProductCondition()\n ])\n );\n\n $mutateOperations[] = self::createMutateOperationForUnit(\n $customerId,\n $assetGroupId,\n $tempId--,\n $conditionOtherSubdivisionId,\n new ListingGroupFilterDimension(\n ['product_brand' => new ProductBrand(['value' => 'CoolBrand'])]\n )\n );\n\n $mutateOperations[] = self::createMutateOperationForUnit(\n $customerId,\n $assetGroupId,\n $tempId--,\n $conditionOtherSubdivisionId,\n new ListingGroupFilterDimension([\n 'product_brand' => new ProductBrand(['value' => 'CheapBrand'])\n ])\n );\n\n $mutateOperations[] = self::createMutateOperationForUnit(\n $customerId,\n $assetGroupId,\n $tempId--,\n $conditionOtherSubdivisionId,\n // All other product brands.\n new ListingGroupFilterDimension(['product_brand' => new ProductBrand()])\n );\n\n // Issues a mutate request to create everything and prints its information.\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n $response = $googleAdsServiceClient->mutate(\n MutateGoogleAdsRequest::build($customerId, $mutateOperations)\n );\n\n self::printResponseDetails($mutateOperations, $response);\n }\n\n /**\n * Fetches all of the asset group listing group filters in an asset group.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param string $assetGroupResourceName the resource name of the asset group\n * @return AssetGroupListingGroupFilter[] the list of asset group listing group filters\n */\n private static function getAllExistingListingGroupFilterAssetsInAssetGroup(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $assetGroupResourceName\n ): array {\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n // Creates a query that retrieves asset group listing group filters.\n // The limit to the number of listing group filters permitted in a Performance\n // Max campaign can be found here:\n // https://developers.google.com/google-ads/api/docs/best-practices/system-limits.\n $query = sprintf(\n 'SELECT asset_group_listing_group_filter.resource_name, '\n . 'asset_group_listing_group_filter.parent_listing_group_filter '\n . 'FROM asset_group_listing_group_filter '\n . 'WHERE asset_group_listing_group_filter.asset_group = \"%s\"',\n $assetGroupResourceName\n );\n\n // Issues a search request.\n $response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n $assetGroupListingGroupFilters = [];\n // Iterates over all rows in all pages to get an asset group listing group filter.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $assetGroupListingGroupFilters[] = $googleAdsRow->getAssetGroupListingGroupFilter();\n }\n return $assetGroupListingGroupFilters;\n }\n\n /**\n * Creates mutate operations for removing an existing tree of asset group listing group filters.\n *\n * Asset group listing group filters must be removed in a specific order: all of the children\n * of a filter must be removed before the filter itself, otherwise the API will return an\n * error.\n *\n * @param AssetGroupListingGroupFilter[] $assetGroupListingGroupFilters the existing asset\n * group listing group filters\n * @return MutateOperation[] the list of MutateOperations to remove all listing groups\n */\n private static function createMutateOperationsForRemovingListingGroupFiltersTree(\n array $assetGroupListingGroupFilters\n ): array {\n if (empty($assetGroupListingGroupFilters)) {\n throw new \\UnexpectedValueException('No listing group filters to remove');\n }\n\n $resourceNamesToListingGroupFilters = [];\n $parentsToChildren = [];\n $rootResourceName = null;\n foreach ($assetGroupListingGroupFilters as $assetGroupListingGroupFilter) {\n $resourceNamesToListingGroupFilters[$assetGroupListingGroupFilter->getResourceName()] =\n $assetGroupListingGroupFilter;\n // When the node has no parent, it means it's the root node, which is treated\n // differently.\n if (empty($assetGroupListingGroupFilter->getParentListingGroupFilter())) {\n if (!is_null($rootResourceName)) {\n throw new \\UnexpectedValueException('More than one root node found.');\n }\n $rootResourceName = $assetGroupListingGroupFilter->getResourceName();\n continue;\n }\n\n $parentResourceName = $assetGroupListingGroupFilter->getParentListingGroupFilter();\n $siblings = [];\n\n // Checks to see if we've already visited a sibling in this group and fetches it.\n if (array_key_exists($parentResourceName, $parentsToChildren)) {\n $siblings = $parentsToChildren[$parentResourceName];\n }\n $siblings[] = $assetGroupListingGroupFilter->getResourceName();\n $parentsToChildren[$parentResourceName] = $siblings;\n }\n\n return self::createMutateOperationsForRemovingDescendents(\n $rootResourceName,\n $parentsToChildren\n );\n }\n\n /**\n * Creates a list of mutate operations that remove all the descendents of the specified\n * asset group listing group filter's resource name. The order of removal is post-order,\n * where all the children (and their children, recursively) are removed first. Then,\n * the node itself is removed.\n *\n * @param string $assetGroupListingGroupFilterResourceName the resource name of the root of\n * listing group tree\n * @param array $parentsToChildren the map from parent resource names to children resource\n * names\n * @return MutateOperation[] the list of MutateOperations to remove all listing groups\n */\n private static function createMutateOperationsForRemovingDescendents(\n string $assetGroupListingGroupFilterResourceName,\n array $parentsToChildren\n ): array {\n $operations = [];\n if (array_key_exists($assetGroupListingGroupFilterResourceName, $parentsToChildren)) {\n foreach ($parentsToChildren[$assetGroupListingGroupFilterResourceName] as $child) {\n $operations = array_merge(\n $operations,\n self::createMutateOperationsForRemovingDescendents($child, $parentsToChildren)\n );\n }\n }\n\n $operations[] = new MutateOperation([\n 'asset_group_listing_group_filter_operation'\n => new AssetGroupListingGroupFilterOperation([\n 'remove' => $assetGroupListingGroupFilterResourceName\n ])\n ]);\n return $operations;\n }\n\n /**\n * Creates a mutate operation that creates a root asset group listing group filter for the\n * factory's asset group.\n *\n * The root node or partition is the default, which is displayed as \"All Products\".\n *\n * @param int $customerId the customer ID\n * @param int $assetGroupId the asset group ID\n * @param int $rootListingGroupId the root listing group ID\n * @return MutateOperation the mutate operation for creating the root\n */\n private static function createMutateOperationForRoot(\n int $customerId,\n int $assetGroupId,\n int $rootListingGroupId\n ): MutateOperation {\n $assetGroupListingGroupFilter = new AssetGroupListingGroupFilter([\n 'resource_name' => ResourceNames::forAssetGroupListingGroupFilter(\n $customerId,\n $assetGroupId,\n $rootListingGroupId\n ),\n 'asset_group' => ResourceNames::forAssetGroup($customerId, $assetGroupId),\n // Since this is the root node, do not set the 'parent_listing_group_filter' field. For\n // all other nodes, this would refer to the parent listing group filter resource\n // name.\n\n // Unlike AddPerformanceMaxRetailCampaign, the type for the root node here must\n // be SUBDIVISION because we add child partitions under it.\n 'type' => ListingGroupFilterType::SUBDIVISION,\n // Because this is a Performance Max campaign for retail, we need to specify\n // that this is in the shopping listing source.\n 'listing_source' => ListingGroupFilterListingSource::SHOPPING\n ]);\n\n return new MutateOperation([\n 'asset_group_listing_group_filter_operation'\n => new AssetGroupListingGroupFilterOperation([\n 'create' => $assetGroupListingGroupFilter\n ])\n ]);\n }\n\n /**\n * Creates a mutate operation that creates a intermediate asset group listing group filter.\n *\n * @param int $customerId the customer ID\n * @param int $assetGroupId the asset group ID\n * @param int $assetGroupListingGroupFilterId the ID of the asset group listing group filter to\n * be created\n * @param int $parentId the ID of the parent of asset group listing group filter to be created\n * @param ListingGroupFilterDimension $listingGroupFilterDimension the listing group\n * filter dimension to associate with the asset group listing group filter\n * @return MutateOperation the mutate operation for creating a subdivision\n */\n private static function createMutateOperationForSubdivision(\n int $customerId,\n int $assetGroupId,\n int $assetGroupListingGroupFilterId,\n int $parentId,\n ListingGroupFilterDimension $listingGroupFilterDimension\n ): MutateOperation {\n $assetGroupListingGroupFilter = new AssetGroupListingGroupFilter([\n 'resource_name' => ResourceNames::forAssetGroupListingGroupFilter(\n $customerId,\n $assetGroupId,\n $assetGroupListingGroupFilterId\n ),\n 'asset_group' => ResourceNames::forAssetGroup($customerId, $assetGroupId),\n // Sets the type as a SUBDIVISION, which will allow the node to be the parent of\n // another sub-tree.\n 'type' => ListingGroupFilterType::SUBDIVISION,\n // Because this is a Performance Max campaign for retail, we need to specify\n // that this is in the shopping listing source.\n 'listing_source' => ListingGroupFilterListingSource::SHOPPING,\n 'parent_listing_group_filter' => ResourceNames::forAssetGroupListingGroupFilter(\n $customerId,\n $assetGroupId,\n $parentId\n ),\n // Case values contain the listing dimension used for the node.\n 'case_value' => $listingGroupFilterDimension\n ]);\n\n return new MutateOperation([\n 'asset_group_listing_group_filter_operation'\n => new AssetGroupListingGroupFilterOperation([\n 'create' => $assetGroupListingGroupFilter\n ])\n ]);\n }\n\n /**\n * Creates a mutate operation that creates a child asset group listing group filter (unit\n * node).\n *\n * Use this method if the filter won't have child filters. Otherwise, use\n * createMutateOperationForSubdivision().\n *\n * @param int $customerId the customer ID\n * @param int $assetGroupId the asset group ID\n * @param int $assetGroupListingGroupFilterId the ID of the asset group listing group filter to\n * be created\n * @param int $parentId the ID of the parent of asset group listing group filter to be\n * created\n * @param ListingGroupFilterDimension $listingGroupFilterDimension the listing group\n * filter dimension to associate with the asset group listing group filter\n * @return MutateOperation the mutate operation for creating a unit\n */\n private static function createMutateOperationForUnit(\n int $customerId,\n int $assetGroupId,\n int $assetGroupListingGroupFilterId,\n string $parentId,\n ListingGroupFilterDimension $listingGroupFilterDimension\n ): MutateOperation {\n $assetGroupListingGroupFilter = new AssetGroupListingGroupFilter([\n 'resource_name' => ResourceNames::forAssetGroupListingGroupFilter(\n $customerId,\n $assetGroupId,\n $assetGroupListingGroupFilterId\n ),\n 'asset_group' => ResourceNames::forAssetGroup($customerId, $assetGroupId),\n 'parent_listing_group_filter' => ResourceNames::forAssetGroupListingGroupFilter(\n $customerId,\n $assetGroupId,\n $parentId\n ),\n // Sets the type as a UNIT_INCLUDED to indicate that this asset group listing group\n // filter won't have children.\n 'type' => ListingGroupFilterType::UNIT_INCLUDED,\n // Because this is a Performance Max campaign for retail, we need to specify\n // that this is in the shopping listing source.\n 'listing_source' => ListingGroupFilterListingSource::SHOPPING,\n 'case_value' => $listingGroupFilterDimension\n ]);\n\n return new MutateOperation([\n 'asset_group_listing_group_filter_operation'\n => new AssetGroupListingGroupFilterOperation([\n 'create' => $assetGroupListingGroupFilter\n ])\n ]);\n }\n\n /**\n * Prints the details of a mutate google ads response. Parses the \"response\" oneof field name\n * and uses it to extract the new entity's name and resource name.\n *\n * @param MutateOperation[] $mutateOperations the submitted mutate operations\n * @param MutateGoogleAdsResponse $mutateGoogleAdsResponse the mutate Google Ads response\n */\n private static function printResponseDetails(\n array $mutateOperations,\n MutateGoogleAdsResponse $mutateGoogleAdsResponse\n ): void {\n foreach (\n $mutateGoogleAdsResponse->getMutateOperationResponses() as $i => $operationResponse\n ) {\n /** @var MutateOperationResponse $operationResponse */\n if (\n $operationResponse->getResponse()\n !== 'asset_group_listing_group_filter_result'\n ) {\n // Trims the substring \"_result\" from the end of the entity name.\n printf(\n \"Unsupported entity type: %s.%s\",\n substr($operationResponse->getResponse(), 0, -strlen('_result')),\n PHP_EOL\n );\n continue;\n }\n\n $operation = $mutateOperations[$i]->getAssetGroupListingGroupFilterOperation();\n $getter = Serializer::getGetter($operationResponse->getResponse());\n switch ($operation->getOperation()) {\n case 'create':\n printf(\n \"Created an asset group listing group filter with resource name: \"\n . \" '%s'.%s\",\n $operationResponse->$getter()->getResourceName(),\n PHP_EOL\n );\n break;\n case 'remove':\n printf(\n \"Removed an asset group listing group filter with resource name: \"\n . \" '%s'.%s\",\n $operationResponse->$getter()->getResourceName(),\n PHP_EOL\n );\n break;\n default:\n printf(\n \"Unsupported operation type: '%s'.%s\",\n $operation->getOperation(),\n PHP_EOL\n );\n }\n }\n }\n}\n\nAddPerformanceMaxProductListingGroupTree::main();\nAddPerformanceMaxProductListingGroupTree.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2022 Google LLC\n#\n# 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.\n\"\"\"Shows how to add product partitions to a Performance Max retail campaign.\n\nFor Performance Max campaigns, product partitions are represented using the\nAssetGroupListingGroupFilter resource. This resource can be combined with\nitself to form a hierarchy that creates a product partition tree.\n\nFor more information about Performance Max retail campaigns, see the\nshopping_ads/add_performance_max_retail_campaign.py example.\n\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import Dict, List, Optional\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.resources.types.asset_group_listing_group_filter import (\n ListingGroupFilterDimension,\n)\nfrom google.ads.googleads.v24.resources.types.asset_group_listing_group_filter import (\n AssetGroupListingGroupFilter,\n)\nfrom google.ads.googleads.v24.services.services.google_ads_service import (\n GoogleAdsServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.google_ads_service import (\n MutateGoogleAdsResponse,\n SearchGoogleAdsRequest,\n SearchGoogleAdsResponse,\n)\nfrom google.ads.googleads.v24.services.types.google_ads_service import (\n MutateOperation,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\n#\n# See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n# for further details.\n#\n# These temporary IDs are fixed because they are used in multiple places.\n_TEMPORARY_ID_LISTING_GROUP_ROOT: int = -1\n\n\nclass AssetGroupListingGroupFilterRemoveOperationFactory:\n def __init__(\n self,\n client: GoogleAdsClient,\n listing_group_filters: List[AssetGroupListingGroupFilter],\n ):\n \"\"\"Factory class for creating sorted list of MutateOperations.\n\n The operations remove the given tree of AssetGroupListingGroupFilters,\n When removing these listing group filters, the remove operations must be\n sent in a specific order that removes leaf nodes before removing parent\n nodes.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n listing_group_filters: a list of AssetGroupListingGroupFilters.\n \"\"\"\n if not listing_group_filters:\n raise ValueError(\"No listing group filters to remove.\")\n\n self.client: GoogleAdsClient = client\n self.root_resource_name: Optional[str] = None\n self.parents_to_children: Dict[str, List[str]] = {}\n\n # Process the given list of listing group filters to identify the root\n # node and any parent to child edges in the tree.\n for listing_group_filter_node in listing_group_filters:\n resource_name: str = listing_group_filter_node.resource_name\n parent_resource_name: Optional[str] = (\n listing_group_filter_node.parent_listing_group_filter\n )\n\n # When the node has no parent, it means it's the root node.\n if not parent_resource_name:\n if self.root_resource_name:\n # Check if another root node has already been detected and\n # raise an error if so, as only one root node can exist for\n # a given tree.\n raise ValueError(\"More than one listing group parent node.\")\n else:\n self.root_resource_name = resource_name\n else:\n # Check if we've already visited a sibling in this group, and\n # either update it or create a new branch accordingly.\n if parent_resource_name in self.parents_to_children:\n # If we've visited a sibling already, add this resource\n # name to the existing list.\n self.parents_to_children[parent_resource_name].append(\n resource_name\n )\n else:\n # If we haven't visited any siblings, then create a new list\n # for this parent node and add this resource name to it.\n self.parents_to_children[parent_resource_name] = [\n resource_name\n ]\n\n def remove_all(self) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations for the listing group filter tree.\n\n Returns:\n A list of MutateOperations that remove each specified\n AssetGroupListingGroupFilter in the tree passed in when this\n class was initialized.\n \"\"\"\n if not self.root_resource_name:\n # This case should ideally be prevented by the __init__ check,\n # but as a safeguard for type checking remove_descendants_and_filter.\n return []\n return self.remove_descendants_and_filter(self.root_resource_name)\n\n def remove_descendants_and_filter(\n self, resource_name: str\n ) -> List[MutateOperation]:\n \"\"\"Builds a post-order sorted list of MutateOperations.\n\n Creates a list of MutateOperations that remove all the descendents of\n the specified AssetGroupListingGroupFilter resource name. The order of\n removal is post-order, where all the children (and their children,\n recursively) are removed first. Then, the root node itself is removed.\n\n Args:\n resource_name: an AssetGroupListingGroupFilter resource name.\n\n Returns:\n a sorted list of MutateOperations.\n \"\"\"\n operations: List[MutateOperation] = []\n\n # Check if resource name is a parent.\n if resource_name in self.parents_to_children:\n # If this resource name is a parent, call this method recursively\n # on each of its children.\n for child_resource_name in self.parents_to_children[resource_name]:\n operations.extend(\n self.remove_descendants_and_filter(child_resource_name)\n )\n\n mutate_operation: MutateOperation = self.client.get_type(\n \"MutateOperation\"\n )\n mutate_operation.asset_group_listing_group_filter_operation.remove = (\n resource_name\n )\n operations.append(mutate_operation)\n\n return operations\n\n\nclass AssetGroupListingGroupFilterCreateOperationFactory:\n def __init__(\n self,\n client: GoogleAdsClient,\n customer_id: str,\n asset_group_id: int, # Will be str for path construction\n root_listing_id: int,\n ):\n \"\"\"A factory class for creating MutateOperations.\n\n These operations create new AssetGroupListingGroupFilterMutateOperation\n instances using the given customer ID and asset group ID.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n asset_group_id: the asset group id for the Performance Max campaign.\n root_listing_id: a temporary ID to use as the listing group root.\n \"\"\"\n self.client: GoogleAdsClient = client\n self.customer_id: str = customer_id\n # asset_group_id is used as a string in path construction.\n self.asset_group_id: str = str(asset_group_id)\n self.root_listing_id: int = root_listing_id\n self.next_temp_id: int = self.root_listing_id - 1\n\n def next_id(self) -> int:\n \"\"\"Returns the next temporary ID for use in a sequence.\n\n The temporary IDs are used in the list of MutateOperations in order to\n refer to objects in the request that aren't in the API yet. For more\n details see:\n https://developers.google.com/google-ads/api/docs/mutating/best-practices#temporary_resource_names\n\n Returns:\n A new temporary ID.\n \"\"\"\n self.next_temp_id -= 1\n return self.next_temp_id\n\n def create_root(self) -> MutateOperation:\n \"\"\"Creates a MutateOperation to add a root AssetGroupListingGroupFilter.\n\n Returns:\n A MutateOperation for a new AssetGroupListingGroupFilter.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = self.client.get_service(\n \"GoogleAdsService\"\n )\n\n mutate_operation: MutateOperation = self.client.get_type(\n \"MutateOperation\"\n )\n asset_group_listing_group_filter: AssetGroupListingGroupFilter = (\n mutate_operation.asset_group_listing_group_filter_operation.create\n )\n\n asset_group_listing_group_filter.resource_name = (\n googleads_service.asset_group_listing_group_filter_path(\n self.customer_id,\n self.asset_group_id,\n str(self.root_listing_id),\n )\n )\n asset_group_listing_group_filter.asset_group = (\n googleads_service.asset_group_path(\n self.customer_id, self.asset_group_id\n )\n )\n # Since this is the root node, do not set the\n # parent_listing_group_filter field. For all other nodes, this would\n # refer to the parent listing group filter resource name.\n # asset_group_listing_group_filter.parent_listing_group_filter = \"<PARENT FILTER NAME>\"\n\n # Unlike the add_performance_max_retail_campaign example, the type for\n # the root node here must be a subdivision because we add child\n # partitions under it.\n asset_group_listing_group_filter.type_ = (\n self.client.enums.ListingGroupFilterTypeEnum.SUBDIVISION\n )\n\n # Because this is a Performance Max campaign for retail, we need to\n # specify that this is a shopping listing source.\n asset_group_listing_group_filter.listing_source = (\n self.client.enums.ListingGroupFilterListingSourceEnum.SHOPPING\n )\n\n return mutate_operation\n\n def create_subdivision(\n self,\n parent_id: int,\n temporary_id: int,\n dimension: ListingGroupFilterDimension,\n ) -> MutateOperation:\n \"\"\"Creates a MutateOperation to add an AssetGroupListingGroupFilter.\n\n Use this method if the filter will have child filters. Otherwise use\n the create_unit method.\n\n Args:\n parent_id: the ID of the parent AssetGroupListingGroupFilter.\n temporary_id: a temporary ID for the operation being created.\n dimension: The dimension to associate with this new\n AssetGroupListingGroupFilter.\n\n Returns:\n a MutateOperation for a new AssetGroupListingGroupFilter\n \"\"\"\n googleads_service: GoogleAdsServiceClient = self.client.get_service(\n \"GoogleAdsService\"\n )\n\n mutate_operation: MutateOperation = self.client.get_type(\n \"MutateOperation\"\n )\n asset_group_listing_group_filter: AssetGroupListingGroupFilter = (\n mutate_operation.asset_group_listing_group_filter_operation.create\n )\n\n asset_group_listing_group_filter.resource_name = (\n googleads_service.asset_group_listing_group_filter_path(\n self.customer_id, self.asset_group_id, str(temporary_id)\n )\n )\n asset_group_listing_group_filter.asset_group = (\n googleads_service.asset_group_path(\n self.customer_id, self.asset_group_id\n )\n )\n asset_group_listing_group_filter.parent_listing_group_filter = (\n googleads_service.asset_group_listing_group_filter_path(\n self.customer_id, self.asset_group_id, str(parent_id)\n )\n )\n # We must use the Subdivision type to indicate that the\n # AssetGroupListingGroupFilter will have children.\n asset_group_listing_group_filter.type_ = (\n self.client.enums.ListingGroupFilterTypeEnum.SUBDIVISION\n )\n # Because this is a Performance Max campaign for retail, we need to\n # specify that this is in the shopping listing source.\n asset_group_listing_group_filter.listing_source = (\n self.client.enums.ListingGroupFilterListingSourceEnum.SHOPPING\n )\n asset_group_listing_group_filter.case_value = dimension\n\n return mutate_operation\n\n def create_unit(\n self,\n parent_id: int,\n temporary_id: int,\n dimension: ListingGroupFilterDimension,\n ) -> MutateOperation:\n \"\"\"Creates a MutateOperation to add an AssetGroupListingGroupFilter.\n\n Use this method if the filter will not have child filters. Otherwise use\n the create_subdivision method.\n\n Args:\n parent_id: the ID of the parent AssetGroupListingGroupFilter.\n temporary_id: a temporary ID for the operation being created.\n dimension: The dimension to associate with this new\n AssetGroupListingGroupFilter.\n\n Returns:\n a MutateOperation for a new AssetGroupListingGroupFilter\n \"\"\"\n googleads_service: GoogleAdsServiceClient = self.client.get_service(\n \"GoogleAdsService\"\n )\n\n mutate_operation: MutateOperation = self.client.get_type(\n \"MutateOperation\"\n )\n asset_group_listing_group_filter: AssetGroupListingGroupFilter = (\n mutate_operation.asset_group_listing_group_filter_operation.create\n )\n\n asset_group_listing_group_filter.resource_name = (\n googleads_service.asset_group_listing_group_filter_path(\n self.customer_id, self.asset_group_id, str(temporary_id)\n )\n )\n asset_group_listing_group_filter.asset_group = (\n googleads_service.asset_group_path(\n self.customer_id, self.asset_group_id\n )\n )\n asset_group_listing_group_filter.parent_listing_group_filter = (\n googleads_service.asset_group_listing_group_filter_path(\n self.customer_id, self.asset_group_id, str(parent_id)\n )\n )\n # We must use the UnitIncluded type to indicate that the\n # AssetGroupListingGroupFilter won't have children.\n asset_group_listing_group_filter.type_ = (\n self.client.enums.ListingGroupFilterTypeEnum.UNIT_INCLUDED\n )\n # Because this is a Performance Max campaign for retail, we need to\n # specify that this is in the shopping listing source.\n asset_group_listing_group_filter.listing_source = (\n self.client.enums.ListingGroupFilterListingSourceEnum.SHOPPING\n )\n asset_group_listing_group_filter.case_value = dimension\n\n return mutate_operation\n\n\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n asset_group_id: int, # Will be str for path construction\n replace_existing_tree: bool,\n) -> None:\n \"\"\"The main method that creates all necessary entities for the example.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n asset_group_id: the asset group id for the Performance Max campaign.\n replace_existing_tree: option to remove existing product tree from the\n passed in asset group.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n # asset_group_id is used as a string in path construction.\n asset_group_resource_name: str = googleads_service.asset_group_path(\n customer_id, str(asset_group_id)\n )\n operations: List[MutateOperation] = []\n\n if replace_existing_tree:\n # Retrieve a list of existing AssetGroupListingGroupFilters\n existing_listing_group_filters: List[AssetGroupListingGroupFilter] = (\n get_all_existing_listing_group_filter_assets_in_asset_group(\n client, customer_id, asset_group_resource_name\n )\n )\n\n # If present, create MutateOperations to remove each\n # AssetGroupListingGroupFilter and add them to the list of operations.\n if existing_listing_group_filters:\n remove_operation_factory = (\n AssetGroupListingGroupFilterRemoveOperationFactory(\n client, existing_listing_group_filters\n )\n )\n operations.extend(remove_operation_factory.remove_all())\n\n create_operation_factory = (\n AssetGroupListingGroupFilterCreateOperationFactory(\n client,\n customer_id,\n asset_group_id, # Pass as int, will be converted to str in __init__\n _TEMPORARY_ID_LISTING_GROUP_ROOT,\n )\n )\n\n operations.append(create_operation_factory.create_root())\n\n new_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n new_dimension.product_condition.condition = (\n client.enums.ListingGroupFilterProductConditionEnum.NEW\n )\n operations.append(\n create_operation_factory.create_unit(\n _TEMPORARY_ID_LISTING_GROUP_ROOT,\n create_operation_factory.next_id(),\n new_dimension,\n )\n )\n\n used_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n used_dimension.product_condition.condition = (\n client.enums.ListingGroupFilterProductConditionEnum.USED\n )\n operations.append(\n create_operation_factory.create_unit(\n _TEMPORARY_ID_LISTING_GROUP_ROOT,\n create_operation_factory.next_id(),\n used_dimension,\n )\n )\n\n # We save this ID because create child nodes underneath it.\n subdivision_id_condition_other: int = create_operation_factory.next_id()\n\n # All sibling nodes must have the same dimension type. We use an empty\n # product_condition to indicate that this is an \"Other\" partition.\n other_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n # This triggers the presence of the product_condition field without\n # specifying any field values. This is important in order to tell the API\n # that this is an \"other\" node.\n other_dimension.product_condition._pb.SetInParent()\n # We're calling create_subdivision because this listing group will have\n # children.\n operations.append(\n create_operation_factory.create_subdivision(\n _TEMPORARY_ID_LISTING_GROUP_ROOT,\n subdivision_id_condition_other,\n other_dimension,\n )\n )\n\n cool_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n cool_dimension.product_brand.value = \"CoolBrand\"\n operations.append(\n create_operation_factory.create_unit(\n subdivision_id_condition_other,\n create_operation_factory.next_id(),\n cool_dimension,\n )\n )\n\n cheap_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n cheap_dimension.product_brand.value = \"CheapBrand\"\n operations.append(\n create_operation_factory.create_unit(\n subdivision_id_condition_other,\n create_operation_factory.next_id(),\n cheap_dimension,\n )\n )\n\n empty_dimension: ListingGroupFilterDimension = client.get_type(\n \"ListingGroupFilterDimension\"\n )\n # This triggers the presence of the product_brand field without specifying\n # any field values. This is important in order to tell the API\n # that this is an \"other\" node.\n empty_dimension.product_brand._pb.SetInParent()\n operations.append(\n create_operation_factory.create_unit(\n subdivision_id_condition_other,\n create_operation_factory.next_id(),\n empty_dimension,\n )\n )\n\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id, mutate_operations=operations\n )\n\n print_response_details(operations, response)\n\n\ndef get_all_existing_listing_group_filter_assets_in_asset_group(\n client: GoogleAdsClient,\n customer_id: str,\n asset_group_resource_name: str,\n) -> List[AssetGroupListingGroupFilter]:\n \"\"\"Fetches all of the listing group filters in an asset group.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n asset_group_resource_name: the asset group resource name for the\n Performance Max campaign.\n\n Returns:\n a list of AssetGroupListingGroupFilters.\n \"\"\"\n query: str = f\"\"\"\n SELECT\n asset_group_listing_group_filter.resource_name,\n asset_group_listing_group_filter.parent_listing_group_filter\n FROM asset_group_listing_group_filter\n WHERE asset_group_listing_group_filter.asset_group = '{asset_group_resource_name}'\"\"\"\n\n request: SearchGoogleAdsRequest = client.get_type(\"SearchGoogleAdsRequest\")\n request.customer_id = customer_id\n request.query = query\n\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n response: SearchGoogleAdsResponse = googleads_service.search(\n request=request\n )\n\n return [\n result.asset_group_listing_group_filter\n for result in response\n if result.asset_group_listing_group_filter\n ]\n\n\ndef print_response_details(\n mutate_operations: List[MutateOperation], response: MutateGoogleAdsResponse\n) -> None:\n \"\"\"Prints the details of the GoogleAdsService.Mutate request.\n\n This uses the original list of mutate operations to map the operation\n result to what was sent. It can be assumed that the initial set of\n operations and the list returned in the response are in the same order.\n\n Args:\n mutate_operations: a list of MutateOperation instances.\n response: a GoogleAdsMutateResponse instance.\n \"\"\"\n # Parse the Mutate response to print details about the entities that were\n # created in the request.\n for i, result_operation in enumerate(response.mutate_operation_responses):\n requested_operation: MutateOperation = mutate_operations[i]\n resource_name: str = (\n result_operation.asset_group_listing_group_filter_result.resource_name\n )\n\n # Check the operation type for the requested operation in order to\n # log whether it was a remove or a create request.\n if (\n requested_operation.asset_group_listing_group_filter_operation.remove\n ):\n print(\n \"Removed an AssetGroupListingGroupFilter with resource name: \"\n f\"'{resource_name}'.\"\n )\n elif (\n requested_operation.asset_group_listing_group_filter_operation.create\n ):\n print(\n \"Created an AssetGroupListingGroupFilter with resource name: \"\n f\"'{resource_name}'.\"\n )\n else:\n print(\"An unknown operation was returned.\")\n\n\nif __name__ == \"__main__\":\n parser = argparse.ArgumentParser(\n description=(\n \"Adds product partitions to a Performance Max retail campaign.\"\n )\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-a\",\n \"--asset_group_id\",\n type=int, # Keep as int for argparse, convert to str for API usage\n required=True,\n help=\"The asset group id for the Performance Max campaign.\",\n )\n parser.add_argument(\n \"-r\",\n \"--replace_existing_tree\",\n action=\"store_true\",\n help=(\n \"Whether or not to replace the existing product partition tree. \"\n \"If the current AssetGroup already has a tree of \"\n \"ListingGroupFilters, attempting to add a new set of \"\n \"ListingGroupFilters including a root filter will result in an \"\n \"ASSET_GROUP_LISTING_GROUP_FILTER_ERROR_MULTIPLE_ROOTS error. \"\n \"Setting this option to true will remove the existing tree and \"\n \"prevent this error.\"\n ),\n )\n\n args: argparse.Namespace = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(\n googleads_client,\n args.customer_id,\n args.asset_group_id, # Pass as int, main will handle conversion\n args.replace_existing_tree,\n )\n except GoogleAdsException as ex:\n print(\n f'Request with ID \"{ex.request_id}\" failed with status '\n f'\"{ex.error.code().name}\" and includes the following errors:'\n )\n for error in ex.failure.errors:\n print(f'\\tError with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nadd_performance_max_product_listing_group_tree.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2022 Google LLC\n#\n# 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.\n#\n# This example shows how to add product partitions to a Performance Max retail\n# campaign.\n#\n# For Performance Max campaigns, product partitions are represented using the\n# AssetGroupListingGroupFilter resource. This resource can be combined with\n# itself to form a hierarchy that creates a product partition tree.\n#\n# For more information about Performance Max retail campaigns, see the\n# add_performance_max_retail_campaign.rb example.\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\n#\n# See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n# for further details.\nTEMPORARY_ID_LISTING_GROUP_ROOT = \"-1\"\n\ndef add_performance_max_product_listing_group_tree(\n customer_id,\n asset_group_id,\n replace_existing_tree)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n asset_group_resource_name = client.path.asset_group(\n customer_id,\n asset_group_id,\n )\n\n # We use a factory to create all the MutateOperations that manipulate a\n # specific AssetGroup for a specific customer. The operations returned by the\n # factory's methods are used to optionally remove all\n # AssetGroupListingGroupFilters from the tree, and then to construct a new\n # tree of filters. These filters can have a parent-child relationship, and\n # also include a special root that includes all children.\n #\n # When creating these filters, we use temporary IDs to create the hierarchy\n # between the root listing group filter, and the subdivisions and leave nodes\n # beneath that.\n #\n # The factory specific to a customerId and assetGroupId is created below.\n create_operation_factory = AssetGroupListingGroupFilterCreateOperationFactory.new(\n customer_id,\n asset_group_id,\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n )\n\n operations = []\n\n if replace_existing_tree\n existing_listing_group_filters = get_existing_listing_group_filters_in_asset_group(\n client,\n customer_id,\n asset_group_resource_name,\n )\n\n if existing_listing_group_filters.length > 0\n # A special factory object that ensures the creation of remove operations\n # in the correct order (child listing group filters must be removed\n # before their parents).\n remove_operation_factory = AssetGroupListingGroupFilterRemoveOperationFactory.new(\n existing_listing_group_filters\n )\n\n operations += remove_operation_factory.remove_all(client)\n end\n end\n\n operations << create_operation_factory.create_root(client)\n\n operations << create_operation_factory.create_unit(\n client,\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n create_operation_factory.next_id,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_condition = client.resource.product_condition do |condition|\n condition.condition = :NEW\n end\n end,\n )\n operations << create_operation_factory.create_unit(\n client,\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n create_operation_factory.next_id,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_condition = client.resource.product_condition do |condition|\n condition.condition = :USED\n end\n end,\n )\n\n # We save this ID because we create child nodes underneath it.\n subdivision_id_condition_other = create_operation_factory.next_id\n\n operations << create_operation_factory.create_subdivision(\n client,\n TEMPORARY_ID_LISTING_GROUP_ROOT,\n subdivision_id_condition_other,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_condition = client.resource.product_condition do |condition|\n # All sibling nodes must have the same dimension type. We use an empty\n # ProductCondition to indicate that this is an \"Other\" partition.\n end\n end,\n )\n\n operations << create_operation_factory.create_unit(\n client,\n subdivision_id_condition_other,\n create_operation_factory.next_id,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_brand = client.resource.product_brand do |brand|\n brand.value = 'CoolBrand'\n end\n end,\n )\n operations << create_operation_factory.create_unit(\n client,\n subdivision_id_condition_other,\n create_operation_factory.next_id,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_brand = client.resource.product_brand do |brand|\n brand.value = 'CheapBrand'\n end\n end,\n )\n operations << create_operation_factory.create_unit(\n client,\n subdivision_id_condition_other,\n create_operation_factory.next_id,\n client.resource.listing_group_filter_dimension do |dimension|\n dimension.product_brand = client.resource.product_brand do |brand|\n end\n end,\n )\n\n response = client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: operations,\n )\n\n print_response_details(operations, response)\nend\n\n# Fetches all of the listing group filters in an asset group.\ndef get_existing_listing_group_filters_in_asset_group(client, customer_id, asset_group_resource_name)\n query = <<~QUERY\n SELECT\n asset_group_listing_group_filter.resource_name,\n asset_group_listing_group_filter.parent_listing_group_filter\n FROM asset_group_listing_group_filter\n WHERE\n asset_group_listing_group_filter.asset_group = '#{asset_group_resource_name}'\n QUERY\n response = client.service.google_ads.search(\n customer_id: customer_id,\n query: query,\n )\n\n response.map { |row| row.asset_group_listing_group_filter }\nend\n\ndef print_response_details(operations, response)\n response.mutate_operation_responses.each_with_index do |row, i|\n resource_name = row.asset_group_listing_group_filter_result.resource_name\n operation_type = operations[i].asset_group_listing_group_filter_operation.operation\n case operation_type\n when :create\n puts \"Created AssetGroupListingGroupFilter with resource name '#{resource_name}'.\"\n when :remove\n puts \"Removed AssetGroupListingGroupFilter with resource name '#{resource_name}'.\"\n else\n puts \"Unsupported operation type #{operation_type}.\"\n end\n end\nend\n\n# A factory that creates MutateOperations for removing an existing tree of\n# AssetGroupListingGroupFilters.\n#\n# AssetGroupListingGroupFilters must be removed in a specific order: all of the\n# children of a filter must be removed before the filter itself, otherwise the\n# API will return an error.\n#\n# This object is intended to be used with an array of MutateOperations to\n# perform a series of related updates to an AssetGroup.\nclass AssetGroupListingGroupFilterRemoveOperationFactory\n def initialize(resources)\n raise \"No listing group filters to remove.\" if resources.size == 0\n\n # By default, each node only knows about its parents.\n # However, to remove children first, we need to have a mapping\n # of parents to children, so we build that here.\n @parents_to_children = {}\n\n resources.each do |filter|\n parent_resource_name = filter.parent_listing_group_filter\n\n if parent_resource_name.nil? || parent_resource_name.empty?\n if !@root_resource_name.nil?\n raise \"More than one root node.\"\n end\n\n @root_resource_name = filter.resource_name\n next\n end\n\n siblings = if @parents_to_children.has_key?(parent_resource_name)\n @parents_to_children[parent_resource_name]\n else\n Set.new\n end\n siblings.add(filter.resource_name)\n @parents_to_children[parent_resource_name] = siblings\n end\n end\n\n # Creates a list of MutateOperations that remove all of the resources in the\n # tree originally used to create this factory object.\n def remove_all(client)\n remove_descendents_and_filter(client, @root_resource_name)\n end\n\n # Creates a list of MutateOperations that remove all the descendents of the\n # specified AssetGroupListingGroupFilter resource name. The order of removal\n # is post-order, where all the children (and their children, recursively) are\n # removed first. Then, the node itself is removed.\n def remove_descendents_and_filter(client, resource_name)\n operations = []\n\n if @parents_to_children.has_key?(resource_name)\n @parents_to_children[resource_name].each do |child|\n operations += remove_descendents_and_filter(client, child)\n end\n end\n\n operations << client.operation.mutate do |m|\n m.asset_group_listing_group_filter_operation =\n client.operation.remove_resource.asset_group_listing_group_filter(resource_name)\n end\n\n operations\n end\nend\n\n# A factory that creates MutateOperations wrapping\n# AssetGroupListingGroupFilterMutateOperations for a specific customerId and\n# assetGroupId.\n#\n# This object is intended to be used with an array of MutateOperations to\n# perform an atomic update to an AssetGroup.\nclass AssetGroupListingGroupFilterCreateOperationFactory\n def initialize(customer_id, asset_group_id, root_listing_group_id)\n @customer_id = customer_id\n @asset_group_id = asset_group_id\n @root_listing_group_id = root_listing_group_id.to_i\n @next_id = @root_listing_group_id - 1\n end\n\n # Returns a new temporary ID to be used for a resource name in a\n # MutateOperation. See\n # https://developers.google.com/google-ads/api/docs/mutating/best-practices#temporary_resource_names\n # for details about temporary IDs.\n def next_id\n @next_id -= 1\n end\n\n # Creates a MutateOperation that creates a root AssetGroupListingGroupFilter\n # for the factory's AssetGroup.\n #\n # The root node or partition is the default, which is displayed as \"All\n # Products\".\n def create_root(client)\n operation = client.operation.create_resource.asset_group_listing_group_filter do |lgf|\n lgf.resource_name = client.path.asset_group_listing_group_filter(\n @customer_id,\n @asset_group_id,\n @root_listing_group_id,\n )\n lgf.asset_group = client.path.asset_group(\n @customer_id,\n @asset_group_id,\n )\n\n # Since this is the root node, do not set the ParentListingGroupFilter.\n # For all other nodes, this would refer to the parent listing group\n # filter resource name.\n # lgf.parent_listing_group_filter = \"<PARENT FILTER NAME>\"\n\n # Unlike AddPerformanceMaxRetailCampaign, the type for the root node here\n # must be SUBDIVISION because we add child partitions under it.\n lgf.type = :SUBDIVISION\n\n # Because this is a Performance Max campaign for retail, we need to\n # specify that this is in the SHOPPING listing source.\n lgf.listing_source = :SHOPPING\n end\n\n client.operation.mutate do |m|\n m.asset_group_listing_group_filter_operation = operation\n end\n end\n\n # Creates a MutateOperation that creates a intermediate\n # AssetGroupListingGroupFilter for the factory's AssetGroup.\n #\n # Use this method if the filter will have child filters. Otherwise, use the\n # create_unit method.\n def create_subdivision(client, parent, id, dimension)\n operation = client.operation.create_resource.asset_group_listing_group_filter do |lgf|\n lgf.resource_name = client.path.asset_group_listing_group_filter(\n @customer_id,\n @asset_group_id,\n id,\n )\n lgf.asset_group = client.path.asset_group(\n @customer_id,\n @asset_group_id,\n )\n lgf.parent_listing_group_filter = client.path.asset_group_listing_group_filter(\n @customer_id,\n @asset_group_id,\n parent,\n )\n\n # We must use the SUBDIVISION type to indicate that the\n # AssetGroupListingGroupFilter will have children.\n lgf.type = :SUBDIVISION\n\n # Because this is a Performance Max campaign for retail, we need to\n # specify that this is in the SHOPPING listing source.\n lgf.listing_source = :SHOPPING\n\n lgf.case_value = dimension\n end\n\n client.operation.mutate do |m|\n m.asset_group_listing_group_filter_operation = operation\n end\n end\n\n # Creates a MutateOperation that creates a child AssetGroupListingGroupFilter\n # for the factory's AssetGroup.\n #\n # Use this method if the filter won't have child filters. Otherwise, use the\n # create_subdivision method.\n def create_unit(client, parent, id, dimension)\n operation = client.operation.create_resource.asset_group_listing_group_filter do |lgf|\n lgf.resource_name = client.path.asset_group_listing_group_filter(\n @customer_id,\n @asset_group_id,\n id,\n )\n lgf.asset_group = client.path.asset_group(\n @customer_id,\n @asset_group_id,\n )\n lgf.parent_listing_group_filter = client.path.asset_group_listing_group_filter(\n @customer_id,\n @asset_group_id,\n parent,\n )\n\n # We must use the UNIT_INCLUDED type to indicate that the\n # AssetGroupListingGroupFilter won't have children.\n lgf.type = :UNIT_INCLUDED\n\n # Because this is a Performance Max campaign for retail, we need to\n # specify that this is in the SHOPPING listing source.\n lgf.listing_source = :SHOPPING\n\n lgf.case_value = dimension\n end\n\n client.operation.mutate do |m|\n m.asset_group_listing_group_filter_operation = operation\n end\n end\nend\n\nif __FILE__ == $0\n options = {}\n\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:asset_group_id] = 'INSERT_ASSET_GROUP_ID_HERE'\n options[:replace_existing_tree] = false\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-g', '--asset-group-id ASSET-GROUP-ID', String, 'Asset Group ID') do |v|\n options[:asset_group_id] = v\n end\n\n opts.on('-r', '--replace-existing-tree REPLACE-EXISTING-TREE',\n String, 'Replace existing tree?') do |v|\n options[:replace_existing_tree] = true\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n add_performance_max_product_listing_group_tree(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options.fetch(:asset_group_id),\n options.fetch(:replace_existing_tree),\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\n end\nadd_performance_max_product_listing_group_tree.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2022, Google LLC\n#\n# 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# http://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.\n#\n# This example shows how to add product partitions to a Performance Max retail campaign.\n#\n# For Performance Max campaigns, product partitions are represented using the\n# AssetGroupListingGroupFilter resource. This resource can be combined with itself\n# to form a hierarchy that creates a product partition tree.\n#\n# For more information about Performance Max retail campaigns, see the\n# add_performance_max_retail_campaign example.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::AssetGroupListingGroupFilter;\nuse Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension;\nuse Google::Ads::GoogleAds::V25::Resources::ProductCondition;\nuse Google::Ads::GoogleAds::V25::Resources::ProductBrand;\nuse Google::Ads::GoogleAds::V25::Enums::ListingGroupFilterTypeEnum\n qw(SUBDIVISION UNIT_INCLUDED);\nuse Google::Ads::GoogleAds::V25::Enums::ListingGroupFilterListingSourceEnum\n qw(SHOPPING);\nuse Google::Ads::GoogleAds::V25::Enums::ListingGroupFilterProductConditionEnum\n qw(NEW USED);\nuse Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::AssetGroupListingGroupFilterService::AssetGroupListingGroupFilterOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\nmy $asset_group_id = \"INSERT_ASSET_GROUP_ID_HERE\";\n# Optional: Removes the existing listing group tree from the asset group or not.\n#\n# If the current asset group already has a tree of listing group filters, and you\n# try to add a new set of listing group filters including a root filter, you'll\n# receive a 'ASSET_GROUP_LISTING_GROUP_FILTER_ERROR_MULTIPLE_ROOTS' error.\n#\n# Setting this option to a defined value will remove the existing tree and prevent\n# this error.\nmy $replace_existing_tree = undef;\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\nuse constant LISTING_GROUP_ROOT_TEMPORARY_ID => -1;\n\nsub add_performance_max_product_listing_group_tree {\n my ($api_client, $customer_id, $asset_group_id, $replace_existing_tree) = @_;\n\n # We create all the mutate operations that manipulate a specific asset group for\n # a specific customer. The operations are used to optionally remove all asset\n # group listing group filters from the tree, and then to construct a new tree\n # of filters. These filters can have a parent-child relationship, and also include\n # a special root that includes all children.\n #\n # When creating these filters, we use temporary IDs to create the hierarchy between\n # the root listing group filter, and the subdivisions and leave nodes beneath that.\n my $mutate_operations = [];\n if (defined $replace_existing_tree) {\n my $existing_listing_group_filters =\n get_all_existing_listing_group_filter_assets_in_asset_group(\n $api_client,\n $customer_id,\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, $asset_group_id\n ));\n\n if (scalar @$existing_listing_group_filters > 0) {\n push @$mutate_operations,\n # Ensure the creation of remove operations in the correct order (child\n # listing group filters must be removed before their parents).\n @{\n create_mutate_operations_for_removing_listing_group_filters_tree(\n $existing_listing_group_filters)};\n }\n }\n\n push @$mutate_operations,\n create_mutate_operation_for_root($customer_id, $asset_group_id,\n LISTING_GROUP_ROOT_TEMPORARY_ID);\n\n # The temporary ID to be used for creating subdivisions and units.\n my $temp_id = LISTING_GROUP_ROOT_TEMPORARY_ID - 1;\n\n push @$mutate_operations,\n create_mutate_operation_for_unit(\n $customer_id,\n $asset_group_id,\n $temp_id--,\n LISTING_GROUP_ROOT_TEMPORARY_ID,\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n productCondition =>\n Google::Ads::GoogleAds::V25::Resources::ProductCondition->new({\n condition => NEW\n })}));\n\n push @$mutate_operations,\n create_mutate_operation_for_unit(\n $customer_id,\n $asset_group_id,\n $temp_id--,\n LISTING_GROUP_ROOT_TEMPORARY_ID,\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n productCondition =>\n Google::Ads::GoogleAds::V25::Resources::ProductCondition->new({\n condition => USED\n })}));\n\n # We save this ID to create child nodes underneath it.\n my $condition_other_subdivision_id = $temp_id--;\n\n # We're calling create_mutate_operation_for_subdivision() because this listing\n # group will have children.\n push @$mutate_operations, create_mutate_operation_for_subdivision(\n $customer_id,\n $asset_group_id,\n $condition_other_subdivision_id,\n LISTING_GROUP_ROOT_TEMPORARY_ID,\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n # All sibling nodes must have the same dimension type. We use an empty\n # ProductCondition to indicate that this is an \"Other\" partition.\n productCondition =>\n Google::Ads::GoogleAds::V25::Resources::ProductCondition->new({})}));\n\n push @$mutate_operations,\n create_mutate_operation_for_unit(\n $customer_id,\n $asset_group_id,\n $temp_id--,\n $condition_other_subdivision_id,\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Resources::ProductBrand->new({\n value => \"CoolBrand\"\n })}));\n\n push @$mutate_operations,\n create_mutate_operation_for_unit(\n $customer_id,\n $asset_group_id,\n $temp_id--,\n $condition_other_subdivision_id,\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Resources::ProductBrand->new({\n value => \"CheapBrand\"\n })}));\n\n push @$mutate_operations, create_mutate_operation_for_unit(\n $customer_id,\n $asset_group_id,\n $temp_id--,\n $condition_other_subdivision_id,\n # All other product brands.\n Google::Ads::GoogleAds::V25::Resources::ListingGroupFilterDimension->new({\n productBrand =>\n Google::Ads::GoogleAds::V25::Resources::ProductBrand->new({})}));\n\n # Issue a mutate request to create everything and print its information.\n my $response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $mutate_operations\n });\n\n print_response_details($mutate_operations, $response);\n\n return 1;\n}\n\n# Fetches all of the asset group listing group filters in an asset group.\nsub get_all_existing_listing_group_filter_assets_in_asset_group {\n my ($api_client, $customer_id, $asset_group_resource_name) = @_;\n\n # Create a query that retrieves asset group listing group filters.\n # The limit to the number of listing group filters permitted in a Performance\n # Max campaign can be found here:\n # https://developers.google.com/google-ads/api/docs/best-practices/system-limits.\n my $query =\n sprintf \"SELECT asset_group_listing_group_filter.resource_name, \" .\n \"asset_group_listing_group_filter.parent_listing_group_filter \" .\n \"FROM asset_group_listing_group_filter \" .\n \"WHERE asset_group_listing_group_filter.asset_group = '%s'\",\n $asset_group_resource_name;\n\n # Issue a search request by specifying page size.\n my $response = $api_client->GoogleAdsService()->search({\n customerId => $customer_id,\n query => $query\n });\n\n my $asset_group_listing_group_filters = [];\n # Iterate over all rows in all pages to get an asset group listing group filter.\n foreach my $google_ads_row (@{$response->{results}}) {\n push @$asset_group_listing_group_filters,\n $google_ads_row->{assetGroupListingGroupFilter};\n }\n\n return $asset_group_listing_group_filters;\n}\n\n# Creates mutate operations for removing an existing tree of asset group listing\n# group filters.\n#\n# Asset group listing group filters must be removed in a specific order: all of\n# the children of a filter must be removed before the filter itself, otherwise\n# the API will return an error.\nsub create_mutate_operations_for_removing_listing_group_filters_tree {\n my ($asset_group_listing_group_filters) = @_;\n if (scalar @$asset_group_listing_group_filters == 0) {\n die \"No listing group filters to remove.\";\n }\n\n my $resource_names_to_listing_group_filters = {};\n my $parents_to_children = {};\n my $root_resource_name = undef;\n foreach\n my $asset_group_listing_group_filter (@$asset_group_listing_group_filters)\n {\n $resource_names_to_listing_group_filters->\n {$asset_group_listing_group_filter->{resourceName}} =\n $asset_group_listing_group_filter;\n # When the node has no parent, it means it's the root node, which is treated\n # differently.\n if (!defined $asset_group_listing_group_filter->{parentListingGroupFilter})\n {\n if (defined $root_resource_name) {\n die \"More than one root node found.\";\n }\n $root_resource_name = $asset_group_listing_group_filter->{resourceName};\n next;\n }\n\n my $parent_resource_name =\n $asset_group_listing_group_filter->{parentListingGroupFilter};\n my $siblings = [];\n\n # Check to see if we've already visited a sibling in this group and fetch it.\n if (exists $parents_to_children->{$parent_resource_name}) {\n $siblings = $parents_to_children->{$parent_resource_name};\n }\n push @$siblings, $asset_group_listing_group_filter->{resourceName};\n $parents_to_children->{$parent_resource_name} = $siblings;\n }\n\n return create_mutate_operations_for_removing_descendents($root_resource_name,\n $parents_to_children);\n}\n\n# Creates a list of mutate operations that remove all the descendents of the\n# specified asset group listing group filter's resource name. The order of removal\n# is post-order, where all the children (and their children, recursively) are\n# removed first. Then, the node itself is removed.\nsub create_mutate_operations_for_removing_descendents {\n my ($asset_group_listing_group_filter_resource_name, $parents_to_children) =\n @_;\n\n my $operations = [];\n if (\n exists $parents_to_children->\n {$asset_group_listing_group_filter_resource_name})\n {\n foreach my $child (\n @{$parents_to_children->{$asset_group_listing_group_filter_resource_name}}\n )\n {\n push @$operations,\n @{\n create_mutate_operations_for_removing_descendents($child,\n $parents_to_children)};\n }\n }\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupListingGroupFilterOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupListingGroupFilterService::AssetGroupListingGroupFilterOperation\n ->new({\n remove => $asset_group_listing_group_filter_resource_name\n })});\n\n return $operations;\n}\n\n# Creates a mutate operation that creates a root asset group listing group filter\n# for the factory's asset group.\n#\n# The root node or partition is the default, which is displayed as \"All Products\".\nsub create_mutate_operation_for_root {\n my ($customer_id, $asset_group_id, $root_listing_group_id) = @_;\n\n my $asset_group_listing_group_filter =\n Google::Ads::GoogleAds::V25::Resources::AssetGroupListingGroupFilter->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group_listing_group_filter(\n $customer_id, $asset_group_id, $root_listing_group_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, $asset_group_id\n ),\n # Since this is the root node, do not set the 'parentListingGroupFilter' field.\n # For all other nodes, this would refer to the parent listing group filter\n # resource name.\n\n # Unlike add_performance_max_retail_campaign, the type for the root node\n # here must be SUBDIVISION because we add child partitions under it.\n type => SUBDIVISION,\n # Because this is a Performance Max campaign for retail, we need to specify\n # that this is in the shopping listing source.\n listingSource => SHOPPING\n });\n\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupListingGroupFilterOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupListingGroupFilterService::AssetGroupListingGroupFilterOperation\n ->new({\n create => $asset_group_listing_group_filter\n })});\n}\n\n# Creates a mutate operation that creates a intermediate asset group listing group filter.\nsub create_mutate_operation_for_subdivision {\n my ($customer_id, $asset_group_id, $asset_group_listing_group_filter_id,\n $parent_id, $listing_group_filter_dimension)\n = @_;\n\n my $asset_group_listing_group_filter =\n Google::Ads::GoogleAds::V25::Resources::AssetGroupListingGroupFilter->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group_listing_group_filter(\n $customer_id, $asset_group_id, $asset_group_listing_group_filter_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, $asset_group_id\n ),\n # Set the type as a SUBDIVISION, which will allow the node to be the parent\n # of another sub-tree.\n type => SUBDIVISION,\n # Because this is a Performance Max campaign for retail, we need to specify\n # that this is in the shopping listing source.\n listingSource => SHOPPING,\n parentListingGroupFilter =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group_listing_group_filter(\n $customer_id, $asset_group_id, $parent_id\n ),\n # Case values contain the listing dimension used for the node.\n caseValue => $listing_group_filter_dimension\n });\n\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupListingGroupFilterOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupListingGroupFilterService::AssetGroupListingGroupFilterOperation\n ->new({\n create => $asset_group_listing_group_filter\n })});\n}\n\n# Creates a mutate operation that creates a child asset group listing group filter\n# (unit node).\n#\n# Use this method if the filter won't have child filters. Otherwise, use\n# create_mutate_operation_for_subdivision().\nsub create_mutate_operation_for_unit {\n my ($customer_id, $asset_group_id, $asset_group_listing_group_filter_id,\n $parent_id, $listing_group_filter_dimension)\n = @_;\n\n my $asset_group_listing_group_filter =\n Google::Ads::GoogleAds::V25::Resources::AssetGroupListingGroupFilter->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group_listing_group_filter(\n $customer_id, $asset_group_id, $asset_group_listing_group_filter_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, $asset_group_id\n ),\n parentListingGroupFilter =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group_listing_group_filter(\n $customer_id, $asset_group_id, $parent_id\n ),\n # Set the type as a UNIT_INCLUDED to indicate that this asset group listing\n # group filter won't have children.\n type => UNIT_INCLUDED,\n # Because this is a Performance Max campaign for retail, we need to specify\n # that this is in the shopping listing source.\n listingSource => SHOPPING,\n caseValue => $listing_group_filter_dimension\n });\n\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupListingGroupFilterOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupListingGroupFilterService::AssetGroupListingGroupFilterOperation\n ->new({\n create => $asset_group_listing_group_filter\n })});\n}\n\n# Prints the details of a mutate google ads response. Parses the \"response\" oneof\n# field name and uses it to extract the new entity's name and resource name.\nsub print_response_details {\n my ($mutate_operations, $mutate_google_ads_response) = @_;\n\n while (my ($i, $operation_response) =\n each @{$mutate_google_ads_response->{mutateOperationResponses}})\n {\n if (!exists $operation_response->{assetGroupListingGroupFilterResult}) {\n # Trim the substring \"Result\" from the end of the entity name.\n my $result_type = [keys %$operation_response]->[0];\n printf \"Unsupported entity type: %s.\\n\", $result_type =~ s/Result$//r;\n next;\n }\n\n my $operation =\n $mutate_operations->[$i]{assetGroupListingGroupFilterOperation};\n if (exists $operation->{create}) {\n printf \"Created an asset group listing group filter with resource name: \"\n . \"'%s'.\\n\",\n $operation_response->{assetGroupListingGroupFilterResult}{resourceName};\n } elsif (exists $operation->{remove}) {\n printf \"Removed an asset group listing group filter with resource name: \"\n . \"'%s'.\\n\",\n $operation_response->{assetGroupListingGroupFilterResult}{resourceName};\n } else {\n printf\n \"Unsupported operation type: '%s'.\\n\",\n [keys %$operation]->[0];\n }\n }\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"asset_group_id=i\" => \\$asset_group_id,\n \"replace_existing_tree=s\" => \\$replace_existing_tree\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id, $asset_group_id);\n\n# Call the example.\nadd_performance_max_product_listing_group_tree(\n $api_client, $customer_id =~ s/-//gr,\n $asset_group_id, $replace_existing_tree\n);\n\n=pod\n\n=head1 NAME\n\nadd_performance_max_product_listing_group_tree\n\n=head1 DESCRIPTION\n\nThis example shows how to add product partitions to a Performance Max retail campaign.\n\nFor Performance Max campaigns, product partitions are represented using the\nAssetGroupListingGroupFilter resource. This resource can be combined with itself\nto form a hierarchy that creates a product partition tree.\n\nFor more information about Performance Max retail campaigns, see the\nadd_performance_max_retail_campaign example.\n\n=head1 SYNOPSIS\n\nadd_performance_max_product_listing_group_tree.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -asset_group_id The asset group ID.\n -replace_existing_tree [optional] Whether it should replace the existing\n listing group tree on an asset group.\n\n=cut\nadd_performance_max_product_listing_group_tree.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.395Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":3514,"estimatedTokens":36127}}167{"id":"doc-create_and_manage_audiences_google_ads_api_googl-1ab11a64","source":"documentation","title":"Create and manage audiences | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audiences","text":"Example:\n```text\nSELECT\n audience.id,\n audience.resource_name,\n audience.name,\n audience.status,\n audience.description,\n audience.dimensions,\n audience.exclusion_dimension\nFROM audience\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.397Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":14,"estimatedTokens":53}}168{"id":"doc-add_performance_max_campaign_google_ads_api_goog-e1adca9a","source":"documentation","title":"Add Performance Max Campaign | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/samples/add-performance-max-campaign","text":"Example:\n```text\n// Copyright 2021 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.advancedoperations;\n\nimport static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime;\nimport static com.google.ads.googleads.v25.enums.EuPoliticalAdvertisingStatusEnum.EuPoliticalAdvertisingStatus.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.AudienceInfo;\nimport com.google.ads.googleads.v25.common.ImageAsset;\nimport com.google.ads.googleads.v25.common.LanguageInfo;\nimport com.google.ads.googleads.v25.common.LocationInfo;\nimport com.google.ads.googleads.v25.common.MaximizeConversionValue;\nimport com.google.ads.googleads.v25.common.SearchThemeInfo;\nimport com.google.ads.googleads.v25.common.TextAsset;\nimport com.google.ads.googleads.v25.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType;\nimport com.google.ads.googleads.v25.enums.AssetAutomationStatusEnum.AssetAutomationStatus;\nimport com.google.ads.googleads.v25.enums.AssetAutomationTypeEnum.AssetAutomationType;\nimport com.google.ads.googleads.v25.enums.AssetFieldTypeEnum.AssetFieldType;\nimport com.google.ads.googleads.v25.enums.AssetGroupStatusEnum.AssetGroupStatus;\nimport com.google.ads.googleads.v25.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod;\nimport com.google.ads.googleads.v25.enums.CampaignStatusEnum.CampaignStatus;\nimport com.google.ads.googleads.v25.enums.MessagingRestrictionTypeEnum.MessagingRestrictionType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.Asset;\nimport com.google.ads.googleads.v25.resources.AssetGroup;\nimport com.google.ads.googleads.v25.resources.AssetGroupAsset;\nimport com.google.ads.googleads.v25.resources.AssetGroupSignal;\nimport com.google.ads.googleads.v25.resources.Campaign;\nimport com.google.ads.googleads.v25.resources.Campaign.AssetAutomationSetting;\nimport com.google.ads.googleads.v25.resources.Campaign.MessagingRestriction;\nimport com.google.ads.googleads.v25.resources.Campaign.TextGuidelines;\nimport com.google.ads.googleads.v25.resources.CampaignAsset;\nimport com.google.ads.googleads.v25.resources.CampaignBudget;\nimport com.google.ads.googleads.v25.resources.CampaignCriterion;\nimport com.google.ads.googleads.v25.services.AssetGroupAssetOperation;\nimport com.google.ads.googleads.v25.services.AssetGroupOperation;\nimport com.google.ads.googleads.v25.services.AssetGroupSignalOperation;\nimport com.google.ads.googleads.v25.services.AssetOperation;\nimport com.google.ads.googleads.v25.services.CampaignAssetOperation;\nimport com.google.ads.googleads.v25.services.CampaignBudgetOperation;\nimport com.google.ads.googleads.v25.services.CampaignCriterionOperation;\nimport com.google.ads.googleads.v25.services.CampaignOperation;\nimport com.google.ads.googleads.v25.services.GoogleAdsServiceClient;\nimport com.google.ads.googleads.v25.services.MutateGoogleAdsResponse;\nimport com.google.ads.googleads.v25.services.MutateOperation;\nimport com.google.ads.googleads.v25.services.MutateOperationResponse;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.io.ByteStreams;\nimport com.google.protobuf.ByteString;\nimport com.google.protobuf.Descriptors.FieldDescriptor;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map.Entry;\nimport java.util.stream.Collectors;\nimport org.joda.time.DateTime;\n\n/**\n * This example shows how to create a Performance Max campaign.\n *\n * <p>For more information about Performance Max campaigns, see\n * https://developers.google.com/google-ads/api/docs/performance-max/overview\n *\n * <p>Prerequisites: - You must have at least one conversion action in the account. For more about\n * conversion actions, see\n * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions\n *\n * <p>This example uses the default customer conversion goals. For an example of setting\n * campaign-specific conversion goals, see {@link\n * com.google.ads.googleads.examples.shoppingads.AddPerformanceMaxRetailCampaign}.\n */\npublic class AddPerformanceMaxCampaign {\n\n // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are always\n // negative and unique within one mutate request.\n //\n // <p>See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further\n // details.\n //\n // <p>These temporary IDs are fixed because they are used in multiple places.\n private static final int BUDGET_TEMPORARY_ID = -1;\n private static final int PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = -2;\n private static final int ASSET_GROUP_TEMPORARY_ID = -3;\n\n // There are also entities that will be created in the same request but do not\n // need to be fixed temporary IDs because they are referenced only once.\n private static long temporaryId = ASSET_GROUP_TEMPORARY_ID - 1;\n\n private static class AddPerformanceMaxCampaignParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(\n names = ArgumentNames.AUDIENCE_ID,\n description =\n \"An audience ID to use to improve the targeting of the Performance Max campaign\")\n private Long audienceId;\n\n @Parameter(\n names = ArgumentNames.BRAND_GUIDELINES_ENABLED,\n arity = 1,\n description =\n \"A boolean value indicating if the created campaign is enabled for brand guidelines\")\n private boolean brandGuidelinesEnabled = true;\n }\n\n public static void main(String[] args) throws IOException {\n AddPerformanceMaxCampaignParams params = new AddPerformanceMaxCampaignParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n\n // Optional: Specify an audience ID.\n // params.audienceId = Long.parseLong(\"INSERT_AUDIENCE_ID_HERE\");\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new AddPerformanceMaxCampaign()\n .runExample(\n googleAdsClient, params.customerId, params.audienceId, params.brandGuidelinesEnabled);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param audienceId the optional audience ID.\n * @param brandGuidelinesEnabled indicates if the campaign is enabled for brand guidelines.\n */\n private void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n Long audienceId,\n boolean brandGuidelinesEnabled)\n throws IOException {\n // Performance Max campaigns require that repeated assets such as headlines\n // and descriptions be created before the campaign.\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n //\n // Creates the headlines.\n List<String> headlines = ImmutableList.of(\"Travel\", \"Travel Reviews\", \"Book travel\");\n List<String> headlineAssetResourceNames =\n createMultipleTextAssets(googleAdsClient, customerId, headlines);\n // Creates the descriptions.\n List<String> descriptions = ImmutableList.of(\"Take to the air!\", \"Fly to the sky!\");\n List<String> descriptionAssetResourceNames =\n createMultipleTextAssets(googleAdsClient, customerId, descriptions);\n\n // The below methods create and return MutateOperations that we later\n // provide to the GoogleAdsService.Mutate method in order to create the\n // entities in a single request. Since the entities for a Performance Max\n // campaign are closely tied to one-another, it's considered a best practice\n // to create them in a single Mutate request, so they all complete\n // successfully or fail entirely, leaving no orphaned entities. See:\n // https://developers.google.com/google-ads/api/docs/mutating/overview\n List<MutateOperation> mutateOperations = new ArrayList<>();\n mutateOperations.add(createCampaignBudgetOperation(customerId));\n mutateOperations.add(createPerformanceMaxCampaignOperation(customerId, brandGuidelinesEnabled));\n mutateOperations.addAll(createCampaignCriterionOperations(customerId));\n String assetGroupResourceName = ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID);\n mutateOperations.addAll(\n createAssetGroupOperations(\n customerId,\n assetGroupResourceName,\n headlineAssetResourceNames,\n descriptionAssetResourceNames,\n brandGuidelinesEnabled));\n mutateOperations.addAll(\n createAssetGroupSignalOperations(customerId, assetGroupResourceName, audienceId));\n\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);\n printResponseDetails(response);\n }\n }\n\n /** Creates a MutateOperation that creates a new CampaignBudget. */\n private MutateOperation createCampaignBudgetOperation(long customerId) {\n CampaignBudget campaignBudget =\n CampaignBudget.newBuilder()\n .setName(\"Performance Max campaign budget #\" + getPrintableDateTime())\n // The budget period already defaults to DAILY.\n .setAmountMicros(50_000_000)\n .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)\n // A Performance Max campaign cannot use a shared campaign budget.\n .setExplicitlyShared(false)\n // Set a temporary ID in the budget's resource name, so it can be referenced\n // by the campaign in later steps.\n .setResourceName(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignBudgetOperation(\n CampaignBudgetOperation.newBuilder().setCreate(campaignBudget).build())\n .build();\n }\n\n\n /** Creates a MutateOperation that creates a new Performance Max campaign. */\n private MutateOperation createPerformanceMaxCampaignOperation(\n long customerId, boolean brandGuidelinesEnabled) {\n TextGuidelines textGuidelines =\n TextGuidelines.newBuilder()\n // Specifies a list of terms that should not be used in any auto-generated\n // text assets.\n .addAllTermExclusions(ImmutableList.of(\"cheap\", \"free\"))\n // Specifies freeform messaging restriction prompts that will apply to all\n // auto-generated text assets.\n .addMessagingRestrictions(\n MessagingRestriction.newBuilder()\n .setRestrictionText(\"Don't mention competitor names\")\n .setRestrictionType(\n MessagingRestrictionType.RESTRICTION_BASED_EXCLUSION)\n .build())\n .build();\n Campaign performanceMaxCampaign =\n Campaign.newBuilder()\n .setName(\"Performance Max campaign #\" + getPrintableDateTime())\n // Sets the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n .setStatus(CampaignStatus.PAUSED)\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n .setAdvertisingChannelType(AdvertisingChannelType.PERFORMANCE_MAX)\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n .setMaximizeConversionValue(\n MaximizeConversionValue.newBuilder().setTargetRoas(3.5).build())\n // Sets if the campaign is enabled for brand guidelines. For more information on brand\n // guidelines, see https://support.google.com/google-ads/answer/14934472.\n .setBrandGuidelinesEnabled(brandGuidelinesEnabled)\n // Sets the text guidelines.\n .setTextGuidelines(textGuidelines)\n // Assigns the resource name with a temporary ID.\n .setResourceName(\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID))\n // Sets the budget using the given budget resource name.\n .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n // Optional fields.\n .setStartDateTime(new DateTime().plusDays(1).toString(\"yyyy-MM-dd 00:00:00\"))\n .setEndDateTime(new DateTime().plusDays(365).toString(\"yyyy-MM-dd 23:59:59\"))\n // Configures the optional opt-in/out status for asset automation settings.\n .addAllAssetAutomationSettings(ImmutableList.of(\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_IMAGE_EXTRACTION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(\n AssetAutomationType.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_ENHANCED_YOUTUBE_VIDEOS)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_IMAGE_ENHANCEMENT)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build()))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignOperation(\n CampaignOperation.newBuilder().setCreate(performanceMaxCampaign).build())\n .build();\n }\n\n\n /** Creates a list of MutateOperations that create new campaign criteria. */\n private List<MutateOperation> createCampaignCriterionOperations(long customerId) {\n String campaignResourceName =\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID);\n List<CampaignCriterion> campaignCriteria = new ArrayList<>();\n // Sets the LOCATION campaign criteria.\n // Targets all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n //\n // We will add one positive location target for New York City (ID=1023191)\n // and one negative location target for Brooklyn (ID=1022762).\n // First, adds the positive (negative = False) for New York City.\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n .setLocation(\n LocationInfo.newBuilder()\n .setGeoTargetConstant(ResourceNames.geoTargetConstant(1023191))\n .build())\n .setNegative(false)\n .build());\n // Next adds the negative target for Brooklyn.\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n .setLocation(\n LocationInfo.newBuilder()\n .setGeoTargetConstant(ResourceNames.geoTargetConstant(1022762))\n .build())\n .setNegative(true)\n .build());\n // Sets the LANGUAGE campaign criterion.\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n // Sets the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n .setLanguage(\n LanguageInfo.newBuilder()\n .setLanguageConstant(ResourceNames.languageConstant(1000)) // English\n .build())\n .build());\n // Returns a list of mutate operations with one operation per criterion.\n return campaignCriteria.stream()\n .map(\n criterion ->\n MutateOperation.newBuilder()\n .setCampaignCriterionOperation(\n CampaignCriterionOperation.newBuilder().setCreate(criterion).build())\n .build())\n .collect(Collectors.toList());\n }\n\n\n /** Creates multiple text assets and returns the list of resource names. */\n private List<String> createMultipleTextAssets(\n GoogleAdsClient googleAdsClient, long customerId, List<String> texts) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n for (String text : texts) {\n Asset asset = Asset.newBuilder().setTextAsset(TextAsset.newBuilder().setText(text)).build();\n AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build();\n mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build());\n }\n\n List<String> assetResourceNames = new ArrayList<>();\n // Creates the service client.\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Sends the operations in a single Mutate request.\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);\n for (MutateOperationResponse result : response.getMutateOperationResponsesList()) {\n if (result.hasAssetResult()) {\n assetResourceNames.add(result.getAssetResult().getResourceName());\n }\n }\n printResponseDetails(response);\n }\n return assetResourceNames;\n }\n\n\n /** Creates a list of MutateOperations that create a new AssetGroup. */\n private List<MutateOperation> createAssetGroupOperations(\n long customerId,\n String assetGroupResourceName,\n List<String> headlineAssetResourceNames,\n List<String> descriptionAssetResourceNames,\n boolean brandGuidelinesEnabled)\n throws IOException {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n String campaignResourceName =\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID);\n // Creates the AssetGroup.\n AssetGroup assetGroup =\n AssetGroup.newBuilder()\n .setName(\"Performance Max asset group #\" + getPrintableDateTime())\n .setCampaign(campaignResourceName)\n .addFinalUrls(\"http://www.example.com\")\n .addFinalMobileUrls(\"http://www.example.com\")\n .setStatus(AssetGroupStatus.PAUSED)\n .setResourceName(assetGroupResourceName)\n .build();\n AssetGroupOperation assetGroupOperation =\n AssetGroupOperation.newBuilder().setCreate(assetGroup).build();\n mutateOperations.add(\n MutateOperation.newBuilder().setAssetGroupOperation(assetGroupOperation).build());\n\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // the resource name of the AssetGroup\n // the resource name of the Asset\n // the field_type of the Asset in this AssetGroup.\n\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n // Links the previously created multiple text assets.\n\n // Links the headline assets.\n for (String resourceName : headlineAssetResourceNames) {\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.HEADLINE, resourceName, assetGroupResourceName));\n }\n\n // Links the description assets.\n for (String resourceName : descriptionAssetResourceNames) {\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.DESCRIPTION, resourceName, assetGroupResourceName));\n }\n\n // Creates and links the long headline text asset.\n List<MutateOperation> createAndLinkTextAssetOperations =\n createAndLinkTextAsset(customerId, \"Travel the World\", AssetFieldType.LONG_HEADLINE);\n mutateOperations.addAll(createAndLinkTextAssetOperations);\n\n // Creates and links the business name and logo assets.\n List<MutateOperation> createAndLinkBrandAssets =\n createAndLinkBrandAssets(\n customerId,\n brandGuidelinesEnabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\");\n mutateOperations.addAll(createAndLinkBrandAssets);\n\n // Creates and links the image assets.\n\n // Creates and links the Marketing Image Asset.\n createAndLinkTextAssetOperations =\n createAndLinkImageAsset(\n customerId,\n \"https://gaagl.page.link/Eit5\",\n AssetFieldType.MARKETING_IMAGE,\n \"Marketing Image\");\n mutateOperations.addAll(createAndLinkTextAssetOperations);\n\n // Creates and links the Square Marketing Image Asset.\n createAndLinkTextAssetOperations =\n createAndLinkImageAsset(\n customerId,\n \"https://gaagl.page.link/bjYi\",\n AssetFieldType.SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\");\n mutateOperations.addAll(createAndLinkTextAssetOperations);\n\n return mutateOperations;\n }\n\n\n /** Creates a list of MutateOperations that create a new linked text asset. */\n List<MutateOperation> createAndLinkTextAsset(\n long customerId, String text, AssetFieldType assetFieldType) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());\n // Creates the Text Asset.\n Asset asset =\n Asset.newBuilder()\n .setResourceName(assetResourceName)\n .setTextAsset(TextAsset.newBuilder().setText(text).build())\n .build();\n AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build();\n mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build());\n\n // Creates an AssetGroupAsset to link the Asset to the AssetGroup.\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n assetFieldType,\n assetResourceName,\n ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));\n\n return mutateOperations;\n }\n\n\n /** Creates a list of MutateOperations that create a new linked image asset. */\n List<MutateOperation> createAndLinkImageAsset(\n long customerId, String url, AssetFieldType assetFieldType, String assetName)\n throws IOException {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());\n // Creates a media file.\n byte[] assetBytes = ByteStreams.toByteArray(new URL(url).openStream());\n\n // Creates the Image Asset.\n Asset asset =\n Asset.newBuilder()\n .setResourceName(assetResourceName)\n .setImageAsset(ImageAsset.newBuilder().setData(ByteString.copyFrom(assetBytes)).build())\n // Provides a unique friendly name to identify your asset. When there is an existing\n // image asset with the same content but a different name, the new name will be dropped\n // silently.\n .setName(assetName)\n .build();\n AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build();\n mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build());\n\n // Creates an AssetGroupAsset to link the Asset to the AssetGroup.\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n assetFieldType,\n assetResourceName,\n ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));\n\n return mutateOperations;\n }\n\n /** Creates a list of MutateOperations that create linked brand assets. */\n List<MutateOperation> createAndLinkBrandAssets(\n long customerId,\n boolean brandGuidelinesEnabled,\n String businessName,\n String logoUrl,\n String logoName)\n throws IOException {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n\n // Creates the brand name text asset.\n String businessNameAssetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());\n Asset businessNameAsset =\n Asset.newBuilder()\n .setResourceName(businessNameAssetResourceName)\n .setTextAsset(TextAsset.newBuilder().setText(businessName).build())\n .build();\n AssetOperation businessNameAssetOperation =\n AssetOperation.newBuilder().setCreate(businessNameAsset).build();\n mutateOperations.add(\n MutateOperation.newBuilder().setAssetOperation(businessNameAssetOperation).build());\n\n // Creates the logo image asset.\n String logoAssetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());\n // Creates a media file.\n byte[] logoBytes = ByteStreams.toByteArray(new URL(logoUrl).openStream());\n Asset logoAsset =\n Asset.newBuilder()\n .setResourceName(logoAssetResourceName)\n .setImageAsset(ImageAsset.newBuilder().setData(ByteString.copyFrom(logoBytes)).build())\n // Provides a unique friendly name to identify your asset. When there is an existing\n // image asset with the same content but a different name, the new name will be dropped\n // silently.\n .setName(logoName)\n .build();\n AssetOperation logoImageAssetOperation =\n AssetOperation.newBuilder().setCreate(logoAsset).build();\n mutateOperations.add(\n MutateOperation.newBuilder().setAssetOperation(logoImageAssetOperation).build());\n\n if (brandGuidelinesEnabled) {\n // Creates CampaignAsset resources to link the Asset resources to the Campaign.\n mutateOperations.add(\n createCampaignAssetMutateOperation(\n customerId, AssetFieldType.BUSINESS_NAME, businessNameAssetResourceName));\n mutateOperations.add(\n createCampaignAssetMutateOperation(\n customerId, AssetFieldType.LOGO, logoAssetResourceName));\n } else {\n // Creates an AssetGroupAsset to link the Asset to the AssetGroup.\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.BUSINESS_NAME,\n businessNameAssetResourceName,\n ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.LOGO,\n logoAssetResourceName,\n ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));\n }\n\n return mutateOperations;\n }\n\n /** Creates a MutateOperation to add an AssetGroupAsset. */\n MutateOperation createAssetGroupAssetMutateOperation(\n AssetFieldType fieldType, String assetResourceName, String assetGroupResourceName) {\n AssetGroupAsset assetGroupAsset =\n AssetGroupAsset.newBuilder()\n .setFieldType(fieldType)\n .setAssetGroup(assetGroupResourceName)\n .setAsset(assetResourceName)\n .build();\n AssetGroupAssetOperation assetGroupAssetOperation =\n AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build();\n return MutateOperation.newBuilder()\n .setAssetGroupAssetOperation(assetGroupAssetOperation)\n .build();\n }\n\n /** Creates a MutateOperation to add a CampaignAsset. */\n MutateOperation createCampaignAssetMutateOperation(\n long customerId, AssetFieldType fieldType, String assetResourceName) {\n CampaignAsset campaignAsset =\n CampaignAsset.newBuilder()\n .setFieldType(fieldType)\n .setCampaign(ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID))\n .setAsset(assetResourceName)\n .build();\n CampaignAssetOperation campaignAssetOperation =\n CampaignAssetOperation.newBuilder().setCreate(campaignAsset).build();\n return MutateOperation.newBuilder().setCampaignAssetOperation(campaignAssetOperation).build();\n }\n\n\n /**\n * Creates a list of MutateOperations that create {@link\n * com.google.ads.googleads.v25.resources.AssetGroupSignal} objects.\n */\n private List<MutateOperation> createAssetGroupSignalOperations(\n long customerId, String assetGroupResourceName, Long audienceId) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n\n if (audienceId != null) {\n // Creates an audience asset group signal.\n // To learn more about Audience Signals, see:\n // https://developers.google.com/google-ads/api/performance-max/asset-group-signals#audiences\n AssetGroupSignal audienceSignal =\n AssetGroupSignal.newBuilder()\n .setAssetGroup(assetGroupResourceName)\n .setAudience(\n AudienceInfo.newBuilder()\n .setAudience(ResourceNames.audience(customerId, audienceId)))\n .build();\n\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupSignalOperation(\n AssetGroupSignalOperation.newBuilder().setCreate(audienceSignal))\n .build());\n }\n\n // Creates a search theme asset group signal.\n // To learn more about Search Themes Signals, see:\n // https://developers.google.com/google-ads/api/performance-max/asset-group-signals#search_themes\n AssetGroupSignal searchThemeSignal =\n AssetGroupSignal.newBuilder()\n .setAssetGroup(assetGroupResourceName)\n .setSearchTheme(SearchThemeInfo.newBuilder().setText(\"travel\").build())\n .build();\n\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupSignalOperation(\n AssetGroupSignalOperation.newBuilder().setCreate(searchThemeSignal))\n .build());\n\n return mutateOperations;\n }\n\n /**\n * Prints the details of a MutateGoogleAdsResponse.\n *\n * <p>Parses the \"response\" oneof field name and uses it to extract the new entity's name and\n * resource name.\n */\n private void printResponseDetails(MutateGoogleAdsResponse response) {\n // Parses the Mutate response to print details about the entities that were created by the\n // request.\n String suffix = \"_result\";\n for (MutateOperationResponse result : response.getMutateOperationResponsesList()) {\n for (Entry<FieldDescriptor, Object> responseFields : result.getAllFields().entrySet()) {\n String fieldName = responseFields.getKey().getName();\n String value = responseFields.getValue().toString().trim();\n if (fieldName.endsWith(suffix)) {\n fieldName = fieldName.substring(0, fieldName.length() - suffix.length());\n }\n System.out.printf(\"Created a(n) %s with %s.%n\", fieldName, value);\n }\n }\n }\n\n /** Returns the next temporary ID and decreases it by one. */\n private long getNextTemporaryId() {\n return temporaryId--;\n }\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n// Copyright 2021 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.Gax.Util;\nusing Google.Ads.GoogleAds.Config;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing Google.Protobuf;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing static Google.Ads.GoogleAds.V25.Enums.AdvertisingChannelTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AssetAutomationStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AssetAutomationTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AssetFieldTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AssetGroupStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.CampaignStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.EuPoliticalAdvertisingStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.MessagingRestrictionTypeEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This example shows how to create a Performance Max campaign.\n ///\n /// For more information about Performance Max campaigns, see\n /// https://developers.google.com/google-ads/api/docs/performance-max/overview\n ///\n /// Prerequisites:\n /// - You must have at least one conversion action in the account. For\n /// more about conversion actions, see\n /// https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions\n ///\n /// This example uses the default customer conversion goals. For an example\n /// of setting campaign-specific conversion goals, see\n /// ShoppingAds/AddPerformanceMaxRetailCampaign.cs\n /// </summary>\n public class AddPerformanceMaxCampaign : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"AddPerformanceMaxCampaign\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// Optional: An audience ID to use to improve the targeting of the Performance Max\n /// campaign.\n /// </summary>\n [Option(\"audienceId\", Required = false, HelpText = \"The ID of an audience.\")]\n public long? AudienceId { get; set; }\n\n /// <summary>\n /// Optional: A boolean value indicating if the campaign is enabled for brand\n /// guidelines.\n /// </summary>\n [Option(\"brandGuidelinesEnabled\", Required = false, HelpText =\n \"A boolean value indicating if the campaign is enabled for brand guidelines.\")]\n public bool BrandGuidelinesEnabled { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n AddPerformanceMaxCampaign codeExample = new AddPerformanceMaxCampaign();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(\n new GoogleAdsClient(),\n options.CustomerId,\n options.AudienceId,\n options.BrandGuidelinesEnabled\n );\n }\n\n // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are\n // always negative and unique within one mutate request.\n //\n // See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further\n // details.\n //\n // These temporary IDs are fixed because they are used in multiple places.\n private const int TEMPORARY_ID_BUDGET = -1;\n\n private const int TEMPORARY_ID_CAMPAIGN = -2;\n private const int TEMPORARY_ID_ASSET_GROUP = -3;\n\n // There are also entities that will be created in the same request but do not need to be\n // fixed temporary IDs because they are referenced only once.\n private class AssetTemporaryResourceNameGenerator\n {\n private long customerId;\n private long next;\n\n public AssetTemporaryResourceNameGenerator(long customerId, long assetGroupId)\n {\n this.customerId = customerId;\n this.next = assetGroupId - 1;\n }\n\n public string Next()\n {\n long i = next;\n Interlocked.Decrement(ref next);\n return ResourceNames.Asset(customerId, i);\n }\n }\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This example shows how to create a Performance Max campaign.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID.</param>\n /// <param name=\"audienceId\">The optional audience ID.</param>\n /// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n public void Run(GoogleAdsClient client, long customerId, long? audienceId,\n bool brandGuidelinesEnabled)\n {\n try\n {\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n // Performance Max campaigns require that repeated assets such as headlines and\n // descriptions be created before the campaign.\n //\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n //\n // Create the headlines.\n List<string> headlineAssetResourceNames = CreateMultipleTextAssets(\n client,\n customerId,\n new[] {\n \"Travel\",\n \"Travel Reviews\",\n \"Book travel\"\n }\n );\n\n // Create the descriptions.\n List<string> descriptionAssetResourceNames = CreateMultipleTextAssets(\n client,\n customerId,\n new[] {\n \"Take to the air!\",\n \"Fly to the sky!\"\n }\n );\n\n string tempResourceNameCampaignBudget = ResourceNames.CampaignBudget(\n customerId,\n TEMPORARY_ID_BUDGET\n );\n\n // The below methods create and return MutateOperations that we later provide to\n // the GoogleAdsService.Mutate method in order to create the entities in a single\n // request. Since the entities for a Performance Max campaign are closely tied to\n // one-another, it is considered a best practice to create them in a single Mutate\n // request so they all complete successfully or fail entirely, leaving no\n // orphaned entities.\n //\n // See: https://developers.google.com/google-ads/api/docs/mutating/overview\n MutateOperation campaignBudgetOperation = CreateCampaignBudgetOperation(\n tempResourceNameCampaignBudget\n );\n\n string tempResourceNameCampaign = ResourceNames.Campaign(\n customerId,\n TEMPORARY_ID_CAMPAIGN\n );\n\n MutateOperation performanceMaxCampaignOperation =\n CreatePerformanceMaxCampaignOperation(\n tempResourceNameCampaign,\n tempResourceNameCampaignBudget,\n brandGuidelinesEnabled\n );\n\n List<MutateOperation> campaignCriterionOperations =\n CreateCampaignCriterionOperations(tempResourceNameCampaign);\n\n List<MutateOperation> assetGroupOperations =\n CreateAssetGroupOperations(\n tempResourceNameCampaign,\n ResourceNames.AssetGroup(customerId, TEMPORARY_ID_ASSET_GROUP),\n headlineAssetResourceNames,\n descriptionAssetResourceNames,\n new AssetTemporaryResourceNameGenerator(\n customerId,\n TEMPORARY_ID_ASSET_GROUP\n ),\n client.Config,\n brandGuidelinesEnabled\n );\n\n List<MutateOperation> assetGroupSignalOperations =\n CreateAssetGroupSignalOperations(\n customerId,\n ResourceNames.AssetGroup(customerId, TEMPORARY_ID_ASSET_GROUP),\n audienceId\n );\n\n MutateGoogleAdsRequest request = new MutateGoogleAdsRequest\n {\n CustomerId = customerId.ToString()\n };\n\n // It's important to create these entities in this order because they depend on\n // each other.\n //\n // Additionally, we take several lists of operations and flatten them into one\n // large list.\n request.MutateOperations.Add(campaignBudgetOperation);\n request.MutateOperations.Add(performanceMaxCampaignOperation);\n request.MutateOperations.AddRange(campaignCriterionOperations);\n request.MutateOperations.AddRange(assetGroupOperations);\n request.MutateOperations.AddRange(assetGroupSignalOperations);\n\n MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);\n\n PrintResponseDetails(response);\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Creates a MutateOperation that creates a new CampaignBudget.\n ///\n /// A temporary ID will be assigned to this campaign budget so that it can be\n /// referenced by other objects being created in the same Mutate request.\n /// </summary>\n /// <param name=\"budgetResourceName\">The temporary resource name of the budget to\n /// create.</param>\n /// <returns>A MutateOperation that creates a CampaignBudget.</returns>\n private MutateOperation CreateCampaignBudgetOperation(string budgetResourceName)\n {\n MutateOperation operation = new MutateOperation\n {\n CampaignBudgetOperation = new CampaignBudgetOperation\n {\n Create = new CampaignBudget\n {\n Name = \"Performance Max campaign budget #\"\n + ExampleUtilities.GetRandomString(),\n\n // The budget period already defaults to DAILY.\n AmountMicros = 50000000,\n\n // A Performance Max campaign cannot use a shared campaign budget.\n ExplicitlyShared = false,\n\n // Set a temporary ID in the budget's resource name so it can be referenced\n // by the campaign in later steps.\n ResourceName = budgetResourceName\n }\n }\n };\n\n return operation;\n }\n\n\n /// Creates a MutateOperation that creates a new Performance Max campaign.\n /// <param name=\"campaignResourceName\">The campaign resource name.</param>\n /// <param name=\"campaignBudgetResourceName\">The campaign budget resource name.</param>\n /// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n /// <returns>A MutateOperations that will create this new campaign.</returns>\n private MutateOperation CreatePerformanceMaxCampaignOperation(\n string campaignResourceName,\n string campaignBudgetResourceName,\n bool brandGuidelinesEnabled)\n {\n Campaign.Types.TextGuidelines textGuidelines =\n new Campaign.Types.TextGuidelines();\n textGuidelines.TermExclusions.AddRange([\"cheap\", \"free\"]);\n textGuidelines.MessagingRestrictions.Add(\n new Campaign.Types.MessagingRestriction()\n {\n RestrictionText = \"Don't mention competitor names\",\n RestrictionType = MessagingRestrictionType.RestrictionBasedExclusion\n }\n );\n\n Campaign campaign = new Campaign()\n {\n Name = \"Performance Max campaign #\" + ExampleUtilities.GetRandomString(),\n\n // Set the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n Status = CampaignStatus.Paused,\n\n // All Performance Max campaigns have an AdvertisingChannelType of\n // PerformanceMax. The AdvertisingChannelSubType should not be set.\n AdvertisingChannelType = AdvertisingChannelType.PerformanceMax,\n\n // Bidding strategy must be set directly on the campaign. Setting a\n // portfolio bidding strategy by resource name is not supported. Max\n // Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns. BiddingStrategyType is\n // read-only and cannot be set by the API. An optional ROAS (Return on\n // Advertising Spend) can be set to enable the MaximizeConversionValue\n // bidding strategy. The ROAS value must be specified as a ratio in the API.\n // It is calculated by dividing \"total value\" by \"total spend\".\n //\n // For more information on Maximize Conversion Value, see the support\n // article:\n // http://support.google.com/google-ads/answer/7684216.\n //\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n MaximizeConversionValue = new MaximizeConversionValue()\n {\n TargetRoas = 3.5\n },\n\n // Use the temporary resource name created earlier\n ResourceName = campaignResourceName,\n\n // Set the budget using the given budget resource name.\n CampaignBudget = campaignBudgetResourceName,\n\n // Set if the campaign is enabled for brand guidelines. For more information\n // on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n BrandGuidelinesEnabled = brandGuidelinesEnabled,\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n\n TextGuidelines = textGuidelines,\n\n // Optional fields\n StartDateTime = DateTime.Now.AddDays(1).ToString(\"yyyyMMdd 00:00:00\"),\n EndDateTime = DateTime.Now.AddDays(365).ToString(\"yyyyMMdd 23:59:59\")\n };\n\n campaign.AssetAutomationSettings.AddRange(new[]{\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateImageExtraction,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.FinalUrlExpansionTextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.TextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateEnhancedYoutubeVideos,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateImageEnhancement,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n });\n\n MutateOperation operation = new MutateOperation()\n {\n CampaignOperation = new CampaignOperation()\n {\n Create = campaign\n }\n };\n\n return operation;\n }\n\n\n /// <summary>\n /// Creates a list of MutateOperations that create new campaign criteria.\n /// </summary>\n /// <param name=\"campaignResourceName\">The campaign resource name.</param>\n /// <returns>A list of MutateOperations that create new campaign criteria.</returns>\n private List<MutateOperation> CreateCampaignCriterionOperations(\n string campaignResourceName)\n {\n List<MutateOperation> operations = new List<MutateOperation>();\n\n // Set the LOCATION campaign criteria.\n // Target all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n //\n // We will add one positive location target for New York City (ID=1023191)\n // and one negative location target for Brooklyn (ID=1022762).\n // First, add the positive (negative = False) for New York City.\n MutateOperation operation1 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n Location = new LocationInfo()\n {\n GeoTargetConstant = ResourceNames.GeoTargetConstant(1023191)\n },\n\n Negative = false\n }\n }\n };\n\n operations.Add(operation1);\n\n // Next add the negative target for Brooklyn.\n MutateOperation operation2 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n Location = new LocationInfo()\n {\n GeoTargetConstant = ResourceNames.GeoTargetConstant(1022762)\n },\n\n Negative = true\n }\n }\n };\n\n operations.Add(operation2);\n\n // Set the LANGUAGE campaign criterion.\n MutateOperation operation3 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n\n // Set the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n Language = new LanguageInfo()\n {\n LanguageConstant = ResourceNames.LanguageConstant(1000) // English\n },\n }\n }\n };\n\n operations.Add(operation3);\n\n return operations;\n }\n\n\n /// <summary>\n /// Creates multiple text assets and returns the list of resource names.\n /// </summary>\n /// <param name=\"client\">The Google Ads Client.</param>\n /// <param name=\"customerId\">The customer's ID.</param>\n /// <param name=\"texts\">The texts to add.</param>\n /// <returns>A list of asset resource names.</returns>\n private List<string> CreateMultipleTextAssets(\n GoogleAdsClient client,\n long customerId,\n string[] texts)\n {\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n MutateGoogleAdsRequest request = new MutateGoogleAdsRequest()\n {\n CustomerId = customerId.ToString()\n };\n\n foreach (string text in texts)\n {\n request.MutateOperations.Add(\n new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n TextAsset = new TextAsset()\n {\n Text = text\n }\n }\n }\n }\n );\n }\n\n // Send the operations in a single Mutate request.\n MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);\n\n List<string> assetResourceNames = new List<string>();\n\n foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)\n {\n MutateAssetResult assetResult = operationResponse.AssetResult;\n assetResourceNames.Add(assetResult.ResourceName);\n }\n\n PrintResponseDetails(response);\n\n return assetResourceNames;\n }\n\n\n /// <summary>\n /// Creates a list of MutateOperations that create a new asset_group.\n /// </summary>\n /// <param name=\"campaignResourceName\">The campaign resource name.</param>\n /// <param name=\"assetGroupResourceName\">The asset group resource name.</param>\n /// <param name=\"headlineAssetResourceNames\">The headline asset resource names.</param>\n /// <param name=\"descriptionAssetResourceNames\">The description asset resource\n /// names.</param>\n /// <param name=\"resourceNameGenerator\">A generator for unique temporary ID's.</param>\n /// <param name=\"config\">The Google Ads config.</param>\n /// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n /// <returns>A list of MutateOperations that create the new asset group.</returns>\n private List<MutateOperation> CreateAssetGroupOperations(\n string campaignResourceName,\n string assetGroupResourceName,\n List<string> headlineAssetResourceNames,\n List<string> descriptionAssetResourceNames,\n AssetTemporaryResourceNameGenerator resourceNameGenerator,\n GoogleAdsConfig config,\n bool brandGuidelinesEnabled)\n {\n List<MutateOperation> operations = new List<MutateOperation>();\n\n // Create the AssetGroup\n operations.Add(\n new MutateOperation()\n {\n AssetGroupOperation = new AssetGroupOperation()\n {\n Create = new AssetGroup()\n {\n Name = \"Performance Max asset group #\" +\n ExampleUtilities.GetRandomString(),\n Campaign = campaignResourceName,\n FinalUrls = { \"http://www.example.com\" },\n FinalMobileUrls = { \"http://www.example.com\" },\n Status = AssetGroupStatus.Paused,\n ResourceName = assetGroupResourceName\n }\n }\n }\n );\n\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // the resource name of the AssetGroup\n // the resource name of the Asset\n // the field_type of the Asset in this AssetGroup.\n //\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n // Link the previously created multiple text assets.\n\n // Link the headline assets.\n foreach (string resourceName in headlineAssetResourceNames)\n {\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.Headline,\n AssetGroup = assetGroupResourceName,\n Asset = resourceName\n }\n }\n }\n );\n }\n\n // Link the description assets.\n foreach (string resourceName in descriptionAssetResourceNames)\n {\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.Description,\n AssetGroup = assetGroupResourceName,\n Asset = resourceName\n }\n }\n }\n );\n }\n\n // Create and link the brand assets.\n operations.AddRange(\n CreateAndLinkBrandAssets(\n assetGroupResourceName,\n campaignResourceName,\n resourceNameGenerator,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\",\n config,\n brandGuidelinesEnabled\n )\n );\n\n // Create and link the long headline text asset.\n operations.AddRange(\n CreateAndLinkTextAsset(\n assetGroupResourceName,\n resourceNameGenerator.Next(),\n \"Travel the World\",\n AssetFieldType.LongHeadline\n )\n );\n\n // Create and link the image assets.\n\n // Create and link the Marketing Image Asset.\n operations.AddRange(\n CreateAndLinkImageAsset(\n assetGroupResourceName,\n resourceNameGenerator.Next(),\n \"https://gaagl.page.link/Eit5\",\n AssetFieldType.MarketingImage,\n \"Marketing Image\",\n config\n )\n );\n\n // Create and link the Square Marketing Image Asset.\n operations.AddRange(\n CreateAndLinkImageAsset(\n assetGroupResourceName,\n resourceNameGenerator.Next(),\n \"https://gaagl.page.link/bjYi\",\n AssetFieldType.SquareMarketingImage,\n \"Square Marketing Image\",\n config\n )\n );\n\n return operations;\n }\n\n\n /// <summary>\n /// Creates a list of MutateOperations that create a new linked text asset.\n /// </summary>\n /// <param name=\"assetGroupResourceName\">The resource name of the asset group to be\n /// created.</param>\n /// <param name=\"assetResourceName\">The resource name of the text asset to be\n /// created.</param>\n /// <param name=\"text\">The text of the asset to be created.</param>\n /// <param name=\"fieldType\">The field type of the asset to be created.</param>\n /// <returns>A list of MutateOperations that create the new linked text asset.</returns>\n private List<MutateOperation> CreateAndLinkTextAsset(\n string assetGroupResourceName,\n string assetResourceName,\n string text,\n AssetFieldType fieldType)\n {\n List<MutateOperation> operations = new List<MutateOperation>();\n\n // Create the Text Asset.\n operations.Add(\n new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n ResourceName = assetResourceName,\n TextAsset = new TextAsset()\n {\n Text = text\n }\n }\n }\n }\n );\n\n // Create an AssetGroupAsset to link the Asset to the AssetGroup.\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = fieldType,\n AssetGroup = assetGroupResourceName,\n Asset = assetResourceName\n }\n }\n }\n );\n\n return operations;\n }\n\n\n /// <summary>\n /// Creates a list of MutateOperations that create a new linked image asset.\n /// </summary>\n /// <param name=\"assetGroupResourceName\">The resource name of the asset group to be\n /// created.</param>\n /// <param name=\"assetResourceName\">The resource name of the text asset to be\n /// created.</param>\n /// <param name=\"url\">The url of the image to be retrieved and put into an asset.</param>\n /// <param name=\"fieldType\">The field type of the asset to be created.</param>\n /// <param name=\"assetName\">The asset name.</param>\n /// <param name=\"config\">The Google Ads Config.</param>\n /// <returns>A list of MutateOperations that create a new linked image asset.</returns>\n private List<MutateOperation> CreateAndLinkImageAsset(\n string assetGroupResourceName,\n string assetResourceName,\n string url,\n AssetFieldType fieldType,\n string assetName, GoogleAdsConfig config)\n {\n List<MutateOperation> operations = new List<MutateOperation>();\n\n // Create the Image Asset.\n operations.Add(\n new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n ResourceName = assetResourceName,\n ImageAsset = new ImageAsset()\n {\n Data =\n ByteString.CopyFrom(\n MediaUtilities.GetAssetDataFromUrl(url, config)\n )\n },\n // Provide a unique friendly name to identify your asset.\n // When there is an existing image asset with the same content but a\n // different name, the new name will be dropped silently.\n Name = assetName\n }\n }\n }\n );\n\n // Create an AssetGroupAsset to link the Asset to the AssetGroup.\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = fieldType,\n AssetGroup = assetGroupResourceName,\n Asset = assetResourceName\n }\n }\n }\n );\n\n return operations;\n }\n\n\n /// <summary>\n /// Creates a list of MutateOperations that create and link the brand assets.\n /// </summary>\n /// <param name=\"assetGroupResourceName\">The resource name of the asset group to link assets\n /// to.</param>\n /// <param name=\"campaignResourceName\">The resource name of the campaign to link assets\n /// to.</param>\n /// <param name=\"assetResourceNameGenerator\">The resource name generator of the assets to be\n /// created.</param>\n /// <param name=\"businessName\">The business name text to be put into an asset.</param>\n /// <param name=\"logoUrl\">The url of the logo to be retrieved and put into an asset.</param>\n /// <param name=\"logoName\">The asset name of the logo.</param>\n /// <param name=\"config\">The Google Ads Config.</param>\n /// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n /// <returns>A list of MutateOperations that create a new linked image asset.</returns>\n private List<MutateOperation> CreateAndLinkBrandAssets(\n string assetGroupResourceName,\n string campaignResourceName,\n AssetTemporaryResourceNameGenerator assetResourceNameGenerator,\n string businessName,\n string logoUrl,\n string logoName,\n GoogleAdsConfig config,\n bool brandGuidelinesEnabled)\n {\n List<MutateOperation> operations = new List<MutateOperation>();\n\n string logoAssetResourceName = assetResourceNameGenerator.Next();\n string businessNameAssetResourceName = assetResourceNameGenerator.Next();\n\n // Create the Image Asset.\n operations.Add(\n new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n ResourceName = logoAssetResourceName,\n ImageAsset = new ImageAsset()\n {\n Data =\n ByteString.CopyFrom(\n MediaUtilities.GetAssetDataFromUrl(logoUrl, config)\n )\n },\n // Provide a unique friendly name to identify your asset.\n // When there is an existing image asset with the same content but a\n // different name, the new name will be dropped silently.\n Name = logoName\n }\n }\n }\n );\n\n // Create the business name asset.\n operations.Add(\n new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n ResourceName = businessNameAssetResourceName,\n TextAsset = new TextAsset()\n {\n Text = businessName,\n }\n }\n }\n }\n );\n\n if (brandGuidelinesEnabled)\n {\n // Create CampaignAssets to link the Assets to the Campaign.\n operations.Add(\n new MutateOperation()\n {\n CampaignAssetOperation = new CampaignAssetOperation()\n {\n Create = new CampaignAsset()\n {\n FieldType = AssetFieldType.Logo,\n Campaign = campaignResourceName,\n Asset = logoAssetResourceName\n }\n }\n }\n );\n\n operations.Add(\n new MutateOperation()\n {\n CampaignAssetOperation = new CampaignAssetOperation()\n {\n Create = new CampaignAsset()\n {\n FieldType = AssetFieldType.BusinessName,\n Campaign = campaignResourceName,\n Asset = businessNameAssetResourceName\n }\n }\n }\n );\n } else {\n // Create AssetGroupAssets to link the Assets to the AssetGroup.\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.Logo,\n AssetGroup = assetGroupResourceName,\n Asset = logoAssetResourceName\n }\n }\n }\n );\n\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.BusinessName,\n AssetGroup = assetGroupResourceName,\n Asset = businessNameAssetResourceName\n }\n }\n }\n );\n\n }\n\n\n return operations;\n }\n\n /// <summary>\n /// Creates a list of MutateOperations that may create AssetGroupSignals\n /// </summary>\n /// <param name=\"customerId\">The customer ID.</param>\n /// <param name=\"assetGroupResourceName\">The resource name of the asset group to be\n /// created.</param>\n /// <param name=\"audienceId\">The optional audience ID.</param>\n /// <returns>A list of MutateOperations that create may create AssetGroupSignals.</returns>\n private List<MutateOperation> CreateAssetGroupSignalOperations(\n long customerId,\n string assetGroupResourceName,\n long? audienceId)\n {\n List<MutateOperation> operations = new List<MutateOperation>();\n\n if (audienceId.HasValue)\n {\n // Create an audience asset group signal.\n // To learn more about Audience Signals, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals\n operations.Add(\n new MutateOperation()\n {\n AssetGroupSignalOperation = new AssetGroupSignalOperation()\n {\n Create = new AssetGroupSignal()\n {\n AssetGroup = assetGroupResourceName,\n Audience = new AudienceInfo()\n {\n Audience = ResourceNames.Audience(customerId, audienceId.Value)\n }\n }\n }\n }\n );\n }\n\n // Create a search theme asset group signal.\n // To learn more about Search Themes Signals, see:\n // https://developers.google.com/google-ads/api/performance-max/asset-group-signals#search_themes\n operations.Add(\n new MutateOperation()\n {\n AssetGroupSignalOperation = new AssetGroupSignalOperation()\n {\n Create = new AssetGroupSignal()\n {\n AssetGroup = assetGroupResourceName,\n SearchTheme = new SearchThemeInfo()\n {\n Text = \"travel\"\n }\n }\n }\n }\n );\n\n return operations;\n }\n\n /// <summary>\n /// Prints the details of a MutateGoogleAdsResponse. Parses the \"response\" oneof field name\n /// and uses it to extract the new entity's name and resource name.\n /// </summary>\n /// <param name=\"response\">A MutateGoogleAdsResponse instance.</param>\n private void PrintResponseDetails(MutateGoogleAdsResponse response)\n {\n // Parse the Mutate response to print details about the entities that were created\n // in the request.\n foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)\n {\n string entityName = operationResponse.ResponseCase.ToString();\n // Trim the substring \"Result\" from the end of the entity name.\n entityName = entityName.Remove(entityName.Length - 6);\n\n string resourceName;\n switch (operationResponse.ResponseCase)\n {\n case MutateOperationResponse.ResponseOneofCase.AdGroupResult:\n resourceName = operationResponse.AdGroupResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AdGroupAdResult:\n resourceName = operationResponse.AdGroupAdResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.CampaignResult:\n resourceName = operationResponse.CampaignResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.CampaignBudgetResult:\n resourceName = operationResponse.CampaignBudgetResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.CampaignCriterionResult:\n resourceName = operationResponse.CampaignCriterionResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.SmartCampaignSettingResult:\n resourceName = operationResponse.SmartCampaignSettingResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AssetResult:\n resourceName = operationResponse.AssetResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AssetGroupResult:\n resourceName = operationResponse.AssetGroupResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AssetGroupAssetResult:\n resourceName = operationResponse.AssetGroupAssetResult.ResourceName;\n break;\n\n default:\n resourceName = \"<not found>\";\n break;\n }\n\n Console.WriteLine(\n $\"Created a(n) {entityName} with resource name: '{resourceName}'.\");\n }\n }\n }\n}AddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2021 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\AdvancedOperations;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\AudienceInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ImageAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\LanguageInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\LocationInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\MaximizeConversionValue;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\TextAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetAutomationTypeEnum\\AssetAutomationType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetAutomationStatusEnum\\AssetAutomationStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdvertisingChannelTypeEnum\\AdvertisingChannelType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetFieldTypeEnum\\AssetFieldType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetGroupStatusEnum\\AssetGroupStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\BudgetDeliveryMethodEnum\\BudgetDeliveryMethod;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\CampaignStatusEnum\\CampaignStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\EuPoliticalAdvertisingStatusEnum\\EuPoliticalAdvertisingStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Asset;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AssetGroup;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AssetGroupAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AssetGroupSignal;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Campaign;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Campaign\\AssetAutomationSetting;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CampaignAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CampaignBudget;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CampaignCriterion;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetGroupAssetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetGroupOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetGroupSignalOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignAssetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignBudgetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignCriterionOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateGoogleAdsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateGoogleAdsResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateOperationResponse;\nuse Google\\ApiCore\\ApiException;\nuse Google\\ApiCore\\Serializer;\n\n/**\n * This example shows how to create a Performance Max campaign.\n *\n * For more information about Performance Max campaigns, see\n * https://developers.google.com/google-ads/api/docs/performance-max/overview.\n *\n * Prerequisites:\n * - You must have at least one conversion action in the account. For more about conversion actions,\n * see\n * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n *\n * This example uses the default customer conversion goals. For an example of setting\n * campaign-specific conversion goals, see ShoppingAds/AddPerformanceMaxRetailCampaign.php.\n */\nclass AddPerformanceMaxCampaign\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n // Optional: An audience ID to use to improve the targeting of the Performance Max campaign.\n private const AUDIENCE_ID = null;\n // Optional: Indicates whether the created campaign is enabled for brand guidelines.\n private const BRAND_GUIDELINES_ENABLED = true;\n\n // We specify temporary IDs that are specific to a single mutate request.\n // Temporary IDs are always negative and unique within one mutate request.\n //\n // See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n // for further details.\n //\n // These temporary IDs are fixed because they are used in multiple places.\n private const BUDGET_TEMPORARY_ID = -1;\n private const PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = -2;\n private const ASSET_GROUP_TEMPORARY_ID = -3;\n\n // There are also entities that will be created in the same request but do not need to be fixed\n // temporary IDs because they are referenced only once.\n /** @var int the negative temporary ID used in bulk mutates. */\n private static $nextTempId = self::ASSET_GROUP_TEMPORARY_ID - 1;\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::AUDIENCE_ID => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::BRAND_GUIDELINES_ENABLED => GetOpt::OPTIONAL_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())\n ->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::AUDIENCE_ID] ?: self::AUDIENCE_ID,\n filter_var(\n $options[ArgumentNames::BRAND_GUIDELINES_ENABLED]\n ?: self::BRAND_GUIDELINES_ENABLED,\n FILTER_VALIDATE_BOOLEAN\n )\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int|null $audienceId the audience ID\n * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand\n * guidelines\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n ?int $audienceId,\n bool $brandGuidelinesEnabled\n ) {\n // Performance Max campaigns require that repeated assets such as headlines\n // and descriptions be created before the campaign.\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets.\n //\n // Creates the headlines.\n $headlineAssetResourceNames = self::createMultipleTextAssets(\n $googleAdsClient,\n $customerId,\n [\"Travel\", \"Travel Reviews\", \"Book travel\"]\n );\n // Creates the descriptions.\n $descriptionAssetResourceNames = self::createMultipleTextAssets(\n $googleAdsClient,\n $customerId,\n [\"Take to the air!\", \"Fly to the sky!\"]\n );\n\n // It's important to create the below entities in this order because they depend on\n // each other.\n $operations = [];\n // The below methods create and return MutateOperations that we later\n // provide to the GoogleAdsService.Mutate method in order to create the\n // entities in a single request. Since the entities for a Performance Max\n // campaign are closely tied to one-another, it's considered a best practice\n // to create them in a single Mutate request so they all complete\n // successfully or fail entirely, leaving no orphaned entities. See:\n // https://developers.google.com/google-ads/api/docs/mutating/overview.\n $operations[] = self::createCampaignBudgetOperation($customerId);\n $operations[] =\n self::createPerformanceMaxCampaignOperation($customerId, $brandGuidelinesEnabled);\n $operations =\n array_merge($operations, self::createCampaignCriterionOperations($customerId));\n $operations = array_merge($operations, self::createAssetGroupOperations(\n $customerId,\n $headlineAssetResourceNames,\n $descriptionAssetResourceNames,\n $brandGuidelinesEnabled\n ));\n $operations = array_merge($operations, self::createAssetGroupSignalOperations(\n $customerId,\n ResourceNames::forAssetGroup($customerId, self::ASSET_GROUP_TEMPORARY_ID),\n $audienceId\n ));\n\n // Issues a mutate request to create everything and prints its information.\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n $response = $googleAdsServiceClient->mutate(MutateGoogleAdsRequest::build(\n $customerId,\n $operations\n ));\n\n self::printResponseDetails($response);\n }\n\n /**\n * Creates a MutateOperation that creates a new CampaignBudget.\n *\n * A temporary ID will be assigned to this campaign budget so that it can be\n * referenced by other objects being created in the same Mutate request.\n *\n * @param int $customerId the customer ID\n * @return MutateOperation the mutate operation that creates a campaign budget\n */\n private static function createCampaignBudgetOperation(int $customerId): MutateOperation\n {\n // Creates a mutate operation that creates a campaign budget operation.\n return new MutateOperation([\n 'campaign_budget_operation' => new CampaignBudgetOperation([\n 'create' => new CampaignBudget([\n // Sets a temporary ID in the budget's resource name so it can be referenced\n // by the campaign in later steps.\n 'resource_name' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n 'name' => 'Performance Max campaign budget #' . Helper::getPrintableDatetime(),\n // The budget period already defaults to DAILY.\n 'amount_micros' => 50000000,\n 'delivery_method' => BudgetDeliveryMethod::STANDARD,\n // A Performance Max campaign cannot use a shared campaign budget.\n 'explicitly_shared' => false\n ])\n ])\n ]);\n }\n\n /**\n * Creates a MutateOperation that creates a new Performance Max campaign.\n *\n * A temporary ID will be assigned to this campaign so that it can\n * be referenced by other objects being created in the same Mutate request.\n *\n * @param int $customerId the customer ID\n * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand\n * guidelines\n * @return MutateOperation the mutate operation that creates the campaign\n */\n private static function createPerformanceMaxCampaignOperation(\n int $customerId,\n bool $brandGuidelinesEnabled\n ): MutateOperation {\n // Creates a mutate operation that creates a campaign operation.\n return new MutateOperation([\n 'campaign_operation' => new CampaignOperation([\n 'create' => new Campaign([\n 'name' => 'Performance Max campaign #' . Helper::getPrintableDatetime(),\n // Assigns the resource name with a temporary ID.\n 'resource_name' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n // Sets the budget using the given budget resource name.\n 'campaign_budget' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n // The campaign is the only entity in the mutate request that should have its\n // status set.\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving.\n 'status' => CampaignStatus::PAUSED,\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n 'advertising_channel_type' => AdvertisingChannelType::PERFORMANCE_MAX,\n\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n 'maximize_conversion_value' => new MaximizeConversionValue([\n 'target_roas' => 3.5\n ]),\n\n 'asset_automation_settings' => [\n new AssetAutomationSetting([\n 'asset_automation_type' => AssetAutomationType::TEXT_ASSET_AUTOMATION,\n 'asset_automation_status' => AssetAutomationStatus::OPTED_IN\n ]),\n new AssetAutomationSetting([\n 'asset_automation_type' => AssetAutomationType::URL_EXPANSION,\n 'asset_automation_status' => AssetAutomationStatus::OPTED_IN\n ])\n ],\n\n\n // Sets if the campaign is enabled for brand guidelines. For more information\n // on brand guidelines, see\n // https://support.google.com/google-ads/answer/14934472.\n 'brand_guidelines_enabled' => $brandGuidelinesEnabled,\n\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n\n // Optional fields.\n 'start_date_time' => date('Y-m-d 00:00:00', strtotime('+1 day')),\n 'end_date_time' => date('Y-m-d 23:59:59', strtotime('+365 days'))\n ])\n ])\n ]);\n }\n\n /**\n * Creates a list of MutateOperations that create new campaign criteria.\n *\n * @param int $customerId the customer ID\n * @return MutateOperation[] a list of MutateOperations that create the new campaign criteria\n */\n private static function createCampaignCriterionOperations(int $customerId): array\n {\n $operations = [];\n // Set the LOCATION campaign criteria.\n // Target all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n //\n // We will add one positive location target for New York City (ID=1023191)\n // and one negative location target for Brooklyn (ID=1022762).\n // First, adds the positive (negative = false) for New York City.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'location' => new LocationInfo([\n 'geo_target_constant' => ResourceNames::forGeoTargetConstant(1023191)\n ]),\n 'negative' => false\n ])\n ])\n ]);\n\n // Next adds the negative target for Brooklyn.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'location' => new LocationInfo([\n 'geo_target_constant' => ResourceNames::forGeoTargetConstant(1022762)\n ]),\n 'negative' => true\n ])\n ])\n ]);\n\n // Sets the LANGUAGE campaign criterion.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n // Set the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n 'language' => new LanguageInfo([\n 'language_constant' => ResourceNames::forLanguageConstant(1000) // English\n ])\n ])\n ])\n ]);\n\n return $operations;\n }\n\n /**\n * Creates multiple text assets and returns the list of resource names.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param string[] $texts a list of strings, each of which will be used to create a text asset\n * @return string[] a list of asset resource names\n */\n private static function createMultipleTextAssets(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n array $texts\n ): array {\n // Here again, we use the GoogleAdService to create multiple text assets in a single\n // request.\n $operations = [];\n foreach ($texts as $text) {\n // Creates a mutate operation for a text asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset(['text_asset' => new TextAsset(['text' => $text])])\n ])\n ]);\n }\n\n // Issues a mutate request to add all assets.\n $googleAdsService = $googleAdsClient->getGoogleAdsServiceClient();\n /** @var MutateGoogleAdsResponse $mutateGoogleAdsResponse */\n $mutateGoogleAdsResponse =\n $googleAdsService->mutate(MutateGoogleAdsRequest::build($customerId, $operations));\n\n $assetResourceNames = [];\n foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {\n /** @var MutateOperationResponse $response */\n $assetResourceNames[] = $response->getAssetResult()->getResourceName();\n }\n self::printResponseDetails($mutateGoogleAdsResponse);\n\n return $assetResourceNames;\n }\n\n /**\n * Creates a list of MutateOperations that create a new asset group.\n *\n * A temporary ID will be assigned to this asset group so that it can\n * be referenced by other objects being created in the same Mutate request.\n *\n * @param int $customerId the customer ID\n * @param string[] $headlineAssetResourceNames a list of headline resource names\n * @param string[] $descriptionAssetResourceNames a list of description resource names\n * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand\n * guidelines\n * @return MutateOperation[] a list of MutateOperations that create new asset group\n */\n private static function createAssetGroupOperations(\n int $customerId,\n array $headlineAssetResourceNames,\n array $descriptionAssetResourceNames,\n bool $brandGuidelinesEnabled\n ): array {\n $operations = [];\n // Creates a new mutate operation that creates an asset group operation.\n $operations[] = new MutateOperation([\n 'asset_group_operation' => new AssetGroupOperation([\n 'create' => new AssetGroup([\n 'resource_name' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'name' => 'Performance Max asset group #' . Helper::getPrintableDatetime(),\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'final_urls' => ['http://www.example.com'],\n 'final_mobile_urls' => ['http://www.example.com'],\n 'status' => AssetGroupStatus::PAUSED\n ])\n ])\n ]);\n\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // - the resource name of the AssetGroup\n // - the resource name of the Asset\n // - the field_type of the Asset in this AssetGroup\n //\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n // Links the previously created multiple text assets.\n\n // Links the headline assets.\n foreach ($headlineAssetResourceNames as $resourceName) {\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $resourceName,\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::HEADLINE\n ])\n ])\n ]);\n }\n // Links the description assets.\n foreach ($descriptionAssetResourceNames as $resourceName) {\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $resourceName,\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::DESCRIPTION\n ])\n ])\n ]);\n }\n\n // Creates and links the long headline text asset.\n $operations = array_merge($operations, self::createAndLinkTextAsset(\n $customerId,\n 'Travel the World',\n AssetFieldType::LONG_HEADLINE\n ));\n // Creates and links the business name text asset.\n $operations = array_merge($operations, self::createAndLinkBrandAssets(\n $customerId,\n $brandGuidelinesEnabled,\n 'Interplanetary Cruises',\n 'https://gaagl.page.link/bjYi',\n 'Marketing Logo'\n ));\n\n // Creates and links the image assets.\n\n // Creates and links the Marketing Image Asset.\n $operations = array_merge($operations, self::createAndLinkImageAsset(\n $customerId,\n 'https://gaagl.page.link/Eit5',\n AssetFieldType::MARKETING_IMAGE,\n 'Marketing Image'\n ));\n // Creates and links the Square Marketing Image Asset.\n $operations = array_merge($operations, self::createAndLinkImageAsset(\n $customerId,\n 'https://gaagl.page.link/bjYi',\n AssetFieldType::SQUARE_MARKETING_IMAGE,\n 'Square Marketing Image'\n ));\n\n return $operations;\n }\n\n /**\n * Creates a list of MutateOperations that create a new linked text asset.\n *\n * @param int $customerId the customer ID\n * @param string $text the text of the asset to be created\n * @param int $fieldType the field type of the new asset in the AssetGroupAsset\n * @return MutateOperation[] a list of MutateOperations that create a new linked text asset\n */\n private static function createAndLinkTextAsset(\n int $customerId,\n string $text,\n int $fieldType\n ): array {\n $operations = [];\n // Creates a new mutate operation that creates a text asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId),\n 'text_asset' => new TextAsset(['text' => $text])\n ])\n ])\n ]);\n\n // Creates an asset group asset to link the asset to the asset group.\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => ResourceNames::forAsset($customerId, self::$nextTempId),\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => $fieldType\n ])\n ])\n ]);\n self::$nextTempId--;\n\n return $operations;\n }\n\n /**\n * Creates a list of MutateOperations that create a new linked image asset.\n *\n * @param int $customerId the customer ID\n * @param string $url the URL of the image to be retrieved and put into an asset\n * @param int $fieldType the field type of the new asset in the AssetGroupAsset\n * @param string $assetName the asset name\n * @return MutateOperation[] a list of MutateOperations that create a new linked image asset\n */\n private static function createAndLinkImageAsset(\n int $customerId,\n string $url,\n int $fieldType,\n string $assetName\n ): array {\n $operations = [];\n // Creates a new mutate operation that creates an image asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId),\n // Provide a unique friendly name to identify your asset.\n // When there is an existing image asset with the same content but a different\n // name, the new name will be dropped silently.\n 'name' => $assetName,\n 'image_asset' => new ImageAsset(['data' => file_get_contents($url)])\n ])\n ])\n ]);\n\n // Creates an asset group asset to link the asset to the asset group.\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => ResourceNames::forAsset($customerId, self::$nextTempId),\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => $fieldType\n ])\n ])\n ]);\n self::$nextTempId--;\n\n return $operations;\n }\n\n\n /**\n * Creates a list of MutateOperations that create linked brand assets.\n *\n * @param int $customerId the customer ID\n * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand\n * guidelines\n * @param string $businessName the business name text to be put into an asset\n * @param string $logoUrl the URL of the logo to be retrieved and put into an asset\n * @param string $logoName the asset name of the logo\n * @return MutateOperation[] a list of MutateOperations that create a new linked text asset\n */\n private static function createAndLinkBrandAssets(\n int $customerId,\n bool $brandGuidelinesEnabled,\n string $businessName,\n string $logoUrl,\n string $logoName\n ): array {\n $operations = [];\n // Creates a new mutate operation that creates a text asset.\n $businessNameTempId = self::$nextTempId--;\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'resource_name' => ResourceNames::forAsset($customerId, $businessNameTempId),\n 'text_asset' => new TextAsset(['text' => $businessName])\n ])\n ])\n ]);\n\n $logoTempId = self::$nextTempId--;\n // Creates a new mutate operation that creates an image asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'resource_name' => ResourceNames::forAsset($customerId, $logoTempId),\n // Provide a unique friendly name to identify your asset.\n // When there is an existing image asset with the same content but a different\n // name, the new name will be dropped silently.\n 'name' => $logoName,\n 'image_asset' => new ImageAsset(['data' => file_get_contents($logoUrl)])\n ])\n ])\n ]);\n\n if ($brandGuidelinesEnabled) {\n // Creates a campaign asset to link the business name and logo assets to the campaign.\n $operations[] = new MutateOperation([\n 'campaign_asset_operation' => new CampaignAssetOperation([\n 'create' => new CampaignAsset([\n 'asset' => ResourceNames::forAsset($customerId, $businessNameTempId),\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::BUSINESS_NAME\n ])\n ])\n ]);\n $operations[] = new MutateOperation([\n 'campaign_asset_operation' => new CampaignAssetOperation([\n 'create' => new CampaignAsset([\n 'asset' => ResourceNames::forAsset($customerId, $logoTempId),\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::LOGO\n ])\n ])\n ]);\n } else {\n // Creates an asset group asset to link the business name and logo assets to the asset\n // group.\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => ResourceNames::forAsset($customerId, $businessNameTempId),\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::BUSINESS_NAME\n ])\n ])\n ]);\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => ResourceNames::forAsset($customerId, $logoTempId),\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::LOGO\n ])\n ])\n ]);\n }\n\n return $operations;\n }\n\n\n /**\n * Creates a list of MutateOperations that may create asset group signals.\n *\n * @param int $customerId the customer ID\n * @param string $assetGroupResourceName the resource name of the asset group\n * @param int|null $audienceId the audience ID\n * @return MutateOperation[] a list of MutateOperations that may create asset group signals\n */\n private static function createAssetGroupSignalOperations(\n int $customerId,\n string $assetGroupResourceName,\n ?int $audienceId\n ): array {\n $operations = [];\n if (is_null($audienceId)) {\n return $operations;\n }\n\n $operations[] = new MutateOperation([\n 'asset_group_signal_operation' => new AssetGroupSignalOperation([\n // To learn more about Audience Signals, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals.\n 'create' => new AssetGroupSignal([\n 'asset_group' => $assetGroupResourceName,\n 'audience' => new AudienceInfo([\n 'audience' => ResourceNames::forAudience($customerId, $audienceId)\n ])\n ])\n ])\n ]);\n\n return $operations;\n }\n\n /**\n * Prints the details of a MutateGoogleAdsResponse. Parses the \"response\" oneof field name and\n * uses it to extract the new entity's name and resource name.\n *\n * @param MutateGoogleAdsResponse $mutateGoogleAdsResponse the mutate Google Ads response\n */\n private static function printResponseDetails(\n MutateGoogleAdsResponse $mutateGoogleAdsResponse\n ): void {\n foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {\n /** @var MutateOperationResponse $response */\n $getter = Serializer::getGetter($response->getResponse());\n printf(\n \"Created a(n) %s with '%s'.%s\",\n preg_replace(\n '/Result$/',\n '',\n ucfirst(Serializer::toCamelCase($response->getResponse()))\n ),\n $response->$getter()->getResourceName(),\n PHP_EOL\n );\n }\n }\n}\n\nAddPerformanceMaxCampaign::main();\nAddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2021 Google LLC\n#\n# 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.\n\"\"\"This example shows how to create a Performance Max campaign.\n\nFor more information about Performance Max campaigns, see\nhttps://developers.google.com/google-ads/api/docs/performance-max/overview\n\nPrerequisites:\n- You must have at least one conversion action in the account. For\nmore about conversion actions, see\nhttps://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions\n\nThis example uses the default customer conversion goals. For an example\nof setting campaign-specific conversion goals, see\nshopping_ads/add_performance_max_retail_campaign.py\n\"\"\"\n\nimport argparse\nfrom datetime import datetime, timedelta\nimport logging\nimport sys\nfrom typing import List, Optional, Iterable\nfrom uuid import uuid4\n\nfrom examples.utils.example_helpers import get_image_bytes_from_url\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.util import convert_snake_case_to_upper_case\nfrom google.ads.googleads.v24.enums.types.asset_field_type import (\n AssetFieldTypeEnum,\n)\nfrom google.ads.googleads.v24.resources.types.asset import Asset\nfrom google.ads.googleads.v24.resources.types.asset_group import AssetGroup\nfrom google.ads.googleads.v24.resources.types.asset_group_asset import (\n AssetGroupAsset,\n)\nfrom google.ads.googleads.v24.resources.types.asset_group_signal import (\n AssetGroupSignal,\n)\nfrom google.ads.googleads.v24.resources.types.campaign import Campaign\nfrom google.ads.googleads.v24.resources.types.campaign_asset import (\n CampaignAsset,\n)\nfrom google.ads.googleads.v24.resources.types.campaign_budget import (\n CampaignBudget,\n)\nfrom google.ads.googleads.v24.resources.types.campaign_criterion import (\n CampaignCriterion,\n)\nfrom google.ads.googleads.v24.services.services.asset_group_service import (\n AssetGroupServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.asset_service import (\n AssetServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.campaign_service import (\n CampaignServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.geo_target_constant_service import (\n GeoTargetConstantServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.google_ads_service import (\n GoogleAdsServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.campaign_budget_service import (\n CampaignBudgetOperation,\n)\nfrom google.ads.googleads.v24.services.types.google_ads_service import (\n MutateGoogleAdsResponse,\n MutateOperation,\n MutateOperationResponse,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\n#\n# See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n# for further details.\n#\n# These temporary IDs are fixed because they are used in multiple places.\n_BUDGET_TEMPORARY_ID = \"-1\"\n_PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = \"-2\"\n_ASSET_GROUP_TEMPORARY_ID = \"-3\"\n\n# There are also entities that will be created in the same request but do not\n# need to be fixed temporary IDs because they are referenced only once.\nnext_temp_id = int(_ASSET_GROUP_TEMPORARY_ID) - 1\n\n\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n audience_id: Optional[str],\n brand_guidelines_enabled: bool,\n) -> None:\n \"\"\"The main method that creates all necessary entities for the example.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n audience_id: an optional audience ID.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n # Performance Max campaigns require that repeated assets such as headlines\n # and descriptions be created before the campaign.\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n #\n # Create the headlines.\n headline_asset_resource_names: List[str] = create_multiple_text_assets(\n client,\n customer_id,\n [\n \"Travel\",\n \"Travel Reviews\",\n \"Book travel\",\n ],\n )\n # Create the descriptions.\n description_asset_resource_names: List[str] = create_multiple_text_assets(\n client,\n customer_id,\n [\n \"Take to the air!\",\n \"Fly to the sky!\",\n ],\n )\n\n # The below methods create and return MutateOperations that we later\n # provide to the GoogleAdsService.Mutate method in order to create the\n # entities in a single request. Since the entities for a Performance Max\n # campaign are closely tied to one-another, it's considered a best practice\n # to create them in a single Mutate request so they all complete\n # successfully or fail entirely, leaving no orphaned entities. See:\n # https://developers.google.com/google-ads/api/docs/mutating/overview\n campaign_budget_operation: MutateOperation = (\n create_campaign_budget_operation(\n client,\n customer_id,\n )\n )\n performance_max_campaign_operation: MutateOperation = (\n create_performance_max_campaign_operation(\n client,\n customer_id,\n brand_guidelines_enabled,\n )\n )\n campaign_criterion_operations: List[MutateOperation] = (\n create_campaign_criterion_operations(\n client,\n customer_id,\n )\n )\n asset_group_operations: List[MutateOperation] = (\n create_asset_group_operation(\n client,\n customer_id,\n headline_asset_resource_names,\n description_asset_resource_names,\n brand_guidelines_enabled,\n )\n )\n asset_group_signal_operations: List[MutateOperation] = (\n create_asset_group_signal_operations(client, customer_id, audience_id)\n )\n\n mutate_operations: List[MutateOperation] = [\n # It's important to create these entities in this order because\n # they depend on each other.\n campaign_budget_operation,\n performance_max_campaign_operation,\n # Expand the list of multiple operations into the list of\n # other mutate operations\n *campaign_criterion_operations,\n *asset_group_operations,\n *asset_group_signal_operations,\n ]\n\n # Send the operations in a single Mutate request.\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id, mutate_operations=mutate_operations\n )\n\n print_response_details(response)\n\n\ndef create_campaign_budget_operation(\n client: GoogleAdsClient,\n customer_id: str,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new CampaignBudget.\n\n A temporary ID will be assigned to this campaign budget so that it can be\n referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a MutateOperation that creates a CampaignBudget.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_budget_operation: CampaignBudgetOperation = (\n mutate_operation.campaign_budget_operation\n )\n campaign_budget: CampaignBudget = campaign_budget_operation.create\n campaign_budget.name = f\"Performance Max campaign budget #{uuid4()}\"\n # The budget period already defaults to DAILY.\n campaign_budget.amount_micros = 50000000\n campaign_budget.delivery_method = (\n client.enums.BudgetDeliveryMethodEnum.STANDARD\n )\n # A Performance Max campaign cannot use a shared campaign budget.\n campaign_budget.explicitly_shared = False\n\n # Set a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n campaign_budget.resource_name = client.get_service(\n \"CampaignBudgetService\"\n ).campaign_budget_path(customer_id, _BUDGET_TEMPORARY_ID)\n\n return mutate_operation\n\n\ndef create_performance_max_campaign_operation(\n client: GoogleAdsClient,\n customer_id: str,\n brand_guidelines_enabled: bool,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new Performance Max campaign.\n\n A temporary ID will be assigned to this campaign so that it can\n be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n\n Returns:\n a MutateOperation that creates a campaign.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign: Campaign = mutate_operation.campaign_operation.create\n campaign.name = f\"Performance Max campaign #{uuid4()}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.PERFORMANCE_MAX\n )\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n campaign.bidding_strategy_type = (\n client.enums.BiddingStrategyTypeEnum.MAXIMIZE_CONVERSION_VALUE\n )\n campaign.maximize_conversion_value.target_roas = 3.5\n\n # Set if the campaign is enabled for brand guidelines. For more information\n # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n campaign.brand_guidelines_enabled = brand_guidelines_enabled\n\n # Assign the resource name with a temporary ID.\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n campaign.resource_name = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n # Set the budget using the given budget resource name.\n campaign.campaign_budget = campaign_service.campaign_budget_path(\n customer_id, _BUDGET_TEMPORARY_ID\n )\n\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n # Optional fields\n campaign.start_date_time = (datetime.now() + timedelta(1)).strftime(\n \"%Y%m%d 00:00:00\"\n )\n campaign.end_date_time = (datetime.now() + timedelta(365)).strftime(\n \"%Y%m%d 23:59:59\"\n )\n\n campaign.text_guidelines.term_exclusions = [\"cheap\", \"free\"]\n messaging_restriction = campaign.MessagingRestriction()\n messaging_restriction.restriction_text = \"Don't mention competitor names\"\n messaging_restriction.restriction_type = (\n client.enums.MessagingRestrictionTypeEnum.RESTRICTION_BASED_EXCLUSION\n )\n campaign.text_guidelines.messaging_restrictions.append(\n messaging_restriction\n )\n\n # Configures the optional opt-in/out status for asset automation settings.\n for asset_automation_type_enum in [\n client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_EXTRACTION,\n client.enums.AssetAutomationTypeEnum.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,\n client.enums.AssetAutomationTypeEnum.TEXT_ASSET_AUTOMATION,\n client.enums.AssetAutomationTypeEnum.GENERATE_ENHANCED_YOUTUBE_VIDEOS,\n client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_ENHANCEMENT,\n ]:\n asset_automattion_setting: Campaign.AssetAutomationSetting = (\n client.get_type(\"Campaign\").AssetAutomationSetting()\n )\n asset_automattion_setting.asset_automation_type = (\n asset_automation_type_enum\n )\n asset_automattion_setting.asset_automation_status = (\n client.enums.AssetAutomationStatusEnum.OPTED_IN\n )\n campaign.asset_automation_settings.append(asset_automattion_setting)\n\n return mutate_operation\n\n\ndef create_campaign_criterion_operations(\n client: GoogleAdsClient,\n customer_id: str,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create new campaign criteria.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a list of MutateOperations that create new campaign criteria.\n \"\"\"\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n geo_target_constant_service: GeoTargetConstantServiceClient = (\n client.get_service(\"GeoTargetConstantService\")\n )\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n operations: List[MutateOperation] = []\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n #\n # We will add one positive location target for New York City (ID=1023191)\n # and one negative location target for Brooklyn (ID=1022762).\n # First, add the positive (negative = False) for New York City.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion: CampaignCriterion = (\n mutate_operation.campaign_criterion_operation.create\n )\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n campaign_criterion.location.geo_target_constant = (\n geo_target_constant_service.geo_target_constant_path(\"1023191\")\n )\n campaign_criterion.negative = False\n operations.append(mutate_operation)\n\n # Next add the negative target for Brooklyn.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion: CampaignCriterion = (\n mutate_operation.campaign_criterion_operation.create\n )\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n campaign_criterion.location.geo_target_constant = (\n geo_target_constant_service.geo_target_constant_path(\"1022762\")\n )\n campaign_criterion.negative = True\n operations.append(mutate_operation)\n\n # Set the LANGUAGE campaign criterion.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion: CampaignCriterion = (\n mutate_operation.campaign_criterion_operation.create\n )\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n campaign_criterion.language.language_constant = (\n googleads_service.language_constant_path(\"1000\")\n ) # English\n operations.append(mutate_operation)\n\n return operations\n\n\ndef create_multiple_text_assets(\n client: GoogleAdsClient, customer_id: str, texts: List[str]\n) -> List[str]:\n \"\"\"Creates multiple text assets and returns the list of resource names.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n texts: a list of strings, each of which will be used to create a text\n asset.\n\n Returns:\n asset_resource_names: a list of asset resource names.\n \"\"\"\n # Here again we use the GoogleAdService to create multiple text\n # assets in a single request.\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n operations: List[MutateOperation] = []\n for text in texts:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = mutate_operation.asset_operation.create\n asset.text_asset.text = text\n operations.append(mutate_operation)\n\n # Send the operations in a single Mutate request.\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id,\n mutate_operations=operations,\n )\n asset_resource_names: List[str] = []\n for result in response.mutate_operation_responses:\n if result._pb.HasField(\"asset_result\"):\n asset_resource_names.append(result.asset_result.resource_name)\n print_response_details(response)\n return asset_resource_names\n\n\ndef create_asset_group_operation(\n client: GoogleAdsClient,\n customer_id: str,\n headline_asset_resource_names: List[str],\n description_asset_resource_names: List[str],\n brand_guidelines_enabled: bool,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create a new asset_group.\n\n A temporary ID will be assigned to this asset group so that it can\n be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n headline_asset_resource_names: a list of headline resource names.\n description_asset_resource_names: a list of description resource names.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n\n Returns:\n MutateOperations that create a new asset group and related assets.\n \"\"\"\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n operations: List[MutateOperation] = []\n\n # Create the AssetGroup\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group: AssetGroup = mutate_operation.asset_group_operation.create\n asset_group.name = f\"Performance Max asset group #{uuid4()}\"\n asset_group.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n asset_group.final_urls.append(\"http://www.example.com\")\n asset_group.final_mobile_urls.append(\"http://www.example.com\")\n asset_group.status = client.enums.AssetGroupStatusEnum.PAUSED\n asset_group.resource_name = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n operations.append(mutate_operation)\n\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # the resource name of the AssetGroup\n # the resource name of the Asset\n # the field_type of the Asset in this AssetGroup.\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n for resource_name in headline_asset_resource_names:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_asset: AssetGroupAsset = (\n mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.HEADLINE\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = resource_name\n operations.append(mutate_operation)\n\n # Link the description assets.\n for resource_name in description_asset_resource_names:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_asset: AssetGroupAsset = (\n mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = (\n client.enums.AssetFieldTypeEnum.DESCRIPTION\n )\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = resource_name\n operations.append(mutate_operation)\n\n # Create and link the long headline text asset.\n mutate_operations: List[MutateOperation] = create_and_link_text_asset(\n client,\n customer_id,\n \"Travel the World\",\n client.enums.AssetFieldTypeEnum.LONG_HEADLINE,\n )\n operations.extend(mutate_operations)\n\n # Create and link the business name and logo asset.\n mutate_operations: List[MutateOperation] = create_and_link_brand_assets(\n client,\n customer_id,\n brand_guidelines_enabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\",\n )\n operations.extend(mutate_operations)\n\n # Create and link the image assets.\n\n # Create and link the Marketing Image Asset.\n mutate_operations: List[MutateOperation] = create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/Eit5\",\n client.enums.AssetFieldTypeEnum.MARKETING_IMAGE,\n \"Marketing Image\",\n )\n operations.extend(mutate_operations)\n\n # Create and link the Square Marketing Image Asset.\n mutate_operations: List[MutateOperation] = create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/bjYi\",\n client.enums.AssetFieldTypeEnum.SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\",\n )\n operations.extend(mutate_operations)\n return operations\n\n\ndef create_and_link_text_asset(\n client: GoogleAdsClient,\n customer_id: str,\n text: str,\n field_type: AssetFieldTypeEnum.AssetFieldType,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create a new linked text asset.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n text: the text of the asset to be created.\n field_type: the field_type of the new asset in the AssetGroupAsset.\n\n Returns:\n MutateOperations that create a new linked text asset.\n \"\"\"\n global next_temp_id\n operations: List[MutateOperation] = []\n asset_service: AssetServiceClient = client.get_service(\"AssetService\")\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n\n # Create the Text Asset.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = mutate_operation.asset_operation.create\n asset.resource_name = asset_service.asset_path(customer_id, next_temp_id)\n asset.text_asset.text = text\n operations.append(mutate_operation)\n\n # Create an AssetGroupAsset to link the Asset to the AssetGroup.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_asset: AssetGroupAsset = (\n mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = field_type\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = asset_service.asset_path(\n customer_id, next_temp_id\n )\n operations.append(mutate_operation)\n\n next_temp_id -= 1\n return operations\n\n\ndef create_and_link_image_asset(\n client: GoogleAdsClient,\n customer_id: str,\n url: str,\n field_type: AssetFieldTypeEnum.AssetFieldType,\n asset_name: str,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create a new linked image asset.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n url: the url of the image to be retrieved and put into an asset.\n field_type: the field_type of the new asset in the AssetGroupAsset.\n asset_name: the asset name.\n\n Returns:\n MutateOperations that create a new linked image asset.\n \"\"\"\n global next_temp_id\n operations: List[MutateOperation] = []\n asset_service: AssetServiceClient = client.get_service(\"AssetService\")\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n\n # Create the Image Asset.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = mutate_operation.asset_operation.create\n asset.resource_name = asset_service.asset_path(customer_id, next_temp_id)\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n asset.name = asset_name\n asset.type_ = client.enums.AssetTypeEnum.IMAGE\n asset.image_asset.data = get_image_bytes_from_url(url)\n operations.append(mutate_operation)\n\n # Create an AssetGroupAsset to link the Asset to the AssetGroup.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_asset: AssetGroupAsset = (\n mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = field_type\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = asset_service.asset_path(\n customer_id, next_temp_id\n )\n operations.append(mutate_operation)\n\n next_temp_id -= 1\n return operations\n\n\ndef create_and_link_brand_assets(\n client: GoogleAdsClient,\n customer_id: str,\n brand_guidelines_enabled: bool,\n business_name: str,\n logo_url: str,\n logo_name: str,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create linked brand assets.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n business_name: the business name text to be put into an asset.\n logo_url: the url of the logo to be retrieved and put into an asset.\n logo_name: the asset name of the logo.\n\n Returns:\n MutateOperations that create linked brand assets.\n \"\"\"\n global next_temp_id\n operations: List[MutateOperation] = []\n asset_service: AssetServiceClient = client.get_service(\"AssetService\")\n\n # Create the Text Asset.\n text_asset_temp_id = next_temp_id\n next_temp_id -= 1\n\n text_mutate_operation = client.get_type(\"MutateOperation\")\n text_asset: Asset = text_mutate_operation.asset_operation.create\n text_asset.resource_name = asset_service.asset_path(\n customer_id, text_asset_temp_id\n )\n text_asset.text_asset.text = business_name\n operations.append(text_mutate_operation)\n\n # Create the Image Asset.\n image_asset_temp_id = next_temp_id\n next_temp_id -= 1\n\n image_mutate_operation = client.get_type(\"MutateOperation\")\n image_asset: Asset = image_mutate_operation.asset_operation.create\n image_asset.resource_name = asset_service.asset_path(\n customer_id, image_asset_temp_id\n )\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n image_asset.name = logo_name\n image_asset.type_ = client.enums.AssetTypeEnum.IMAGE\n image_asset.image_asset.data = get_image_bytes_from_url(logo_url)\n operations.append(image_mutate_operation)\n\n if brand_guidelines_enabled:\n # Create CampaignAsset resources to link the Asset resources to the Campaign.\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n business_name_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n business_name_campaign_asset: CampaignAsset = (\n business_name_mutate_operation.campaign_asset_operation.create\n )\n business_name_campaign_asset.field_type = (\n client.enums.AssetFieldTypeEnum.BUSINESS_NAME\n )\n business_name_campaign_asset.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n business_name_campaign_asset.asset = asset_service.asset_path(\n customer_id, text_asset_temp_id\n )\n operations.append(business_name_mutate_operation)\n\n logo_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n logo_campaign_asset: CampaignAsset = (\n logo_mutate_operation.campaign_asset_operation.create\n )\n logo_campaign_asset.field_type = client.enums.AssetFieldTypeEnum.LOGO\n logo_campaign_asset.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n logo_campaign_asset.asset = asset_service.asset_path(\n customer_id, image_asset_temp_id\n )\n operations.append(logo_mutate_operation)\n\n else:\n # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n\n business_name_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n business_name_asset_group_asset: AssetGroupAsset = (\n business_name_mutate_operation.asset_group_asset_operation.create\n )\n business_name_asset_group_asset.field_type = (\n client.enums.AssetFieldTypeEnum.BUSINESS_NAME\n )\n business_name_asset_group_asset.asset_group = (\n asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n )\n business_name_asset_group_asset.asset = asset_service.asset_path(\n customer_id, text_asset_temp_id\n )\n operations.append(business_name_mutate_operation)\n\n logo_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n logo_asset_group_asset: AssetGroupAsset = (\n logo_mutate_operation.asset_group_asset_operation.create\n )\n logo_asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.LOGO\n logo_asset_group_asset.asset_group = (\n asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n )\n logo_asset_group_asset.asset = asset_service.asset_path(\n customer_id, image_asset_temp_id\n )\n operations.append(logo_mutate_operation)\n\n return operations\n\n\ndef print_response_details(response: MutateGoogleAdsResponse) -> None:\n \"\"\"Prints the details of a MutateGoogleAdsResponse.\n\n Parses the \"response\" oneof field name and uses it to extract the new\n entity's name and resource name.\n\n Args:\n response: a MutateGoogleAdsResponse object.\n \"\"\"\n # Parse the Mutate response to print details about the entities that\n # were created by the request.\n results: Iterable[MutateOperation] = response.mutate_operation_responses\n suffix = \"_result\"\n for result in results:\n for field_descriptor, value in result._pb.ListFields():\n if field_descriptor.name.endswith(suffix):\n name = field_descriptor.name[: -len(suffix)]\n else:\n name = field_descriptor.name\n print(\n f\"Created a(n) {convert_snake_case_to_upper_case(name)} with \"\n f\"{str(value).strip()}.\"\n )\n\n\ndef create_asset_group_signal_operations(\n client: GoogleAdsClient, customer_id: str, audience_id: Optional[str]\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that may create asset group signals.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n audience_id: an optional audience ID.\n\n Returns:\n MutateOperations that create new asset group signals.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n asset_group_resource_name: str = googleads_service.asset_group_path(\n customer_id, _ASSET_GROUP_TEMPORARY_ID\n )\n\n operations: List[MutateOperation] = []\n\n if audience_id:\n # Create an audience asset group signal.\n # To learn more about Audience Signals, see:\n # https://developers.google.com/google-ads/api/performance-max/asset-group-signals#audiences\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n operation: AssetGroupSignal = (\n mutate_operation.asset_group_signal_operation.create\n )\n operation.asset_group = asset_group_resource_name\n operation.audience.audience = googleads_service.audience_path(\n customer_id, audience_id\n )\n operations.append(mutate_operation)\n\n # Create a search theme asset group signal.\n # To learn more about Search Themes Signals, see:\n # https://developers.google.com/google-ads/api/performance-max/asset-group-signals#search_themes\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n operation: AssetGroupSignal = (\n mutate_operation.asset_group_signal_operation.create\n )\n operation.asset_group = asset_group_resource_name\n operation.search_theme.text = \"travel\"\n operations.append(mutate_operation)\n\n return operations\n\n\n\nif __name__ == \"__main__\":\n parser: argparse.ArgumentParser = argparse.ArgumentParser(\n description=(\"Creates a Performance Max campaign.\")\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-a\",\n \"--audience_id\",\n type=str,\n help=\"The ID of an audience.\",\n )\n parser.add_argument(\n \"-b\",\n \"--brand_guidelines_enabled\",\n type=bool,\n default=True,\n help=(\n \"A boolean value indicating if the created campaign is enabled \"\n \"for brand guidelines.\"\n ),\n )\n\n args: argparse.Namespace = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(\n googleads_client,\n args.customer_id,\n args.audience_id,\n args.brand_guidelines_enabled,\n )\n except GoogleAdsException as ex:\n print(\n f'Request with ID \"{ex.request_id}\" failed with status '\n f'\"{ex.error.code().name}\" and includes the following errors:'\n )\n for error in ex.failure.errors:\n print(f'Error with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nadd_performance_max_campaign.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2021 Google LLC\n#\n# 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.\n#\n# This example shows how to create a Performance Max campaign.\n#\n# For more information about Performance Max campaigns, see\n# https://developers.google.com/google-ads/api/docs/performance-max/overview\n#\n# Prerequisites:\n# - You must have at least one conversion action in the account. For\n# more about conversion actions, see\n# https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions\n#\n# This example uses the default customer conversion goals. For an example\n# of setting campaign-specific conversion goals, see\n# shopping_ads/add_performance_max_retail_campaign.rb\n\nrequire 'optparse'\nrequire 'date'\nrequire 'open-uri'\nrequire 'google/ads/google_ads'\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\n#\n# See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n# for further details.\n#\n# These temporary IDs are fixed because they are used in multiple places.\nBUDGET_TEMPORARY_ID = \"-1\"\nPERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = \"-2\"\nASSET_GROUP_TEMPORARY_ID = \"-3\"\n\n# There are also entities that will be created in the same request but do not\n# need to be fixed temporary IDs because they are referenced only once.\ndef next_temp_id\n @id ||= ASSET_GROUP_TEMPORARY_ID.to_i\n @id -= 1\nend\n\ndef add_performance_max_campaign(\n customer_id,\n audience_id,\n brand_guidelines_enabled)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Performance Max campaigns require that repeated assets such as headlines\n # and descriptions be created before the campaign.\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n #\n # Create the headlines.\n headline_asset_resource_names = create_multiple_text_assets(\n client,\n customer_id,\n [\n \"Travel\",\n \"Travel Reviews\",\n \"Book travel\",\n ])\n # Create the descriptions.\n description_asset_resource_names = create_multiple_text_assets(\n client,\n customer_id,\n [\n \"Take to the air!\",\n \"Fly to the sky!\",\n ])\n\n # The below methods create and return MutateOperations that we later\n # provide to the GoogleAdsService.Mutate method in order to create the\n # entities in a single request. Since the entities for a Performance Max\n # campaign are closely tied to one-another, it's considered a best practice\n # to create them in a single Mutate request so they all complete\n # successfully or fail entirely, leaving no orphaned entities. See:\n # https://developers.google.com/google-ads/api/docs/mutating/overview\n campaign_budget_operation = create_campaign_budget_operation(\n client,\n customer_id,\n )\n performance_max_campaign_operation = create_performance_max_campaign_operation(\n client,\n customer_id,\n brand_guidelines_enabled,\n )\n campaign_criterion_operations = create_campaign_criterion_operations(\n client,\n customer_id,\n )\n asset_group_operations = create_asset_group_operation(\n client,\n customer_id,\n headline_asset_resource_names,\n description_asset_resource_names,\n brand_guidelines_enabled,\n )\n asset_group_signal_operations = create_asset_group_signal_operations(\n client,\n customer_id,\n audience_id,\n )\n\n # Send the operations in a single Mutate request.\n response = client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: [\n # It's important to create these entities in this order because\n # they depend on each other.\n campaign_budget_operation,\n performance_max_campaign_operation,\n # Expand the list of multiple operations into the list of\n # other mutate operations\n campaign_criterion_operations,\n asset_group_operations,\n asset_group_signal_operations,\n ].flatten)\n\n print_response_details(response)\nend\n\n# Creates a MutateOperation that creates a new CampaignBudget.\n#\n# A temporary ID will be assigned to this campaign budget so that it can be\n# referenced by other objects being created in the same Mutate request.\ndef create_campaign_budget_operation(client, customer_id)\n client.operation.mutate do |m|\n m.campaign_budget_operation = client.operation.create_resource.campaign_budget do |cb|\n cb.name = \"Performance Max campaign budget #{SecureRandom.uuid}\"\n # The budget period already defaults to DAILY.\n cb.amount_micros = 50_000_000\n cb.delivery_method = :STANDARD\n # A Performance Max campaign cannot use a shared campaign budget.\n cb.explicitly_shared = false\n\n # Set a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n cb.resource_name = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n end\n end\nend\n\n# Creates a MutateOperation that creates a new Performance Max campaign.\n#\n# A temporary ID will be assigned to this campaign so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_performance_max_campaign_operation(\n client,\n customer_id,\n brand_guidelines_enabled)\n client.operation.mutate do |m|\n m.campaign_operation = client.operation.create_resource.campaign do |c|\n c.name = \"Performance Max campaign #{SecureRandom.uuid}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n c.status = :PAUSED\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n c.advertising_channel_type = :PERFORMANCE_MAX\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n c.bidding_strategy_type = :MAXIMIZE_CONVERSION_VALUE\n c.maximize_conversion_value = client.resource.maximize_conversion_value do |mcv|\n mcv.target_roas = 3.5\n end\n\n # Configures the optional opt-in/out status for asset automation settings.\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_IMAGE_EXTRACTION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :TEXT_ASSET_AUTOMATION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_ENHANCED_YOUTUBE_VIDEOS\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_IMAGE_ENHANCEMENT\n aas.asset_automation_status = :OPTED_IN\n end\n\n # Set if the campaign is enabled for brand guidelines. For more\n # information on brand guidelines, see\n # https://support.google.com/google-ads/answer/14934472.\n c.brand_guidelines_enabled = brand_guidelines_enabled\n\n # Assign the resource name with a temporary ID.\n c.resource_name = client.path.campaign(customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n # Set the budget using the given budget resource name.\n c.campaign_budget = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n # Optional fields\n c.start_date_time = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d %H:%M:%S')\n c.end_date_time = DateTime.parse(Date.today.next_year.to_s).strftime('%Y%m%d %H:%M:%S')\n end\n end\nend\n\n# Creates a list of MutateOperations that create new campaign criteria.\ndef create_campaign_criterion_operations(client, customer_id)\n operations = []\n\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n #\n # We will add one positive location target for New York City (ID=1023191)\n # and one negative location target for Brooklyn (ID=1022762).\n # First, add the positive (negative = false) for New York City.\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n cc.location = client.resource.location_info do |li|\n li.geo_target_constant = client.path.geo_target_constant(\"1023191\")\n end\n cc.negative = false\n end\n end\n\n # Next add the negative target for Brooklyn.\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n cc.location = client.resource.location_info do |li|\n li.geo_target_constant = client.path.geo_target_constant(\"1022762\")\n end\n cc.negative = true\n end\n end\n\n # Set the LANGUAGE campaign criterion.\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n cc.language = client.resource.language_info do |li|\n li.language_constant = client.path.language_constant(\"1000\") # English\n end\n end\n end\n\n operations\nend\n\n# Creates multiple text assets and returns the list of resource names.\ndef create_multiple_text_assets(client, customer_id, texts)\n operations = texts.map do |text|\n client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |asset|\n asset.text_asset = client.resource.text_asset do |text_asset|\n text_asset.text = text\n end\n end\n end\n end\n\n # Send the operations in a single Mutate request.\n response = client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: operations,\n )\n\n asset_resource_names = []\n response.mutate_operation_responses.each do |result|\n if result.asset_result\n asset_resource_names.append(result.asset_result.resource_name)\n end\n end\n print_response_details(response)\n asset_resource_names\nend\n\n# Creates a list of MutateOperations that create a new asset_group.\n#\n# A temporary ID will be assigned to this asset group so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_asset_group_operation(\n client,\n customer_id,\n headline_asset_resource_names,\n description_asset_resource_names,\n brand_guidelines_enabled)\n operations = []\n\n # Create the AssetGroup\n operations << client.operation.mutate do |m|\n m.asset_group_operation = client.operation.create_resource.asset_group do |ag|\n ag.name = \"Performance Max asset group #{SecureRandom.uuid}\"\n ag.campaign = client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n ag.final_urls << \"http://www.example.com\"\n ag.final_mobile_urls << \"http://www.example.com\"\n ag.status = :PAUSED\n ag.resource_name = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n end\n end\n\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n #\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # the resource name of the AssetGroup\n # the resource name of the Asset\n # the field_type of the Asset in this AssetGroup.\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n headline_asset_resource_names.each do |resource_name|\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource\n .asset_group_asset do |aga|\n aga.field_type = :HEADLINE\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n aga.asset = resource_name\n end\n end\n end\n\n # Link the description assets.\n description_asset_resource_names.each do |resource_name|\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource\n .asset_group_asset do |aga|\n aga.field_type = :DESCRIPTION\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n aga.asset = resource_name\n end\n end\n end\n\n # Create and link the long headline text asset.\n operations += create_and_link_text_asset(\n client,\n customer_id,\n \"Travel the World\",\n :LONG_HEADLINE)\n\n # Create and link the business name and logo asset.\n operations += create_and_link_brand_assets(\n client,\n customer_id,\n brand_guidelines_enabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\")\n\n # Create and link the image assets.\n\n # Create and link the Marketing Image Asset.\n operations += create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/Eit5\",\n :MARKETING_IMAGE,\n \"Marketing Image\")\n\n # Create and link the Square Marketing Image Asset.\n operations += create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/bjYi\",\n :SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\")\n\n operations\nend\n\n# Creates a list of MutateOperations that create a new linked text asset.\ndef create_and_link_text_asset(client, customer_id, text, field_type)\n operations = []\n temp_id = next_temp_id\n\n # Create the Text Asset.\n operations << client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, temp_id)\n a.text_asset = client.resource.text_asset do |text_asset|\n text_asset.text = text\n end\n end\n end\n\n # Create an AssetGroupAsset to link the Asset to the AssetGroup.\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource\n .asset_group_asset do |aga|\n aga.field_type = field_type\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n aga.asset = client.path.asset(customer_id, temp_id)\n end\n end\n\n operations\nend\n\n# Creates a list of MutateOperations that create a new linked image asset.\ndef create_and_link_image_asset(client, customer_id, url, field_type, asset_name)\n operations = []\n temp_id = next_temp_id\n\n # Create the Image Asset.\n operations << client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, temp_id)\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n a.name = asset_name\n a.type = :IMAGE\n a.image_asset = client.resource.image_asset do |image_asset|\n image_asset.data = get_image_bytes(url)\n end\n end\n end\n\n # Create an AssetGroupAsset to link the Asset to the AssetGroup.\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource.\n asset_group_asset do |aga|\n aga.field_type = field_type\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n aga.asset = client.path.asset(customer_id, temp_id)\n end\n end\n\n operations\nend\n\n# Creates a list of MutateOperations that create linked brand assets.\ndef create_and_link_brand_assets(\n client,\n customer_id,\n brand_guidelines_enabled,\n business_name,\n logo_url,\n logo_name)\n operations = []\n\n # Create the Text Asset.\n text_asset_temp_id = next_temp_id\n operations << client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, text_asset_temp_id)\n a.text_asset = client.resource.text_asset do |text_asset|\n text_asset.text = business_name\n end\n end\n end\n\n # Create the Image Asset.\n image_asset_temp_id = next_temp_id\n operations << client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, image_asset_temp_id)\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n a.name = logo_name\n a.type = :IMAGE\n a.image_asset = client.resource.image_asset do |image_asset|\n image_asset.data = get_image_bytes(logo_url)\n end\n end\n end\n\n if brand_guidelines_enabled\n # Create CampaignAsset resources to link the Asset resources to the Campaign.\n operations << client.operation.mutate do |m|\n m.campaign_asset_operation = client.operation.create_resource.\n campaign_asset do |ca|\n ca.field_type = :BUSINESS_NAME\n ca.campaign = client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n )\n ca.asset = client.path.asset(customer_id, text_asset_temp_id)\n end\n end\n\n operations << client.operation.mutate do |m|\n m.campaign_asset_operation = client.operation.create_resource.\n campaign_asset do |ca|\n ca.field_type = :LOGO\n ca.campaign = client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n )\n ca.asset = client.path.asset(customer_id, image_asset_temp_id)\n end\n end\n else\n # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource.\n asset_group_asset do |aga|\n aga.field_type = :BUSINESS_NAME\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n aga.asset = client.path.asset(customer_id, text_asset_temp_id)\n end\n end\n\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource.\n asset_group_asset do |aga|\n aga.field_type = :LOGO\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n aga.asset = client.path.asset(customer_id, image_asset_temp_id)\n end\n end\n end\n\n operations\nend\n\n# Create a list of MutateOperations that create AssetGroupSignals.\ndef create_asset_group_signal_operations(client, customer_id, audience_id)\n operations = []\n return operations if audience_id.nil?\n\n operations << client.operation.mutate do |m|\n m.asset_group_signal_operation = client.operation.create_resource.\n asset_group_signal do |ags|\n ags.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n ags.audience = client.resource.audience_info do |ai|\n ai.audience = client.path.audience(customer_id, audience_id)\n end\n end\n end\n\n operations\nend\n\n# Loads image data from a URL.\ndef get_image_bytes(url)\n URI.open(url).read\nend\n\n# Prints the details of a MutateGoogleAdsResponse.\ndef print_response_details(response)\n # Parse the mutate response to print details about the entities that\n # were created by the request.\n suffix = \"_result\"\n response.mutate_operation_responses.each do |result|\n result.to_h.select {|k, v| v }.each do |name, value|\n if name.to_s.end_with?(suffix)\n name = name.to_s.delete_suffix(suffix)\n end\n\n puts \"Created a(n) #{::Google::Ads::GoogleAds::Utils.camelize(name)} \" \\\n \"with #{value.to_s.strip}.\"\n end\n end\nend\n\nif __FILE__ == $0\n options = {}\n\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:audience_id] = nil\n options[:brand_guidelines_enabled] = true\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-D', '--audience-id AUDIENCE-ID', String, 'Audience ID (optional)') do |v|\n options[:audience_id] = v\n end\n\n opts.on('-B', '--brand-guidelines-enabled', 'Enable brand guidelines (optional)') do\n options[:brand_guidelines_enabled] = true\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n add_performance_max_campaign(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options[:audience_id],\n options[:brand_guidelines_enabled]\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\nadd_performance_max_campaign.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2021, Google LLC\n#\n# 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# http://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.\n#\n# This example shows how to create a Performance Max campaign.\n#\n# For more information about Performance Max campaigns, see\n# https://developers.google.com/google-ads/api/docs/performance-max/overview.\n#\n# Prerequisites:\n# - You must have at least one conversion action in the account. For\n# more about conversion actions, see\n# https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n#\n# This example uses the default customer conversion goals. For an example of\n# setting campaign-specific conversion goals, see\n# shopping_ads/add_performance_max_retail_campaign.pl.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::Utils::MediaUtils;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignBudget;\nuse Google::Ads::GoogleAds::V25::Resources::Campaign;\nuse Google::Ads::GoogleAds::V25::Resources::TextGuidelines;\nuse Google::Ads::GoogleAds::V25::Resources::MessagingRestriction;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignCriterion;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignAsset;\nuse Google::Ads::GoogleAds::V25::Resources::Asset;\nuse Google::Ads::GoogleAds::V25::Resources::AssetAutomationSetting;\nuse Google::Ads::GoogleAds::V25::Resources::AssetGroup;\nuse Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset;\nuse Google::Ads::GoogleAds::V25::Resources::AssetGroupSignal;\nuse Google::Ads::GoogleAds::V25::Common::MaximizeConversionValue;\nuse Google::Ads::GoogleAds::V25::Common::LocationInfo;\nuse Google::Ads::GoogleAds::V25::Common::LanguageInfo;\nuse Google::Ads::GoogleAds::V25::Common::TextAsset;\nuse Google::Ads::GoogleAds::V25::Common::ImageAsset;\nuse Google::Ads::GoogleAds::V25::Common::AudienceInfo;\nuse Google::Ads::GoogleAds::V25::Enums::BudgetDeliveryMethodEnum qw(STANDARD);\nuse Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum;\nuse Google::Ads::GoogleAds::V25::Enums::AdvertisingChannelTypeEnum\n qw(PERFORMANCE_MAX);\nuse Google::Ads::GoogleAds::V25::Enums::AssetAutomationStatusEnum qw(OPTED_IN);\nuse Google::Ads::GoogleAds::V25::Enums::MessagingRestrictionTypeEnum\n qw(RESTRICTION_BASED_EXCLUSION);\nuse Google::Ads::GoogleAds::V25::Enums::AssetAutomationTypeEnum\n qw(GENERATE_IMAGE_EXTRACTION FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION TEXT_ASSET_AUTOMATION GENERATE_ENHANCED_YOUTUBE_VIDEOS GENERATE_IMAGE_ENHANCEMENT);\nuse Google::Ads::GoogleAds::V25::Enums::AssetGroupStatusEnum;\nuse Google::Ads::GoogleAds::V25::Enums::AssetFieldTypeEnum\n qw(HEADLINE DESCRIPTION LONG_HEADLINE BUSINESS_NAME LOGO MARKETING_IMAGE SQUARE_MARKETING_IMAGE);\nuse Google::Ads::GoogleAds::V25::Enums::EuPoliticalAdvertisingStatusEnum\n qw(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING);\nuse Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation;\nuse Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation;\nuse Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::AssetGroupService::AssetGroupOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::AssetGroupSignalService::AssetGroupSignalOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignAssetService::CampaignAssetOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\nuse POSIX qw(strftime);\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\n#\n# See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n# for further details.\n#\n# These temporary IDs are fixed because they are used in multiple places.\nuse constant BUDGET_TEMPORARY_ID => -1;\nuse constant PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID => -2;\nuse constant ASSET_GROUP_TEMPORARY_ID => -3;\n\n# There are also entities that will be created in the same request but do not\n# need to be fixed temporary IDs because they are referenced only once.\nour $next_temp_id = ASSET_GROUP_TEMPORARY_ID - 1;\n\nsub add_performance_max_campaign {\n my ($api_client, $customer_id, $audience_id, $brand_guidelines_enabled) = @_;\n\n # Performance Max campaigns require that repeated assets such as headlines\n # and descriptions be created before the campaign.\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets.\n #\n # Create the headlines.\n my $headline_asset_resource_names =\n create_multiple_text_assets($api_client, $customer_id,\n [\"Travel\", \"Travel Reviews\", \"Book travel\"]);\n # Create the descriptions.\n my $description_asset_resource_names =\n create_multiple_text_assets($api_client, $customer_id,\n [\"Take to the air!\", \"Fly to the sky!\"]);\n\n # It's important to create the below entities in this order because they depend\n # on each other.\n my $operations = [];\n # The below methods create and return MutateOperations that we later provide to\n # the GoogleAdsService->mutate() method in order to create the entities in a\n # single request. Since the entities for a Performance Max campaign are closely\n # tied to one-another, it's considered a best practice to create them in a\n # single mutate request so they all complete successfully or fail entirely,\n # leaving no orphaned entities. See:\n # https://developers.google.com/google-ads/api/docs/mutating/overview.\n push @$operations, create_campaign_budget_operation($customer_id);\n push @$operations,\n create_performance_max_campaign_operation($customer_id,\n $brand_guidelines_enabled);\n push @$operations, @{create_campaign_criterion_operations($customer_id)};\n push @$operations,\n @{\n create_asset_group_operations(\n $customer_id, $headline_asset_resource_names,\n $description_asset_resource_names, $brand_guidelines_enabled\n )};\n push @$operations,\n @{create_asset_group_signal_operations($customer_id, $audience_id)};\n\n # Issue a mutate request to create everything and print its information.\n my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $operations\n });\n\n print_response_details($mutate_google_ads_response);\n\n return 1;\n}\n\n# Creates a MutateOperation that creates a new CampaignBudget.\n#\n# A temporary ID will be assigned to this campaign budget so that it can be\n# referenced by other objects being created in the same mutate request.\nsub create_campaign_budget_operation {\n my ($customer_id) = @_;\n\n # Create a mutate operation that creates a campaign budget operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignBudgetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::CampaignBudget->new(\n {\n # Set a temporary ID in the budget's resource name so it can be\n # referenced by the campaign in later steps.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n name => \"Performance Max campaign budget #\" . uniqid(),\n # The budget period already defaults to DAILY.\n amountMicros => 50000000,\n deliveryMethod => STANDARD,\n # A Performance Max campaign cannot use a shared campaign budget.\n explicitlyShared => \"false\",\n })})});\n}\n\n# Creates a MutateOperation that creates a new Performance Max campaign.\n#\n# A temporary ID will be assigned to this campaign so that it can be referenced\n# by other objects being created in the same mutate request.\nsub create_performance_max_campaign_operation {\n my ($customer_id, $brand_guidelines_enabled) = @_;\n # Configures the optional opt-in/out status for asset automation settings.\n # When we create the campaign object, we set campaign->{assetAutomationSettings}\n # equal to $asset_automation_settings.\n my $asset_automation_settings = [];\n my $asset_automation_types = [\n GENERATE_IMAGE_EXTRACTION, FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,\n TEXT_ASSET_AUTOMATION, GENERATE_ENHANCED_YOUTUBE_VIDEOS,\n GENERATE_IMAGE_ENHANCEMENT\n ];\n foreach my $asset_automation_type (@$asset_automation_types) {\n push @$asset_automation_settings,\n Google::Ads::GoogleAds::V25::Resources::AssetAutomationSetting->new({\n assetAutomationStatus => OPTED_IN,\n assetAutomationType => $asset_automation_type\n });\n }\n\n my $text_guidelines =\n Google::Ads::GoogleAds::V25::Resources::TextGuidelines->new({\n termExclusions => [\"cheap\", \"free\"],\n messagingRestrictions => [\n Google::Ads::GoogleAds::V25::Resources::MessagingRestriction->new({\n restrictionText => \"Don't mention competitor names\",\n restrictionType => RESTRICTION_BASED_EXCLUSION\n })]});\n\n # Create a mutate operation that creates a campaign operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n # Assign the resource name with a temporary ID.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n name => \"Performance Max campaign #\" . uniqid(),\n # Set the budget using the given budget resource name.\n campaignBudget =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n status =>\n Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n # All Performance Max campaigns have an advertisingChannelType of\n # PERFORMANCE_MAX. The advertisingChannelSubType should not be set.\n advertisingChannelType => PERFORMANCE_MAX,\n\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximizeConversionValue. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n maximizeConversionValue =>\n Google::Ads::GoogleAds::V25::Common::MaximizeConversionValue->\n new({\n targetRoas => 3.5\n }\n ),\n\n # Set if the campaign is enabled for brand guidelines. For more information\n # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n brandGuidelinesEnabled => $brand_guidelines_enabled,\n\n # Configures the optional opt-in/out status for asset automation settings.\n assetAutomationSettings => $asset_automation_settings,\n\n # Set the text guidelines.\n textGuidelines => $text_guidelines,\n\n # Optional fields.\n startDateTime =>\n strftime(\"%Y%m%d 00:00:00\", localtime(time + 60 * 60 * 24)),\n endDateTime => strftime(\n \"%Y%m%d 23:59:59\",\n localtime(time + 60 * 60 * 24 * 365)\n ),\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n })})});\n}\n\n# Creates a list of MutateOperations that create new campaign criteria.\nsub create_campaign_criterion_operations {\n my ($customer_id) = @_;\n\n my $operations = [];\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting.\n #\n # We will add one positive location target for New York City (ID=1023191)\n # and one negative location target for Brooklyn (ID=1022762).\n # First, add the positive (negative = false) for New York City.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n location =>\n Google::Ads::GoogleAds::V25::Common::LocationInfo->new({\n geoTargetConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n 1023191)}\n ),\n negative => \"false\"\n })})});\n\n # Next add the negative target for Brooklyn.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n location =>\n Google::Ads::GoogleAds::V25::Common::LocationInfo->new({\n geoTargetConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n 1022762)}\n ),\n negative => \"true\"\n })})});\n\n # Set the LANGUAGE campaign criterion.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7.\n language =>\n Google::Ads::GoogleAds::V25::Common::LanguageInfo->new({\n languageConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::language_constant(\n 1000) # English\n })})})});\n\n return $operations;\n}\n\n# Creates multiple text assets and returns the list of resource names.\nsub create_multiple_text_assets {\n my ($api_client, $customer_id, $texts) = @_;\n\n # Here again we use the GoogleAdService to create multiple text assets in a\n # single request.\n my $operations = [];\n foreach my $text (@$texts) {\n # Create a mutate operation for a text asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n textAsset =>\n Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $text\n })})})});\n }\n\n # Issue a mutate request to add all assets.\n my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $operations\n });\n\n my $asset_resource_names = [];\n foreach\n my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})\n {\n push @$asset_resource_names, $response->{assetResult}{resourceName};\n }\n print_response_details($mutate_google_ads_response);\n\n return $asset_resource_names;\n}\n\n# Creates a list of MutateOperations that create a new asset group.\n#\n# A temporary ID will be assigned to this asset group so that it can be referenced\n# by other objects being created in the same mutate request.\nsub create_asset_group_operations {\n my (\n $customer_id,\n $headline_asset_resource_names,\n $description_asset_resource_names,\n $brand_guidelines_enabled\n ) = @_;\n\n my $operations = [];\n # Create a mutate operation that creates an asset group operation.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupService::AssetGroupOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::AssetGroup->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n name => \"Performance Max asset group #\" . uniqid(),\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n finalUrls => [\"http://www.example.com\"],\n finalMobileUrls => [\"http://www.example.com\"],\n status =>\n Google::Ads::GoogleAds::V25::Enums::AssetGroupStatusEnum::PAUSED\n })})});\n\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets.\n\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # - the resource name of the AssetGroup\n # - the resource name of the Asset\n # - the fieldType of the Asset in this AssetGroup\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n foreach my $resource_name (@$headline_asset_resource_names) {\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $resource_name,\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => HEADLINE\n })})});\n }\n\n # Link the description assets.\n foreach my $resource_name (@$description_asset_resource_names) {\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $resource_name,\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => DESCRIPTION\n })})});\n }\n\n # Create and link the long headline text asset.\n push @$operations,\n @{create_and_link_text_asset($customer_id, \"Travel the World\",\n LONG_HEADLINE)};\n\n # Create and link the business name and logo asset.\n push @$operations,\n @{\n create_and_link_brand_assets(\n $customer_id, $brand_guidelines_enabled,\n \"Interplanetary Cruises\", \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\"\n )};\n\n # Create and link the image assets.\n\n # Create and link the marketing image asset.\n push @$operations,\n @{\n create_and_link_image_asset(\n $customer_id, \"https://gaagl.page.link/Eit5\",\n MARKETING_IMAGE, \"Marketing Image\"\n )};\n\n # Create and link the square marketing image asset.\n push @$operations,\n @{\n create_and_link_image_asset(\n $customer_id, \"https://gaagl.page.link/bjYi\",\n SQUARE_MARKETING_IMAGE, \"Square Marketing Image\"\n )};\n\n return $operations;\n}\n\n# Creates a list of MutateOperations that create a new linked text asset.\nsub create_and_link_text_asset {\n my ($customer_id, $text, $field_type) = @_;\n\n my $operations = [];\n # Create a new mutate operation for a text asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n textAsset => Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $text\n })})})});\n\n # Create an asset group asset to link the asset to the asset group.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => $field_type\n })})});\n\n $next_temp_id--;\n return $operations;\n}\n\n# Creates a list of MutateOperations that create a new linked image asset.\nsub create_and_link_image_asset {\n my ($customer_id, $url, $field_type, $asset_name) = @_;\n\n my $operations = [];\n # Create a new mutate operation for an image asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n name => $asset_name,\n imageAsset =>\n Google::Ads::GoogleAds::V25::Common::ImageAsset->new({\n data => get_base64_data_from_url($url)})})})});\n\n # Create an asset group asset to link the asset to the asset group.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => $field_type\n })})});\n\n $next_temp_id--;\n return $operations;\n}\n\n# Creates a list of MutateOperations that create linked brand assets.\nsub create_and_link_brand_assets {\n my ($customer_id, $brand_guidelines_enabled, $business_name, $logo_url,\n $logo_name)\n = @_;\n\n my $operations = [];\n\n # Create the text asset.\n my $text_asset_temp_id = $next_temp_id--;\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $text_asset_temp_id\n ),\n textAsset => Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $business_name\n })})})});\n\n # Create the image asset.\n my $image_asset_temp_id = $next_temp_id--;\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $image_asset_temp_id\n ),\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n name => $logo_name,\n imageAsset =>\n Google::Ads::GoogleAds::V25::Common::ImageAsset->new({\n data => get_base64_data_from_url($logo_url)})})})});\n\n if ($brand_guidelines_enabled) {\n # Create CampaignAsset resources to link the Asset resources to the Campaign.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignAssetService::CampaignAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignAsset->new({\n fieldType => BUSINESS_NAME,\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $text_asset_temp_id\n )})})});\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignAssetService::CampaignAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignAsset->new({\n fieldType => LOGO,\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $image_asset_temp_id\n )})})});\n } else {\n # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $text_asset_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => BUSINESS_NAME\n })})});\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $image_asset_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => LOGO\n })})});\n }\n\n return $operations;\n}\n\n# Creates a list of MutateOperations that create asset group signals.\nsub create_asset_group_signal_operations {\n my ($customer_id, $audience_id) = @_;\n\n my $operations = [];\n return $operations if not defined $audience_id;\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupSignalOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupSignalService::AssetGroupSignalOperation\n ->new({\n # To learn more about Audience Signals, see:\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupSignal->new({\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n audience =>\n Google::Ads::GoogleAds::V25::Common::AudienceInfo->new({\n audience =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::audience(\n $customer_id, $audience_id\n )})})})});\n return $operations;\n}\n\n# Prints the details of a MutateGoogleAdsResponse.\n# Parses the \"response\" oneof field name and uses it to extract the new entity's\n# name and resource name.\nsub print_response_details {\n my ($mutate_google_ads_response) = @_;\n\n foreach\n my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})\n {\n my $result_type = [keys %$response]->[0];\n\n printf \"Created a(n) %s with '%s'.\\n\",\n ucfirst $result_type =~ s/Result$//r,\n $response->{$result_type}{resourceName};\n }\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\nmy $customer_id = undef;\nmy $audience_id = undef;\nmy $brand_guidelines_enabled = \"true\";\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"audience_id=i\" => \\$audience_id,\n \"brand_guidelines_enabled=s\" => \\$brand_guidelines_enabled\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id);\n\n# Call the example.\nadd_performance_max_campaign(\n $api_client, $customer_id =~ s/-//gr,\n $audience_id, $brand_guidelines_enabled\n);\n\n=pod\n\n=head1 NAME\n\nadd_performance_max_campaign\n\n=head1 DESCRIPTION\n\nThis example shows how to create a Performance Max campaign.\n\nFor more information about Performance Max campaigns, see\nhttps://developers.google.com/google-ads/api/docs/performance-max/overview.\n\nPrerequisites:\n- You must have at least one conversion action in the account. For\n more about conversion actions, see\n https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n\nThis example uses the default customer conversion goals. For an example of\nsetting campaign-specific conversion goals, see\nshopping_ads/add_performance_max_retail_campaign.pl.\n\n=head1 SYNOPSIS\n\nadd_performance_max_campaign.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -audience_id [optional] An audience ID to use to improve the\n targeting of the Performance Max campaign.\n -brand_guidelines_enabled\t[optional] A boolean value indicating if the campaign is enabled for brand guidelines. Defaults to false.\n\n=cut\nadd_performance_max_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.406Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":5249,"estimatedTokens":54447}}169{"id":"doc-visitors_to_specific_pages_google_ads_api_google-2ad8b9f4","source":"documentation","title":"Visitors to Specific Pages | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audience-segments/visited-specific-pages","text":"Example:\n```text\nSELECT\n remarketing_action.id,\n remarketing_action.name,\n remarketing_action.tag_snippets\nFROM remarketing_action\nWHERE remarketing_action.resource_name = 'REMARKETING_ACTION_RESOURCE_NAME'\n```\n\nExample:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient, long customerId) {\n // Creates a UserListRuleInfo object containing the first rule.\n UserListRuleInfo userVisitedSite1Rule =\n createUserListRuleInfoFromUrl(\"http://example.com/example1\");\n\n // Creates a UserListRuleInfo object containing the second rule.\n UserListRuleInfo userVisitedSite2Rule =\n createUserListRuleInfoFromUrl(\"http://example.com/example2\");\n\n // Creates a UserListRuleInfo object containing the third rule.\n UserListRuleInfo userVisitedSite3Rule =\n createUserListRuleInfoFromUrl(\"http://example.com/example3\");\n\n // Create the user list \"Visitors of page 1 AND page 2, but not page 3\". To create the user list\n // \"Visitors of page 1 *OR* page 2, but not page 3\", change the UserListFlexibleRuleOperator\n // from AND to OR.\n FlexibleRuleUserListInfo flexibleRuleUserListInfo =\n FlexibleRuleUserListInfo.newBuilder()\n .setInclusiveRuleOperator(UserListFlexibleRuleOperator.AND)\n // Inclusive operands are joined together with the specified inclusiveRuleOperator. This\n // represents the set of users that should be included in the user list.\n .addInclusiveOperands(\n FlexibleRuleOperandInfo.newBuilder()\n .setRule(userVisitedSite1Rule)\n // Optional: adds a lookback window for this rule, in days.\n .setLookbackWindowDays(7L))\n .addInclusiveOperands(\n FlexibleRuleOperandInfo.newBuilder()\n .setRule(userVisitedSite2Rule)\n // Optional: adds a lookback window for this rule, in days.\n .setLookbackWindowDays(7L))\n .addExclusiveOperands(\n // Exclusive operands are joined together with OR. This represents the set of users\n // to be excluded from the user list.\n FlexibleRuleOperandInfo.newBuilder().setRule(userVisitedSite3Rule))\n .build();\n\n // Defines a representation of a user list that is generated by a rule.\n RuleBasedUserListInfo ruleBasedUserListInfo =\n RuleBasedUserListInfo.newBuilder()\n // Optional: To include past users in the user list, set the prepopulation_status to\n // REQUESTED.\n .setPrepopulationStatus(UserListPrepopulationStatus.REQUESTED)\n .setFlexibleRuleUserList(flexibleRuleUserListInfo)\n .build();\n\n // Creates a user list.\n UserList userList =\n UserList.newBuilder()\n .setName(\"Flexible rule user list for example.com #\" + getPrintableDateTime())\n .setDescription(\n \"Visitors of both http://example.com/example1 AND http://example.com/example2 but\"\n + \" NOT http://example.com/example3\")\n .setMembershipStatus(UserListMembershipStatus.OPEN)\n .setRuleBasedUserList(ruleBasedUserListInfo)\n .build();\n\n // Creates the operation.\n UserListOperation operation = UserListOperation.newBuilder().setCreate(userList).build();\n\n // Creates the user list service client.\n try (UserListServiceClient userListServiceClient =\n googleAdsClient.getLatestVersion().createUserListServiceClient()) {\n // Adds the user list.\n MutateUserListsResponse response =\n userListServiceClient.mutateUserLists(\n Long.toString(customerId), ImmutableList.of(operation));\n String userListResourceName = response.getResults(0).getResourceName();\n // Prints the result.\n System.out.printf(\"Created user list with resource name '%s'.%n\", userListResourceName);\n }\n}\n\n/**\n * Creates a UserListRuleInfo object containing a rule targeting any user that visited the\n * provided URL.\n */\nprivate UserListRuleInfo createUserListRuleInfoFromUrl(String urlString) {\n // Creates a rule targeting any user that visited a URL that equals the given urlString.\n UserListRuleItemInfo userVisitedSiteRule =\n UserListRuleItemInfo.newBuilder()\n // Uses a built-in parameter to create a domain URL rule.\n .setName(URL_STRING)\n .setStringRuleItem(\n UserListStringRuleItemInfo.newBuilder()\n .setOperator(UserListStringRuleItemOperator.EQUALS)\n .setValue(urlString))\n .build();\n\n // Returns a UserListRuleInfo object containing the rule.\n return UserListRuleInfo.newBuilder()\n .addRuleItemGroups(UserListRuleItemGroupInfo.newBuilder().addRuleItems(userVisitedSiteRule))\n .build();\n}AddFlexibleRuleUserList.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId)\n{\n // Creates a UserListRuleInfo object containing the first rule.\n UserListRuleInfo userVisitedSite1Rule =\n CreateUserListRuleInfoFromUrl(\"http://example.com/example1\");\n\n // Creates a UserListRuleInfo object containing the second rule.\n UserListRuleInfo userVisitedSite2Rule =\n CreateUserListRuleInfoFromUrl(\"http://example.com/example2\");\n\n // Creates a UserListRuleInfo object containing the third rule.\n UserListRuleInfo userVisitedSite3Rule =\n CreateUserListRuleInfoFromUrl(\"http://example.com/example3\");\n\n // Create the user list \"Visitors of page 1 AND page 2, but not page 3\". To create the\n // user list \"Visitors of page 1 *OR* page 2, but not page 3\", change the\n // UserListFlexibleRuleOperator from And to Or.\n FlexibleRuleUserListInfo flexibleRuleUserListInfo = new FlexibleRuleUserListInfo\n {\n InclusiveRuleOperator = UserListFlexibleRuleOperator.And\n };\n\n // Inclusive operands are joined together with the specified inclusiveRuleOperator. This\n // represents the set of users that should be included in the user list.\n flexibleRuleUserListInfo.InclusiveOperands.Add(new FlexibleRuleOperandInfo\n {\n Rule = userVisitedSite1Rule,\n // Optional: adds a lookback window for this rule, in days.\n LookbackWindowDays = 7\n });\n flexibleRuleUserListInfo.InclusiveOperands.Add(new FlexibleRuleOperandInfo\n {\n Rule = userVisitedSite2Rule,\n // Optional: adds a lookback window for this rule, in days.\n LookbackWindowDays = 7\n });\n\n // Exclusive operands are joined together with OR. This represents the set of users\n // to be excluded from the user list.\n flexibleRuleUserListInfo.InclusiveOperands.Add(new FlexibleRuleOperandInfo\n {\n Rule = userVisitedSite3Rule\n });\n\n // Defines a representation of a user list that is generated by a rule.\n RuleBasedUserListInfo ruleBasedUserListInfo = new RuleBasedUserListInfo\n {\n // Optional: To include past users in the user list, set the prepopulation_status to\n // REQUESTED.\n PrepopulationStatus = UserListPrepopulationStatus.Requested,\n FlexibleRuleUserList = flexibleRuleUserListInfo\n };\n\n // Creates a user list.\n UserList userList = new UserList\n {\n Name = $\"Flexible rule user list example.com #{ExampleUtilities.GetRandomString()}\",\n Description = \"Visitors of both https://example.com/example1 AND \" +\n \"https://example.com/example2 but NOT https://example.com/example3\",\n MembershipStatus = UserListMembershipStatus.Open,\n RuleBasedUserList = ruleBasedUserListInfo\n };\n\n // Creates the operation.\n UserListOperation operation = new UserListOperation\n {\n Create = userList\n };\n\n try\n {\n UserListServiceClient userListServiceClient =\n client.GetService(Services.V25.UserListService);\n MutateUserListsResponse response =\n userListServiceClient.MutateUserLists(customerId.ToString(),\n new[] { operation });\n\n string userListResourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Created user list with resource name '{userListResourceName}'.\");\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}\n\n/// <summary>\n/// Creates a UserListRuleInfo object containing a rule targeting any user that visited the\n/// provided URL.\n/// </summary>\nprivate UserListRuleInfo CreateUserListRuleInfoFromUrl(string urlString)\n{\n // Creates a rule targeting any user that visited a URL that equals the given urlString.\n UserListRuleItemInfo userVisitedSiteRule = new UserListRuleItemInfo\n {\n Name = URL_STRING,\n StringRuleItem = new UserListStringRuleItemInfo\n {\n Operator = UserListStringRuleItemOperator.Equals,\n Value = urlString\n }\n };\n\n // Returns a UserListRuleInfo object containing the rule.\n UserListRuleInfo userListRuleInfo = new UserListRuleInfo();\n UserListRuleItemGroupInfo userListRuleItemGroupInfo = new UserListRuleItemGroupInfo();\n userListRuleItemGroupInfo.RuleItems.Add(userVisitedSiteRule);\n userListRuleInfo.RuleItemGroups.Add(userListRuleItemGroupInfo);\n\n return userListRuleInfo;\n}AddFlexibleRuleUserList.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n) {\n // Creates a rule targeting any user that visited a url that equals\n // http://example.com/example1'.\n $userVisitedSite1RuleInfo = self::createUserListRuleFromUrl('http://example.com/example1');\n // Creates a rule targeting any user that visited a url that equals\n // http://example.com/example2'.\n $userVisitedSite2RuleInfo = self::createUserListRuleFromUrl('http://example.com/example2');\n // Creates a rule targeting any user that visited a url that equals\n // http://example.com/example3'.\n $userVisitedSite3RuleInfo = self::createUserListRuleFromUrl('http://example.com/example3');\n\n // Create the user list \"Visitors of page 1 AND page 2, but not page 3\".\n // To create the user list \"Visitors of page 1 *OR* page 2, but not page 3\",\n // change the UserListFlexibleRuleOperator from PBAND to OR.\n $flexibleRuleUserListInfo = new FlexibleRuleUserListInfo([\n 'inclusive_rule_operator' => UserListFlexibleRuleOperator::PBAND,\n 'inclusive_operands' => [\n new FlexibleRuleOperandInfo([\n 'rule' => $userVisitedSite1RuleInfo,\n // Optionally add a lookback window for this rule, in days.\n 'lookback_window_days' => 7\n ]),\n new FlexibleRuleOperandInfo([\n 'rule' => $userVisitedSite2RuleInfo,\n // Optionally add a lookback window for this rule, in days.\n 'lookback_window_days' => 7\n ])\n ],\n // Exclusive operands are joined together with OR. This represents the set of users to\n // be excluded from the user list.\n 'exclusive_operands' => [\n new FlexibleRuleOperandInfo(['rule' => $userVisitedSite3RuleInfo])\n ]\n ]);\n\n // Defines a representation of a user list that is generated by a rule.\n $ruleBasedUserListInfo = new RuleBasedUserListInfo([\n // Optional: To include past users in the user list, set the prepopulation_status to\n // REQUESTED.\n 'prepopulation_status' => UserListPrepopulationStatus::REQUESTED,\n 'flexible_rule_user_list' => $flexibleRuleUserListInfo\n ]);\n\n // Creates a user list.\n $userList = new UserList([\n 'name' => 'All visitors to http://example.com/example1 AND ' .\n 'http://example.com/example2 but NOT http://example.com/example3 #'\n . Helper::getPrintableDatetime(),\n 'description' => 'Visitors of both http://example.com/example1 AND ' .\n 'http://example.com/example2 but NOT http://example.com/example3',\n 'membership_status' => UserListMembershipStatus::OPEN,\n 'rule_based_user_list' => $ruleBasedUserListInfo\n ]);\n\n // Creates the operation.\n $operation = new UserListOperation();\n $operation->setCreate($userList);\n\n // Issues a mutate request to add the user list and prints some information.\n $userListServiceClient = $googleAdsClient->getUserListServiceClient();\n $response = $userListServiceClient->mutateUserLists(\n MutateUserListsRequest::build($customerId, [$operation])\n );\n printf(\n \"Created user list with resource name '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n}AddFlexibleRuleUserList.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient, customer_id: str) -> None:\n \"\"\"Creates a rule-based user list.\n\n The list will be defined by a combination of rules for users who have\n visited two different pages of a website.\n\n Args:\n client: The Google Ads client.\n customer_id: The customer ID for which to add the user list.\n \"\"\"\n # Create a UserListRuleInfo object containing the first rule.\n user_visited_site1_rule_info: UserListRuleInfo = (\n create_user_list_rule_info_from_url(\n client, \"http://example.com/example1\"\n )\n )\n # Create a UserListRuleInfo object containing the second rule.\n user_visited_site2_rule_info: UserListRuleInfo = (\n create_user_list_rule_info_from_url(\n client, \"http://example.com/example2\"\n )\n )\n # Create a UserListRuleInfo object containing the third rule.\n user_visited_site3_rule_info: UserListRuleInfo = (\n create_user_list_rule_info_from_url(\n client, \"http://example.com/example3\"\n )\n )\n\n # Create the user list \"Visitors of page 1 AND page 2, but not page 3\".\n # To create the user list \"Visitors of page 1 *OR* page 2, but not page 3\",\n # change the UserListFlexibleRuleOperator from AND to OR.\n flexible_rule_user_list_info: FlexibleRuleUserListInfo = client.get_type(\n \"FlexibleRuleUserListInfo\"\n )\n flexible_rule_user_list_info.inclusive_rule_operator = (\n client.enums.UserListFlexibleRuleOperatorEnum.AND\n )\n\n # Inclusive operands are joined together with the specified\n # inclusive_rule_operator. This represents the set of users that should be\n # included in the user list.\n operand_1: FlexibleRuleOperandInfo = client.get_type(\n \"FlexibleRuleOperandInfo\"\n )\n operand_1.rule = user_visited_site1_rule_info\n # Optionally add a lookback window for this rule, in days.\n operand_1.lookback_window_days = 7\n flexible_rule_user_list_info.inclusive_operands.append(operand_1)\n\n operand_2: FlexibleRuleOperandInfo = client.get_type(\n \"FlexibleRuleOperandInfo\"\n )\n operand_2.rule = user_visited_site2_rule_info\n # Optionally add a lookback window for this rule, in days.\n operand_2.lookback_window_days = 7\n flexible_rule_user_list_info.inclusive_operands.append(operand_2)\n\n # Exclusive operands are joined together with OR.\n # This represents the set of users to be excluded from the user list.\n operand_3: FlexibleRuleOperandInfo = client.get_type(\n \"FlexibleRuleOperandInfo\"\n )\n operand_3.rule = user_visited_site3_rule_info\n flexible_rule_user_list_info.exclusive_operands.append(operand_3)\n\n # Define a representation of a user list that is generated by a rule.\n rule_based_user_list_info: RuleBasedUserListInfo = client.get_type(\n \"RuleBasedUserListInfo\"\n )\n # Optional: To include past users in the user list, set the\n # prepopulation_status to REQUESTED.\n rule_based_user_list_info.prepopulation_status = (\n client.enums.UserListPrepopulationStatusEnum.REQUESTED\n )\n rule_based_user_list_info.flexible_rule_user_list = (\n flexible_rule_user_list_info\n )\n\n # Create a user list.\n user_list_operation: UserListOperation = client.get_type(\n \"UserListOperation\"\n )\n user_list: UserList = user_list_operation.create\n user_list.name = (\n \"All visitors to http://example.com/example1 AND \"\n \"http://example.com/example2 but NOT \"\n f\"http://example.com/example3 #{uuid4()}\"\n )\n user_list.description = (\n \"Visitors of both http://example.com/example1 AND \"\n \"http://example.com/example2 but NOT\"\n \"http://example.com/example3\"\n )\n user_list.membership_status = client.enums.UserListMembershipStatusEnum.OPEN\n user_list.rule_based_user_list = rule_based_user_list_info\n\n # Issue a mutate request to add the user list, then print the results.\n user_list_service: UserListServiceClient = client.get_service(\n \"UserListService\"\n )\n response: MutateUserListsResponse = user_list_service.mutate_user_lists(\n customer_id=customer_id, operations=[user_list_operation]\n )\n print(\n \"Created user list with resource name: \"\n f\"'{response.results[0].resource_name}.'\"\n )add_flexible_rule_user_list.py\n```\n\nExample:\n```text\ndef add_combined_rule_user_list(customer_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n user_visited_site1_rule_info = create_user_list_rule_info_from_url(\n client,\n 'http://example.com/example1',\n )\n user_visited_site2_rule_info = create_user_list_rule_info_from_url(\n client,\n 'http://example.com/example2',\n )\n user_visited_site3_rule_info = create_user_list_rule_info_from_url(\n client,\n 'http://example.com/example3',\n )\n\n # Creates a user list.\n operation = client.operation.create_resource.user_list do |u|\n u.name = \"Flexible rule user list for example.com ##{(Time.new.to_f * 1000).to_i}\"\n u.description = \"Visitors of both http://example.com/example1 AND \" \\\n \"http://example.com/example2 but NOT http://example.com/example3\"\n u.membership_status = :OPEN\n # Defines a representation of a user list that is generated by a rule.\n u.rule_based_user_list = client.resource.rule_based_user_list_info do |r|\n # Optional: To include past users in the user list, set the\n # prepopulation_status to REQUESTED.\n r.prepopulation_status = :REQUESTED\n r.flexible_rule_user_list = client.resource.flexible_rule_user_list_info do |frul|\n frul.inclusive_rule_operator = :AND\n frul.inclusive_operands += [\n client.resource.flexible_rule_operand_info do |froi|\n froi.rule = user_visited_site1_rule_info\n # Optionally add a lookback window for this rule, in days.\n froi.lookback_window_days = 7\n end,\n client.resource.flexible_rule_operand_info do |froi|\n froi.rule = user_visited_site2_rule_info\n # Optionally add a lookback window for this rule, in days.\n froi.lookback_window_days = 7\n end,\n ]\n frul.exclusive_operands << client.resource.flexible_rule_operand_info do |froi|\n froi.rule = user_visited_site3_rule_info\n end\n end\n end\n end\n\n # Issues a mutate request to add the user list and prints some information.\n response = client.service.user_list.mutate_user_lists(\n customer_id: customer_id,\n operations: [operation],\n )\n\n puts \"Created user list with resource name \" \\\n \"#{response.results.first.resource_name}\"\nendadd_flexible_rule_user_list.rb\n```\n\nExample:\n```text\nsub add_combined_rule_user_list {\n my ($api_client, $customer_id) = @_;\n\n # Create a UserListRuleInfo object containing the first rule.\n my $user_visited_site1_rule_info =\n create_user_list_rule_info_from_url(\"http://example.com/example1\");\n\n # Create a UserListRuleInfo object containing the second rule.\n my $user_visited_site2_rule_info =\n create_user_list_rule_info_from_url(\"http://example.com/example2\");\n\n # Create a UserListRuleInfo object containing the third rule.\n my $user_visited_site3_rule_info =\n create_user_list_rule_info_from_url(\"http://example.com/example3\");\n\n # Create the user list \"Visitors of page 1 AND page 2, but not page 3\".\n # To create the user list \"Visitors of page 1 *OR* page 2, but not page 3\",\n # change the UserListFlexibleRuleOperator from AND to OR.\n my $flexible_rule_user_list_info =\n Google::Ads::GoogleAds::V25::Common::FlexibleRuleUserListInfo->new({\n inclusiveRuleOperator => AND,\n # Inclusive operands are joined together with the specified inclusiveRuleOperator.\n # This represents the set of users that should be included in the user list.\n inclusiveOperands => [\n Google::Ads::GoogleAds::V25::Common::FlexibleRuleOperandInfo->new({\n rule => $user_visited_site1_rule_info,\n # Optionally add a lookback window for this rule, in days.\n lookbackWindowDays => 7\n }\n ),\n Google::Ads::GoogleAds::V25::Common::FlexibleRuleOperandInfo->new({\n rule => $user_visited_site2_rule_info,\n # Optionally add a lookback window for this rule, in days.\n lookbackWindowDays => 7\n })\n ],\n # Exclusive operands are joined together with OR.\n # This represents the set of users to be excluded from the user list.\n exclusiveOperands => [\n Google::Ads::GoogleAds::V25::Common::FlexibleRuleOperandInfo->new({\n rule => $user_visited_site3_rule_info\n })\n ],\n });\n\n # Define a representation of a user list that is generated by a rule.\n my $rule_based_user_list_info =\n Google::Ads::GoogleAds::V25::Common::RuleBasedUserListInfo->new({\n # Optional: To include past users in the user list, set the prepopulationStatus\n # to REQUESTED.\n prepopulationStatus => REQUESTED,\n flexibleRuleUserList => $flexible_rule_user_list_info\n });\n\n # Create a user list.\n my $user_list = Google::Ads::GoogleAds::V25::Resources::UserList->new({\n name => \"Flexible rule user list for example.com #\" . uniqid(),\n description => \"Visitors of both http://example.com/example1 AND \" .\n \"http://example.com/example2 but NOT http://example.com/example3\",\n membershipStatus => OPEN,\n ruleBasedUserList => $rule_based_user_list_info\n });\n\n # Create the operation.\n my $user_list_operation =\n Google::Ads::GoogleAds::V25::Services::UserListService::UserListOperation->\n new({\n create => $user_list\n });\n\n # Issue a mutate request to add the user list and print some information.\n my $user_lists_response = $api_client->UserListService()->mutate({\n customerId => $customer_id,\n operations => [$user_list_operation]});\n printf \"Created user list with resource name '%s'.\\n\",\n $user_lists_response->{results}[0]{resourceName};\n\n return 1;\n}add_flexible_rule_user_list.pl\n```\n\nExample:\n```text\nSELECT\n user_list.name,\n user_list.membership_status,\n user_list.membership_life_span\nFROM user_list\nWHERE\n user_list.resource_name = 'USER_LIST_RESOURCE_NAME'\n```\n\nExample:\n```text\nprivate String targetAdsInAdGroupToUserList(\n GoogleAdsClient googleAdsClient, long customerId, long adGroupId, String userList) {\n // Creates the ad group criterion targeting members of the user list.\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))\n .setUserList(UserListInfo.newBuilder().setUserList(userList).build())\n .build();\n\n // Creates the operation.\n AdGroupCriterionOperation operation =\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterion).build();\n\n // Creates the ad group criterion service.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Adds the ad group criterion.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n // Gets and prints the results.\n String adGroupCriterionResourceName = response.getResults(0).getResourceName();\n System.out.printf(\n \"Successfully created ad group criterion with resource name '%s' \"\n + \"targeting user list with resource name '%s' with ad group with ID %d.%n\",\n adGroupCriterionResourceName, userList, adGroupId);\n return adGroupCriterionResourceName;\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate string TargetAdsInAdGroupToUserList(\n GoogleAdsClient client, long customerId, long adGroupId, string userListResourceName)\n{\n // Get the AdGroupCriterionService client.\n AdGroupCriterionServiceClient adGroupCriterionServiceClient = client.GetService\n (Services.V25.AdGroupCriterionService);\n\n // Create the ad group criterion targeting members of the user list.\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion\n {\n AdGroup = ResourceNames.AdGroup(customerId, adGroupId),\n UserList = new UserListInfo\n {\n UserList = userListResourceName\n }\n };\n\n // Create the operation.\n AdGroupCriterionOperation adGroupCriterionOperation = new AdGroupCriterionOperation\n {\n Create = adGroupCriterion\n };\n\n // Add the ad group criterion, then print and return the new criterion's resource name.\n MutateAdGroupCriteriaResponse mutateAdGroupCriteriaResponse =\n adGroupCriterionServiceClient.MutateAdGroupCriteria(customerId.ToString(),\n new[] { adGroupCriterionOperation });\n\n string adGroupCriterionResourceName =\n mutateAdGroupCriteriaResponse.Results.First().ResourceName;\n Console.WriteLine(\"Successfully created ad group criterion with resource name \" +\n $\"'{adGroupCriterionResourceName}' targeting user list with resource name \" +\n $\"'{userListResourceName}' with ad group with ID {adGroupId}.\");\n return adGroupCriterionResourceName;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function targetAdsInAdGroupToUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n string $userListResourceName\n): string {\n // Creates the ad group criterion targeting members of the user list.\n $adGroupCriterion = new AdGroupCriterion([\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'user_list' => new UserListInfo(['user_list' => $userListResourceName])\n ]);\n\n // Creates the operation.\n $operation = new AdGroupCriterionOperation();\n $operation->setCreate($adGroupCriterion);\n\n // Issues a mutate request to add an ad group criterion.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n /** @var MutateAdGroupCriteriaResponse $adGroupCriterionResponse */\n $adGroupCriterionResponse = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$operation])\n );\n\n $adGroupCriterionResourceName =\n $adGroupCriterionResponse->getResults()[0]->getResourceName();\n printf(\n \"Successfully created ad group criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with ad group with ID %d.%s\",\n $adGroupCriterionResourceName,\n $userListResourceName,\n $adGroupId,\n PHP_EOL\n );\n\n return $adGroupCriterionResourceName;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef target_ads_in_ad_group_to_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n user_list_resource_name: str,\n) -> str:\n \"\"\"Creates an ad group criterion that targets a user list with an ad group.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID used to create an ad group\n criterion.\n ad_group_id: a str ID for an ad group used to create an ad group\n criterion that targets members of a user list.\n user_list_resource_name: a str resource name for a user list.\n\n Returns:\n a str resource name for an ad group criterion.\n \"\"\"\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n # Creates the ad group criterion targeting members of the user list.\n ad_group_criterion: AdGroupCriterion = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = client.get_service(\n \"AdGroupService\"\n ).ad_group_path(customer_id, ad_group_id)\n ad_group_criterion.user_list.user_list = user_list_resource_name\n\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n print(\n \"Successfully created ad group criterion with resource name: \"\n f\"'{resource_name}' targeting user list with resource name: \"\n f\"'{user_list_resource_name}' and with ad group with ID \"\n f\"{ad_group_id}.\"\n )\n return resource_nameset_up_remarketing.py\n```\n\nExample:\n```text\ndef target_ads_in_ad_group_to_user_list(\n client,\n customer_id,\n ad_group_id,\n user_list\n)\n # Creates the ad group criterion targeting members of the user list.\n operation = client.operation.create_resource.ad_group_criterion do |agc|\n agc.ad_group = client.path.ad_group(customer_id, ad_group_id)\n agc.user_list = client.resource.user_list_info do |info|\n info.user_list = user_list\n end\n end\n\n # Issues a mutate request to create the ad group criterion.\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [operation],\n )\n ad_group_criterion_resource_name = response.results.first.resource_name\n puts \"Successfully created ad group criterion with resource name \" \\\n \"'#{ad_group_criterion_resource_name}' targeting user list with resource name \" \\\n \"'#{user_list}' with ad group with ID #{ad_group_id}\"\n\n ad_group_criterion_resource_name\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub target_ads_in_ad_group_to_user_list {\n my ($api_client, $customer_id, $ad_group_id, $user_list_resource_name) = @_;\n\n # Create the ad group criterion targeting members of the user list.\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({\n userList => $user_list_resource_name\n })});\n\n # Create the operation.\n my $ad_group_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion\n });\n\n # Add the ad group criterion, then print and return the new criterion's resource name.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n my $ad_group_criterion_resource_name =\n $ad_group_criteria_response->{results}[0]{resourceName};\n printf \"Successfully created ad group criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with ad group with ID %d.\\n\",\n $ad_group_criterion_resource_name, $user_list_resource_name, $ad_group_id;\n\n return $ad_group_criterion_resource_name;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate List<String> getUserListAdGroupCriterion(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n List<String> userListCriteria = new ArrayList<>();\n // Creates the Google Ads service client.\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Creates a request that will retrieve all of the ad group criteria under a campaign.\n SearchGoogleAdsRequest request =\n SearchGoogleAdsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setQuery(\n \"SELECT ad_group_criterion.criterion_id\"\n + \" FROM ad_group_criterion\"\n + \" WHERE campaign.id = \"\n + campaignId\n + \" AND ad_group_criterion.type = 'USER_LIST'\")\n .build();\n // Issues the search request.\n SearchPagedResponse searchPagedResponse = googleAdsServiceClient.search(request);\n // Iterates over all rows in all pages. Prints the results and adds the ad group criteria\n // resource names to the list.\n for (GoogleAdsRow googleAdsRow : searchPagedResponse.iterateAll()) {\n String adGroupCriterionResourceName = googleAdsRow.getAdGroupCriterion().getResourceName();\n System.out.printf(\n \"Ad group criterion with resource name '%s' was found.%n\",\n adGroupCriterionResourceName);\n userListCriteria.add(adGroupCriterionResourceName);\n }\n }\n return userListCriteria;\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate List<string> GetUserListAdGroupCriteria(\n GoogleAdsClient client, long customerId, long campaignId)\n{\n // Get the GoogleAdsService client.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n List<string> userListCriteriaResourceNames = new List<string>();\n\n // Create a query that will retrieve all of the ad group criteria under a campaign.\n string query = $@\"\n SELECT ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE\n campaign.id = {campaignId}\n AND ad_group_criterion.type = 'USER_LIST'\";\n\n // Issue the search request.\n googleAdsServiceClient.SearchStream(customerId.ToString(), query,\n delegate (SearchGoogleAdsStreamResponse resp)\n {\n // Display the results and add the resource names to the list.\n foreach (GoogleAdsRow googleAdsRow in resp.Results)\n {\n string adGroupCriterionResourceName =\n googleAdsRow.AdGroupCriterion.ResourceName;\n Console.WriteLine(\"Ad group criterion with resource name \" +\n $\"{adGroupCriterionResourceName} was found.\");\n userListCriteriaResourceNames.Add(adGroupCriterionResourceName);\n }\n });\n\n return userListCriteriaResourceNames;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function getUserListAdGroupCriteria(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n): array {\n // Creates a query that retrieves all of the ad group criteria under a campaign.\n $query = sprintf(\n \"SELECT ad_group_criterion.criterion_id \" .\n \"FROM ad_group_criterion \" .\n \"WHERE campaign.id = %d \" .\n \"AND ad_group_criterion.type = 'USER_LIST'\",\n $campaignId\n );\n\n // Creates the Google Ads service client.\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n\n // Issues the search request.\n $response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n $userListCriteria = [];\n // Iterates over all rows in all pages. Prints the user list criteria and adds the ad group\n // criteria resource names to the list.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $adGroupCriterionResourceName = $googleAdsRow->getAdGroupCriterion()->getResourceName();\n\n printf(\n \"Ad group criterion with resource name '%s' was found.%s\",\n $adGroupCriterionResourceName,\n PHP_EOL\n );\n\n $userListCriteria[] = $adGroupCriterionResourceName;\n }\n\n return $userListCriteria;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef get_user_list_ad_group_criteria(\n client: GoogleAdsClient, customer_id: str, campaign_id: str\n) -> List[str]:\n \"\"\"Finds all of user list ad group criteria under a campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID.\n campaign_id: a str campaign ID.\n\n Returns:\n a list of ad group criterion resource names.\n \"\"\"\n # Creates a query that retrieves all of the ad group criteria under a\n # campaign.\n query: str = f\"\"\"\n SELECT\n ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE campaign.id = {campaign_id}\n AND ad_group_criterion.type = USER_LIST\"\"\"\n\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n search_request: SearchGoogleAdsRequest = client.get_type(\n \"SearchGoogleAdsRequest\"\n )\n search_request.customer_id = customer_id\n search_request.query = query\n response: SearchGoogleAdsResponse = googleads_service.search(\n request=search_request\n )\n\n # Iterates over all rows in all pages. Prints the user list criteria and\n # adds the ad group criteria resource names to the list.\n user_list_criteria: List[str] = []\n row: GoogleAdsRow\n for row in response:\n resource_name: str = row.ad_group_criterion.resource_name\n print(\n \"Ad group criterion with resource name '{resource_name}' was \"\n \"found.\"\n )\n user_list_criteria.append(resource_name)\n\n return user_list_criteriaset_up_remarketing.py\n```\n\nExample:\n```text\ndef get_user_list_ad_group_criterion(\n client,\n customer_id,\n campaign_id\n)\n user_list_criteria = []\n\n # Creates a query that will retrieve all of the ad group criteria \n # under a campaign.\n query = <<~QUERY\n SELECT ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE campaign.id = #{campaign_id}\n AND ad_group_criterion.type = 'USER_LIST'\n QUERY\n\n # Issues the search request.\n response = client.service.google_ads.search(\n customer_id: customer_id,\n query: query,\n )\n\n # Iterates over all rows in all pages. Prints the results and adds the ad\n # group criteria resource names to the list.\n response.each do |row|\n ad_group_criterion_resource_name = row.ad_group_criterion.resource_name\n puts \"Ad group criterion with resource name \" \\\n \"'#{ad_group_criterion_resource_name}' was found\"\n user_list_criteria << ad_group_criterion_resource_name\n end\n\n user_list_criteria\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub get_user_list_ad_group_criteria {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n my $user_list_criterion_resource_names = [];\n\n # Create a search stream request that will retrieve all of the user list ad\n # group criteria under a campaign.\n my $search_stream_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsStreamRequest\n ->new({\n customerId => $customer_id,\n query => sprintf(\n \"SELECT ad_group_criterion.criterion_id \" .\n \"FROM ad_group_criterion \" .\n \"WHERE campaign.id = %d AND ad_group_criterion.type = 'USER_LIST'\",\n $campaign_id\n )});\n\n my $search_stream_handler =\n Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({\n service => $api_client->GoogleAdsService(),\n request => $search_stream_request\n });\n\n # Issue a search request and process the stream response.\n $search_stream_handler->process_contents(\n sub {\n # Display the results and add the resource names to the list.\n my $google_ads_row = shift;\n\n my $ad_group_criterion_resource_name =\n $google_ads_row->{adGroupCriterion}{resourceName};\n printf \"Ad group criterion with resource name '%s' was found.\\n\",\n $ad_group_criterion_resource_name;\n push(@$user_list_criterion_resource_names,\n $ad_group_criterion_resource_name);\n });\n\n return $user_list_criterion_resource_names;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate void removeExistingListCriteriaFromAdGroup(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n // Retrieves all of the ad group criteria under a campaign.\n List<String> adGroupCriteria =\n getUserListAdGroupCriterion(googleAdsClient, customerId, campaignId);\n\n List<AdGroupCriterionOperation> operations = new ArrayList<>();\n\n // Creates a list of remove operations.\n for (String adGroupCriterion : adGroupCriteria) {\n operations.add(AdGroupCriterionOperation.newBuilder().setRemove(adGroupCriterion).build());\n }\n\n // Creates the ad group criterion service.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Removes the ad group criterion.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), operations);\n // Gets and prints the results.\n System.out.printf(\"Removed %d ad group criteria.%n\", response.getResultsCount());\n for (MutateAdGroupCriterionResult result : response.getResultsList()) {\n System.out.printf(\n \"Successfully removed ad group criterion with resource name '%s'.%n\",\n result.getResourceName());\n }\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate void RemoveExistingListCriteriaFromAdGroup(GoogleAdsClient client, long customerId,\n long campaignId)\n{\n // Get the AdGroupCriterionService client.\n AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n client.GetService(Services.V25.AdGroupCriterionService);\n\n // Retrieve all of the ad group criteria under a campaign.\n List<string> adGroupCriteria =\n GetUserListAdGroupCriteria(client, customerId, campaignId);\n\n // Create a list of remove operations.\n List<AdGroupCriterionOperation> operations = adGroupCriteria.Select(adGroupCriterion =>\n new AdGroupCriterionOperation { Remove = adGroupCriterion }).ToList();\n\n // Remove the ad group criteria and print the resource names of the removed criteria.\n MutateAdGroupCriteriaResponse mutateAdGroupCriteriaResponse =\n adGroupCriterionServiceClient.MutateAdGroupCriteria(customerId.ToString(),\n operations);\n\n Console.WriteLine($\"Removed {mutateAdGroupCriteriaResponse.Results.Count} ad group \" +\n \"criteria.\");\n foreach (MutateAdGroupCriterionResult result in mutateAdGroupCriteriaResponse.Results)\n {\n Console.WriteLine(\"Successfully removed ad group criterion with resource name \" +\n $\"'{result.ResourceName}'.\");\n }\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function removeExistingListCriteriaFromAdGroup(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n) {\n // Retrieves all of the ad group criteria under a campaign.\n $allAdGroupCriteria = self::getUserListAdGroupCriteria(\n $googleAdsClient,\n $customerId,\n $campaignId\n );\n\n $removeOperations = [];\n // Creates a list of remove operations.\n foreach ($allAdGroupCriteria as $adGroupCriterionResourceName) {\n $operation = new AdGroupCriterionOperation();\n $operation->setRemove($adGroupCriterionResourceName);\n $removeOperations[] = $operation;\n }\n\n // Issues a mutate request to remove the ad group criteria.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n /** @var MutateAdGroupCriteriaResponse $adGroupCriteriaResponse */\n $adGroupCriteriaResponse = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, $removeOperations)\n );\n\n foreach ($adGroupCriteriaResponse->getResults() as $adGroupCriteriaResult) {\n printf(\n \"Successfully removed ad group criterion with resource name '%s'.%s\",\n $adGroupCriteriaResult->getResourceName(),\n PHP_EOL\n );\n }\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef remove_existing_criteria_from_ad_group(\n client: GoogleAdsClient, customer_id: str, campaign_id: str\n) -> None:\n \"\"\"Removes all ad group criteria targeting a user list under a campaign.\n\n This is a necessary step before targeting a user list at the campaign level.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID.\n campaign_id: a str ID for a campaign that will have all ad group\n criteria that targets user lists removed.\n \"\"\"\n # Retrieves all of the ad group criteria under a campaign.\n all_ad_group_criteria: List[str] = get_user_list_ad_group_criteria(\n client, customer_id, campaign_id\n )\n\n # Creates a list of remove operations.\n remove_operations: List[AdGroupCriterionOperation] = []\n for ad_group_criterion_resource_name in all_ad_group_criteria:\n remove_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n remove_operation.remove = ad_group_criterion_resource_name\n remove_operations.append(remove_operation)\n\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=remove_operations\n )\n )\n print(\n \"Successfully removed ad group criterion with resource name: \"\n f\"'{response.results[0].resource_name}'\"\n )set_up_remarketing.py\n```\n\nExample:\n```text\ndef remove_existing_list_criteria_from_ad_group(\n client,\n customer_id,\n campaign_id\n)\n # Retrieves all of the ad group criteria under a campaign.\n ad_group_criteria = get_user_list_ad_group_criterion(\n client, customer_id, campaign_id)\n\n # Creates a list of remove operations.\n operations = []\n ad_group_criteria.each do |agc|\n operations << client.operation.remove_resource.ad_group_criterion(agc)\n end\n\n # Issues a mutate request to remove all ad group criteria.\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: operations,\n )\n puts \"Removed #{response.results.size} ad group criteria.\"\n response.results.each do |result|\n puts \"Successfully removed ad group criterion with resource name \" \\\n \"'#{result.resource_name}'\"\n end\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub remove_existing_list_criteria_from_ad_group {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n # Retrieve all of the ad group criteria under a campaign.\n my $ad_group_criteria =\n get_user_list_ad_group_criteria($api_client, $customer_id, $campaign_id);\n\n # Create a list of remove operations.\n my $operations = [];\n foreach my $ad_group_criterion (@$ad_group_criteria) {\n push(\n @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n remove => $ad_group_criterion\n }));\n }\n\n # Remove the ad group criteria and print the resource names of the removed criteria.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => $operations\n });\n\n printf \"Removed %d ad group criteria.\\n\",\n scalar @{$ad_group_criteria_response->{results}};\n foreach my $result (@{$ad_group_criteria_response->{results}}) {\n printf \"Successfully removed ad group criterion with resource name '%s'.\\n\",\n $result->{resourceName};\n }\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate String targetAdsInCampaignToUserList(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId, String userList) {\n // Creates the campaign criterion.\n CampaignCriterion campaignCriterion =\n CampaignCriterion.newBuilder()\n .setCampaign(ResourceNames.campaign(customerId, campaignId))\n .setUserList(UserListInfo.newBuilder().setUserList(userList).build())\n .build();\n\n // Creates the operation.\n CampaignCriterionOperation operation =\n CampaignCriterionOperation.newBuilder().setCreate(campaignCriterion).build();\n\n // Creates the campaign criterion service client.\n try (CampaignCriterionServiceClient campaignCriterionServiceClient =\n googleAdsClient.getLatestVersion().createCampaignCriterionServiceClient()) {\n // Adds the campaign criterion.\n MutateCampaignCriteriaResponse response =\n campaignCriterionServiceClient.mutateCampaignCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n // Gets and prints the campaign criterion resource name.\n String campaignCriterionResourceName = response.getResults(0).getResourceName();\n System.out.printf(\n \"Successfully created campaign criterion with resource name '%s' \"\n + \"targeting user list with resource name '%s' with campaign with ID %d.%n\",\n campaignCriterionResourceName, userList, campaignId);\n return campaignCriterionResourceName;\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate string TargetAdsInCampaignToUserList(\n GoogleAdsClient client, long customerId, long campaignId, string userListResourceName)\n{\n // Get the CampaignCriterionService client.\n CampaignCriterionServiceClient campaignCriterionServiceClient =\n client.GetService(Services.V25.CampaignCriterionService);\n\n // Create the campaign criterion.\n CampaignCriterion campaignCriterion = new CampaignCriterion\n {\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n UserList = new UserListInfo\n {\n UserList = userListResourceName\n }\n };\n\n // Create the operation.\n CampaignCriterionOperation campaignCriterionOperation = new CampaignCriterionOperation\n {\n Create = campaignCriterion\n };\n\n // Add the campaign criterion and print the resulting criterion's resource name.\n MutateCampaignCriteriaResponse mutateCampaignCriteriaResponse =\n campaignCriterionServiceClient.MutateCampaignCriteria(customerId.ToString(),\n new[] { campaignCriterionOperation });\n\n string campaignCriterionResourceName =\n mutateCampaignCriteriaResponse.Results.First().ResourceName;\n Console.WriteLine(\"Successfully created campaign criterion with resource name \" +\n $\"'{campaignCriterionResourceName}' targeting user list with resource name \" +\n $\"'{userListResourceName}' with campaign with ID {campaignId}.\");\n\n return campaignCriterionResourceName;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function targetAdsInCampaignToUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId,\n string $userListResourceName\n): string {\n // Creates the campaign criterion.\n $campaignCriterion = new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign($customerId, $campaignId),\n 'user_list' => new UserListInfo(['user_list' => $userListResourceName])\n ]);\n\n // Creates the operation.\n $operation = new CampaignCriterionOperation();\n $operation->setCreate($campaignCriterion);\n\n // Issues a mutate request to create a campaign criterion.\n $campaignCriterionServiceClient = $googleAdsClient->getCampaignCriterionServiceClient();\n /** @var MutateCampaignCriteriaResponse $campaignCriteriaResponse */\n $campaignCriteriaResponse = $campaignCriterionServiceClient->mutateCampaignCriteria(\n MutateCampaignCriteriaRequest::build($customerId, [$operation])\n );\n\n $campaignCriterionResourceName =\n $campaignCriteriaResponse->getResults()[0]->getResourceName();\n printf(\n \"Successfully created campaign criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with campaign with ID %d.%s\",\n $campaignCriterionResourceName,\n $userListResourceName,\n $campaignId,\n PHP_EOL\n );\n\n return $campaignCriterionResourceName;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef target_ads_in_campaign_to_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n campaign_id: str,\n user_list_resource_name: str,\n) -> str:\n \"\"\"Creates a campaign criterion that targets a user list with a campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID used to create an campaign\n criterion.\n campaign_id: a str ID for a campaign used to create a campaign\n criterion that targets members of a user list.\n user_list_resource_name: a str resource name for a user list.\n\n Returns:\n a str resource name for a campaign criterion.\n \"\"\"\n campaign_criterion_operation: CampaignCriterionOperation = client.get_type(\n \"CampaignCriterionOperation\"\n )\n campaign_criterion: CampaignCriterion = campaign_criterion_operation.create\n campaign_criterion.campaign = client.get_service(\n \"CampaignService\"\n ).campaign_path(customer_id, campaign_id)\n campaign_criterion.user_list.user_list = user_list_resource_name\n\n campaign_criterion_service: CampaignCriterionServiceClient = (\n client.get_service(\"CampaignCriterionService\")\n )\n response: MutateCampaignCriteriaResponse = (\n campaign_criterion_service.mutate_campaign_criteria(\n customer_id=customer_id, operations=[campaign_criterion_operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n print(\n \"Successfully created campaign criterion with resource name \"\n f\"'{resource_name}' targeting user list with resource name \"\n f\"'{user_list_resource_name}' with campaign with ID {campaign_id}\"\n )\n return resource_nameset_up_remarketing.py\n```\n\nExample:\n```text\ndef target_ads_in_campaign_to_user_list(\n client,\n customer_id,\n campaign_id,\n user_list\n)\n # Creates the campaign criterion targeting members of the user list.\n operation = client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(customer_id, campaign_id)\n cc.user_list = client.resource.user_list_info do |info|\n info.user_list = user_list\n end\n end\n\n # Issues a mutate request to create the campaign criterion.\n response = client.service.campaign_criterion.mutate_campaign_criteria(\n customer_id: customer_id,\n operations: [operation],\n )\n campaign_criterion_resource_name = response.results.first.resource_name\n puts \"Successfully created campaign criterion with resource name \" \\\n \"'#{campaign_criterion_resource_name}' targeting user list with resource name \" \\\n \"'#{user_list}' with campaign with ID #{campaign_id}\"\n\n campaign_criterion_resource_name\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub target_ads_in_campaign_to_user_list {\n my ($api_client, $customer_id, $campaign_id, $user_list_resource_name) = @_;\n\n # Create the campaign criterion.\n my $campaign_criterion =\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign => Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $campaign_id\n ),\n userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({\n userList => $user_list_resource_name\n })});\n\n # Create the operation.\n my $campaign_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create => $campaign_criterion\n });\n\n # Add the campaign criterion and print the resulting criterion's resource name.\n my $campaign_criteria_response =\n $api_client->CampaignCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_criterion_operation]});\n\n my $campaign_criterion_resource_name =\n $campaign_criteria_response->{results}[0]{resourceName};\n printf \"Successfully created campaign criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with campaign with ID %d.\\n\",\n $campaign_criterion_resource_name, $user_list_resource_name, $campaign_id;\n\n return $campaign_criterion_resource_name;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nSELECT\n ad_group_criterion.criterion_id,\n metrics.conversions,\n metrics.cost_per_conversion\nFROM ad_group_audience_view\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.411Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":33,"totalLines":1504,"estimatedTokens":14180}}170{"id":"doc-visitors_to_your_website_google_ads_api_google_f-8754f2a4","source":"documentation","title":"Visitors to Your Website | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audience-segments/website-visitors","text":"Example:\n```text\nSELECT\n remarketing_action.id,\n remarketing_action.name,\n remarketing_action.tag_snippets\nFROM remarketing_action\nWHERE remarketing_action.resource_name = 'REMARKETING_ACTION_RESOURCE_NAME'\n```\n\nExample:\n```text\nprivate String createUserList(GoogleAdsClient googleAdsClient, long customerId) {\n // Creates a rule targeting any user that visited a url containing 'example.com'.\n UserListRuleItemInfo rule =\n UserListRuleItemInfo.newBuilder()\n // Uses a built-in parameter to create a domain URL rule.\n .setName(\"url__\")\n .setStringRuleItem(\n UserListStringRuleItemInfo.newBuilder()\n .setOperator(UserListStringRuleItemOperator.CONTAINS)\n .setValue(\"example.com\")\n .build())\n .build();\n\n // Specifies that the user list targets visitors of a page based on the provided rule.\n FlexibleRuleUserListInfo flexibleRuleUserListInfo =\n FlexibleRuleUserListInfo.newBuilder()\n .setInclusiveRuleOperator(UserListFlexibleRuleOperator.AND)\n // Inclusive operands are joined together with the specified inclusiveRuleOperator.\n .addInclusiveOperands(\n FlexibleRuleOperandInfo.newBuilder()\n .setRule(\n UserListRuleInfo.newBuilder()\n .addRuleItemGroups(\n UserListRuleItemGroupInfo.newBuilder().addRuleItems(rule)))\n // Optional: adds a lookback window for this rule, in days.\n .setLookbackWindowDays(7L))\n .build();\n\n // Defines a representation of a user list that is generated by a rule.\n RuleBasedUserListInfo ruleBasedUserListInfo =\n RuleBasedUserListInfo.newBuilder()\n // Optional: To include past users in the user list, set the prepopulation_status to\n // REQUESTED.\n .setPrepopulationStatus(UserListPrepopulationStatus.REQUESTED)\n .setFlexibleRuleUserList(flexibleRuleUserListInfo)\n .build();\n\n // Creates the user list.\n UserList userList =\n UserList.newBuilder()\n .setName(\"All visitors to example.com\" + getPrintableDateTime())\n .setDescription(\"Any visitor to any page of example.com\")\n .setMembershipStatus(UserListMembershipStatus.OPEN)\n .setMembershipLifeSpan(365)\n .setRuleBasedUserList(ruleBasedUserListInfo)\n .build();\n\n // Creates the operation.\n UserListOperation operation = UserListOperation.newBuilder().setCreate(userList).build();\n\n // Creates the user list service client.\n try (UserListServiceClient userListServiceClient =\n googleAdsClient.getLatestVersion().createUserListServiceClient()) {\n // Adds the user list.\n MutateUserListsResponse response =\n userListServiceClient.mutateUserLists(\n Long.toString(customerId), ImmutableList.of(operation));\n String userListResourceName = response.getResults(0).getResourceName();\n // Prints the result.\n System.out.printf(\"Created user list with resource name '%s'.%n\", userListResourceName);\n return userListResourceName;\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate string CreateUserList(GoogleAdsClient client, long customerId)\n{\n // Get the UserListService client.\n UserListServiceClient userListServiceClient =\n client.GetService(Services.V25.UserListService);\n\n // Create a rule targeting any user that visited a url containing 'example.com'.\n UserListRuleItemInfo rule = new UserListRuleItemInfo\n {\n // Use a built-in parameter to create a domain URL rule.\n Name = \"url__\",\n StringRuleItem = new UserListStringRuleItemInfo\n {\n Operator = UserListStringRuleItemOperator.Contains,\n Value = \"example.com\"\n }\n };\n\n // Specify that the user list targets visitors of a page based on the provided rule.\n FlexibleRuleUserListInfo flexibleRuleUserListInfo = new FlexibleRuleUserListInfo();\n FlexibleRuleOperandInfo flexibleRuleOperandInfo = new FlexibleRuleOperandInfo() {\n Rule = new UserListRuleInfo()\n };\n UserListRuleItemGroupInfo userListRuleItemGroupInfo = new UserListRuleItemGroupInfo();\n userListRuleItemGroupInfo.RuleItems.Add(rule);\n flexibleRuleOperandInfo.Rule.RuleItemGroups.Add(userListRuleItemGroupInfo);\n flexibleRuleUserListInfo.InclusiveOperands.Add(flexibleRuleOperandInfo);\n\n // Define a representation of a user list that is generated by a rule.\n RuleBasedUserListInfo ruleBasedUserListInfo = new RuleBasedUserListInfo\n {\n // Optional: To include past users in the user list, set the prepopulation_status to\n // REQUESTED.\n PrepopulationStatus = UserListPrepopulationStatus.Requested,\n FlexibleRuleUserList = flexibleRuleUserListInfo\n };\n\n // Create the user list.\n UserList userList = new UserList\n {\n Name = $\"All visitors to example.com #{ExampleUtilities.GetRandomString()}\",\n Description = \"Any visitor to any page of example.com\",\n MembershipStatus = UserListMembershipStatus.Open,\n MembershipLifeSpan = 365L,\n RuleBasedUserList = ruleBasedUserListInfo\n };\n\n // Create the operation.\n UserListOperation userListOperation = new UserListOperation\n {\n Create = userList\n };\n\n // Add the user list, then print and return the new list's resource name.\n MutateUserListsResponse mutateUserListsResponse = userListServiceClient\n .MutateUserLists(customerId.ToString(), new[] { userListOperation });\n string userListResourceName = mutateUserListsResponse.Results.First().ResourceName;\n Console.WriteLine($\"Created user list with resource name '{userListResourceName}'.\");\n\n return userListResourceName;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function createUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n): string {\n // Creates a rule targeting any user that visited a URL containing 'example.com'.\n $rule = new UserListRuleItemInfo([\n // Uses a built-in parameter to create a domain URL rule.\n 'name' => 'url__',\n 'string_rule_item' => new UserListStringRuleItemInfo([\n 'operator' => UserListStringRuleItemOperator::CONTAINS,\n 'value' => 'example.com'\n ])\n ]);\n\n // Specifies that the user list targets visitors of a page based on the provided rule.\n $flexibleRuleUserListInfo = new FlexibleRuleUserListInfo([\n 'inclusive_rule_operator' => UserListFlexibleRuleOperator::PBAND,\n // Inclusive operands are joined together with the specified inclusive rule operator.\n 'inclusive_operands' => [\n new FlexibleRuleOperandInfo([\n 'rule' => new UserListRuleInfo([\n 'rule_item_groups' =>\n [new UserListRuleItemGroupInfo(['rule_items' => [$rule]])]\n ]),\n // Optionally add a lookback window for this rule, in days.\n 'lookback_window_days' => 7\n ])\n ],\n 'exclusive_operands' => []\n ]);\n\n // Defines a representation of a user list that is generated by a rule.\n $ruleBasedUserListInfo = new RuleBasedUserListInfo([\n 'flexible_rule_user_list' => $flexibleRuleUserListInfo,\n // Optional: To include past users in the user list, set the prepopulation_status to\n // REQUESTED.\n 'prepopulation_status' => UserListPrepopulationStatus::REQUESTED\n ]);\n\n // Creates the user list.\n $userList = new UserList([\n 'name' => \"All visitors to example.com #\" . Helper::getPrintableDatetime(),\n 'description' => \"Any visitor to any page of example.com\",\n 'membership_status' => UserListMembershipStatus::OPEN,\n 'membership_life_span' => 365,\n 'rule_based_user_list' => $ruleBasedUserListInfo\n ]);\n\n // Creates the operation.\n $operation = new UserListOperation();\n $operation->setCreate($userList);\n\n // Issues a mutate request to add a user list.\n $userListServiceClient = $googleAdsClient->getUserListServiceClient();\n /** @var MutateUserListsResponse $userListResponse */\n $userListResponse = $userListServiceClient->mutateUserLists(\n MutateUserListsRequest::build($customerId, [$operation])\n );\n\n $userListResourceName = $userListResponse->getResults()[0]->getResourceName();\n printf(\"Created user list with resource name '%s'.%s\", $userListResourceName, PHP_EOL);\n\n return $userListResourceName;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef create_user_list(client: GoogleAdsClient, customer_id: str) -> str:\n \"\"\"Creates a user list targeting users that have visited a given URL.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID used to create a user list.\n\n Returns:\n a str resource name for the newly created user list.\n \"\"\"\n # Creates a UserListOperation.\n user_list_operation: UserListOperation = client.get_type(\n \"UserListOperation\"\n )\n # Creates a UserList.\n user_list: UserList = user_list_operation.create\n user_list.name = f\"All visitors to example.com #{uuid4()}\"\n user_list.description = \"Any visitor to any page of example.com\"\n user_list.membership_status = client.enums.UserListMembershipStatusEnum.OPEN\n user_list.membership_life_span = 365\n # Optional: To include past users in the user list, set the\n # prepopulation_status to REQUESTED.\n user_list.rule_based_user_list.prepopulation_status = (\n client.enums.UserListPrepopulationStatusEnum.REQUESTED\n )\n # Specifies that the user list targets visitors of a page with a URL that\n # contains 'example.com'.\n user_list_rule_item_group_info: UserListRuleItemGroupInfo = client.get_type(\n \"UserListRuleItemGroupInfo\"\n )\n user_list_rule_item_info: UserListRuleItemInfo = client.get_type(\n \"UserListRuleItemInfo\"\n )\n # Uses a built-in parameter to create a domain URL rule.\n user_list_rule_item_info.name = \"url__\"\n user_list_rule_item_info.string_rule_item.operator = (\n client.enums.UserListStringRuleItemOperatorEnum.CONTAINS\n )\n user_list_rule_item_info.string_rule_item.value = \"example.com\"\n user_list_rule_item_group_info.rule_items.append(user_list_rule_item_info)\n\n # Specify that the user list targets visitors of a page based on the\n # provided rule.\n flexible_rule_user_list_info: FlexibleRuleUserListInfo = (\n user_list.rule_based_user_list.flexible_rule_user_list\n )\n flexible_rule_user_list_info.inclusive_rule_operator = (\n client.enums.UserListFlexibleRuleOperatorEnum.AND\n )\n # Inclusive operands are joined together with the specified\n # inclusive rule operator.\n rule_operand: FlexibleRuleOperandInfo = client.get_type(\n \"FlexibleRuleOperandInfo\"\n )\n rule_operand.rule.rule_item_groups.extend([user_list_rule_item_group_info])\n rule_operand.lookback_window_days = 7\n flexible_rule_user_list_info.inclusive_operands.append(rule_operand)\n\n user_list_service: UserListServiceClient = client.get_service(\n \"UserListService\"\n )\n response: MutateUserListsResponse = user_list_service.mutate_user_lists(\n customer_id=customer_id, operations=[user_list_operation]\n )\n resource_name: str = response.results[0].resource_name\n print(f\"Created user list with resource name: '{resource_name}'\")\n return resource_nameset_up_remarketing.py\n```\n\nExample:\n```text\ndef create_user_list(client, customer_id)\n # Creates the user list operation.\n operation = client.operation.create_resource.user_list do |ul|\n ul.name = \"All visitors to example.com ##{(Time.new.to_f * 1000).to_i}\"\n ul.description = \"Any visitor to any page of example.com\"\n ul.membership_status = :OPEN\n ul.membership_life_span = 365\n # Defines a representation of a user list that is generated by a rule.\n ul.rule_based_user_list = client.resource.rule_based_user_list_info do |r|\n # To include past users in the user list, set the prepopulation_status\n # to REQUESTED.\n r.prepopulation_status = :REQUESTED\n # Specifies that the user list targets visitors of a page based on\n # the provided rule.\n r.flexible_rule_user_list = client.resource.flexible_rule_user_list_info do |frul|\n frul.inclusive_rule_operator = :AND\n frul.inclusive_operands << client.resource.flexible_rule_operand_info do |froi|\n froi.rule = client.resource.user_list_rule_info do |u|\n u.rule_item_groups << client.resource.user_list_rule_item_group_info do |group|\n group.rule_items << client.resource.user_list_rule_item_info do |item|\n # Uses a built-in parameter to create a domain URL rule.\n item.name = \"url__\"\n item.string_rule_item = client.resource.user_list_string_rule_item_info do |s|\n s.operator = :CONTAINS\n s.value = \"example.com\"\n end\n end\n end\n end\n # Optionally add a lookback window for this rule, in days.\n froi.lookback_window_days = 7\n end\n end\n end\n end\n\n # Issues a mutate request to add the user list.\n response = client.service.user_list.mutate_user_lists(\n customer_id: customer_id,\n operations: [operation],\n )\n user_list_resource_name = response.results.first.resource_name\n puts \"Created user list with resource name '#{user_list_resource_name}'\"\n\n user_list_resource_name\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub create_user_list {\n my ($api_client, $customer_id) = @_;\n\n # Create a rule targeting any user that visited a url containing 'example.com'.\n my $rule = Google::Ads::GoogleAds::V25::Common::UserListRuleItemInfo->new({\n # Use a built-in parameter to create a domain URL rule.\n name => \"url__\",\n stringRuleItem =>\n Google::Ads::GoogleAds::V25::Common::UserListStringRuleItemInfo->new({\n operator => CONTAINS,\n value => \"example.com\"\n })});\n\n # Specify that the user list targets visitors of a page based on the provided rule.\n my $user_list_rule_item_group_info =\n Google::Ads::GoogleAds::V25::Common::UserListRuleItemGroupInfo->new(\n {ruleItems => [$rule]});\n my $flexible_rule_user_list_info =\n Google::Ads::GoogleAds::V25::Common::FlexibleRuleUserListInfo->new({\n inclusiveRuleOperator => AND,\n # Inclusive operands are joined together with the specified inclusiveRuleOperator.\n inclusiveOperands => [\n Google::Ads::GoogleAds::V25::Common::FlexibleRuleOperandInfo->new({\n rule => Google::Ads::GoogleAds::V25::Common::UserListRuleInfo->new({\n ruleItemGroups => [$user_list_rule_item_group_info]}\n ),\n # Optionally add a lookback window for this rule, in days.\n lookbackWindowDays => 7\n })\n ],\n exclusiveOperands => []});\n\n # Define a representation of a user list that is generated by a rule.\n my $rule_based_user_list_info =\n Google::Ads::GoogleAds::V25::Common::RuleBasedUserListInfo->new({\n # Optional: To include past users in the user list, set the\n # prepopulationStatus to REQUESTED.\n prepopulationStatus => REQUESTED,\n flexibleRuleUserList => $flexible_rule_user_list_info\n });\n\n # Create the user list.\n my $user_list = Google::Ads::GoogleAds::V25::Resources::UserList->new({\n name => \"All visitors to example.com #\" . uniqid(),\n description => \"Any visitor to any page of example.com\",\n membershipLifespan => 365,\n membershipStatus => OPEN,\n ruleBasedUserList => $rule_based_user_list_info\n });\n\n # Create the operation.\n my $user_list_operation =\n Google::Ads::GoogleAds::V25::Services::UserListService::UserListOperation->\n new({\n create => $user_list\n });\n\n # Add the user list, then print and return the new list's resource name.\n my $user_lists_response = $api_client->UserListService()->mutate({\n customerId => $customer_id,\n operations => [$user_list_operation]});\n\n my $user_list_resource_name =\n $user_lists_response->{results}[0]{resourceName};\n printf \"Created user list with resource name '%s'.\\n\",\n $user_list_resource_name;\n\n return $user_list_resource_name;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nSELECT\n user_list.name,\n user_list.membership_status,\n user_list.membership_life_span\nFROM user_list\nWHERE\n user_list.resource_name = 'USER_LIST_RESOURCE_NAME'\n```\n\nExample:\n```text\nprivate String targetAdsInAdGroupToUserList(\n GoogleAdsClient googleAdsClient, long customerId, long adGroupId, String userList) {\n // Creates the ad group criterion targeting members of the user list.\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))\n .setUserList(UserListInfo.newBuilder().setUserList(userList).build())\n .build();\n\n // Creates the operation.\n AdGroupCriterionOperation operation =\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterion).build();\n\n // Creates the ad group criterion service.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Adds the ad group criterion.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n // Gets and prints the results.\n String adGroupCriterionResourceName = response.getResults(0).getResourceName();\n System.out.printf(\n \"Successfully created ad group criterion with resource name '%s' \"\n + \"targeting user list with resource name '%s' with ad group with ID %d.%n\",\n adGroupCriterionResourceName, userList, adGroupId);\n return adGroupCriterionResourceName;\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate string TargetAdsInAdGroupToUserList(\n GoogleAdsClient client, long customerId, long adGroupId, string userListResourceName)\n{\n // Get the AdGroupCriterionService client.\n AdGroupCriterionServiceClient adGroupCriterionServiceClient = client.GetService\n (Services.V25.AdGroupCriterionService);\n\n // Create the ad group criterion targeting members of the user list.\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion\n {\n AdGroup = ResourceNames.AdGroup(customerId, adGroupId),\n UserList = new UserListInfo\n {\n UserList = userListResourceName\n }\n };\n\n // Create the operation.\n AdGroupCriterionOperation adGroupCriterionOperation = new AdGroupCriterionOperation\n {\n Create = adGroupCriterion\n };\n\n // Add the ad group criterion, then print and return the new criterion's resource name.\n MutateAdGroupCriteriaResponse mutateAdGroupCriteriaResponse =\n adGroupCriterionServiceClient.MutateAdGroupCriteria(customerId.ToString(),\n new[] { adGroupCriterionOperation });\n\n string adGroupCriterionResourceName =\n mutateAdGroupCriteriaResponse.Results.First().ResourceName;\n Console.WriteLine(\"Successfully created ad group criterion with resource name \" +\n $\"'{adGroupCriterionResourceName}' targeting user list with resource name \" +\n $\"'{userListResourceName}' with ad group with ID {adGroupId}.\");\n return adGroupCriterionResourceName;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function targetAdsInAdGroupToUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n string $userListResourceName\n): string {\n // Creates the ad group criterion targeting members of the user list.\n $adGroupCriterion = new AdGroupCriterion([\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'user_list' => new UserListInfo(['user_list' => $userListResourceName])\n ]);\n\n // Creates the operation.\n $operation = new AdGroupCriterionOperation();\n $operation->setCreate($adGroupCriterion);\n\n // Issues a mutate request to add an ad group criterion.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n /** @var MutateAdGroupCriteriaResponse $adGroupCriterionResponse */\n $adGroupCriterionResponse = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$operation])\n );\n\n $adGroupCriterionResourceName =\n $adGroupCriterionResponse->getResults()[0]->getResourceName();\n printf(\n \"Successfully created ad group criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with ad group with ID %d.%s\",\n $adGroupCriterionResourceName,\n $userListResourceName,\n $adGroupId,\n PHP_EOL\n );\n\n return $adGroupCriterionResourceName;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef target_ads_in_ad_group_to_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n user_list_resource_name: str,\n) -> str:\n \"\"\"Creates an ad group criterion that targets a user list with an ad group.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID used to create an ad group\n criterion.\n ad_group_id: a str ID for an ad group used to create an ad group\n criterion that targets members of a user list.\n user_list_resource_name: a str resource name for a user list.\n\n Returns:\n a str resource name for an ad group criterion.\n \"\"\"\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n # Creates the ad group criterion targeting members of the user list.\n ad_group_criterion: AdGroupCriterion = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = client.get_service(\n \"AdGroupService\"\n ).ad_group_path(customer_id, ad_group_id)\n ad_group_criterion.user_list.user_list = user_list_resource_name\n\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n print(\n \"Successfully created ad group criterion with resource name: \"\n f\"'{resource_name}' targeting user list with resource name: \"\n f\"'{user_list_resource_name}' and with ad group with ID \"\n f\"{ad_group_id}.\"\n )\n return resource_nameset_up_remarketing.py\n```\n\nExample:\n```text\ndef target_ads_in_ad_group_to_user_list(\n client,\n customer_id,\n ad_group_id,\n user_list\n)\n # Creates the ad group criterion targeting members of the user list.\n operation = client.operation.create_resource.ad_group_criterion do |agc|\n agc.ad_group = client.path.ad_group(customer_id, ad_group_id)\n agc.user_list = client.resource.user_list_info do |info|\n info.user_list = user_list\n end\n end\n\n # Issues a mutate request to create the ad group criterion.\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [operation],\n )\n ad_group_criterion_resource_name = response.results.first.resource_name\n puts \"Successfully created ad group criterion with resource name \" \\\n \"'#{ad_group_criterion_resource_name}' targeting user list with resource name \" \\\n \"'#{user_list}' with ad group with ID #{ad_group_id}\"\n\n ad_group_criterion_resource_name\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub target_ads_in_ad_group_to_user_list {\n my ($api_client, $customer_id, $ad_group_id, $user_list_resource_name) = @_;\n\n # Create the ad group criterion targeting members of the user list.\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({\n userList => $user_list_resource_name\n })});\n\n # Create the operation.\n my $ad_group_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion\n });\n\n # Add the ad group criterion, then print and return the new criterion's resource name.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n my $ad_group_criterion_resource_name =\n $ad_group_criteria_response->{results}[0]{resourceName};\n printf \"Successfully created ad group criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with ad group with ID %d.\\n\",\n $ad_group_criterion_resource_name, $user_list_resource_name, $ad_group_id;\n\n return $ad_group_criterion_resource_name;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate List<String> getUserListAdGroupCriterion(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n List<String> userListCriteria = new ArrayList<>();\n // Creates the Google Ads service client.\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Creates a request that will retrieve all of the ad group criteria under a campaign.\n SearchGoogleAdsRequest request =\n SearchGoogleAdsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setQuery(\n \"SELECT ad_group_criterion.criterion_id\"\n + \" FROM ad_group_criterion\"\n + \" WHERE campaign.id = \"\n + campaignId\n + \" AND ad_group_criterion.type = 'USER_LIST'\")\n .build();\n // Issues the search request.\n SearchPagedResponse searchPagedResponse = googleAdsServiceClient.search(request);\n // Iterates over all rows in all pages. Prints the results and adds the ad group criteria\n // resource names to the list.\n for (GoogleAdsRow googleAdsRow : searchPagedResponse.iterateAll()) {\n String adGroupCriterionResourceName = googleAdsRow.getAdGroupCriterion().getResourceName();\n System.out.printf(\n \"Ad group criterion with resource name '%s' was found.%n\",\n adGroupCriterionResourceName);\n userListCriteria.add(adGroupCriterionResourceName);\n }\n }\n return userListCriteria;\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate List<string> GetUserListAdGroupCriteria(\n GoogleAdsClient client, long customerId, long campaignId)\n{\n // Get the GoogleAdsService client.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n List<string> userListCriteriaResourceNames = new List<string>();\n\n // Create a query that will retrieve all of the ad group criteria under a campaign.\n string query = $@\"\n SELECT ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE\n campaign.id = {campaignId}\n AND ad_group_criterion.type = 'USER_LIST'\";\n\n // Issue the search request.\n googleAdsServiceClient.SearchStream(customerId.ToString(), query,\n delegate (SearchGoogleAdsStreamResponse resp)\n {\n // Display the results and add the resource names to the list.\n foreach (GoogleAdsRow googleAdsRow in resp.Results)\n {\n string adGroupCriterionResourceName =\n googleAdsRow.AdGroupCriterion.ResourceName;\n Console.WriteLine(\"Ad group criterion with resource name \" +\n $\"{adGroupCriterionResourceName} was found.\");\n userListCriteriaResourceNames.Add(adGroupCriterionResourceName);\n }\n });\n\n return userListCriteriaResourceNames;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function getUserListAdGroupCriteria(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n): array {\n // Creates a query that retrieves all of the ad group criteria under a campaign.\n $query = sprintf(\n \"SELECT ad_group_criterion.criterion_id \" .\n \"FROM ad_group_criterion \" .\n \"WHERE campaign.id = %d \" .\n \"AND ad_group_criterion.type = 'USER_LIST'\",\n $campaignId\n );\n\n // Creates the Google Ads service client.\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n\n // Issues the search request.\n $response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n $userListCriteria = [];\n // Iterates over all rows in all pages. Prints the user list criteria and adds the ad group\n // criteria resource names to the list.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $adGroupCriterionResourceName = $googleAdsRow->getAdGroupCriterion()->getResourceName();\n\n printf(\n \"Ad group criterion with resource name '%s' was found.%s\",\n $adGroupCriterionResourceName,\n PHP_EOL\n );\n\n $userListCriteria[] = $adGroupCriterionResourceName;\n }\n\n return $userListCriteria;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef get_user_list_ad_group_criteria(\n client: GoogleAdsClient, customer_id: str, campaign_id: str\n) -> List[str]:\n \"\"\"Finds all of user list ad group criteria under a campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID.\n campaign_id: a str campaign ID.\n\n Returns:\n a list of ad group criterion resource names.\n \"\"\"\n # Creates a query that retrieves all of the ad group criteria under a\n # campaign.\n query: str = f\"\"\"\n SELECT\n ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE campaign.id = {campaign_id}\n AND ad_group_criterion.type = USER_LIST\"\"\"\n\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n search_request: SearchGoogleAdsRequest = client.get_type(\n \"SearchGoogleAdsRequest\"\n )\n search_request.customer_id = customer_id\n search_request.query = query\n response: SearchGoogleAdsResponse = googleads_service.search(\n request=search_request\n )\n\n # Iterates over all rows in all pages. Prints the user list criteria and\n # adds the ad group criteria resource names to the list.\n user_list_criteria: List[str] = []\n row: GoogleAdsRow\n for row in response:\n resource_name: str = row.ad_group_criterion.resource_name\n print(\n \"Ad group criterion with resource name '{resource_name}' was \"\n \"found.\"\n )\n user_list_criteria.append(resource_name)\n\n return user_list_criteriaset_up_remarketing.py\n```\n\nExample:\n```text\ndef get_user_list_ad_group_criterion(\n client,\n customer_id,\n campaign_id\n)\n user_list_criteria = []\n\n # Creates a query that will retrieve all of the ad group criteria \n # under a campaign.\n query = <<~QUERY\n SELECT ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE campaign.id = #{campaign_id}\n AND ad_group_criterion.type = 'USER_LIST'\n QUERY\n\n # Issues the search request.\n response = client.service.google_ads.search(\n customer_id: customer_id,\n query: query,\n )\n\n # Iterates over all rows in all pages. Prints the results and adds the ad\n # group criteria resource names to the list.\n response.each do |row|\n ad_group_criterion_resource_name = row.ad_group_criterion.resource_name\n puts \"Ad group criterion with resource name \" \\\n \"'#{ad_group_criterion_resource_name}' was found\"\n user_list_criteria << ad_group_criterion_resource_name\n end\n\n user_list_criteria\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub get_user_list_ad_group_criteria {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n my $user_list_criterion_resource_names = [];\n\n # Create a search stream request that will retrieve all of the user list ad\n # group criteria under a campaign.\n my $search_stream_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsStreamRequest\n ->new({\n customerId => $customer_id,\n query => sprintf(\n \"SELECT ad_group_criterion.criterion_id \" .\n \"FROM ad_group_criterion \" .\n \"WHERE campaign.id = %d AND ad_group_criterion.type = 'USER_LIST'\",\n $campaign_id\n )});\n\n my $search_stream_handler =\n Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({\n service => $api_client->GoogleAdsService(),\n request => $search_stream_request\n });\n\n # Issue a search request and process the stream response.\n $search_stream_handler->process_contents(\n sub {\n # Display the results and add the resource names to the list.\n my $google_ads_row = shift;\n\n my $ad_group_criterion_resource_name =\n $google_ads_row->{adGroupCriterion}{resourceName};\n printf \"Ad group criterion with resource name '%s' was found.\\n\",\n $ad_group_criterion_resource_name;\n push(@$user_list_criterion_resource_names,\n $ad_group_criterion_resource_name);\n });\n\n return $user_list_criterion_resource_names;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate void removeExistingListCriteriaFromAdGroup(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n // Retrieves all of the ad group criteria under a campaign.\n List<String> adGroupCriteria =\n getUserListAdGroupCriterion(googleAdsClient, customerId, campaignId);\n\n List<AdGroupCriterionOperation> operations = new ArrayList<>();\n\n // Creates a list of remove operations.\n for (String adGroupCriterion : adGroupCriteria) {\n operations.add(AdGroupCriterionOperation.newBuilder().setRemove(adGroupCriterion).build());\n }\n\n // Creates the ad group criterion service.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Removes the ad group criterion.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), operations);\n // Gets and prints the results.\n System.out.printf(\"Removed %d ad group criteria.%n\", response.getResultsCount());\n for (MutateAdGroupCriterionResult result : response.getResultsList()) {\n System.out.printf(\n \"Successfully removed ad group criterion with resource name '%s'.%n\",\n result.getResourceName());\n }\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate void RemoveExistingListCriteriaFromAdGroup(GoogleAdsClient client, long customerId,\n long campaignId)\n{\n // Get the AdGroupCriterionService client.\n AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n client.GetService(Services.V25.AdGroupCriterionService);\n\n // Retrieve all of the ad group criteria under a campaign.\n List<string> adGroupCriteria =\n GetUserListAdGroupCriteria(client, customerId, campaignId);\n\n // Create a list of remove operations.\n List<AdGroupCriterionOperation> operations = adGroupCriteria.Select(adGroupCriterion =>\n new AdGroupCriterionOperation { Remove = adGroupCriterion }).ToList();\n\n // Remove the ad group criteria and print the resource names of the removed criteria.\n MutateAdGroupCriteriaResponse mutateAdGroupCriteriaResponse =\n adGroupCriterionServiceClient.MutateAdGroupCriteria(customerId.ToString(),\n operations);\n\n Console.WriteLine($\"Removed {mutateAdGroupCriteriaResponse.Results.Count} ad group \" +\n \"criteria.\");\n foreach (MutateAdGroupCriterionResult result in mutateAdGroupCriteriaResponse.Results)\n {\n Console.WriteLine(\"Successfully removed ad group criterion with resource name \" +\n $\"'{result.ResourceName}'.\");\n }\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function removeExistingListCriteriaFromAdGroup(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n) {\n // Retrieves all of the ad group criteria under a campaign.\n $allAdGroupCriteria = self::getUserListAdGroupCriteria(\n $googleAdsClient,\n $customerId,\n $campaignId\n );\n\n $removeOperations = [];\n // Creates a list of remove operations.\n foreach ($allAdGroupCriteria as $adGroupCriterionResourceName) {\n $operation = new AdGroupCriterionOperation();\n $operation->setRemove($adGroupCriterionResourceName);\n $removeOperations[] = $operation;\n }\n\n // Issues a mutate request to remove the ad group criteria.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n /** @var MutateAdGroupCriteriaResponse $adGroupCriteriaResponse */\n $adGroupCriteriaResponse = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, $removeOperations)\n );\n\n foreach ($adGroupCriteriaResponse->getResults() as $adGroupCriteriaResult) {\n printf(\n \"Successfully removed ad group criterion with resource name '%s'.%s\",\n $adGroupCriteriaResult->getResourceName(),\n PHP_EOL\n );\n }\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef remove_existing_criteria_from_ad_group(\n client: GoogleAdsClient, customer_id: str, campaign_id: str\n) -> None:\n \"\"\"Removes all ad group criteria targeting a user list under a campaign.\n\n This is a necessary step before targeting a user list at the campaign level.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID.\n campaign_id: a str ID for a campaign that will have all ad group\n criteria that targets user lists removed.\n \"\"\"\n # Retrieves all of the ad group criteria under a campaign.\n all_ad_group_criteria: List[str] = get_user_list_ad_group_criteria(\n client, customer_id, campaign_id\n )\n\n # Creates a list of remove operations.\n remove_operations: List[AdGroupCriterionOperation] = []\n for ad_group_criterion_resource_name in all_ad_group_criteria:\n remove_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n remove_operation.remove = ad_group_criterion_resource_name\n remove_operations.append(remove_operation)\n\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=remove_operations\n )\n )\n print(\n \"Successfully removed ad group criterion with resource name: \"\n f\"'{response.results[0].resource_name}'\"\n )set_up_remarketing.py\n```\n\nExample:\n```text\ndef remove_existing_list_criteria_from_ad_group(\n client,\n customer_id,\n campaign_id\n)\n # Retrieves all of the ad group criteria under a campaign.\n ad_group_criteria = get_user_list_ad_group_criterion(\n client, customer_id, campaign_id)\n\n # Creates a list of remove operations.\n operations = []\n ad_group_criteria.each do |agc|\n operations << client.operation.remove_resource.ad_group_criterion(agc)\n end\n\n # Issues a mutate request to remove all ad group criteria.\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: operations,\n )\n puts \"Removed #{response.results.size} ad group criteria.\"\n response.results.each do |result|\n puts \"Successfully removed ad group criterion with resource name \" \\\n \"'#{result.resource_name}'\"\n end\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub remove_existing_list_criteria_from_ad_group {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n # Retrieve all of the ad group criteria under a campaign.\n my $ad_group_criteria =\n get_user_list_ad_group_criteria($api_client, $customer_id, $campaign_id);\n\n # Create a list of remove operations.\n my $operations = [];\n foreach my $ad_group_criterion (@$ad_group_criteria) {\n push(\n @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n remove => $ad_group_criterion\n }));\n }\n\n # Remove the ad group criteria and print the resource names of the removed criteria.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => $operations\n });\n\n printf \"Removed %d ad group criteria.\\n\",\n scalar @{$ad_group_criteria_response->{results}};\n foreach my $result (@{$ad_group_criteria_response->{results}}) {\n printf \"Successfully removed ad group criterion with resource name '%s'.\\n\",\n $result->{resourceName};\n }\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate String targetAdsInCampaignToUserList(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId, String userList) {\n // Creates the campaign criterion.\n CampaignCriterion campaignCriterion =\n CampaignCriterion.newBuilder()\n .setCampaign(ResourceNames.campaign(customerId, campaignId))\n .setUserList(UserListInfo.newBuilder().setUserList(userList).build())\n .build();\n\n // Creates the operation.\n CampaignCriterionOperation operation =\n CampaignCriterionOperation.newBuilder().setCreate(campaignCriterion).build();\n\n // Creates the campaign criterion service client.\n try (CampaignCriterionServiceClient campaignCriterionServiceClient =\n googleAdsClient.getLatestVersion().createCampaignCriterionServiceClient()) {\n // Adds the campaign criterion.\n MutateCampaignCriteriaResponse response =\n campaignCriterionServiceClient.mutateCampaignCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n // Gets and prints the campaign criterion resource name.\n String campaignCriterionResourceName = response.getResults(0).getResourceName();\n System.out.printf(\n \"Successfully created campaign criterion with resource name '%s' \"\n + \"targeting user list with resource name '%s' with campaign with ID %d.%n\",\n campaignCriterionResourceName, userList, campaignId);\n return campaignCriterionResourceName;\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate string TargetAdsInCampaignToUserList(\n GoogleAdsClient client, long customerId, long campaignId, string userListResourceName)\n{\n // Get the CampaignCriterionService client.\n CampaignCriterionServiceClient campaignCriterionServiceClient =\n client.GetService(Services.V25.CampaignCriterionService);\n\n // Create the campaign criterion.\n CampaignCriterion campaignCriterion = new CampaignCriterion\n {\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n UserList = new UserListInfo\n {\n UserList = userListResourceName\n }\n };\n\n // Create the operation.\n CampaignCriterionOperation campaignCriterionOperation = new CampaignCriterionOperation\n {\n Create = campaignCriterion\n };\n\n // Add the campaign criterion and print the resulting criterion's resource name.\n MutateCampaignCriteriaResponse mutateCampaignCriteriaResponse =\n campaignCriterionServiceClient.MutateCampaignCriteria(customerId.ToString(),\n new[] { campaignCriterionOperation });\n\n string campaignCriterionResourceName =\n mutateCampaignCriteriaResponse.Results.First().ResourceName;\n Console.WriteLine(\"Successfully created campaign criterion with resource name \" +\n $\"'{campaignCriterionResourceName}' targeting user list with resource name \" +\n $\"'{userListResourceName}' with campaign with ID {campaignId}.\");\n\n return campaignCriterionResourceName;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function targetAdsInCampaignToUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId,\n string $userListResourceName\n): string {\n // Creates the campaign criterion.\n $campaignCriterion = new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign($customerId, $campaignId),\n 'user_list' => new UserListInfo(['user_list' => $userListResourceName])\n ]);\n\n // Creates the operation.\n $operation = new CampaignCriterionOperation();\n $operation->setCreate($campaignCriterion);\n\n // Issues a mutate request to create a campaign criterion.\n $campaignCriterionServiceClient = $googleAdsClient->getCampaignCriterionServiceClient();\n /** @var MutateCampaignCriteriaResponse $campaignCriteriaResponse */\n $campaignCriteriaResponse = $campaignCriterionServiceClient->mutateCampaignCriteria(\n MutateCampaignCriteriaRequest::build($customerId, [$operation])\n );\n\n $campaignCriterionResourceName =\n $campaignCriteriaResponse->getResults()[0]->getResourceName();\n printf(\n \"Successfully created campaign criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with campaign with ID %d.%s\",\n $campaignCriterionResourceName,\n $userListResourceName,\n $campaignId,\n PHP_EOL\n );\n\n return $campaignCriterionResourceName;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef target_ads_in_campaign_to_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n campaign_id: str,\n user_list_resource_name: str,\n) -> str:\n \"\"\"Creates a campaign criterion that targets a user list with a campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID used to create an campaign\n criterion.\n campaign_id: a str ID for a campaign used to create a campaign\n criterion that targets members of a user list.\n user_list_resource_name: a str resource name for a user list.\n\n Returns:\n a str resource name for a campaign criterion.\n \"\"\"\n campaign_criterion_operation: CampaignCriterionOperation = client.get_type(\n \"CampaignCriterionOperation\"\n )\n campaign_criterion: CampaignCriterion = campaign_criterion_operation.create\n campaign_criterion.campaign = client.get_service(\n \"CampaignService\"\n ).campaign_path(customer_id, campaign_id)\n campaign_criterion.user_list.user_list = user_list_resource_name\n\n campaign_criterion_service: CampaignCriterionServiceClient = (\n client.get_service(\"CampaignCriterionService\")\n )\n response: MutateCampaignCriteriaResponse = (\n campaign_criterion_service.mutate_campaign_criteria(\n customer_id=customer_id, operations=[campaign_criterion_operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n print(\n \"Successfully created campaign criterion with resource name \"\n f\"'{resource_name}' targeting user list with resource name \"\n f\"'{user_list_resource_name}' with campaign with ID {campaign_id}\"\n )\n return resource_nameset_up_remarketing.py\n```\n\nExample:\n```text\ndef target_ads_in_campaign_to_user_list(\n client,\n customer_id,\n campaign_id,\n user_list\n)\n # Creates the campaign criterion targeting members of the user list.\n operation = client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(customer_id, campaign_id)\n cc.user_list = client.resource.user_list_info do |info|\n info.user_list = user_list\n end\n end\n\n # Issues a mutate request to create the campaign criterion.\n response = client.service.campaign_criterion.mutate_campaign_criteria(\n customer_id: customer_id,\n operations: [operation],\n )\n campaign_criterion_resource_name = response.results.first.resource_name\n puts \"Successfully created campaign criterion with resource name \" \\\n \"'#{campaign_criterion_resource_name}' targeting user list with resource name \" \\\n \"'#{user_list}' with campaign with ID #{campaign_id}\"\n\n campaign_criterion_resource_name\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub target_ads_in_campaign_to_user_list {\n my ($api_client, $customer_id, $campaign_id, $user_list_resource_name) = @_;\n\n # Create the campaign criterion.\n my $campaign_criterion =\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign => Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $campaign_id\n ),\n userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({\n userList => $user_list_resource_name\n })});\n\n # Create the operation.\n my $campaign_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create => $campaign_criterion\n });\n\n # Add the campaign criterion and print the resulting criterion's resource name.\n my $campaign_criteria_response =\n $api_client->CampaignCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_criterion_operation]});\n\n my $campaign_criterion_resource_name =\n $campaign_criteria_response->{results}[0]{resourceName};\n printf \"Successfully created campaign criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with campaign with ID %d.\\n\",\n $campaign_criterion_resource_name, $user_list_resource_name, $campaign_id;\n\n return $campaign_criterion_resource_name;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nSELECT\n ad_group_criterion.criterion_id,\n metrics.conversions,\n metrics.cost_per_conversion\nFROM ad_group_audience_view\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.415Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":33,"totalLines":1348,"estimatedTokens":12577}}171{"id":"doc-add_performance_max_retail_campaign_google_ads_a-bab077ea","source":"documentation","title":"Add Performance Max Retail Campaign | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/samples/add-performance-max-retail-campaign","text":"Example:\n```text\n// Copyright 2021 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.shoppingads;\n\nimport static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime;\nimport static com.google.ads.googleads.v25.enums.EuPoliticalAdvertisingStatusEnum.EuPoliticalAdvertisingStatus.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.lib.utils.FieldMasks;\nimport com.google.ads.googleads.v25.common.ImageAsset;\nimport com.google.ads.googleads.v25.common.LanguageInfo;\nimport com.google.ads.googleads.v25.common.LocationInfo;\nimport com.google.ads.googleads.v25.common.MaximizeConversionValue;\nimport com.google.ads.googleads.v25.common.TextAsset;\nimport com.google.ads.googleads.v25.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType;\nimport com.google.ads.googleads.v25.enums.AssetAutomationStatusEnum.AssetAutomationStatus;\nimport com.google.ads.googleads.v25.enums.AssetAutomationTypeEnum.AssetAutomationType;\nimport com.google.ads.googleads.v25.enums.AssetFieldTypeEnum.AssetFieldType;\nimport com.google.ads.googleads.v25.enums.AssetGroupStatusEnum.AssetGroupStatus;\nimport com.google.ads.googleads.v25.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod;\nimport com.google.ads.googleads.v25.enums.CampaignStatusEnum.CampaignStatus;\nimport com.google.ads.googleads.v25.enums.ConversionActionCategoryEnum.ConversionActionCategory;\nimport com.google.ads.googleads.v25.enums.ConversionOriginEnum.ConversionOrigin;\nimport com.google.ads.googleads.v25.enums.ListingGroupFilterListingSourceEnum.ListingGroupFilterListingSource;\nimport com.google.ads.googleads.v25.enums.ListingGroupFilterTypeEnum.ListingGroupFilterType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.Asset;\nimport com.google.ads.googleads.v25.resources.AssetGroup;\nimport com.google.ads.googleads.v25.resources.AssetGroupAsset;\nimport com.google.ads.googleads.v25.resources.AssetGroupListingGroupFilter;\nimport com.google.ads.googleads.v25.resources.Campaign;\nimport com.google.ads.googleads.v25.resources.Campaign.AssetAutomationSetting;\nimport com.google.ads.googleads.v25.resources.Campaign.ShoppingSetting;\nimport com.google.ads.googleads.v25.resources.CampaignAsset;\nimport com.google.ads.googleads.v25.resources.CampaignBudget;\nimport com.google.ads.googleads.v25.resources.CampaignConversionGoal;\nimport com.google.ads.googleads.v25.resources.CampaignCriterion;\nimport com.google.ads.googleads.v25.resources.CustomerConversionGoal;\nimport com.google.ads.googleads.v25.services.AssetGroupAssetOperation;\nimport com.google.ads.googleads.v25.services.AssetGroupListingGroupFilterOperation;\nimport com.google.ads.googleads.v25.services.AssetGroupOperation;\nimport com.google.ads.googleads.v25.services.AssetOperation;\nimport com.google.ads.googleads.v25.services.CampaignAssetOperation;\nimport com.google.ads.googleads.v25.services.CampaignBudgetOperation;\nimport com.google.ads.googleads.v25.services.CampaignConversionGoalOperation;\nimport com.google.ads.googleads.v25.services.CampaignCriterionOperation;\nimport com.google.ads.googleads.v25.services.CampaignOperation;\nimport com.google.ads.googleads.v25.services.GoogleAdsRow;\nimport com.google.ads.googleads.v25.services.GoogleAdsServiceClient;\nimport com.google.ads.googleads.v25.services.GoogleAdsServiceClient.SearchPagedResponse;\nimport com.google.ads.googleads.v25.services.MutateGoogleAdsResponse;\nimport com.google.ads.googleads.v25.services.MutateOperation;\nimport com.google.ads.googleads.v25.services.MutateOperationResponse;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.io.ByteStreams;\nimport com.google.protobuf.ByteString;\nimport com.google.protobuf.Descriptors.FieldDescriptor;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map.Entry;\nimport java.util.stream.Collectors;\nimport org.joda.time.DateTime;\n\n/**\n * This example shows how to create a Performance Max retail campaign.\n *\n * <p>This will be created for \"All products\".\n *\n * <p>For more information about Performance Max retail campaigns, see\n * https://developers.google.com/google-ads/api/docs/performance-max/retail\n *\n * <p>Prerequisites: - You need to have access to a Merchant Center account. You can find\n * instructions to create a Merchant Center account here:\n * https://support.google.com/merchants/answer/188924. This account must be linked to your Google\n * Ads account. The integration instructions can be found at:\n * https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center - You need your\n * Google Ads account to track conversions. The different ways to track conversions can be found\n * here: https://support.google.com/google-ads/answer/1722054. - You must have at least one\n * conversion action in the account. For more about conversion actions, see\n * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions\n */\npublic class AddPerformanceMaxRetailCampaign {\n\n // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are always\n // negative and unique within one mutate request.\n //\n // <p>See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further\n // details.\n //\n // <p>These temporary IDs are fixed because they are used in multiple places.\n private static final int BUDGET_TEMPORARY_ID = -1;\n private static final int PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = -2;\n private static final int ASSET_GROUP_TEMPORARY_ID = -3;\n\n // There are also entities that will be created in the same request but do not\n // need to be fixed temporary IDs because they are referenced only once.\n private static long temporaryId = ASSET_GROUP_TEMPORARY_ID - 1;\n\n private static class AddPerformanceMaxRetailCampaignParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(\n names = ArgumentNames.MERCHANT_CENTER_ACCOUNT_ID,\n required = true,\n description = \"The Merchant Center account ID.\")\n private long merchantCenterAccountId;\n\n @Parameter(\n names = ArgumentNames.FINAL_URL,\n required = true,\n description =\n \"The final url for the generated ads. Must have the same domain as the Merchant Center\"\n + \" account.\")\n private String finalUrl;\n\n @Parameter(\n names = ArgumentNames.BRAND_GUIDELINES_ENABLED,\n arity = 1,\n description =\n \"A boolean value indicating if the created campaign is enabled for brand guidelines\")\n private boolean brandGuidelinesEnabled = true;\n }\n\n public static void main(String[] args) throws IOException {\n AddPerformanceMaxRetailCampaignParams params = new AddPerformanceMaxRetailCampaignParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.merchantCenterAccountId = Long.parseLong(\"INSERT_MERCHANT_CENTER_ACCOUNT_ID_HERE\");\n params.finalUrl = \"INSERT_FINAL_URL_HERE\";\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new AddPerformanceMaxRetailCampaign()\n .runExample(\n googleAdsClient,\n params.customerId,\n params.merchantCenterAccountId,\n params.finalUrl,\n params.brandGuidelinesEnabled);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param merchantCenterAccountId the Merchant Center account ID.\n * @param finalUrl final URL for the asset group of the campaign.\n * @param brandGuidelinesEnabled indicates if the campaign is enabled for brand guidelines.\n */\n private void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n long merchantCenterAccountId,\n String finalUrl,\n boolean brandGuidelinesEnabled)\n throws IOException {\n // This campaign will override the customer conversion goals. For more information see\n // https://developers.google.com/google-ads/api/docs/conversions/goals/campaign-goals.\n // Retrieve the current list of customer conversion goals.\n List<CustomerConversionGoal> customerConversionGoals =\n getCustomerConversionGoals(googleAdsClient, customerId);\n\n // Performance Max campaigns require that repeated assets such as headlines\n // and descriptions be created before the campaign.\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n //\n // Creates the headlines.\n List<String> headlines = ImmutableList.of(\"Travel\", \"Travel Reviews\", \"Book travel\");\n List<String> headlineAssetResourceNames =\n createMultipleTextAssets(googleAdsClient, customerId, headlines);\n // Creates the descriptions.\n List<String> descriptions = ImmutableList.of(\"Take to the air!\", \"Fly to the sky!\");\n List<String> descriptionAssetResourceNames =\n createMultipleTextAssets(googleAdsClient, customerId, descriptions);\n\n // The below methods create and return MutateOperations that we later\n // provide to the GoogleAdsService.Mutate method in order to create the\n // entities in a single request. Since the entities for a Performance Max\n // campaign are closely tied to one-another, it's considered a best practice\n // to create them in a single Mutate request, so they all complete\n // successfully or fail entirely, leaving no orphaned entities. See:\n // https://developers.google.com/google-ads/api/docs/mutating/overview\n List<MutateOperation> mutateOperations = new ArrayList<>();\n mutateOperations.add(createCampaignBudgetOperation(customerId));\n mutateOperations.add(\n createPerformanceMaxCampaignOperation(\n customerId, merchantCenterAccountId, brandGuidelinesEnabled));\n mutateOperations.addAll(createCampaignCriterionOperations(customerId));\n String assetGroupResourceName = ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID);\n mutateOperations.add(createAssetGroupOperation(customerId, assetGroupResourceName, finalUrl));\n // Retail Performance Max campaigns require listing groups, which are created via the\n // AssetGroupListingGroupFilter resource.\n mutateOperations.add(createAssetGroupListingGroupFilterOperation(assetGroupResourceName));\n mutateOperations.addAll(\n createAssetAndAssetGroupAssetOperations(\n customerId,\n assetGroupResourceName,\n headlineAssetResourceNames,\n descriptionAssetResourceNames,\n brandGuidelinesEnabled));\n mutateOperations.addAll(createConversionGoalOperations(customerId, customerConversionGoals));\n\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);\n printResponseDetails(response);\n }\n }\n\n /** Creates a MutateOperation that creates a new CampaignBudget. */\n private MutateOperation createCampaignBudgetOperation(long customerId) {\n CampaignBudget campaignBudget =\n CampaignBudget.newBuilder()\n .setName(\"Performance Max retail campaign budget #\" + getPrintableDateTime())\n // The budget period already defaults to DAILY.\n .setAmountMicros(50_000_000)\n .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)\n // A Performance Max campaign cannot use a shared campaign budget.\n .setExplicitlyShared(false)\n // Set a temporary ID in the budget's resource name, so it can be referenced\n // by the campaign in later steps.\n .setResourceName(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignBudgetOperation(\n CampaignBudgetOperation.newBuilder().setCreate(campaignBudget).build())\n .build();\n }\n\n\n /** Creates a MutateOperation that creates a new Performance Max campaign. */\n private MutateOperation createPerformanceMaxCampaignOperation(\n long customerId,\n long merchantCenterAccountId,\n boolean brandGuidelinesEnabled) {\n Campaign performanceMaxCampaign =\n Campaign.newBuilder()\n .setName(\"Performance Max retail campaign #\" + getPrintableDateTime())\n // Sets the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n .setStatus(CampaignStatus.PAUSED)\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n .setAdvertisingChannelType(AdvertisingChannelType.PERFORMANCE_MAX)\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n // For first time users, it's recommended not to set a target ROAS value. Although\n // the target ROAS value is optional, you still need to define the enclosing\n // maximize_conversion_value.\n .setMaximizeConversionValue(\n MaximizeConversionValue.newBuilder()\n // .setTargetRoas(3.5)\n .build())\n // Below is what you would use if you want to maximize conversions:\n // .setMaximizeConversions(\n // MaximizeConversions.newBuilder()\n // // The target CPA is optional. This is the average amount that you would like\n // // to spend per conversion action.\n // // .setTargetCpaMicros(1_000_000)\n // .build())\n // Sets the shopping settings.\n .setShoppingSetting(\n ShoppingSetting.newBuilder()\n .setMerchantId(merchantCenterAccountId)\n // Optional: To use products only from a specific feed, set FeedLabel to the\n // feed label used in Merchant Center. See:\n // https://support.google.com/merchants/answer/12453549.\n // Removing the feedLabel field will use products from all feeds.\n // .setFeedLabel(\"INSERT_FEED_LABEL_HERE\")\n .build())\n // Sets if the campaign is enabled for brand guidelines. For more information on brand\n // guidelines, see https://support.google.com/google-ads/answer/14934472.\n .setBrandGuidelinesEnabled(brandGuidelinesEnabled)\n // Assigns the resource name with a temporary ID.\n .setResourceName(\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID))\n // Sets the budget using the given budget resource name.\n .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n // Optional fields.\n .setStartDateTime(new DateTime().plusDays(1).toString(\"yyyy-MM-dd 00:00:00\"))\n .setEndDateTime(new DateTime().plusDays(365).toString(\"yyyy-MM-dd 23:59:59\"))\n // Configures the optional opt-in/out status for asset automation settings.\n .addAllAssetAutomationSettings(ImmutableList.of(\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_IMAGE_EXTRACTION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(\n AssetAutomationType.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_ENHANCED_YOUTUBE_VIDEOS)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_IMAGE_ENHANCEMENT)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build()))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignOperation(\n CampaignOperation.newBuilder().setCreate(performanceMaxCampaign).build())\n .build();\n }\n\n\n /** Creates a list of MutateOperations that create new campaign criteria. */\n private List<MutateOperation> createCampaignCriterionOperations(long customerId) {\n String campaignResourceName =\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID);\n List<CampaignCriterion> campaignCriteria = new ArrayList<>();\n // Sets the LOCATION campaign criteria.\n // Targets all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n // Adds one positive location target for New York City (ID=1023191), specifically adding\n // the positive criteria before the negative one.\n .setLocation(\n LocationInfo.newBuilder()\n .setGeoTargetConstant(ResourceNames.geoTargetConstant(1023191))\n .build())\n .setNegative(false)\n .build());\n // Next adds the negative target for Brooklyn (ID=1022762).\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n .setLocation(\n LocationInfo.newBuilder()\n .setGeoTargetConstant(ResourceNames.geoTargetConstant(1022762))\n .build())\n .setNegative(true)\n .build());\n // Sets the LANGUAGE campaign criterion.\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n // Sets the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n .setLanguage(\n LanguageInfo.newBuilder()\n .setLanguageConstant(ResourceNames.languageConstant(1000)) // English\n .build())\n .build());\n // Returns a list of mutate operations with one operation per criterion.\n return campaignCriteria.stream()\n .map(\n criterion ->\n MutateOperation.newBuilder()\n .setCampaignCriterionOperation(\n CampaignCriterionOperation.newBuilder().setCreate(criterion).build())\n .build())\n .collect(Collectors.toList());\n }\n\n\n /**\n * Creates multiple text assets and returns the list of resource names.\n *\n * <p>These repeated assets must be created in a separate request prior to creating the campaign.\n */\n private List<String> createMultipleTextAssets(\n GoogleAdsClient googleAdsClient, long customerId, List<String> texts) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n for (String text : texts) {\n Asset asset = Asset.newBuilder().setTextAsset(TextAsset.newBuilder().setText(text)).build();\n AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build();\n mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build());\n }\n\n List<String> assetResourceNames = new ArrayList<>();\n // Creates the service client.\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Sends the operations in a single Mutate request.\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);\n for (MutateOperationResponse result : response.getMutateOperationResponsesList()) {\n if (result.hasAssetResult()) {\n assetResourceNames.add(result.getAssetResult().getResourceName());\n }\n }\n printResponseDetails(response);\n }\n return assetResourceNames;\n }\n\n\n /** Creates a MutateOperation that create a new AssetGroup. */\n private MutateOperation createAssetGroupOperation(\n long customerId, String assetGroupResourceName, String finalUrl) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n String campaignResourceName =\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID);\n // Creates the AssetGroup.\n AssetGroup assetGroup =\n AssetGroup.newBuilder()\n .setName(\"Performance Max retail asset group #\" + getPrintableDateTime())\n .setCampaign(campaignResourceName)\n .addFinalUrls(finalUrl)\n .addFinalMobileUrls(finalUrl)\n .setStatus(AssetGroupStatus.PAUSED)\n .setResourceName(assetGroupResourceName)\n .build();\n AssetGroupOperation assetGroupOperation =\n AssetGroupOperation.newBuilder().setCreate(assetGroup).build();\n return MutateOperation.newBuilder().setAssetGroupOperation(assetGroupOperation).build();\n }\n\n\n /** Creates a list of MutateOperations that create a new AssetGroup. */\n private List<MutateOperation> createAssetAndAssetGroupAssetOperations(\n long customerId,\n String assetGroupResourceName,\n List<String> headlineAssetResourceNames,\n List<String> descriptionAssetResourceNames,\n boolean brandGuidelinesEnabled)\n throws IOException {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // the resource name of the AssetGroup\n // the resource name of the Asset\n // the field_type of the Asset in this AssetGroup.\n\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n // Links the previously created multiple text assets.\n\n // Links the headline assets.\n for (String resourceName : headlineAssetResourceNames) {\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.HEADLINE, resourceName, assetGroupResourceName));\n }\n\n // Links the description assets.\n for (String resourceName : descriptionAssetResourceNames) {\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.DESCRIPTION, resourceName, assetGroupResourceName));\n }\n\n // Creates and links the long headline text asset.\n mutateOperations.addAll(\n createAndLinkTextAsset(\n customerId, assetGroupResourceName, \"Travel the World\", AssetFieldType.LONG_HEADLINE));\n\n // Creates and links the business name and logo assets.\n mutateOperations.addAll(\n createAndLinkBrandAssets(\n customerId,\n brandGuidelinesEnabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/1Crm\",\n \"Logo Image\"));\n\n // Creates and links the image assets.\n\n // Creates and links the Marketing Image Asset.\n mutateOperations.addAll(\n createAndLinkImageAsset(\n customerId,\n assetGroupResourceName,\n \"https://gaagl.page.link/Eit5\",\n AssetFieldType.MARKETING_IMAGE,\n \"Marketing Image\"));\n\n // Creates and links the Square Marketing Image Asset.\n mutateOperations.addAll(\n createAndLinkImageAsset(\n customerId,\n assetGroupResourceName,\n \"https://gaagl.page.link/bjYi\",\n AssetFieldType.SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\"));\n\n return sortMutateOperations(mutateOperations);\n }\n\n\n /** Creates a list of MutateOperations that create a new linked text asset. */\n List<MutateOperation> createAndLinkTextAsset(\n long customerId, String assetGroupResourceName, String text, AssetFieldType assetFieldType) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());\n // Creates the Text Asset.\n Asset asset =\n Asset.newBuilder()\n .setResourceName(assetResourceName)\n .setTextAsset(TextAsset.newBuilder().setText(text).build())\n .build();\n AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build();\n mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build());\n\n // Creates an AssetGroupAsset to link the Asset to the AssetGroup.\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n assetFieldType, assetResourceName, assetGroupResourceName));\n\n return mutateOperations;\n }\n\n\n /** Creates a list of MutateOperations that create a new linked image asset. */\n List<MutateOperation> createAndLinkImageAsset(\n long customerId,\n String assetGroupResourceName,\n String url,\n AssetFieldType assetFieldType,\n String assetName)\n throws IOException {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());\n // Creates a media file.\n byte[] assetBytes = ByteStreams.toByteArray(new URL(url).openStream());\n\n // Creates the Image Asset.\n Asset asset =\n Asset.newBuilder()\n .setResourceName(assetResourceName)\n .setImageAsset(ImageAsset.newBuilder().setData(ByteString.copyFrom(assetBytes)).build())\n // Provides a unique friendly name to identify your asset. When there is an existing\n // image asset with the same content but a different name, the new name will be dropped\n // silently.\n .setName(assetName)\n .build();\n AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build();\n mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build());\n\n // Creates an AssetGroupAsset to link the Asset to the AssetGroup.\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n assetFieldType, assetResourceName, assetGroupResourceName));\n\n return mutateOperations;\n }\n\n /** Creates a list of MutateOperations that create linked brand assets. */\n List<MutateOperation> createAndLinkBrandAssets(\n long customerId,\n boolean brandGuidelinesEnabled,\n String businessName,\n String logoUrl,\n String logoName)\n throws IOException {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n\n // Creates the brand name text asset.\n String businessNameAssetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());\n Asset businessNameAsset =\n Asset.newBuilder()\n .setResourceName(businessNameAssetResourceName)\n .setTextAsset(TextAsset.newBuilder().setText(businessName).build())\n .build();\n AssetOperation businessNameAssetOperation =\n AssetOperation.newBuilder().setCreate(businessNameAsset).build();\n mutateOperations.add(\n MutateOperation.newBuilder().setAssetOperation(businessNameAssetOperation).build());\n\n // Creates the logo image asset.\n String logoAssetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());\n // Creates a media file.\n byte[] logoBytes = ByteStreams.toByteArray(new URL(logoUrl).openStream());\n Asset logoAsset =\n Asset.newBuilder()\n .setResourceName(logoAssetResourceName)\n .setImageAsset(ImageAsset.newBuilder().setData(ByteString.copyFrom(logoBytes)).build())\n // Provides a unique friendly name to identify your asset. When there is an existing\n // image asset with the same content but a different name, the new name will be dropped\n // silently.\n .setName(logoName)\n .build();\n AssetOperation logoImageAssetOperation =\n AssetOperation.newBuilder().setCreate(logoAsset).build();\n mutateOperations.add(\n MutateOperation.newBuilder().setAssetOperation(logoImageAssetOperation).build());\n\n if (brandGuidelinesEnabled) {\n // Creates CampaignAsset resources to link the Asset resources to the Campaign.\n mutateOperations.add(\n createCampaignAssetMutateOperation(\n customerId, AssetFieldType.BUSINESS_NAME, businessNameAssetResourceName));\n mutateOperations.add(\n createCampaignAssetMutateOperation(\n customerId, AssetFieldType.LOGO, logoAssetResourceName));\n } else {\n // Creates an AssetGroupAsset to link the Asset to the AssetGroup.\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.BUSINESS_NAME,\n businessNameAssetResourceName,\n ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.LOGO,\n logoAssetResourceName,\n ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));\n }\n\n return mutateOperations;\n }\n\n /** Creates a MutateOperation to add an AssetGroupAsset. */\n MutateOperation createAssetGroupAssetMutateOperation(\n AssetFieldType fieldType, String assetResourceName, String assetGroupResourceName) {\n AssetGroupAsset assetGroupAsset =\n AssetGroupAsset.newBuilder()\n .setFieldType(fieldType)\n .setAssetGroup(assetGroupResourceName)\n .setAsset(assetResourceName)\n .build();\n AssetGroupAssetOperation assetGroupAssetOperation =\n AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build();\n return MutateOperation.newBuilder()\n .setAssetGroupAssetOperation(assetGroupAssetOperation)\n .build();\n }\n\n /** Creates a MutateOperation to add a CampaignAsset. */\n MutateOperation createCampaignAssetMutateOperation(\n long customerId, AssetFieldType fieldType, String assetResourceName) {\n CampaignAsset campaignAsset =\n CampaignAsset.newBuilder()\n .setFieldType(fieldType)\n .setCampaign(ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID))\n .setAsset(assetResourceName)\n .build();\n CampaignAssetOperation campaignAssetOperation =\n CampaignAssetOperation.newBuilder().setCreate(campaignAsset).build();\n return MutateOperation.newBuilder().setCampaignAssetOperation(campaignAssetOperation).build();\n }\n\n\n /**\n * Sorts a list of mutate operations.\n *\n * <p>This sorts the list such that all asset operations precede all asset group asset and\n * campaign asset operations. If asset group assets are created before assets then an error will\n * be returned by the API.\n */\n private List<MutateOperation> sortMutateOperations(List<MutateOperation> operations) {\n List<MutateOperation> sortedOperations = new ArrayList<>();\n sortedOperations.addAll(\n operations.stream().filter(o -> o.hasAssetOperation()).collect(Collectors.toList()));\n sortedOperations.addAll(\n operations.stream()\n .filter(o -> o.hasAssetGroupAssetOperation())\n .collect(Collectors.toList()));\n sortedOperations.addAll(\n operations.stream()\n .filter(o -> o.hasCampaignAssetOperation())\n .collect(Collectors.toList()));\n return sortedOperations;\n }\n\n\n /** Retrieves the list of customer conversion goals. */\n private static List<CustomerConversionGoal> getCustomerConversionGoals(\n GoogleAdsClient googleAdsClient, long customerId) {\n String query =\n \"SELECT customer_conversion_goal.category, customer_conversion_goal.origin \"\n + \"FROM customer_conversion_goal\";\n\n List<CustomerConversionGoal> customerConversionGoals = new ArrayList<>();\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // The number of conversion goals is typically less than 50, so we use\n // GoogleAdsService.search instead of search_stream.\n SearchPagedResponse response =\n googleAdsServiceClient.search(Long.toString(customerId), query);\n for (GoogleAdsRow googleAdsRow : response.iterateAll()) {\n customerConversionGoals.add(googleAdsRow.getCustomerConversionGoal());\n }\n }\n\n return customerConversionGoals;\n }\n\n /** Creates a list of MutateOperations that override customer conversion goals. */\n private static List<MutateOperation> createConversionGoalOperations(\n long customerId, List<CustomerConversionGoal> customerConversionGoals) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n // To override the customer conversion goals, we will change the\n // biddability of each of the customer conversion goals so that only\n // the desired conversion goal is biddable in this campaign.\n for (CustomerConversionGoal customerConversionGoal : customerConversionGoals) {\n ConversionActionCategory category = customerConversionGoal.getCategory();\n ConversionOrigin origin = customerConversionGoal.getOrigin();\n String campaignConversionGoalResourceName =\n ResourceNames.campaignConversionGoal(\n customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID, category, origin);\n CampaignConversionGoal.Builder campaignConversionGoalBuilder =\n CampaignConversionGoal.newBuilder().setResourceName(campaignConversionGoalResourceName);\n // Change the biddability for the campaign conversion goal.\n // Set biddability to True for the desired (category, origin).\n // Set biddability to False for all other conversion goals.\n // Note:\n // 1- It is assumed that this Conversion Action\n // (category=PURCHASE, origin=WEBSITE) exists in this account.\n // 2- More than one goal can be biddable if desired. This example\n // shows only one.\n if (category == ConversionActionCategory.PURCHASE && origin == ConversionOrigin.WEBSITE) {\n campaignConversionGoalBuilder.setBiddable(true);\n } else {\n campaignConversionGoalBuilder.setBiddable(false);\n }\n CampaignConversionGoal campaignConversionGoal = campaignConversionGoalBuilder.build();\n CampaignConversionGoalOperation campaignConversionGoalOperation =\n CampaignConversionGoalOperation.newBuilder()\n .setUpdate(campaignConversionGoal)\n .setUpdateMask(FieldMasks.allSetFieldsOf(campaignConversionGoal))\n .build();\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setCampaignConversionGoalOperation(campaignConversionGoalOperation)\n .build());\n }\n return mutateOperations;\n }\n\n\n /** Creates a MutateOperation that creates a new asset group listing group filter. */\n private MutateOperation createAssetGroupListingGroupFilterOperation(\n String assetGroupResourceName) {\n\n // Creates a new asset group listing group filter containing the \"default\" listing group (All\n // products).\n AssetGroupListingGroupFilter listingGroupFilter =\n AssetGroupListingGroupFilter.newBuilder()\n .setAssetGroup(assetGroupResourceName)\n // Does not set the parentListingGroupFilter since this is the root node. For all other\n // nodes, this would refer to the parent listing group filter resource name.\n // .setParentListingGroupFilter(\"<PARENT FILTER RESOURCE NAME>\")\n\n // Sets the type to UNIT_INCLUDED since this node has no children.\n .setType(ListingGroupFilterType.UNIT_INCLUDED)\n // Specifies that this uses the SHOPPING listing source, as required for a Performance\n // Max retail campaign.\n .setListingSource(ListingGroupFilterListingSource.SHOPPING)\n .build();\n\n // Returns an operation to the list to create the listing group filter.\n return MutateOperation.newBuilder()\n .setAssetGroupListingGroupFilterOperation(\n AssetGroupListingGroupFilterOperation.newBuilder().setCreate(listingGroupFilter))\n .build();\n }\n\n\n /**\n * Prints the details of a MutateGoogleAdsResponse.\n *\n * <p>Parses the \"response\" oneof field name and uses it to extract the new entity's name and\n * resource name.\n */\n private void printResponseDetails(MutateGoogleAdsResponse response) {\n // Parses the Mutate response to print details about the entities that were created by the\n // request.\n String suffix = \"_result\";\n for (MutateOperationResponse result : response.getMutateOperationResponsesList()) {\n for (Entry<FieldDescriptor, Object> responseFields : result.getAllFields().entrySet()) {\n String fieldName = responseFields.getKey().getName();\n String value = responseFields.getValue().toString().trim();\n if (fieldName.endsWith(suffix)) {\n fieldName = fieldName.substring(0, fieldName.length() - suffix.length());\n }\n System.out.printf(\"Created a(n) %s with %s.%n\", fieldName, value);\n }\n }\n }\n\n /** Returns the next temporary ID and decreases it by one. */\n private long getNextTemporaryId() {\n return temporaryId--;\n }\n}\nAddPerformanceMaxRetailCampaign.java\n```\n\nExample:\n```text\n// Copyright 2021 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.Gax.Util;\nusing Google.Ads.GoogleAds.Config;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing Google.Api.Gax;\nusing Google.Protobuf;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing static Google.Ads.GoogleAds.V25.Enums.AdvertisingChannelTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AssetAutomationStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AssetAutomationTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AssetFieldTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AssetGroupStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.BudgetDeliveryMethodEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.CampaignStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.ConversionActionCategoryEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.ConversionOriginEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.EuPoliticalAdvertisingStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.ListingGroupFilterListingSourceEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.ListingGroupFilterTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Resources.Campaign.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This example shows how to create a Performance Max retail campaign.\n ///\n /// This will be created for \"All products\".\n ///\n /// For more information about Performance Max retail campaigns, see\n /// https://developers.google.com/google-ads/api/docs/performance-max/retail\n ///\n /// Prerequisites:\n /// - You need to have access to a Merchant Center account. You can find\n /// instructions to create a Merchant Center account here:\n /// https://support.google.com/merchants/answer/188924.\n /// This account must be linked to your Google Ads account. The integration\n /// instructions can be found at:\n /// https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center\n /// - You need your Google Ads account to track conversions. The different ways\n /// to track conversions can be found here:\n /// https://support.google.com/google-ads/answer/1722054.\n /// - You must have at least one conversion action in the account. For\n /// more about conversion actions, see\n /// https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions\n /// </summary>\n public class AddPerformanceMaxRetailCampaign : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"AddPerformanceMaxRetailCampaign\"/>\n /// example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// The Merchant Center account ID.\n /// </summary>\n [Option(\"merchantCenterAccountId\", Required = true, HelpText =\n \"The Merchant Center account ID.\")]\n public long MerchantCenterAccountId { get; set; }\n\n /// <summary>\n /// The final url for the generated ads. Must have the same domain as the Merchant\n /// Center account.\n /// </summary>\n [Option(\"finalUrl\", Required = true, HelpText =\n \"The final url for the generated ads.\" +\n \"Must have the same domain as the Merchant Center account.\")]\n public string FinalUrl { get; set; }\n\n /// <summary>\n /// Optional: A boolean value indicating if the campaign is enabled for brand\n /// guidelines.\n /// </summary>\n [Option(\"brandGuidelinesEnabled\", Required = false, HelpText =\n \"A boolean value indicating if the campaign is enabled for brand guidelines.\")]\n public bool BrandGuidelinesEnabled { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n AddPerformanceMaxRetailCampaign codeExample = new AddPerformanceMaxRetailCampaign();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(\n new GoogleAdsClient(),\n options.CustomerId,\n options.MerchantCenterAccountId,\n options.FinalUrl,\n options.BrandGuidelinesEnabled\n );\n }\n\n // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are\n // always negative and unique within one mutate request.\n //\n // See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further\n // details.\n //\n // These temporary IDs are fixed because they are used in multiple places.\n private const int TEMPORARY_ID_BUDGET = -1;\n\n private const int TEMPORARY_ID_CAMPAIGN = -2;\n private const int TEMPORARY_ID_ASSET_GROUP = -3;\n\n // There are also entities that will be created in the same request but do not need to be\n // fixed temporary IDs because they are referenced only once.\n private class AssetTemporaryResourceNameGenerator\n {\n private long customerId;\n private long next;\n\n public AssetTemporaryResourceNameGenerator(long customerId, long assetGroupId)\n {\n this.customerId = customerId;\n this.next = assetGroupId - 1;\n }\n\n public string Next()\n {\n long i = next;\n Interlocked.Decrement(ref next);\n return ResourceNames.Asset(customerId, i);\n }\n }\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This example shows how to create a Performance Max retail campaign.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID.</param>\n /// <param name=\"merchantCenterAccountId\">The Merchant Center account ID.</param>\n /// <param name=\"finalUrl\">The final URL.</param>\n /// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n public void Run(\n GoogleAdsClient client,\n long customerId,\n long merchantCenterAccountId,\n string finalUrl,\n bool brandGuidelinesEnabled)\n {\n try\n {\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n // This campaign will override the customer conversion goals.\n // Retrieve the current list of customer conversion goals.\n List<CustomerConversionGoal> customerConversionGoals =\n GetCustomerConversionGoals(client, customerId);\n\n // Performance Max campaigns require that repeated assets such as headlines and\n // descriptions be created before the campaign.\n //\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n //\n // Create the headlines.\n List<string> headlineAssetResourceNames = CreateMultipleTextAssets(\n client,\n customerId,\n new[] {\n \"Travel\",\n \"Travel Reviews\",\n \"Book travel\"\n }\n );\n\n // Create the descriptions.\n List<string> descriptionAssetResourceNames = CreateMultipleTextAssets(\n client,\n customerId,\n new[] {\n \"Take to the air!\",\n \"Fly to the sky!\"\n }\n );\n\n string tempResourceNameCampaignBudget = ResourceNames.CampaignBudget(\n customerId,\n TEMPORARY_ID_BUDGET\n );\n\n string assetGroupResourceName = ResourceNames.AssetGroup(\n customerId,\n TEMPORARY_ID_ASSET_GROUP\n );\n\n // The below methods create and return MutateOperations that we later provide to the\n // GoogleAdsService.Mutate method in order to create the entities in a single request.\n // Since the entities for a Performance Max campaign are closely tied to one-another,\n // it's considered a best practice to create them in a single Mutate request so they all\n // complete successfully or fail entirely, leaving no orphaned entities.\n //\n // See: https://developers.google.com/google-ads/api/docs/mutating/overview\n MutateOperation campaignBudgetOperation = CreateCampaignBudgetOperation(\n tempResourceNameCampaignBudget\n );\n\n string tempResourceNameCampaign = ResourceNames.Campaign(\n customerId,\n TEMPORARY_ID_CAMPAIGN\n );\n\n MutateOperation performanceMaxCampaignOperation =\n CreatePerformanceMaxCampaignOperation(\n tempResourceNameCampaign,\n tempResourceNameCampaignBudget,\n merchantCenterAccountId,\n brandGuidelinesEnabled\n );\n\n List<MutateOperation> campaignCriterionOperations =\n CreateCampaignCriterionOperations(tempResourceNameCampaign);\n\n List<MutateOperation> assetGroupOperations =\n CreateAssetGroupOperations(\n tempResourceNameCampaign,\n assetGroupResourceName,\n finalUrl,\n headlineAssetResourceNames,\n descriptionAssetResourceNames,\n new AssetTemporaryResourceNameGenerator(\n customerId,\n TEMPORARY_ID_ASSET_GROUP\n ),\n client.Config,\n brandGuidelinesEnabled\n );\n\n List<MutateOperation> conversionGoalOperations =\n CreateCustomerConversionGoalOperations(\n customerId,\n customerConversionGoals\n );\n\n // Retail Performance Max campaigns require listing groups, which are created via the\n // AssetGroupListingGroupFilter resource.\n List<MutateOperation> assetGroupListingGroupOperations =\n CreateAssetGroupListingGroupOperations(\n assetGroupResourceName\n );\n\n MutateGoogleAdsRequest request = new MutateGoogleAdsRequest\n {\n CustomerId = customerId.ToString()\n };\n\n // It's important to create these entities in this order because they depend on\n // each other.\n //\n // Additionally, we take several lists of operations and flatten them into one\n // large list.\n request.MutateOperations.Add(campaignBudgetOperation);\n request.MutateOperations.Add(performanceMaxCampaignOperation);\n request.MutateOperations.AddRange(campaignCriterionOperations);\n request.MutateOperations.AddRange(assetGroupOperations);\n request.MutateOperations.AddRange(conversionGoalOperations);\n request.MutateOperations.AddRange(assetGroupListingGroupOperations);\n\n MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);\n\n PrintResponseDetails(response);\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Creates a MutateOperation that creates a new CampaignBudget.\n ///\n /// A temporary ID will be assigned to this campaign budget so that it can be\n /// referenced by other objects being created in the same Mutate request.\n /// </summary>\n /// <param name=\"budgetResourceName\">The temporary resource name of the budget to\n /// create.</param>\n /// <returns>A MutateOperation that creates a CampaignBudget.</returns>\n private MutateOperation CreateCampaignBudgetOperation(\n string budgetResourceName)\n {\n MutateOperation operation = new MutateOperation\n {\n CampaignBudgetOperation = new CampaignBudgetOperation\n {\n Create = new CampaignBudget\n {\n Name = \"Performance Max campaign budget #\"\n + ExampleUtilities.GetRandomString(),\n\n // The budget period already defaults to Daily.\n AmountMicros = 50000000,\n DeliveryMethod = BudgetDeliveryMethod.Standard,\n\n // A Performance Max campaign cannot use a shared campaign budget.\n ExplicitlyShared = false,\n\n // Set a temporary ID in the budget's resource name so it can be referenced\n // by the campaign in later steps.\n ResourceName = budgetResourceName\n }\n }\n };\n\n return operation;\n }\n\n\n /// Creates a MutateOperation that creates a new Performance Max campaign.\n /// <param name=\"campaignResourceName\">The campaign resource name.</param>\n /// <param name=\"campaignBudgetResourceName\">The campaign budget resource name.</param>\n /// <param name=\"merchantCenterAccountId\">The Merchant Center account ID.</param>\n /// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n /// <returns>A MutateOperations that will create this new campaign.</returns>\n private MutateOperation CreatePerformanceMaxCampaignOperation(\n string campaignResourceName,\n string campaignBudgetResourceName,\n long merchantCenterAccountId,\n bool brandGuidelinesEnabled)\n {\n\n Campaign campaign = new Campaign()\n {\n Name = \"Performance Max campaign #\" + ExampleUtilities.GetRandomString(),\n\n // Set the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n Status = CampaignStatus.Paused,\n\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n AdvertisingChannelType = AdvertisingChannelType.PerformanceMax,\n\n // Bidding strategy must be set directly on the campaign. Setting a\n // portfolio bidding strategy by resource name is not supported. Max\n // Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns. BiddingStrategyTYpe is\n // read-only and cannot be set by the API. An optional ROAS (Return on\n // Advertising Spend) can be set to enable the MaximizeConversionValue\n // bidding strategy. The ROAS value must be specified as a ratio in the API.\n // It is calculated by dividing \"total value\" by \"total spend\".\n //\n // For more information on Maximize Conversion Value, see the support\n // article:\n // http://support.google.com/google-ads/answer/7684216.\n //\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n MaximizeConversionValue = new MaximizeConversionValue()\n {\n TargetRoas = 3.5\n },\n\n ShoppingSetting = new ShoppingSetting()\n {\n MerchantId = merchantCenterAccountId,\n // Optional: To use products only from a specific feed, set FeedLabel\n // to the feed label used in Merchant Center.\n // See: https://support.google.com/merchants/answer/12453549.\n // Omitting the FeedLabel field will use products from all feeds.\n // FeedLabel = \"INSERT_FEED_LABEL_HERE\"\n },\n\n // Use the temporary resource name created earlier\n ResourceName = campaignResourceName,\n\n // Set the budget using the given budget resource name.\n CampaignBudget = campaignBudgetResourceName,\n\n // Set if the campaign is enabled for brand guidelines. For more information\n // on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n BrandGuidelinesEnabled = brandGuidelinesEnabled,\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n\n // Optional fields\n StartDateTime = DateTime.Now.AddDays(1).ToString(\"yyyyMMdd 00:00:00\"),\n EndDateTime = DateTime.Now.AddDays(365).ToString(\"yyyyMMdd 23:59:59\")\n };\n\n // Configures the optional opt-in/out status for asset automation\n // settings.\n campaign.AssetAutomationSettings.AddRange(new[]{\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateImageExtraction,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.FinalUrlExpansionTextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.TextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateEnhancedYoutubeVideos,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateImageEnhancement,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n });\n\n MutateOperation operation = new MutateOperation()\n {\n CampaignOperation = new CampaignOperation()\n {\n Create = campaign\n }\n };\n\n return operation;\n }\n\n\n /// <summary>\n /// Creates a list of MutateOperations that create new campaign criteria.\n /// </summary>\n /// <param name=\"campaignResourceName\">The campaign resource name.</param>\n /// <returns>A list of MutateOperations that create new campaign criteria.</returns>\n private List<MutateOperation> CreateCampaignCriterionOperations(\n string campaignResourceName)\n {\n List<MutateOperation> operations = new List<MutateOperation>();\n\n // Set the LOCATION campaign criteria.\n // Target all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n //\n // We will add one positive location target for New York City (ID=1023191)\n // and one negative location target for Brooklyn (ID=1022762).\n // First, add the positive (negative = False) for New York City.\n MutateOperation operation1 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n Location = new LocationInfo()\n {\n GeoTargetConstant = ResourceNames.GeoTargetConstant(1023191)\n },\n\n Negative = false\n }\n }\n };\n\n operations.Add(operation1);\n\n // Next add the negative target for Brooklyn.\n MutateOperation operation2 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n Location = new LocationInfo()\n {\n GeoTargetConstant = ResourceNames.GeoTargetConstant(1022762)\n },\n\n Negative = true\n }\n }\n };\n\n operations.Add(operation2);\n\n // Set the LANGUAGE campaign criterion.\n MutateOperation operation3 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n\n // Set the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n Language = new LanguageInfo()\n {\n LanguageConstant = ResourceNames.LanguageConstant(1000) // English\n },\n }\n }\n };\n\n operations.Add(operation3);\n\n return operations;\n }\n\n\n /// <summary>\n /// Creates multiple text assets and returns the list of resource names.\n /// These repeated assets must be created in a separate request prior to\n /// creating the campaign.\n /// </summary>\n /// <param name=\"client\">The Google Ads Client.</param>\n /// <param name=\"customerId\">The customer's ID.</param>\n /// <param name=\"texts\">The texts to add.</param>\n /// <returns>A list of asset resource names.</returns>\n private List<string> CreateMultipleTextAssets(\n GoogleAdsClient client,\n long customerId,\n string[] texts)\n {\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n MutateGoogleAdsRequest request = new MutateGoogleAdsRequest()\n {\n CustomerId = customerId.ToString()\n };\n\n foreach (string text in texts)\n {\n request.MutateOperations.Add(\n new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n TextAsset = new TextAsset()\n {\n Text = text\n }\n }\n }\n }\n );\n }\n\n // Send the operations in a single Mutate request.\n MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);\n\n List<string> assetResourceNames = new List<string>();\n\n foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)\n {\n MutateAssetResult assetResult = operationResponse.AssetResult;\n assetResourceNames.Add(assetResult.ResourceName);\n }\n\n PrintResponseDetails(response);\n\n return assetResourceNames;\n }\n\n\n /// <summary>\n /// Creates a list of MutateOperations that create a new asset_group.\n /// </summary>\n /// <param name=\"campaignResourceName\">The campaign resource name.</param>\n /// <param name=\"assetGroupResourceName\">The asset group resource name.</param>\n /// <param name=\"finalUrl\">The final url.</param>\n /// <param name=\"headlineAssetResourceNames\">The headline asset resource names.</param>\n /// <param name=\"descriptionAssetResourceNames\">The description asset resource\n /// names.</param>\n /// <param name=\"resourceNameGenerator\">A generator for unique temporary ID's.</param>\n /// <param name=\"config\">The Google Ads config.</param>\n /// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n /// <returns>A list of MutateOperations that create the new asset group.</returns>\n private List<MutateOperation> CreateAssetGroupOperations(\n string campaignResourceName,\n string assetGroupResourceName,\n string finalUrl,\n List<string> headlineAssetResourceNames,\n List<string> descriptionAssetResourceNames,\n AssetTemporaryResourceNameGenerator resourceNameGenerator,\n GoogleAdsConfig config,\n bool brandGuidelinesEnabled)\n {\n List<MutateOperation> operations = new List<MutateOperation>();\n\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // Create and link the long headline text asset.\n string longHeadlineResourceName = resourceNameGenerator.Next();\n operations.Add(\n CreateTextAssetOperation(\n longHeadlineResourceName,\n \"Travel the World\"\n )\n );\n\n // Create the business name text asset.\n string businessNameResourceName = resourceNameGenerator.Next();\n operations.Add(\n CreateTextAssetOperation(\n businessNameResourceName,\n \"Interplanetary Cruises\"\n )\n );\n\n // Create the Logo Asset.\n string logoResourceName = resourceNameGenerator.Next();\n operations.Add(\n CreateImageAssetOperation(\n logoResourceName,\n \"https://gaagl.page.link/1Crm\",\n \"Logo Image\",\n config\n )\n );\n\n // Create the Marketing Image Asset.\n string marketingImageResourceName = resourceNameGenerator.Next();\n operations.Add(\n CreateImageAssetOperation(\n marketingImageResourceName,\n \"https://gaagl.page.link/Eit5\",\n \"Marketing Image\",\n config\n )\n );\n\n // Create the Square Marketing Image Asset.\n string squareMarketingImageResourceName = resourceNameGenerator.Next();\n operations.Add(\n CreateImageAssetOperation(\n squareMarketingImageResourceName,\n \"https://gaagl.page.link/bjYi\",\n \"Square Marketing Image\",\n config\n )\n );\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // the resource name of the AssetGroup\n // the resource name of the Asset\n // the field_type of the Asset in this AssetGroup.\n //\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n //\n // Also, note that all asset creation operations must be before the\n // asset group creation operation and the asset group linking operations.\n\n // Create the AssetGroup\n operations.Add(\n new MutateOperation()\n {\n AssetGroupOperation = new AssetGroupOperation()\n {\n Create = new AssetGroup()\n {\n Name = \"Performance Max asset group #\" +\n ExampleUtilities.GetRandomString(),\n\n Campaign = campaignResourceName,\n FinalUrls = { finalUrl },\n FinalMobileUrls = { finalUrl },\n Status = AssetGroupStatus.Paused,\n ResourceName = assetGroupResourceName\n }\n }\n }\n );\n\n // Link the previously created assets.\n\n // Link the headline assets.\n foreach (string resourceName in headlineAssetResourceNames)\n {\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.Headline,\n AssetGroup = assetGroupResourceName,\n Asset = resourceName\n }\n }\n }\n );\n }\n\n // Link the description assets.\n foreach (string resourceName in descriptionAssetResourceNames)\n {\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.Description,\n AssetGroup = assetGroupResourceName,\n Asset = resourceName\n }\n }\n }\n );\n }\n\n operations.Add(\n CreateLinkAssetOperation(\n AssetFieldType.LongHeadline,\n assetGroupResourceName,\n longHeadlineResourceName\n )\n );\n\n operations.Add(\n CreateLinkAssetOperation(\n AssetFieldType.BusinessName,\n assetGroupResourceName,\n businessNameResourceName,\n brandGuidelinesEnabled\n )\n );\n\n operations.Add(\n CreateLinkAssetOperation(\n AssetFieldType.Logo,\n assetGroupResourceName,\n logoResourceName,\n brandGuidelinesEnabled\n )\n );\n\n operations.Add(\n CreateLinkAssetOperation(\n AssetFieldType.MarketingImage,\n assetGroupResourceName,\n marketingImageResourceName\n )\n );\n\n operations.Add(\n CreateLinkAssetOperation(\n AssetFieldType.SquareMarketingImage,\n assetGroupResourceName,\n squareMarketingImageResourceName\n )\n );\n\n return operations;\n }\n\n\n /// <summary>\n /// Creates a MutateOperation that creates a new text asset.\n /// </summary>\n /// <param name=\"assetResourceName\">The resource name of the text asset to be\n /// created.</param>\n /// <param name=\"text\">The text of the asset to be created.</param>\n /// <returns>A MutateOperation that creates the new text asset.</returns>\n private MutateOperation CreateTextAssetOperation(\n string assetResourceName,\n string text) => new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n ResourceName = assetResourceName,\n TextAsset = new TextAsset()\n {\n Text = text\n }\n }\n }\n };\n\n\n /// <summary>\n /// Creates a MutateOperation that creates a new image asset.\n /// </summary>\n /// <param name=\"assetResourceName\">The resource name of the text asset to be\n /// created.</param>\n /// <param name=\"url\">The url of the image to be retrieved and put into an asset.</param>\n /// <param name=\"assetName\">The asset name.</param>\n /// <param name=\"config\">The Google Ads config.</param>\n /// <returns>A MutateOperation that creates a new image asset.</returns>\n private MutateOperation CreateImageAssetOperation(\n string assetResourceName,\n string url,\n string assetName,\n GoogleAdsConfig config) => new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n ResourceName = assetResourceName,\n ImageAsset = new ImageAsset()\n {\n Data =\n ByteString.CopyFrom(\n MediaUtilities.GetAssetDataFromUrl(url, config)\n )\n },\n // Provide a unique friendly name to identify your asset.\n // When there is an existing image asset with the same content but a\n // different name, the new name will be dropped silently.\n Name = assetName\n }\n }\n };\n\n\n /// <summary>\n /// Creates a MutateOperation that links an asset to an asset group.\n /// </summary>\n /// <param name=\"fieldType\">The field type of the asset to be linked.</param>\n /// <param name=\"linkedEntityResourceName\">The resource name of the entity (asset group or\n /// campaign) to link the asset to.</param>\n /// <param name=\"assetResourceName\">The resource name of the text asset to be\n /// linked.</param>\n /// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n /// <returns>A MutateOperation that links an asset to an asset group.</returns>\n private MutateOperation CreateLinkAssetOperation(\n AssetFieldType fieldType,\n string linkedEntityResourceName,\n string assetResourceName,\n bool brandGuidelinesEnabled = false)\n { if (brandGuidelinesEnabled)\n {\n return new MutateOperation()\n {\n CampaignAssetOperation = new CampaignAssetOperation()\n {\n Create = new CampaignAsset()\n {\n FieldType = fieldType,\n Campaign = linkedEntityResourceName,\n Asset = assetResourceName\n }\n }\n };\n } else\n { return new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = fieldType,\n AssetGroup = linkedEntityResourceName,\n Asset = assetResourceName\n }\n }\n };\n }\n }\n\n\n /// <summary>\n /// Retrieves the list of customer conversion goals.\n /// </summary>\n /// <param name=\"client\">The Google Ads Client.</param>\n /// <param name=\"customerId\">The customer's id.</param>\n /// <returns>A list customer conversion goals.</returns>\n private List<CustomerConversionGoal> GetCustomerConversionGoals(\n GoogleAdsClient client,\n long customerId)\n {\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n List<CustomerConversionGoal> conversionGoals = new List<CustomerConversionGoal>();\n\n SearchGoogleAdsRequest request = new SearchGoogleAdsRequest()\n {\n CustomerId = customerId.ToString(),\n Query =\n @\"SELECT\n customer_conversion_goal.category,\n customer_conversion_goal.origin\n FROM\n customer_conversion_goal\"\n };\n\n // The number of conversion goals is typically less than 50 so we use\n // GoogleAdsService.search instead of search_stream.\n PagedEnumerable<SearchGoogleAdsResponse, GoogleAdsRow> searchPagedResponse =\n googleAdsServiceClient.Search(request);\n\n // Iterate over the results and build the list of conversion goals.\n foreach (GoogleAdsRow row in searchPagedResponse)\n {\n conversionGoals.Add(row.CustomerConversionGoal);\n }\n\n return conversionGoals;\n }\n\n /// <summary>\n /// Creates a list of MutateOperations that override customer conversion goals.\n /// </summary>\n /// <param name=\"customerId\">The customer's id.</param>\n /// <param name=\"conversionGoals\">A list customer conversion goals.</param>\n /// <returns>A list customer conversion goal operations.</returns>\n private List<MutateOperation> CreateCustomerConversionGoalOperations(\n long customerId,\n List<CustomerConversionGoal> conversionGoals)\n {\n List<MutateOperation> operations =\n new List<MutateOperation>();\n\n foreach (CustomerConversionGoal conversionGoal in conversionGoals)\n {\n CustomerConversionGoal newConversionGoal = new CustomerConversionGoal()\n {\n ResourceName = ResourceNames.CustomerConversionGoal(\n customerId,\n conversionGoal.Category,\n conversionGoal.Origin\n ),\n };\n\n // Change the biddability for the campaign conversion goal.\n // Set biddability to True for the desired (category, origin).\n // Set biddability to False for all other conversion goals.\n // Note:\n // 1- It is assumed that this Conversion Action\n // (category=PURCHASE, origin=WEBSITE) exists in this account.\n // 2- More than one goal can be biddable if desired. This example\n // shows only one.\n newConversionGoal.Biddable =\n conversionGoal.Category == ConversionActionCategory.Purchase &&\n conversionGoal.Origin == ConversionOrigin.Website;\n\n operations.Add(\n new MutateOperation()\n {\n CustomerConversionGoalOperation = new CustomerConversionGoalOperation()\n {\n Update = newConversionGoal,\n UpdateMask = FieldMasks.AllSetFieldsOf(newConversionGoal)\n }\n }\n );\n }\n\n return operations;\n }\n\n\n /// <summary>\n /// Creates a list of MutateOperations that create a new asset group\n /// listing group filter.\n /// </summary>\n /// <param name=\"assetGroupResourceName\">The resource name of the asset group.</param>\n /// <returns>A list of mutate operations.</returns>\n private List<MutateOperation> CreateAssetGroupListingGroupOperations(\n string assetGroupResourceName)\n {\n List<MutateOperation> operations = new List<MutateOperation>();\n\n // Creates a new ad group criterion containing the \"default\" listing group (All\n // products).\n AssetGroupListingGroupFilter listingGroupFilter = new AssetGroupListingGroupFilter()\n {\n AssetGroup = assetGroupResourceName,\n\n // Since this is the root node, do not set the ParentListingGroupFilter. For all\n // other nodes, this would refer to the parent listing group filter resource name.\n // ParentListingGroupFilter = \"<PARENT FILTER NAME>\"\n\n // The UnitIncluded means this node has no children.\n Type = ListingGroupFilterType.UnitIncluded,\n\n // Because this is a Performance Max campaign for retail, we need to specify that\n // this is in the shopping listing source.\n ListingSource = ListingGroupFilterListingSource.Shopping\n };\n\n AssetGroupListingGroupFilterOperation operation =\n new AssetGroupListingGroupFilterOperation()\n {\n Create = listingGroupFilter\n };\n\n operations.Add(\n new MutateOperation()\n {\n AssetGroupListingGroupFilterOperation = operation\n }\n );\n\n return operations;\n }\n\n\n /// <summary>\n /// Prints the details of a MutateGoogleAdsResponse. Parses the \"response\" oneof field name\n /// and uses it to extract the new entity's name and resource name.\n /// </summary>\n /// <param name=\"response\">A MutateGoogleAdsResponse instance.</param>\n private void PrintResponseDetails(MutateGoogleAdsResponse response)\n {\n // Parse the Mutate response to print details about the entities that were created\n // in the request.\n foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)\n {\n string resourceName;\n\n string entityName = operationResponse.ResponseCase.ToString();\n // Trim the substring \"Result\" from the end of the entity name.\n entityName = entityName.Remove(entityName.Length - 6);\n\n switch (operationResponse.ResponseCase)\n {\n case MutateOperationResponse.ResponseOneofCase.AdGroupResult:\n resourceName = operationResponse.AdGroupResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AdGroupAdResult:\n resourceName = operationResponse.AdGroupAdResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.CampaignResult:\n resourceName = operationResponse.CampaignResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.CampaignBudgetResult:\n resourceName = operationResponse.CampaignBudgetResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.CampaignCriterionResult:\n resourceName = operationResponse.CampaignCriterionResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.SmartCampaignSettingResult:\n resourceName = operationResponse.SmartCampaignSettingResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AssetResult:\n resourceName = operationResponse.AssetResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AssetGroupAssetResult:\n resourceName = operationResponse.AssetGroupAssetResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AssetGroupResult:\n resourceName = operationResponse.AssetGroupResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AssetGroupListingGroupFilterResult:\n resourceName = operationResponse.AssetGroupListingGroupFilterResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.CampaignConversionGoalResult:\n resourceName = operationResponse.CampaignConversionGoalResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.CustomerConversionGoalResult:\n resourceName = operationResponse.CustomerConversionGoalResult.ResourceName;\n break;\n\n default:\n resourceName = \"<not found>\";\n break;\n }\n\n Console.WriteLine(\n $\"Created a(n) {entityName} with resource name: '{resourceName}'.\");\n }\n }\n }\n}\nAddPerformanceMaxRetailCampaign.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2021 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\ShoppingAds;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Util\\FieldMasks;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ImageAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\LanguageInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\LocationInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\MaximizeConversionValue;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\TextAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdvertisingChannelTypeEnum\\AdvertisingChannelType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetAutomationTypeEnum\\AssetAutomationType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetAutomationStatusEnum\\AssetAutomationStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetFieldTypeEnum\\AssetFieldType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetGroupStatusEnum\\AssetGroupStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\BudgetDeliveryMethodEnum\\BudgetDeliveryMethod;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\CampaignStatusEnum\\CampaignStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\ConversionActionCategoryEnum\\ConversionActionCategory;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\ConversionOriginEnum\\ConversionOrigin;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\EuPoliticalAdvertisingStatusEnum\\EuPoliticalAdvertisingStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\ListingGroupFilterListingSourceEnum\\ListingGroupFilterListingSource;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\ListingGroupFilterTypeEnum\\ListingGroupFilterType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Asset;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AssetGroup;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AssetGroupAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AssetGroupListingGroupFilter;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Campaign\\AssetAutomationSetting;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Campaign;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Campaign\\ShoppingSetting;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CampaignAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CampaignBudget;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CampaignConversionGoal;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CampaignCriterion;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetGroupAssetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetGroupListingGroupFilterOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetGroupOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignAssetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignBudgetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignConversionGoalOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignCriterionOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\GoogleAdsRow;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateGoogleAdsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateGoogleAdsResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateOperationResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\SearchGoogleAdsRequest;\nuse Google\\ApiCore\\ApiException;\nuse Google\\ApiCore\\Serializer;\n\n/**\n * This example shows how to create a Performance Max retail campaign.\n *\n * This will be created for \"All products\".\n *\n * For more information about Performance Max retail campaigns, see\n * https://developers.google.com/google-ads/api/docs/performance-max/retail.\n *\n * Prerequisites:\n * - You need to have access to a Merchant Center account. You can find\n * instructions to create a Merchant Center account here:\n * https://support.google.com/merchants/answer/188924.\n * This account must be linked to your Google Ads account. The integration\n * instructions can be found at:\n * https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center\n * - You need your Google Ads account to track conversions. The different ways\n * to track conversions can be found here:\n * https://support.google.com/google-ads/answer/1722054.\n * - You must have at least one conversion action in the account. For more about conversion\n * actions, see\n * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n */\nclass AddPerformanceMaxRetailCampaign\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const MERCHANT_CENTER_ACCOUNT_ID = 'INSERT_MERCHANT_CENTER_ACCOUNT_ID_HERE';\n // The final URL for the generated ads. Must have the same domain as the Merchant Center\n // account.\n private const FINAL_URL = 'INSERT_FINAL_URL_HERE';\n // Optional: Indicates whether the created campaign is enabled for brand guidelines.\n private const BRAND_GUIDELINES_ENABLED = true;\n\n // We specify temporary IDs that are specific to a single mutate request.\n // Temporary IDs are always negative and unique within one mutate request.\n //\n // See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n // for further details.\n //\n // These temporary IDs are fixed because they are used in multiple places.\n private const BUDGET_TEMPORARY_ID = -1;\n private const PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = -2;\n private const ASSET_GROUP_TEMPORARY_ID = -3;\n\n // There are also entities that will be created in the same request but do not need to be fixed\n // temporary IDs because they are referenced only once.\n /** @var int the negative temporary ID used in bulk mutates. */\n private static $nextTempId = self::ASSET_GROUP_TEMPORARY_ID - 1;\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::MERCHANT_CENTER_ACCOUNT_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::FINAL_URL => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::BRAND_GUIDELINES_ENABLED => GetOpt::OPTIONAL_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())\n ->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::MERCHANT_CENTER_ACCOUNT_ID]\n ?: self::MERCHANT_CENTER_ACCOUNT_ID,\n $options[ArgumentNames::FINAL_URL] ?: self::FINAL_URL,\n filter_var(\n $options[ArgumentNames::BRAND_GUIDELINES_ENABLED]\n ?: self::BRAND_GUIDELINES_ENABLED,\n FILTER_VALIDATE_BOOLEAN\n )\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $merchantCenterAccountId the Merchant Center account ID\n * @param string $finalUrl the final URL for the asset group of the campaign\n * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand\n * guidelines\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $merchantCenterAccountId,\n string $finalUrl,\n bool $brandGuidelinesEnabled\n ) {\n // This campaign will override the customer conversion goals.\n // Retrieves the current list of customer conversion goals.\n $customerConversionGoals = self::getCustomerConversionGoals(\n $googleAdsClient,\n $customerId\n );\n\n // Performance Max campaigns require that repeated assets such as headlines\n // and descriptions be created before the campaign.\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets.\n //\n // Creates the headlines.\n $headlineAssetResourceNames = self::createMultipleTextAssets(\n $googleAdsClient,\n $customerId,\n [\"Travel\", \"Travel Reviews\", \"Book travel\"]\n );\n // Creates the descriptions.\n $descriptionAssetResourceNames = self::createMultipleTextAssets(\n $googleAdsClient,\n $customerId,\n [\"Take to the air!\", \"Fly to the sky!\"]\n );\n\n // It's important to create the below entities in this order because they depend on\n // each other.\n $operations = [];\n // The below methods create and return MutateOperations that we later\n // provide to the GoogleAdsService.Mutate method in order to create the\n // entities in a single request. Since the entities for a Performance Max\n // campaign are closely tied to one-another, it's considered a best practice\n // to create them in a single Mutate request so they all complete\n // successfully or fail entirely, leaving no orphaned entities. See:\n // https://developers.google.com/google-ads/api/docs/mutating/overview.\n $operations[] = self::createCampaignBudgetOperation($customerId);\n $operations[] = self::createPerformanceMaxCampaignOperation(\n $customerId,\n $merchantCenterAccountId,\n $brandGuidelinesEnabled\n );\n $operations =\n array_merge($operations, self::createCampaignCriterionOperations($customerId));\n $operations[] = self::createAssetGroupOperation($customerId, $finalUrl);\n $operations[] = self::createAssetGroupListingGroupFilterOperation($customerId);\n $operations = array_merge($operations, self::createAssetandAssetGroupAssetOperations(\n $customerId,\n $headlineAssetResourceNames,\n $descriptionAssetResourceNames,\n $brandGuidelinesEnabled\n ));\n $operations = array_merge($operations, self::createConversionGoalOperations(\n $customerId,\n $customerConversionGoals\n ));\n\n // Issues a mutate request to create everything and prints its information.\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n $response = $googleAdsServiceClient->mutate(\n MutateGoogleAdsRequest::build($customerId, $operations)\n );\n\n self::printResponseDetails($response);\n }\n\n /**\n * Creates a MutateOperation that creates a new CampaignBudget.\n *\n * A temporary ID will be assigned to this campaign budget so that it can be\n * referenced by other objects being created in the same Mutate request.\n *\n * @param int $customerId the customer ID\n * @return MutateOperation the mutate operation that creates a campaign budget\n */\n private static function createCampaignBudgetOperation(int $customerId): MutateOperation\n {\n // Creates a mutate operation that creates a campaign budget operation.\n return new MutateOperation([\n 'campaign_budget_operation' => new CampaignBudgetOperation([\n 'create' => new CampaignBudget([\n // Sets a temporary ID in the budget's resource name so it can be referenced\n // by the campaign in later steps.\n 'resource_name' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n 'name' => 'Performance Max retail campaign budget #' .\n Helper::getPrintableDatetime(),\n // The budget period already defaults to DAILY.\n 'amount_micros' => 50000000,\n 'delivery_method' => BudgetDeliveryMethod::STANDARD,\n // A Performance Max campaign cannot use a shared campaign budget.\n 'explicitly_shared' => false\n ])\n ])\n ]);\n }\n\n /**\n * Creates a MutateOperation that creates a new Performance Max campaign.\n *\n * A temporary ID will be assigned to this campaign so that it can\n * be referenced by other objects being created in the same Mutate request.\n *\n * @param int $customerId the customer ID\n * @param int $merchantCenterAccountId the Merchant Center account ID\n * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand\n * guidelines\n * @return MutateOperation the mutate operation that creates the campaign\n */\n private static function createPerformanceMaxCampaignOperation(\n int $customerId,\n int $merchantCenterAccountId,\n bool $brandGuidelinesEnabled\n ): MutateOperation {\n // Creates a mutate operation that creates a campaign operation.\n return new MutateOperation([\n 'campaign_operation' => new CampaignOperation([\n 'create' => new Campaign([\n 'name' => 'Performance Max retail campaign #' . Helper::getPrintableDatetime(),\n // Assigns the resource name with a temporary ID.\n 'resource_name' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n // Sets the budget using the given budget resource name.\n 'campaign_budget' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n // The campaign is the only entity in the mutate request that should have its\n // status set.\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving.\n 'status' => CampaignStatus::PAUSED,\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n 'advertising_channel_type' => AdvertisingChannelType::PERFORMANCE_MAX,\n\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Max Conversion Value are the only strategies supported\n // for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Max Conversion Value, see the support article:\n // http://support.google.com/google-ads/answer/7684216.\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n 'maximize_conversion_value' => new MaximizeConversionValue([\n 'target_roas' => 3.5\n ]),\n // Below is what you would use if you want to maximize conversions\n // You can optionally set the 'target_cpa_micros' field on MaximizeConversions.\n // This is the average amount that you would like to spend per conversion\n // action.\n // 'maximize_conversions' => new MaximizeConversions(),\n\n 'asset_automation_settings' => [\n new AssetAutomationSetting([\n 'asset_automation_type' => AssetAutomationType::TEXT_ASSET_AUTOMATION,\n 'asset_automation_status' => AssetAutomationStatus::OPTED_IN\n ]),\n new AssetAutomationSetting([\n 'asset_automation_type' => AssetAutomationType::URL_EXPANSION,\n 'asset_automation_status' => AssetAutomationStatus::OPTED_IN\n ])\n ],\n\n // Sets if the campaign is enabled for brand guidelines. For more information\n // on brand guidelines, see\n // https://support.google.com/google-ads/answer/14934472.\n 'brand_guidelines_enabled' => $brandGuidelinesEnabled,\n\n // Sets the shopping settings.\n 'shopping_setting' => new ShoppingSetting([\n 'merchant_id' => $merchantCenterAccountId,\n // Optional: To use products only from a specific feed, set feed_label to\n // the feed label used in Merchant Center.\n // See: https://support.google.com/merchants/answer/12453549.\n // Removing the feed_label field will use products from all feeds.\n // 'feed_label' => 'INSERT_FEED_LABEL_HERE'\n ]),\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n // Optional fields.\n 'start_date_time' => date('Y-m-d 00:00:00', strtotime('+1 day')),\n 'end_date_time' => date('Y-m-d 23:59:59', strtotime('+365 days'))\n ])\n ])\n ]);\n }\n\n /**\n * Creates a list of MutateOperations that create new campaign criteria.\n *\n * @param int $customerId the customer ID\n * @return MutateOperation[] a list of MutateOperations that create the new campaign criteria\n */\n private static function createCampaignCriterionOperations(int $customerId): array\n {\n $operations = [];\n // Sets the LOCATION campaign criteria.\n // Target all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'location' => new LocationInfo([\n // Adds one positive location target for New York City (ID=1023191),\n // specifically adding the positive criteria before the negative one.\n 'geo_target_constant' => ResourceNames::forGeoTargetConstant(1023191)\n ]),\n 'negative' => false\n ])\n ])\n ]);\n\n // Next adds the negative target for Brooklyn.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'location' => new LocationInfo([\n // Next add the negative target for Brooklyn (ID=1022762).\n 'geo_target_constant' => ResourceNames::forGeoTargetConstant(1022762)\n ]),\n 'negative' => true\n ])\n ])\n ]);\n\n // Sets the LANGUAGE campaign criterion.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n // Sets the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n 'language' => new LanguageInfo([\n 'language_constant' => ResourceNames::forLanguageConstant(1000) // English\n ])\n ])\n ])\n ]);\n\n return $operations;\n }\n\n /**\n * Creates multiple text assets and returns the list of resource names.\n *\n * These repeated assets must be created in a separate request prior to creating the campaign.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param string[] $texts a list of strings, each of which will be used to create a text asset\n * @return string[] a list of asset resource names\n */\n private static function createMultipleTextAssets(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n array $texts\n ): array {\n // Here again, we use the GoogleAdService to create multiple text assets in a single\n // request.\n $operations = [];\n foreach ($texts as $text) {\n // Creates a mutate operation for a text asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset(['text_asset' => new TextAsset(['text' => $text])])\n ])\n ]);\n }\n\n // Issues a mutate request to add all assets.\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n /** @var MutateGoogleAdsResponse $mutateGoogleAdsResponse */\n $mutateGoogleAdsResponse = $googleAdsServiceClient->mutate(\n MutateGoogleAdsRequest::build($customerId, $operations)\n );\n\n $assetResourceNames = [];\n foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {\n /** @var MutateOperationResponse $response */\n $assetResourceNames[] = $response->getAssetResult()->getResourceName();\n }\n self::printResponseDetails($mutateGoogleAdsResponse);\n\n return $assetResourceNames;\n }\n\n /**\n * Creates a MutateOperation that creates a new asset group.\n *\n * A temporary ID will be assigned to this asset group so that it can\n * be referenced by other objects being created in the same Mutate request.\n *\n * @param int $customerId the customer ID\n * @return MutateOperation a mutate operation creates a new asset group.\n */\n private static function createAssetGroupOperation(\n int $customerId,\n string $finalUrl\n ): MutateOperation {\n // Creates a new mutate operation that creates an asset group operation.\n return new MutateOperation([\n 'asset_group_operation' => new AssetGroupOperation([\n 'create' => new AssetGroup([\n 'resource_name' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'name' => 'Performance Max retail asset group #' .\n Helper::getPrintableDatetime(),\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'final_urls' => [$finalUrl],\n 'final_mobile_urls' => [$finalUrl],\n 'status' => AssetGroupStatus::PAUSED\n ])\n ])\n ]);\n }\n\n /**\n * Creates a MutateOperation that creates a new asset group listing group filter.\n *\n * A temporary ID will be assigned to this listing group filter so that it can be referenced by\n * other objects being created in the same Mutate request.\n *\n * @param int $customerId the customer ID\n * @return MutateOperation a MutateOperation that creates a new asset group listing group filter\n */\n private static function createAssetGroupListingGroupFilterOperation(\n int $customerId\n ): MutateOperation {\n return new MutateOperation([\n 'asset_group_listing_group_filter_operation'\n => new AssetGroupListingGroupFilterOperation([\n // Creates a new asset group listing group filter containing the \"default\"\n // listing group (All products).\n 'create' => new AssetGroupListingGroupFilter([\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n // Since this is the root node, do not set the 'parent_listing_group_filter'\n // field. For all other nodes, this would refer to the parent listing group\n // filter resource name.\n //\n // UNIT_INCLUDED means this node has no children.\n 'type' => ListingGroupFilterType::UNIT_INCLUDED,\n // Because this is a Performance Max campaign for retail, we need to specify\n // that this is in the shopping listing source.\n 'listing_source' => ListingGroupFilterListingSource::SHOPPING\n ])\n ])\n ]);\n }\n\n /**\n * Creates a list of MutateOperations that create new asset group asset and assets.\n *\n * A temporary ID will be assigned to this asset group so that it can\n * be referenced by other objects being created in the same mutate request.\n *\n * @param int $customerId the customer ID\n * @param string[] $headlineAssetResourceNames a list of headline resource names\n * @param string[] $descriptionAssetResourceNames a list of description resource names\n * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand\n * guidelines\n * @return MutateOperation[] a list of MutateOperations that create new asset group assets and\n * assets\n */\n private static function createAssetandAssetGroupAssetOperations(\n int $customerId,\n array $headlineAssetResourceNames,\n array $descriptionAssetResourceNames,\n bool $brandGuidelinesEnabled\n ): array {\n $operations = [];\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // - the resource name of the AssetGroup\n // - the resource name of the Asset\n // - the field_type of the Asset in this AssetGroup.\n //\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n // Links the previously created multiple text assets.\n\n // Links the headline assets.\n foreach ($headlineAssetResourceNames as $resourceName) {\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $resourceName,\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::HEADLINE\n ])\n ])\n ]);\n }\n // Links the description assets.\n foreach ($descriptionAssetResourceNames as $resourceName) {\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $resourceName,\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::DESCRIPTION\n ])\n ])\n ]);\n }\n\n // Creates and links the long headline text asset.\n $operations = array_merge($operations, self::createAndLinkTextAsset(\n $customerId,\n 'Travel the World',\n AssetFieldType::LONG_HEADLINE\n ));\n // Creates and links the business name text asset.\n $operations = array_merge($operations, self::createAndLinkBrandAssets(\n $customerId,\n $brandGuidelinesEnabled,\n 'Interplanetary Cruises',\n 'https://gaagl.page.link/1Crm',\n 'Logo Image'\n ));\n\n // Creates and links the image assets.\n\n // Creates and links the Marketing Image Asset.\n $operations = array_merge($operations, self::createAndLinkImageAsset(\n $customerId,\n 'https://gaagl.page.link/Eit5',\n AssetFieldType::MARKETING_IMAGE,\n 'Marketing Image'\n ));\n // Creates and links the Square Marketing Image Asset.\n $operations = array_merge($operations, self::createAndLinkImageAsset(\n $customerId,\n 'https://gaagl.page.link/bjYi',\n AssetFieldType::SQUARE_MARKETING_IMAGE,\n 'Square Marketing Image'\n ));\n\n // After being created the list must be sorted so that all asset operations come before all\n // the asset group asset operations, otherwise the API will reject the request.\n return self::sortAssetAndAssetGroupAssetOperations($operations);\n }\n\n /**\n * Creates a list of MutateOperations that create a new linked text asset.\n *\n * @param int $customerId the customer ID\n * @param string $text the text of the asset to be created\n * @param int $fieldType the field type of the new asset in the AssetGroupAsset\n * @return MutateOperation[] a list of MutateOperations that create a new linked text asset\n */\n private static function createAndLinkTextAsset(\n int $customerId,\n string $text,\n int $fieldType\n ): array {\n $operations = [];\n // Creates a new mutate operation that creates a text asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId),\n 'text_asset' => new TextAsset(['text' => $text])\n ])\n ])\n ]);\n\n // Creates an asset group asset to link the asset to the asset group.\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => ResourceNames::forAsset($customerId, self::$nextTempId),\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => $fieldType\n ])\n ])\n ]);\n self::$nextTempId--;\n\n return $operations;\n }\n\n /**\n * Creates a list of MutateOperations that create a new linked image asset.\n *\n * @param int $customerId the customer ID\n * @param string $url the URL of the image to be retrieved and put into an asset\n * @param int $fieldType the field type of the new asset in the AssetGroupAsset\n * @param string $assetName the asset name\n * @return MutateOperation[] a list of MutateOperations that create a new linked image asset\n */\n private static function createAndLinkImageAsset(\n int $customerId,\n string $url,\n int $fieldType,\n string $assetName\n ): array {\n $operations = [];\n // Creates a new mutate operation that creates an image asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId),\n // Provide a unique friendly name to identify your asset.\n // When there is an existing image asset with the same content but a different\n // name, the new name will be dropped silently.\n 'name' => $assetName,\n 'image_asset' => new ImageAsset(['data' => file_get_contents($url)])\n ])\n ])\n ]);\n\n // Creates an asset group asset to link the asset to the asset group.\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => ResourceNames::forAsset($customerId, self::$nextTempId),\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => $fieldType\n ])\n ])\n ]);\n self::$nextTempId--;\n\n return $operations;\n }\n\n /**\n * Creates a list of MutateOperations that create linked brand assets.\n *\n * @param int $customerId the customer ID\n * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand\n * guidelines\n * @param string $businessName the business name text to be put into an asset\n * @param string $logoUrl the URL of the logo to be retrieved and put into an asset\n * @param string $logoName the asset name of the logo\n * @return MutateOperation[] a list of MutateOperations that create a new linked text asset\n */\n private static function createAndLinkBrandAssets(\n int $customerId,\n bool $brandGuidelinesEnabled,\n string $businessName,\n string $logoUrl,\n string $logoName\n ): array {\n $operations = [];\n // Creates a new mutate operation that creates a text asset.\n $businessNameTempId = self::$nextTempId--;\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'resource_name' => ResourceNames::forAsset($customerId, $businessNameTempId),\n 'text_asset' => new TextAsset(['text' => $businessName])\n ])\n ])\n ]);\n\n $logoTempId = self::$nextTempId--;\n // Creates a new mutate operation that creates an image asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'resource_name' => ResourceNames::forAsset($customerId, $logoTempId),\n // Provide a unique friendly name to identify your asset.\n // When there is an existing image asset with the same content but a different\n // name, the new name will be dropped silently.\n 'name' => $logoName,\n 'image_asset' => new ImageAsset(['data' => file_get_contents($logoUrl)])\n ])\n ])\n ]);\n\n if ($brandGuidelinesEnabled) {\n // Creates a campaign asset to link the business name and logo assets to the campaign.\n $operations[] = new MutateOperation([\n 'campaign_asset_operation' => new CampaignAssetOperation([\n 'create' => new CampaignAsset([\n 'asset' => ResourceNames::forAsset($customerId, $businessNameTempId),\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::BUSINESS_NAME\n ])\n ])\n ]);\n $operations[] = new MutateOperation([\n 'campaign_asset_operation' => new CampaignAssetOperation([\n 'create' => new CampaignAsset([\n 'asset' => ResourceNames::forAsset($customerId, $logoTempId),\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::LOGO\n ])\n ])\n ]);\n } else {\n // Creates an asset group asset to link the business name and logo assets to the asset\n // group.\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => ResourceNames::forAsset($customerId, $businessNameTempId),\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::BUSINESS_NAME\n ])\n ])\n ]);\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => ResourceNames::forAsset($customerId, $logoTempId),\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::LOGO\n ])\n ])\n ]);\n }\n\n return $operations;\n }\n\n /**\n * Sorts a list of asset and asset group asset operations. This sorts the list such that all\n * asset operations precede all asset group asset operations. If asset group assets are created\n * before assets then an error will be returned by the API.\n *\n * @param MutateOperation[] $operations a list of asset and asset group asset mutate operations\n * @return MutateOperation[] a sorted list of asset and asset group asset mutate operations\n */\n private static function sortAssetAndAssetGroupAssetOperations(array $operations): array\n {\n usort(\n $operations,\n function (MutateOperation $operation1, MutateOperation $operation2) {\n if (!is_null($operation1->getAssetOperation())) {\n return -1;\n } elseif (!is_null($operation1->getAssetOperation())) {\n return 0;\n } else {\n return 1;\n }\n }\n );\n return $operations;\n }\n\n\n /**\n * Retrieves the list of customer conversion goals.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @return array list of dicts containing the category and origin of customer conversion goals\n */\n private static function getCustomerConversionGoals(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n ): array {\n $customerConversionGoals = [];\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n // Creates a query that retrieves all customer conversion goals.\n $query = 'SELECT customer_conversion_goal.category, customer_conversion_goal.origin ' .\n 'FROM customer_conversion_goal';\n // The number of conversion goals is typically less than 50 so we use a search request\n // instead of search stream.\n $response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n // Iterates over all rows in all pages and builds the list of conversion goals.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $customerConversionGoals[] = [\n 'category' => $googleAdsRow->getCustomerConversionGoal()->getCategory(),\n 'origin' => $googleAdsRow->getCustomerConversionGoal()->getOrigin()\n ];\n }\n\n return $customerConversionGoals;\n }\n\n /**\n * Creates a list of MutateOperations that override customer conversion goals.\n *\n * @param int $customerId the customer ID\n * @param array $customerConversionGoals the list of customer conversion goals that will be\n * overridden\n * @return MutateOperation[] a list of MutateOperations that update campaign conversion goals\n */\n private static function createConversionGoalOperations(\n int $customerId,\n array $customerConversionGoals\n ): array {\n $operations = [];\n\n // To override the customer conversion goals, we will change the biddability of each of the\n // customer conversion goals so that only the desired conversion goal is biddable in this\n // campaign.\n foreach ($customerConversionGoals as $customerConversionGoal) {\n $campaignConversionGoal = new CampaignConversionGoal([\n 'resource_name' => ResourceNames::forCampaignConversionGoal(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n ConversionActionCategory::name($customerConversionGoal['category']),\n ConversionOrigin::name($customerConversionGoal['origin'])\n )\n ]);\n // Changes the biddability for the campaign conversion goal.\n // Sets biddability to true for the desired (category, origin).\n // Sets biddability to false for all other conversion goals.\n // Note:\n // 1- It is assumed that this Conversion Action\n // (category=PURCHASE, origin=WEBSITE) exists in this account.\n // 2- More than one goal can be biddable if desired. This example\n // shows only one.\n if (\n $customerConversionGoal[\"category\"] === ConversionActionCategory::PURCHASE\n && $customerConversionGoal[\"origin\"] === ConversionOrigin::WEBSITE\n ) {\n $campaignConversionGoal->setBiddable(true);\n } else {\n $campaignConversionGoal->setBiddable(false);\n }\n\n $operations[] = new MutateOperation([\n 'campaign_conversion_goal_operation' => new CampaignConversionGoalOperation([\n 'update' => $campaignConversionGoal,\n // Sets the update mask on the operation. Here the update mask will be a list\n // of all the fields that were set on the update object.\n 'update_mask' => FieldMasks::allSetFieldsOf($campaignConversionGoal)\n ])\n ]);\n }\n\n return $operations;\n }\n\n /**\n * Prints the details of a MutateGoogleAdsResponse. Parses the \"response\" oneof field name and\n * uses it to extract the new entity's name and resource name.\n *\n * @param MutateGoogleAdsResponse $mutateGoogleAdsResponse the mutate Google Ads response\n */\n private static function printResponseDetails(\n MutateGoogleAdsResponse $mutateGoogleAdsResponse\n ): void {\n foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {\n /** @var MutateOperationResponse $response */\n $getter = Serializer::getGetter($response->getResponse());\n printf(\n \"Created a(n) %s with '%s'.%s\",\n preg_replace(\n '/Result$/',\n '',\n ucfirst(Serializer::toCamelCase($response->getResponse()))\n ),\n $response->$getter()->getResourceName(),\n PHP_EOL\n );\n }\n }\n}\n\nAddPerformanceMaxRetailCampaign::main();\nAddPerformanceMaxRetailCampaign.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2021 Google LLC\n#\n# 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.\n\"\"\"This example shows how to create a Performance Max retail campaign.\n\nThis will be created for \"All products\".\n\nFor more information about Performance Max retail campaigns, see\nhttps://developers.google.com/google-ads/api/docs/performance-max/retail\n\nPrerequisites:\n- You need to have access to a Merchant Center account. You can find\n instructions to create a Merchant Center account here:\n https://support.google.com/merchants/answer/188924.\n This account must be linked to your Google Ads account. The integration\n instructions can be found at:\n https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center\n- You need your Google Ads account to track conversions. The different ways\n to track conversions can be found here:\n https://support.google.com/google-ads/answer/1722054.\n- You must have at least one conversion action in the account. For\n more about conversion actions, see\n https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions\n\"\"\"\n\nimport argparse\nfrom datetime import datetime, timedelta\nimport logging\nimport sys\nfrom typing import Dict, List, Union\nfrom uuid import uuid4\n\nfrom google.api_core import protobuf_helpers\n\nfrom examples.utils.example_helpers import get_image_bytes_from_url\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.util import convert_snake_case_to_upper_case\nfrom google.ads.googleads.v24.enums.types.conversion_action_category import (\n ConversionActionCategoryEnum,\n)\nfrom google.ads.googleads.v24.enums.types.conversion_origin import (\n ConversionOriginEnum,\n)\nfrom google.ads.googleads.v24.enums.types.asset_field_type import (\n AssetFieldTypeEnum,\n)\nfrom google.ads.googleads.v24.resources.types.asset import Asset\nfrom google.ads.googleads.v24.resources.types.asset_group import AssetGroup\nfrom google.ads.googleads.v24.resources.types.asset_group_asset import (\n AssetGroupAsset,\n)\nfrom google.ads.googleads.v24.resources.types.asset_group_listing_group_filter import (\n AssetGroupListingGroupFilter,\n)\nfrom google.ads.googleads.v24.resources.types.campaign import Campaign\nfrom google.ads.googleads.v24.resources.types.campaign_asset import (\n CampaignAsset,\n)\nfrom google.ads.googleads.v24.resources.types.campaign_budget import (\n CampaignBudget,\n)\nfrom google.ads.googleads.v24.resources.types.campaign_conversion_goal import (\n CampaignConversionGoal,\n)\nfrom google.ads.googleads.v24.resources.types.campaign_criterion import (\n CampaignCriterion,\n)\nfrom google.ads.googleads.v24.services.services.asset_group_service import (\n AssetGroupServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.asset_service import (\n AssetServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.campaign_budget_service import (\n CampaignBudgetServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.campaign_conversion_goal_service import (\n CampaignConversionGoalServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.campaign_service import (\n CampaignServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.geo_target_constant_service import (\n GeoTargetConstantServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.google_ads_service import (\n GoogleAdsServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.google_ads_service import (\n MutateGoogleAdsResponse,\n MutateOperationResponse,\n SearchGoogleAdsRequest,\n SearchGoogleAdsResponse,\n)\nfrom google.ads.googleads.v24.services.types.google_ads_service import (\n MutateOperation,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\n#\n# See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n# for further details.\n#\n# These temporary IDs are fixed because they are used in multiple places.\n_BUDGET_TEMPORARY_ID: str = \"-1\"\n_PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID: str = \"-2\"\n_ASSET_GROUP_TEMPORARY_ID: str = \"-3\"\n\n# There are also entities that will be created in the same request but do not\n# need to be fixed temporary IDs because they are referenced only once.\n_next_temp_id: int = int(_ASSET_GROUP_TEMPORARY_ID) - 1\n\n\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n merchant_center_account_id: int,\n final_url: str,\n brand_guidelines_enabled: bool,\n) -> None:\n \"\"\"The main method that creates all necessary entities for the example.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n merchant_center_account_id: The Merchant Center account ID.\n final_url: the final URL.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n # This campaign will override the customer conversion goals.\n # Retrieve the current list of customer conversion goals.\n customer_conversion_goals: List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n ] = get_customer_conversion_goals(client, customer_id)\n\n # Performance Max campaigns require that repeated assets such as headlines\n # and descriptions be created before the campaign.\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n #\n # Create the headlines.\n headline_asset_resource_names: List[str] = create_multiple_text_assets(\n client,\n customer_id,\n [\n \"Travel\",\n \"Travel Reviews\",\n \"Book travel\",\n ],\n )\n # Create the descriptions.\n description_asset_resource_names: List[str] = create_multiple_text_assets(\n client,\n customer_id,\n [\n \"Take to the air!\",\n \"Fly to the sky!\",\n ],\n )\n\n # The below methods create and return MutateOperations that we later\n # provide to the GoogleAdsService.Mutate method in order to create the\n # entities in a single request. Since the entities for a Performance Max\n # campaign are closely tied to one-another, it's considered a best practice\n # to create them in a single Mutate request so they all complete\n # successfully or fail entirely, leaving no orphaned entities. See:\n # https://developers.google.com/google-ads/api/docs/mutating/overview\n campaign_budget_operation: MutateOperation = (\n create_campaign_budget_operation(\n client,\n customer_id,\n )\n )\n performance_max_campaign_operation: MutateOperation = (\n create_performance_max_campaign_operation(\n client,\n customer_id,\n merchant_center_account_id,\n brand_guidelines_enabled,\n )\n )\n campaign_criterion_operations: List[MutateOperation] = (\n create_campaign_criterion_operations(\n client,\n customer_id,\n )\n )\n asset_group_operation: MutateOperation = create_asset_group_operation(\n client, customer_id, final_url\n )\n listing_group_filter_operation: MutateOperation = (\n create_listing_group_filter_operation(client, customer_id)\n )\n asset_and_asset_group_asset_operations: List[MutateOperation] = (\n create_asset_and_asset_group_asset_operations(\n client,\n customer_id,\n headline_asset_resource_names,\n description_asset_resource_names,\n brand_guidelines_enabled,\n )\n )\n conversion_goal_operations: List[MutateOperation] = (\n create_conversion_goal_operations(\n client,\n customer_id,\n customer_conversion_goals,\n )\n )\n\n # Send the operations in a single Mutate request.\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id,\n mutate_operations=[\n # It's important to create these entities in this order because\n # they depend on each other.\n campaign_budget_operation,\n performance_max_campaign_operation,\n # Expand the list of multiple operations into the list of\n # other mutate operations.\n *campaign_criterion_operations,\n asset_group_operation,\n listing_group_filter_operation,\n *asset_and_asset_group_asset_operations,\n *conversion_goal_operations,\n ],\n )\n print_response_details(response)\n\n\ndef create_campaign_budget_operation(\n client: GoogleAdsClient,\n customer_id: str,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new CampaignBudget.\n\n A temporary ID will be assigned to this campaign budget so that it can be\n referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a MutateOperation that creates a CampaignBudget.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_budget_operation = mutate_operation.campaign_budget_operation\n campaign_budget: CampaignBudget = campaign_budget_operation.create\n campaign_budget.name = f\"Performance Max retail campaign budget #{uuid4()}\"\n # The budget period already defaults to DAILY.\n campaign_budget.amount_micros = 50000000\n campaign_budget.delivery_method = (\n client.enums.BudgetDeliveryMethodEnum.STANDARD\n )\n # A Performance Max campaign cannot use a shared campaign budget.\n campaign_budget.explicitly_shared = False\n\n # Set a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n campaign_budget_service: CampaignBudgetServiceClient = client.get_service(\n \"CampaignBudgetService\"\n )\n campaign_budget.resource_name = (\n campaign_budget_service.campaign_budget_path(\n customer_id, _BUDGET_TEMPORARY_ID\n )\n )\n\n return mutate_operation\n\n\ndef create_performance_max_campaign_operation(\n client: GoogleAdsClient,\n customer_id: str,\n merchant_center_account_id: int,\n brand_guidelines_enabled: bool,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new Performance Max campaign.\n\n A temporary ID will be assigned to this campaign so that it can\n be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n merchant_center_account_id: The Merchant Center account ID.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n\n Returns:\n a MutateOperation that creates a campaign.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign: Campaign = mutate_operation.campaign_operation.create\n campaign.name = f\"Performance Max retail campaign #{uuid4()}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.PERFORMANCE_MAX\n )\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Max Conversion Value are the only strategies supported\n # for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Max Conversion Value, see the support article:\n # http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n # campaign.maximize_conversion_value.target_roas = 3.5\n # For first time users, it's recommended not to set a target ROAS.\n # Although target ROAS is optional, you still need to define it\n # even if you do not want to use it.\n campaign.maximize_conversion_value.target_roas = None\n # Below is what you would use if you want to maximize conversions\n # campaign.maximize_conversions.target_cpa_micros = None\n # The target CPA is optional. This is the average amount that you would\n # like to spend per conversion action.\n\n # Set the shopping settings.\n campaign.shopping_setting.merchant_id = merchant_center_account_id\n\n # Optional: To use products only from a specific feed, set\n # shopping_setting.feed_label to the feed label used in Merchant Center.\n # See: https://support.google.com/merchants/answer/12453549.\n # Omitting the shopping_setting.feed_label field will use products from all\n # feeds.\n # campaign.shopping_setting.feed_label = \"INSERT_FEED_LABEL_HERE\"\n\n # Set if the campaign is enabled for brand guidelines. For more information\n # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n campaign.brand_guidelines_enabled = brand_guidelines_enabled\n\n # Assign the resource name with a temporary ID.\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n campaign.resource_name = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n # Set the budget using the given budget resource name.\n campaign_budget_service: CampaignBudgetServiceClient = client.get_service(\n \"CampaignBudgetService\"\n )\n campaign.campaign_budget = campaign_budget_service.campaign_budget_path(\n customer_id, _BUDGET_TEMPORARY_ID\n )\n\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n # Optional fields\n campaign.start_date_time = (datetime.now() + timedelta(1)).strftime(\n \"%Y%m%d 00:00:00\"\n )\n campaign.end_date_time = (datetime.now() + timedelta(365)).strftime(\n \"%Y%m%d 23:59:59\"\n )\n\n # Configures the optional opt-in/out status for asset automation settings.\n for asset_automation_type_enum in [\n client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_EXTRACTION,\n client.enums.AssetAutomationTypeEnum.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,\n client.enums.AssetAutomationTypeEnum.TEXT_ASSET_AUTOMATION,\n client.enums.AssetAutomationTypeEnum.GENERATE_ENHANCED_YOUTUBE_VIDEOS,\n client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_ENHANCEMENT,\n ]:\n asset_automattion_setting: Campaign.AssetAutomationSetting = (\n client.get_type(\"Campaign\").AssetAutomationSetting()\n )\n asset_automattion_setting.asset_automation_type = (\n asset_automation_type_enum\n )\n asset_automattion_setting.asset_automation_status = (\n client.enums.AssetAutomationStatusEnum.OPTED_IN\n )\n campaign.asset_automation_settings.append(asset_automattion_setting)\n\n return mutate_operation\n\n\ndef create_campaign_criterion_operations(\n client: GoogleAdsClient,\n customer_id: str,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create new campaign criteria.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a list of MutateOperations that create new campaign criteria.\n \"\"\"\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n geo_target_constant_service: GeoTargetConstantServiceClient = (\n client.get_service(\"GeoTargetConstantService\")\n )\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n operations: List[MutateOperation] = []\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n mutate_operation_nyc: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion_nyc: CampaignCriterion = (\n mutate_operation_nyc.campaign_criterion_operation.create\n )\n campaign_criterion_nyc.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n # Adds one positive location target for New York City (ID=1023191),\n # specifically adding the positive criteria before the negative one.\n campaign_criterion_nyc.location.geo_target_constant = (\n geo_target_constant_service.geo_target_constant_path(\"1023191\")\n )\n campaign_criterion_nyc.negative = False\n operations.append(mutate_operation_nyc)\n\n # Next add the negative target for Brooklyn (ID=1022762).\n mutate_operation_brooklyn: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n campaign_criterion_brooklyn: CampaignCriterion = (\n mutate_operation_brooklyn.campaign_criterion_operation.create\n )\n campaign_criterion_brooklyn.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n campaign_criterion_brooklyn.location.geo_target_constant = (\n geo_target_constant_service.geo_target_constant_path(\"1022762\")\n )\n campaign_criterion_brooklyn.negative = True\n operations.append(mutate_operation_brooklyn)\n\n # Set the LANGUAGE campaign criterion.\n mutate_operation_lang: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion_lang: CampaignCriterion = (\n mutate_operation_lang.campaign_criterion_operation.create\n )\n campaign_criterion_lang.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n campaign_criterion_lang.language.language_constant = (\n googleads_service.language_constant_path(\"1000\")\n ) # English\n operations.append(mutate_operation_lang)\n\n return operations\n\n\ndef create_multiple_text_assets(\n client: GoogleAdsClient, customer_id: str, texts: List[str]\n) -> List[str]:\n \"\"\"Creates multiple text assets and returns the list of resource names.\n\n These repeated assets must be created in a separate request prior to\n creating the campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n texts: a list of strings, each of which will be used to create a text\n asset.\n\n Returns:\n asset_resource_names: a list of asset resource names.\n \"\"\"\n # Here again we use the GoogleAdService to create multiple text\n # assets in a single request.\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n operations: List[MutateOperation] = []\n for text_content in texts:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = mutate_operation.asset_operation.create\n asset.text_asset.text = text_content\n operations.append(mutate_operation)\n\n # Send the operations in a single Mutate request.\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id,\n mutate_operations=operations,\n )\n asset_resource_names: List[str] = []\n for result in response.mutate_operation_responses:\n if result._pb.HasField(\"asset_result\"):\n asset_resource_names.append(result.asset_result.resource_name)\n print_response_details(response)\n return asset_resource_names\n\n\ndef create_asset_group_operation(\n client: GoogleAdsClient, customer_id: str, final_url: str\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new asset group.\n\n A temporary ID will be assigned to this asset group so that it can\n be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n final_url: the final URL.\n\n Returns:\n a MutateOperation that creates a new asset group.\n \"\"\"\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n # Create the AssetGroup.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group: AssetGroup = mutate_operation.asset_group_operation.create\n asset_group.name = f\"Performance Max retail asset group #{uuid4()}\"\n asset_group.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n asset_group.final_urls.append(final_url)\n asset_group.final_mobile_urls.append(final_url)\n asset_group.status = client.enums.AssetGroupStatusEnum.PAUSED\n asset_group.resource_name = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n\n return mutate_operation\n\n\ndef create_listing_group_filter_operation(\n client: GoogleAdsClient, customer_id: str\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new listing group filter.\n\n A temporary ID will be assigned to this listing group filter so that it\n can be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a MutateOperation that creates a new listing group filter.\n \"\"\"\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n # Creates a new ad group criterion containing the \"default\" listing\n # group (All products).\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_listing_group: AssetGroupListingGroupFilter = (\n mutate_operation.asset_group_listing_group_filter_operation.create\n )\n asset_group_listing_group.asset_group = (\n asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n )\n asset_group_listing_group.type_ = (\n client.enums.ListingGroupFilterTypeEnum.UNIT_INCLUDED\n )\n # Because this is a Performance Max campaign for retail, we need to specify\n # that this is in the shopping listing source.\n asset_group_listing_group.listing_source = (\n client.enums.ListingGroupFilterListingSourceEnum.SHOPPING\n )\n\n return mutate_operation\n\n\ndef create_asset_and_asset_group_asset_operations(\n client: GoogleAdsClient,\n customer_id: str,\n headline_asset_resource_names: List[str],\n description_asset_resource_names: List[str],\n brand_guidelines_enabled: bool,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create a new asset_group.\n\n A temporary ID will be assigned to this asset group so that it can\n be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n headline_asset_resource_names: a list of headline resource names.\n description_asset_resource_names: a list of description resource names.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n\n Returns:\n MutateOperations that create a new asset group and related assets.\n \"\"\"\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n\n operations: List[MutateOperation] = []\n\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # the resource name of the AssetGroup\n # the resource name of the Asset\n # the field_type of the Asset in this AssetGroup.\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n for resource_name in headline_asset_resource_names:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_asset: AssetGroupAsset = (\n mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.HEADLINE\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = resource_name\n operations.append(mutate_operation)\n\n # Link the description assets.\n for resource_name in description_asset_resource_names:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_asset: AssetGroupAsset = (\n mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = (\n client.enums.AssetFieldTypeEnum.DESCRIPTION\n )\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = resource_name\n operations.append(mutate_operation)\n\n # Create and link the long headline text asset.\n mutate_operations_long_headline: List[MutateOperation] = (\n create_and_link_text_asset(\n client,\n customer_id,\n \"Travel the World\",\n client.enums.AssetFieldTypeEnum.LONG_HEADLINE,\n )\n )\n operations.extend(mutate_operations_long_headline)\n\n # Create and link the business name and logo asset.\n mutate_operations_brand: List[MutateOperation] = (\n create_and_link_brand_assets(\n client,\n customer_id,\n brand_guidelines_enabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/1Crm\",\n \"Logo Image\",\n )\n )\n operations.extend(mutate_operations_brand)\n\n # Create and link the image assets.\n\n # Create and link the Marketing Image Asset.\n mutate_operations_marketing_image: List[MutateOperation] = (\n create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/Eit5\",\n client.enums.AssetFieldTypeEnum.MARKETING_IMAGE,\n \"Marketing Image\",\n )\n )\n operations.extend(mutate_operations_marketing_image)\n\n # Create and link the Square Marketing Image Asset.\n mutate_operations_square_image: List[MutateOperation] = (\n create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/bjYi\",\n client.enums.AssetFieldTypeEnum.SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\",\n )\n )\n operations.extend(mutate_operations_square_image)\n\n # After being created the list must be sorted so that all asset\n # operations come before all the asset group asset operations,\n # otherwise the API will reject the request.\n return sort_asset_and_asset_group_asset_operations(operations)\n\n\ndef create_and_link_text_asset(\n client: GoogleAdsClient,\n customer_id: str,\n text: str,\n field_type: AssetFieldTypeEnum.AssetFieldType,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create a new linked text asset.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n text: the text of the asset to be created.\n field_type: the field_type of the new asset in the AssetGroupAsset.\n\n Returns:\n MutateOperations that create a new linked text asset.\n \"\"\"\n global _next_temp_id\n operations: List[MutateOperation] = []\n asset_service: AssetServiceClient = client.get_service(\"AssetService\")\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n\n # Create the Text Asset.\n asset_temp_resource_name = asset_service.asset_path(\n customer_id, str(_next_temp_id)\n )\n mutate_operation_asset: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = mutate_operation_asset.asset_operation.create\n asset.resource_name = asset_temp_resource_name\n asset.text_asset.text = text\n operations.append(mutate_operation_asset)\n\n # Create an AssetGroupAsset to link the Asset to the AssetGroup.\n mutate_operation_group_asset: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n asset_group_asset: AssetGroupAsset = (\n mutate_operation_group_asset.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = field_type\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = asset_temp_resource_name\n operations.append(mutate_operation_group_asset)\n\n _next_temp_id -= 1\n return operations\n\n\ndef create_and_link_image_asset(\n client: GoogleAdsClient,\n customer_id: str,\n url: str,\n field_type: AssetFieldTypeEnum.AssetFieldType,\n asset_name: str,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create a new linked image asset.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n url: the url of the image to be retrieved and put into an asset.\n field_type: the field_type of the new asset in the AssetGroupAsset.\n asset_name: the asset name.\n\n Returns:\n MutateOperations that create a new linked image asset.\n \"\"\"\n global _next_temp_id\n operations: List[MutateOperation] = []\n asset_service: AssetServiceClient = client.get_service(\"AssetService\")\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n\n # Create the Image Asset.\n asset_temp_resource_name = asset_service.asset_path(\n customer_id, str(_next_temp_id)\n )\n mutate_operation_asset: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = mutate_operation_asset.asset_operation.create\n asset.resource_name = asset_temp_resource_name\n asset.type_ = client.enums.AssetTypeEnum.IMAGE\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n asset.name = asset_name\n asset.image_asset.data = get_image_bytes_from_url(url)\n operations.append(mutate_operation_asset)\n\n # Create an AssetGroupAsset to link the Asset to the AssetGroup.\n mutate_operation_group_asset: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n asset_group_asset: AssetGroupAsset = (\n mutate_operation_group_asset.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = field_type\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = asset_temp_resource_name\n operations.append(mutate_operation_group_asset)\n\n _next_temp_id -= 1\n return operations\n\n\ndef sort_asset_and_asset_group_asset_operations(\n operations: List[MutateOperation],\n) -> List[MutateOperation]:\n \"\"\"Sorts a list of asset and asset group asset operations.\n\n This sorts the list such that all asset operations precede\n all asset group asset operations. If asset group assets are\n created before assets then an error will be returned by\n the API.\n\n Args:\n operations: a list of asset and asset group asset operations.\n\n Returns:\n a sorted list of asset and asset group asset operations.\n \"\"\"\n\n def sorter(operation: MutateOperation) -> bool:\n \"\"\"Determines whether the operation creates an asset group asset.\n\n Args:\n operation: a MutateOperation instance.\n\n Returns:\n True if the MutateOperation creates an asset group asset.\n \"\"\"\n # Check if the oneof field 'asset_group_asset_operation' is set.\n return (\n operation.asset_group_asset_operation\n != type(operation.asset_group_asset_operation)()\n )\n\n return sorted(operations, key=sorter)\n\n\ndef get_customer_conversion_goals(\n client: GoogleAdsClient, customer_id: str\n) -> List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n]:\n \"\"\"Retrieves the list of customer conversion goals.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a list of dicts containing the category and origin of customer\n conversion goals.\n \"\"\"\n ga_service: GoogleAdsServiceClient = client.get_service(\"GoogleAdsService\")\n customer_conversion_goals: List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n ] = []\n query: str = \"\"\"\n SELECT\n customer_conversion_goal.category,\n customer_conversion_goal.origin\n FROM customer_conversion_goal\n \"\"\"\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService.search instead of search_stream.\n search_request: SearchGoogleAdsRequest = client.get_type(\n \"SearchGoogleAdsRequest\"\n )\n search_request.customer_id = customer_id\n search_request.query = query\n results: SearchGoogleAdsResponse = ga_service.search(request=search_request)\n\n # Iterate over the results and build the list of conversion goals.\n for row in results:\n customer_conversion_goals.append(\n {\n \"category\": row.customer_conversion_goal.category,\n \"origin\": row.customer_conversion_goal.origin,\n }\n )\n return customer_conversion_goals\n\n\ndef create_conversion_goal_operations(\n client: GoogleAdsClient,\n customer_id: str,\n customer_conversion_goals: List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n ],\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that override customer conversion goals.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n customer_conversion_goals: the list of customer conversion goals that\n will be overridden.\n\n Returns:\n MutateOperations that update campaign conversion goals.\n \"\"\"\n campaign_conversion_goal_service: CampaignConversionGoalServiceClient = (\n client.get_service(\"CampaignConversionGoalService\")\n )\n operations: List[MutateOperation] = []\n\n # To override the customer conversion goals, we will change the\n # biddability of each of the customer conversion goals so that only\n # the desired conversion goal is biddable in this campaign.\n for customer_goal_dict in customer_conversion_goals:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_conversion_goal: CampaignConversionGoal = (\n mutate_operation.campaign_conversion_goal_operation.update\n )\n\n category_enum_value: (\n ConversionActionCategoryEnum.ConversionActionCategory\n ) = customer_goal_dict[\"category\"]\n origin_enum_value: ConversionOriginEnum.ConversionOrigin = (\n customer_goal_dict[\"origin\"]\n )\n\n campaign_conversion_goal.resource_name = (\n campaign_conversion_goal_service.campaign_conversion_goal_path(\n customer_id,\n _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n category_enum_value.name,\n origin_enum_value.name,\n )\n )\n # Change the biddability for the campaign conversion goal.\n # Set biddability to True for the desired (category, origin).\n # Set biddability to False for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n if (\n category_enum_value\n == client.enums.ConversionActionCategoryEnum.PURCHASE\n and origin_enum_value == client.enums.ConversionOriginEnum.WEBSITE\n ):\n biddable = True\n else:\n biddable = False\n campaign_conversion_goal.biddable = biddable\n field_mask = protobuf_helpers.field_mask(\n None, campaign_conversion_goal._pb\n )\n client.copy_from(\n mutate_operation.campaign_conversion_goal_operation.update_mask,\n field_mask,\n )\n operations.append(mutate_operation)\n\n return operations\n\n\ndef create_and_link_brand_assets(\n client: GoogleAdsClient,\n customer_id: str,\n brand_guidelines_enabled: bool,\n business_name: str,\n logo_url: str,\n logo_name: str,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create linked brand assets.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n business_name: the business name text to be put into an asset.\n logo_url: the url of the logo to be retrieved and put into an asset.\n logo_name: the asset name of the logo.\n\n Returns:\n MutateOperations that create linked brand assets.\n \"\"\"\n global _next_temp_id\n operations: List[MutateOperation] = []\n asset_service: AssetServiceClient = client.get_service(\"AssetService\")\n\n # Create the Text Asset.\n text_asset_temp_id: int = _next_temp_id\n _next_temp_id -= 1\n text_asset_resource_name = asset_service.asset_path(\n customer_id, str(text_asset_temp_id)\n )\n\n text_mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n text_asset_obj: Asset = text_mutate_operation.asset_operation.create\n text_asset_obj.resource_name = text_asset_resource_name\n text_asset_obj.text_asset.text = business_name\n operations.append(text_mutate_operation)\n\n # Create the Image Asset.\n image_asset_temp_id: int = _next_temp_id\n _next_temp_id -= 1\n image_asset_resource_name = asset_service.asset_path(\n customer_id, str(image_asset_temp_id)\n )\n\n image_mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n image_asset_obj: Asset = image_mutate_operation.asset_operation.create\n image_asset_obj.resource_name = image_asset_resource_name\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n image_asset_obj.name = logo_name\n image_asset_obj.type_ = client.enums.AssetTypeEnum.IMAGE\n image_asset_obj.image_asset.data = get_image_bytes_from_url(logo_url)\n operations.append(image_mutate_operation)\n\n if brand_guidelines_enabled:\n # Create CampaignAsset resources to link the Asset resources to the Campaign.\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n business_name_ca_mutate_op: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n business_name_campaign_asset: CampaignAsset = (\n business_name_ca_mutate_op.campaign_asset_operation.create\n )\n business_name_campaign_asset.field_type = (\n client.enums.AssetFieldTypeEnum.BUSINESS_NAME\n )\n business_name_campaign_asset.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n business_name_campaign_asset.asset = text_asset_resource_name\n operations.append(business_name_ca_mutate_op)\n\n logo_ca_mutate_op: MutateOperation = client.get_type(\"MutateOperation\")\n logo_campaign_asset: CampaignAsset = (\n logo_ca_mutate_op.campaign_asset_operation.create\n )\n logo_campaign_asset.field_type = client.enums.AssetFieldTypeEnum.LOGO\n logo_campaign_asset.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n logo_campaign_asset.asset = image_asset_resource_name\n operations.append(logo_ca_mutate_op)\n\n else:\n # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n\n business_name_aga_mutate_op: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n business_name_asset_group_asset: AssetGroupAsset = (\n business_name_aga_mutate_op.asset_group_asset_operation.create\n )\n business_name_asset_group_asset.field_type = (\n client.enums.AssetFieldTypeEnum.BUSINESS_NAME\n )\n business_name_asset_group_asset.asset_group = (\n asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n )\n business_name_asset_group_asset.asset = text_asset_resource_name\n operations.append(business_name_aga_mutate_op)\n\n logo_aga_mutate_op: MutateOperation = client.get_type(\"MutateOperation\")\n logo_asset_group_asset: AssetGroupAsset = (\n logo_aga_mutate_op.asset_group_asset_operation.create\n )\n logo_asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.LOGO\n logo_asset_group_asset.asset_group = (\n asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n )\n logo_asset_group_asset.asset = image_asset_resource_name\n operations.append(logo_aga_mutate_op)\n\n return operations\n\n\ndef print_response_details(response: MutateGoogleAdsResponse) -> None:\n \"\"\"Prints the details of a MutateGoogleAdsResponse.\n\n Parses the \"response\" oneof field name and uses it to extract the new\n entity's name and resource name.\n\n Args:\n response: a MutateGoogleAdsResponse object.\n \"\"\"\n # Parse the Mutate response to print details about the entities that\n # were created by the request.\n suffix: str = \"_result\"\n for result_item in response.mutate_operation_responses:\n # Ensure result_item is MutateOperationResponse, not just Any\n result_pb: MutateOperationResponse = result_item\n for field_descriptor, value in result_pb._pb.ListFields():\n field_name_str: str = field_descriptor.name\n if field_name_str.endswith(suffix):\n name = field_name_str[: -len(suffix)]\n else:\n name = field_name_str\n print(\n f\"Created a(n) {convert_snake_case_to_upper_case(name)} with \"\n f\"{str(value).strip()}.\"\n )\n\n\n\nif __name__ == \"__main__\":\n parser = argparse.ArgumentParser(\n description=(\"Creates a Performance Max retail campaign.\")\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-m\",\n \"--merchant_center_account_id\",\n type=int,\n required=True,\n help=\"The Merchant Center account ID.\",\n )\n parser.add_argument(\n \"-u\",\n \"--final_url\",\n type=str,\n required=False,\n default=\"http://www.example.com\",\n help=\"The final URL for the asset group of the campaign.\",\n )\n parser.add_argument(\n \"-b\",\n \"--brand_guidelines_enabled\",\n type=bool,\n default=True,\n help=(\n \"A boolean value indicating if the created campaign is enabled \"\n \"for brand guidelines.\"\n ),\n )\n\n args: argparse.Namespace = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(\n googleads_client,\n args.customer_id,\n args.merchant_center_account_id,\n args.final_url,\n args.brand_guidelines_enabled,\n )\n except GoogleAdsException as ex:\n print(\n f'Request with ID \"{ex.request_id}\" failed with status '\n f'\"{ex.error.code().name}\" and includes the following errors:'\n )\n for error in ex.failure.errors:\n print(f'\\tError with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nadd_performance_max_retail_campaign.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2021 Google LLC\n#\n# 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.\n#\n# This example shows how to create a Performance Max retail campaign.\n#\n# This will be created for \"All products\".\n#\n# For more information about Performance Max retail campaigns, see\n# https://developers.google.com/google-ads/api/docs/performance-max/retail\n#\n# Prerequisites:\n# - You need to have access to a Merchant Center account. You can find\n# instructions to create a Merchant Center account here:\n# https://support.google.com/merchants/answer/188924.\n# This account must be linked to your Google Ads account. The integration\n# instructions can be found at:\n# https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center\n# - You need your Google Ads account to track conversions. The different ways\n# to track conversions can be found here:\n# https://support.google.com/google-ads/answer/1722054.\n# - You must have at least one conversion action in the account. For\n# more about conversion actions, see\n# https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions\n\n\nrequire 'optparse'\nrequire 'date'\nrequire 'open-uri'\nrequire 'google/ads/google_ads'\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\n#\n# See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n# for further details.\n#\n# These temporary IDs are fixed because they are used in multiple places.\nBUDGET_TEMPORARY_ID = \"-1\"\nPERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = \"-2\"\nASSET_GROUP_TEMPORARY_ID = \"-3\"\n\n# There are also entities that will be created in the same request but do not\n# need to be fixed temporary IDs because they are referenced only once.\ndef next_temp_id\n @id ||= ASSET_GROUP_TEMPORARY_ID.to_i\n @id -= 1\nend\n\ndef add_performance_max_retail_campaign(\n customer_id,\n merchant_center_account_id,\n final_url,\n brand_guidelines_enabled)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # This campaign will override the customer conversion goals.\n # Retrieve the current list of customer conversion goals.\n customer_conversion_goals = _get_customer_conversion_goals(\n client, customer_id)\n\n # Performance Max campaigns require that repeated assets such as headlines\n # and descriptions be created before the campaign.\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n #\n # Create the headlines.\n headline_asset_resource_names = create_multiple_text_assets(\n client,\n customer_id,\n [\n \"Travel\",\n \"Travel Reviews\",\n \"Book travel\",\n ])\n # Create the descriptions.\n description_asset_resource_names = create_multiple_text_assets(\n client,\n customer_id,\n [\n \"Take to the air!\",\n \"Fly to the sky!\",\n ])\n\n # The below methods create and return MutateOperations that we later\n # provide to the GoogleAdsService.Mutate method in order to create the\n # entities in a single request. Since the entities for a Performance Max\n # campaign are closely tied to one-another, it's considered a best practice\n # to create them in a single Mutate request so they all complete\n # successfully or fail entirely, leaving no orphaned entities. See:\n # https://developers.google.com/google-ads/api/docs/mutating/overview\n campaign_budget_operation = create_campaign_budget_operation(\n client,\n customer_id,\n )\n performance_max_campaign_operation = create_performance_max_campaign_operation(\n client,\n customer_id,\n merchant_center_account_id,\n brand_guidelines_enabled,\n )\n campaign_criterion_operations = create_campaign_criterion_operations(\n client,\n customer_id,\n )\n asset_group_operation = create_asset_group_operation(\n client,\n customer_id,\n final_url,\n )\n listing_group_filter_operation = create_listing_group_filter_operation(\n client,\n customer_id,\n )\n asset_and_asset_group_asset_operations = create_asset_and_asset_group_asset_operations(\n client,\n customer_id,\n headline_asset_resource_names,\n description_asset_resource_names,\n brand_guidelines_enabled,\n )\n conversion_goal_operations = create_conversion_goal_operations(\n client,\n customer_id,\n customer_conversion_goals,\n )\n\n # Send the operations in a single Mutate request.\n response = client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: [\n # It's important to create these entities in this order because\n # they depend on each other.\n campaign_budget_operation,\n performance_max_campaign_operation,\n # Expand the list of multiple operations into the list of\n # other mutate operations\n campaign_criterion_operations,\n asset_group_operation,\n listing_group_filter_operation,\n asset_and_asset_group_asset_operations,\n conversion_goal_operations,\n ].flatten)\n\n print_response_details(response)\nend\n\n# Creates a MutateOperation that creates a new CampaignBudget.\n#\n# A temporary ID will be assigned to this campaign budget so that it can be\n# referenced by other objects being created in the same Mutate request.\ndef create_campaign_budget_operation(client, customer_id)\n client.operation.mutate do |m|\n m.campaign_budget_operation = client.operation.create_resource.campaign_budget do |cb|\n cb.name = \"Performance Max campaign budget #{SecureRandom.uuid}\"\n # The budget period already defaults to DAILY.\n cb.amount_micros = 50_000_000\n cb.delivery_method = :STANDARD\n # A Performance Max campaign cannot use a shared campaign budget.\n cb.explicitly_shared = false\n\n # Set a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n cb.resource_name = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n end\n end\n end\n\n# Creates a MutateOperation that creates a new Performance Max campaign.\n#\n# A temporary ID will be assigned to this campaign so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_performance_max_campaign_operation(\n client,\n customer_id,\n merchant_center_account_id,\n brand_guidelines_enabled)\n client.operation.mutate do |m|\n m.campaign_operation = client.operation.create_resource.campaign do |c|\n c.name = \"Performance Max retail campaign #{SecureRandom.uuid}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n c.status = :PAUSED\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n c.advertising_channel_type = :PERFORMANCE_MAX\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n c.bidding_strategy_type = :MAXIMIZE_CONVERSION_VALUE\n c.maximize_conversion_value = client.resource.maximize_conversion_value do |mcv|\n mcv.target_roas = 3.5\n end\n\n # Set the shopping settings.\n c.shopping_setting = client.resource.shopping_setting do |ss|\n ss.merchant_id = merchant_center_account_id\n # Optional: To use products only from a specific feed, set feed_label\n # to the feed label used in Merchant Center.\n # See: https://support.google.com/merchants/answer/12453549.\n # Omitting the feed_label field will use products from all feeds.\n # feed_label = \"INSERT_FEED_LABEL_HERE\"\n end\n\n # Configures the optional opt-in/out status for asset automation settings.\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_IMAGE_EXTRACTION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :TEXT_ASSET_AUTOMATION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_ENHANCED_YOUTUBE_VIDEOS\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_IMAGE_ENHANCEMENT\n aas.asset_automation_status = :OPTED_IN\n end\n\n # Set if the campaign is enabled for brand guidelines. For more\n # information on brand guidelines, see\n # https://support.google.com/google-ads/answer/14934472.\n c.brand_guidelines_enabled = brand_guidelines_enabled\n\n # Assign the resource name with a temporary ID.\n c.resource_name = client.path.campaign(customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n # Set the budget using the given budget resource name.\n c.campaign_budget = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n\n # Declare whether or not this campaign serves political ads targeting the EU\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n # Optional fields\n c.start_date_time = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d %H:%M:%S')\n c.end_date_time = DateTime.parse(Date.today.next_year.to_s).strftime('%Y%m%d %H:%M:%S')\n end\n end\n end\n\n# Creates a list of MutateOperations that create new campaign criteria.\ndef create_campaign_criterion_operations(client, customer_id)\n operations = []\n\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n # Adds one positive location target for New York City (ID=1023191),\n # specifically adding the positive criteria before the negative one.\n cc.location = client.resource.location_info do |li|\n li.geo_target_constant = client.path.geo_target_constant(\"1023191\")\n end\n cc.negative = false\n end\n end\n\n # Next add the negative target for Brooklyn (ID=1022762).\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n cc.location = client.resource.location_info do |li|\n li.geo_target_constant = client.path.geo_target_constant(\"1022762\")\n end\n cc.negative = true\n end\n end\n\n # Set the LANGUAGE campaign criterion.\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n cc.language = client.resource.language_info do |li|\n li.language_constant = client.path.language_constant(\"1000\") # English\n end\n end\n end\n\n operations\nend\n\n# Creates multiple text assets and returns the list of resource names.\n# These repeated assets must be created in a separate request prior to creating\n# the campaign.\ndef create_multiple_text_assets(client, customer_id, texts)\n operations = texts.map do |text|\n client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |asset|\n asset.text_asset = client.resource.text_asset do |text_asset|\n text_asset.text = text\n end\n end\n end\n end\n\n # Send the operations in a single Mutate request.\n response = client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: operations,\n )\n\n asset_resource_names = []\n response.mutate_operation_responses.each do |result|\n if result.asset_result\n asset_resource_names.append(result.asset_result.resource_name)\n end\n end\n print_response_details(response)\n asset_resource_names\nend\n\n# Creates a MutateOperation that creates a new asset_group.\n#\n# A temporary ID will be assigned to this asset group so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_asset_group_operation(\n client,\n customer_id,\n final_url)\n\n # Create the AssetGroup\n client.operation.mutate do |m|\n m.asset_group_operation = client.operation.create_resource.asset_group do |ag|\n ag.name = \"Performance Max retail asset group #{SecureRandom.uuid}\"\n ag.campaign = client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n ag.final_urls << final_url\n ag.final_mobile_urls << final_url\n ag.status = :PAUSED\n ag.resource_name = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n end\n end\nend\n\n# Creates a MutateOperation that creates a new listing group filter.\n# A temporary ID will be assigned to this listing group filter so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_listing_group_filter_operation(client, customer_id)\n client.operation.mutate do |m|\n m.asset_group_listing_group_filter_operation =\n client.operation.create_resource.asset_group_listing_group_filter do |aglg|\n aglg.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n aglg.type = :UNIT_INCLUDED\n # Because this is a Performance Max campaign for retail, we need to\n # specify that this is in the shopping listing source.\n aglg.listing_source = :SHOPPING\n end\n end\nend\n\n# Creates a list of MutateOperations that create a new asset_group.\n# A temporary ID will be assigned to this asset group so that it can be\n# referenced by other objects being created in the same Mutate request.\ndef create_asset_and_asset_group_asset_operations(\n client,\n customer_id,\n headline_asset_resource_names,\n description_asset_resource_names,\n brand_guidelines_enabled)\n operations = []\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n #\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # the resource name of the AssetGroup\n # the resource name of the Asset\n # the field_type of the Asset in this AssetGroup.\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n headline_asset_resource_names.each do |resource_name|\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource\n .asset_group_asset do |aga|\n aga.field_type = :HEADLINE\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n aga.asset = resource_name\n end\n end\n end\n\n # Link the description assets.\n description_asset_resource_names.each do |resource_name|\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource\n .asset_group_asset do |aga|\n aga.field_type = :DESCRIPTION\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n aga.asset = resource_name\n end\n end\n end\n\n # Create and link the long headline text asset.\n operations += create_and_link_text_asset(\n client,\n customer_id,\n \"Travel the World\",\n :LONG_HEADLINE)\n\n # Create and link the business name and logo asset.\n operations += create_and_link_brand_assets(\n client,\n customer_id,\n brand_guidelines_enabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/1Crm\",\n \"Logo Image\")\n\n # Create and link the image assets.\n\n # Create and link the Marketing Image Asset.\n operations += create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/Eit5\",\n :MARKETING_IMAGE,\n \"Marketing Image\")\n\n # Create and link the Square Marketing Image Asset.\n operations += create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/bjYi\",\n :SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\")\n\n # After being created the list must be sorted so that all asset\n # operations come before all the asset group asset operations,\n # otherwise the API will reject the request.\n sort_asset_and_asset_group_asset_operations(operations)\nend\n\n# Creates a list of MutateOperations that create a new linked text asset.\ndef create_and_link_text_asset(client, customer_id, text, field_type)\n operations = []\n temp_id = next_temp_id\n\n # Create the Text Asset.\n operations << client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, temp_id)\n a.text_asset = client.resource.text_asset do |text_asset|\n text_asset.text = text\n end\n end\n end\n\n # Create an AssetGroupAsset to link the Asset to the AssetGroup.\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource\n .asset_group_asset do |aga|\n aga.field_type = field_type\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n aga.asset = client.path.asset(customer_id, temp_id)\n end\n end\n\n operations\nend\n\n# Creates a list of MutateOperations that create a new linked image asset.\ndef create_and_link_image_asset(client, customer_id, url, field_type, asset_name)\n operations = []\n temp_id = next_temp_id\n\n # Create the Image Asset.\n operations << client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, temp_id)\n a.type = :IMAGE\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n a.name = asset_name\n a.image_asset = client.resource.image_asset do |image_asset|\n image_asset.data = get_image_bytes(url)\n end\n end\n end\n\n # Create an AssetGroupAsset to link the Asset to the AssetGroup.\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource\n .asset_group_asset do |aga|\n aga.field_type = field_type\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n aga.asset = client.path.asset(customer_id, temp_id)\n end\n end\n\n operations\nend\n\n# Sorts a list of asset and asset group asset operations. This sorts the list\n# such that all asset operations precede all asset group asset operations. If\n# asset group assets are created before assets then an error will be returned\n# by the API.\ndef sort_asset_and_asset_group_asset_operations(operations)\n operations.sort_by do |operation|\n if operation.asset_group_asset_operation\n 1\n else\n 0\n end\n end\nend\n\ndef _get_customer_conversion_goals(client, customer_id)\n query = <<~EOD\n SELECT\n customer_conversion_goal.category,\n customer_conversion_goal.origin\n FROM customer_conversion_goal\n EOD\n\n customer_conversion_goals = []\n\n ga_service = client.service.google_ads\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService.search instead of search_stream.\n response = ga_service.search(\n customer_id: customer_id,\n query: query,\n )\n\n # Iterate over the results and build the list of conversion goals.\n response.each do |row|\n customer_conversion_goals << {\n \"category\" => row.customer_conversion_goal.category,\n \"origin\" => row.customer_conversion_goal.origin\n }\n end\n\n customer_conversion_goals\nend\n\ndef create_conversion_goal_operations(client, customer_id, customer_conversion_goals)\n campaign_conversion_goal_service = client.service.campaign_conversion_goal\n\n operations = []\n\n # To override the customer conversion goals, we will change the\n # biddability of each of the customer conversion goals so that only\n # the desired conversion goal is biddable in this campaign.\n customer_conversion_goals.each do |customer_conversion_goal|\n operations << client.operation.mutate do |m|\n m.campaign_conversion_goal_operation = client.operation.campaign_conversion_goal do |op|\n op.update = client.resource.campaign_conversion_goal do |ccg|\n ccg.resource_name = client.path.campaign_conversion_goal(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n customer_conversion_goal[\"category\"].to_s,\n customer_conversion_goal[\"origin\"].to_s)\n # Change the biddability for the campaign conversion goal.\n # Set biddability to True for the desired (category, origin).\n # Set biddability to False for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n ccg.biddable = (customer_conversion_goal[\"category\"] == :PURCHASE &&\n customer_conversion_goal[\"origin\"] == :WEBSITE)\n end\n op.update_mask = Google::Ads::GoogleAds::FieldMaskUtil.all_set_fields_of(op.update)\n end\n end\n end\n\n operations\nend\n\n# Creates a list of MutateOperations that create linked brand assets.\ndef create_and_link_brand_assets(\n client,\n customer_id,\n brand_guidelines_enabled,\n business_name,\n logo_url,\n logo_name)\n operations = []\n\n # Create the Text Asset.\n text_asset_temp_id = next_temp_id\n operations << client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, text_asset_temp_id)\n a.text_asset = client.resource.text_asset do |text_asset|\n text_asset.text = business_name\n end\n end\n end\n\n # Create the Image Asset.\n image_asset_temp_id = next_temp_id\n operations << client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, image_asset_temp_id)\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n a.name = logo_name\n a.type = :IMAGE\n a.image_asset = client.resource.image_asset do |image_asset|\n image_asset.data = get_image_bytes(logo_url)\n end\n end\n end\n\n if brand_guidelines_enabled\n # Create CampaignAsset resources to link the Asset resources to the Campaign.\n operations << client.operation.mutate do |m|\n m.campaign_asset_operation = client.operation.create_resource.\n campaign_asset do |ca|\n ca.field_type = :BUSINESS_NAME\n ca.campaign = client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n )\n ca.asset = client.path.asset(customer_id, text_asset_temp_id)\n end\n end\n\n operations << client.operation.mutate do |m|\n m.campaign_asset_operation = client.operation.create_resource.\n campaign_asset do |ca|\n ca.field_type = :LOGO\n ca.campaign = client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n )\n ca.asset = client.path.asset(customer_id, image_asset_temp_id)\n end\n end\n else\n # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource.\n asset_group_asset do |aga|\n aga.field_type = :BUSINESS_NAME\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n aga.asset = client.path.asset(customer_id, text_asset_temp_id)\n end\n end\n\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource.\n asset_group_asset do |aga|\n aga.field_type = :LOGO\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n aga.asset = client.path.asset(customer_id, image_asset_temp_id)\n end\n end\n end\n\n operations\nend\n\n# Loads image data from a URL.\ndef get_image_bytes(url)\n URI.open(url).read\n end\n\n # Prints the details of a MutateGoogleAdsResponse.\n def print_response_details(response)\n # Parse the mutate response to print details about the entities that\n # were created by the request.\n suffix = \"_result\"\n response.mutate_operation_responses.each do |result|\n result.to_h.select {|k, v| v }.each do |name, value|\n if name.to_s.end_with?(suffix)\n name = name.to_s.delete_suffix(suffix)\n end\n\n puts \"Created a(n) #{::Google::Ads::GoogleAds::Utils.camelize(name)} \" \\\n \"with #{value.to_s.strip}.\"\n end\n end\n end\n\nif __FILE__ == $0\n options = {}\n\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:merchant_center_account_id] = 'INSERT_MERCHANT_CENTER_ACCOUNT_ID_HERE'\n options[:final_url] = 'INSERT_FINAL_URL_HERE'\n options[:brand_guidelines_enabled] = true\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-m', '--merchant-center-account-id MERCHANT-CENTER-ACCOUNT-ID',\n Integer, 'Merchant Center Account ID') do |v|\n options[:merchant_center_account_id] = v\n end\n\n opts.on('-f', '--final-url FINAL-URL', String, 'Final URL') do |v|\n options[:final_url] = v\n end\n\n opts.on('-B', '--brand-guidelines-enabled', 'Enable brand guidelines (optional)') do\n options[:brand_guidelines_enabled] = true\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n add_performance_max_retail_campaign(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options.fetch(:merchant_center_account_id),\n options.fetch(:final_url),\n options[:brand_guidelines_enabled])\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\n end\nadd_performance_max_retail_campaign.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2021, Google LLC\n#\n# 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# http://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.\n#\n# This example shows how to create a Performance Max retail campaign.\n#\n# This will be created for \"All products\".\n#\n# For more information about Performance Max retail campaigns, see\n# https://developers.google.com/google-ads/api/docs/performance-max/retail.\n#\n# Prerequisites:\n# - You need to have access to a Merchant Center account. You can find\n# instructions to create a Merchant Center account here:\n# https://support.google.com/merchants/answer/188924.\n# This account must be linked to your Google Ads account. The integration\n# instructions can be found at:\n# https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center.\n# - You need your Google Ads account to track conversions. The different ways\n# to track conversions can be found here:\n# https://support.google.com/google-ads/answer/1722054.\n# - You must have at least one conversion action in the account. For more about\n# conversion actions, see\n# https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::Utils::MediaUtils;\nuse Google::Ads::GoogleAds::Utils::FieldMasks;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignBudget;\nuse Google::Ads::GoogleAds::V25::Resources::Campaign;\nuse Google::Ads::GoogleAds::V25::Resources::ShoppingSetting;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignCriterion;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignAsset;\nuse Google::Ads::GoogleAds::V25::Resources::Asset;\nuse Google::Ads::GoogleAds::V25::Resources::AssetAutomationSetting;\nuse Google::Ads::GoogleAds::V25::Resources::AssetGroup;\nuse Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignConversionGoal;\nuse Google::Ads::GoogleAds::V25::Resources::AssetGroupListingGroupFilter;\nuse Google::Ads::GoogleAds::V25::Common::MaximizeConversionValue;\nuse Google::Ads::GoogleAds::V25::Common::LocationInfo;\nuse Google::Ads::GoogleAds::V25::Common::LanguageInfo;\nuse Google::Ads::GoogleAds::V25::Common::TextAsset;\nuse Google::Ads::GoogleAds::V25::Common::ImageAsset;\nuse Google::Ads::GoogleAds::V25::Enums::BudgetDeliveryMethodEnum qw(STANDARD);\nuse Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum;\nuse Google::Ads::GoogleAds::V25::Enums::AdvertisingChannelTypeEnum\n qw(PERFORMANCE_MAX);\nuse Google::Ads::GoogleAds::V25::Enums::AssetAutomationStatusEnum qw(OPTED_IN);\nuse Google::Ads::GoogleAds::V25::Enums::AssetAutomationTypeEnum\n qw(GENERATE_IMAGE_EXTRACTION FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION TEXT_ASSET_AUTOMATION GENERATE_ENHANCED_YOUTUBE_VIDEOS GENERATE_IMAGE_ENHANCEMENT);\nuse Google::Ads::GoogleAds::V25::Enums::AssetGroupStatusEnum;\nuse Google::Ads::GoogleAds::V25::Enums::AssetFieldTypeEnum\n qw(HEADLINE DESCRIPTION LONG_HEADLINE BUSINESS_NAME LOGO MARKETING_IMAGE SQUARE_MARKETING_IMAGE);\nuse Google::Ads::GoogleAds::V25::Enums::ConversionActionCategoryEnum\n qw(PURCHASE);\nuse Google::Ads::GoogleAds::V25::Enums::ConversionOriginEnum qw(WEBSITE);\nuse Google::Ads::GoogleAds::V25::Enums::ListingGroupFilterTypeEnum\n qw(UNIT_INCLUDED);\nuse Google::Ads::GoogleAds::V25::Enums::ListingGroupFilterListingSourceEnum\n qw(SHOPPING);\nuse Google::Ads::GoogleAds::V25::Enums::EuPoliticalAdvertisingStatusEnum\n qw(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING);\nuse Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation;\nuse Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation;\nuse Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::AssetGroupService::AssetGroupOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignConversionGoalService::CampaignConversionGoalOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::AssetGroupListingGroupFilterService::AssetGroupListingGroupFilterOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignAssetService::CampaignAssetOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\nuse POSIX qw(strftime);\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\n#\n# See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n# for further details.\n#\n# These temporary IDs are fixed because they are used in multiple places.\nuse constant BUDGET_TEMPORARY_ID => -1;\nuse constant PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID => -2;\nuse constant ASSET_GROUP_TEMPORARY_ID => -3;\n\n# There are also entities that will be created in the same request but do not\n# need to be fixed temporary IDs because they are referenced only once.\nour $next_temp_id = ASSET_GROUP_TEMPORARY_ID - 1;\n\nsub add_performance_max_retail_campaign {\n my ($api_client, $customer_id, $merchant_center_account_id,\n $final_url, $brand_guidelines_enabled)\n = @_;\n\n # This campaign will override the customer conversion goals.\n # Retrieve the current list of customer conversion goals.\n my $customer_conversion_goals =\n get_customer_conversion_goals($api_client, $customer_id);\n\n # Performance Max campaigns require that repeated assets such as headlines\n # and descriptions be created before the campaign.\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets.\n #\n # Create the headlines.\n my $headline_asset_resource_names =\n create_multiple_text_assets($api_client, $customer_id,\n [\"Travel\", \"Travel Reviews\", \"Book travel\"]);\n # Create the descriptions.\n my $description_asset_resource_names =\n create_multiple_text_assets($api_client, $customer_id,\n [\"Take to the air!\", \"Fly to the sky!\"]);\n\n # It's important to create the below entities in this order because they depend\n # on each other.\n my $operations = [];\n # The below methods create and return MutateOperations that we later provide to\n # the GoogleAdsService->mutate() method in order to create the entities in a\n # single request. Since the entities for a Performance Max campaign are closely\n # tied to one-another, it's considered a best practice to create them in a\n # single mutate request so they all complete successfully or fail entirely,\n # leaving no orphaned entities. See:\n # https://developers.google.com/google-ads/api/docs/mutating/overview.\n push @$operations, create_campaign_budget_operation($customer_id);\n push @$operations,\n create_performance_max_campaign_operation($customer_id,\n $merchant_center_account_id, $brand_guidelines_enabled);\n push @$operations, @{create_campaign_criterion_operations($customer_id)};\n push @$operations, create_asset_group_operation($customer_id, $final_url);\n push @$operations, create_listing_group_filter_operation($customer_id);\n push @$operations,\n @{\n create_asset_and_asset_group_asset_operations(\n $customer_id, $headline_asset_resource_names,\n $description_asset_resource_names, $brand_guidelines_enabled\n )};\n push @$operations,\n @{create_conversion_goal_operations($customer_id,\n $customer_conversion_goals)};\n\n # Issue a mutate request to create everything and print its information.\n my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $operations\n });\n\n print_response_details($mutate_google_ads_response);\n\n return 1;\n}\n\n# Creates a MutateOperation that creates a new CampaignBudget.\n#\n# A temporary ID will be assigned to this campaign budget so that it can be\n# referenced by other objects being created in the same mutate request.\nsub create_campaign_budget_operation {\n my ($customer_id) = @_;\n\n # Create a mutate operation that creates a campaign budget operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignBudgetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::CampaignBudget->new(\n {\n # Set a temporary ID in the budget's resource name so it can be\n # referenced by the campaign in later steps.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n name => \"Performance Max retail campaign budget #\" . uniqid(),\n # The budget period already defaults to DAILY.\n amountMicros => 50000000,\n deliveryMethod => STANDARD,\n # A Performance Max campaign cannot use a shared campaign budget.\n explicitlyShared => \"false\",\n })})});\n}\n\n# Creates a MutateOperation that creates a new Performance Max campaign.\n#\n# A temporary ID will be assigned to this campaign so that it can be referenced\n# by other objects being created in the same mutate request.\nsub create_performance_max_campaign_operation {\n my ($customer_id, $merchant_center_account_id, $brand_guidelines_enabled) =\n @_;\n\n # Configures the optional opt-in/out status for asset automation settings.\n my $asset_automation_types = [\n GENERATE_IMAGE_EXTRACTION, FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,\n TEXT_ASSET_AUTOMATION, GENERATE_ENHANCED_YOUTUBE_VIDEOS,\n GENERATE_IMAGE_ENHANCEMENT\n ];\n my $asset_automation_settings = [];\n foreach my $asset_automation_type (@$asset_automation_types) {\n push @$asset_automation_settings,\n Google::Ads::GoogleAds::V25::Resources::AssetAutomationSetting->new({\n assetAutomationStatus => OPTED_IN,\n assetAutomationType => $asset_automation_type\n });\n }\n\n # Create a mutate operation that creates a campaign operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n # Assign the resource name with a temporary ID.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n name => \"Performance Max retail campaign #'\" . uniqid(),\n # Set the budget using the given budget resource name.\n campaignBudget =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n status =>\n Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n # All Performance Max campaigns have an advertisingChannelType of\n # PERFORMANCE_MAX. The advertisingChannelSubType should not be set.\n advertisingChannelType => PERFORMANCE_MAX,\n\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Max Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximizeConversionValue. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Max Conversion Value, see the support article:\n # http://support.google.com/google-ads/answer/7684216.\n # A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n # For first time users, it's recommended not to set a target ROAS.\n maximizeConversionValue =>\n Google::Ads::GoogleAds::V25::Common::MaximizeConversionValue->\n new({\n targetRoas => 3.5\n }\n ),\n # Below is what you would use if you want to maximize conversions.\n # maximizeConversions =>\n # Google::Ads::GoogleAds::V25::Common::MaximizeConversions->\n # new({\n # targetCpaMicros => 1000000\n # }\n # ),\n # The target CPA is optional. This is the average amount that you would\n # like to spend per conversion action.\n\n # Set the shopping settings.\n shoppingSetting =>\n Google::Ads::GoogleAds::V25::Resources::ShoppingSetting->new({\n merchantId => $merchant_center_account_id,\n # Optional: To use products only from a specific feed, set feedLabel\n # to the feed label used in Merchant Center.\n # See: https://support.google.com/merchants/answer/12453549.\n # Omitting the feedLabel field will use products from all feeds.\n # feedLabel => \"INSERT_FEED_LABEL_HERE\"\n }\n ),\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n\n # Set if the campaign is enabled for brand guidelines. For more information\n # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n brandGuidelinesEnabled => $brand_guidelines_enabled,\n\n # Configures the optional opt-in/out status for asset automation settings.\n assetAutomationSettings => $asset_automation_settings,\n\n # Optional fields.\n startDateTime =>\n strftime(\"%Y%m%d 00:00:00\", localtime(time + 60 * 60 * 24)),\n endDateTime => strftime(\n \"%Y%m%d 23:59:59\",\n localtime(time + 60 * 60 * 24 * 365)\n ),\n })})});\n}\n\n# Creates a list of MutateOperations that create new campaign criteria.\nsub create_campaign_criterion_operations {\n my ($customer_id) = @_;\n\n my $operations = [];\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n # Adds one positive location target for New York City (ID=1023191),\n # specifically adding the positive criteria before the negative one.\n location =>\n Google::Ads::GoogleAds::V25::Common::LocationInfo->new({\n geoTargetConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n 1023191)}\n ),\n negative => \"false\"\n })})});\n\n # Next add the negative target for Brooklyn (ID=1022762).\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n location =>\n Google::Ads::GoogleAds::V25::Common::LocationInfo->new({\n geoTargetConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n 1022762)}\n ),\n negative => \"true\"\n })})});\n\n # Set the LANGUAGE campaign criterion.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7.\n language =>\n Google::Ads::GoogleAds::V25::Common::LanguageInfo->new({\n languageConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::language_constant(\n 1000) # English\n })})})});\n\n return $operations;\n}\n\n# Creates multiple text assets and returns the list of resource names.\n#\n# These repeated assets must be created in a separate request prior to\n# creating the campaign.\nsub create_multiple_text_assets {\n my ($api_client, $customer_id, $texts) = @_;\n\n # Here again we use the GoogleAdService to create multiple text assets in a\n # single request.\n my $operations = [];\n foreach my $text (@$texts) {\n # Create a mutate operation for a text asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n textAsset =>\n Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $text\n })})})});\n }\n\n # Issue a mutate request to add all assets.\n my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $operations\n });\n\n my $asset_resource_names = [];\n foreach\n my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})\n {\n push @$asset_resource_names, $response->{assetResult}{resourceName};\n }\n print_response_details($mutate_google_ads_response);\n\n return $asset_resource_names;\n}\n\n# Creates a MutateOperation that creates a new asset group.\n#\n# A temporary ID will be assigned to this asset group so that it can be referenced\n# by other objects being created in the same mutate request.\nsub create_asset_group_operation {\n my ($customer_id, $final_url) = @_;\n\n # Create a mutate operation that creates an asset group operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupService::AssetGroupOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::AssetGroup->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n name => \"Performance Max retail asset group #\" . uniqid(),\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n finalUrls => [$final_url],\n finalMobileUrls => [$final_url],\n status =>\n Google::Ads::GoogleAds::V25::Enums::AssetGroupStatusEnum::PAUSED\n })})});\n}\n\n# Creates a MutateOperation that creates a new listing group filter.\n# A temporary ID will be assigned to this listing group filter so that it\n# can be referenced by other objects being created in the same Mutate request.\nsub create_listing_group_filter_operation {\n my ($customer_id) = @_;\n\n # Creates a new ad group criterion containing the \"default\" listing group\n # (All products).\n my $listing_group_filter =\n Google::Ads::GoogleAds::V25::Resources::AssetGroupListingGroupFilter->new({\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n\n # Since this is the root node, do not set the parentListingGroupFilter.\n # For all other nodes, this would refer to the parent listing group filter\n # resource name.\n # parentListingGroupFilter => \"<PARENT FILTER NAME>\"\n\n # The subdivision type means this node has children. This type is used for\n # the root node as well.\n type => UNIT_INCLUDED,\n\n # Because this is a Performance Max campaign for retail, we need to specify\n # that this is in the shopping listing source.\n listingSource => SHOPPING\n });\n\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupListingGroupFilterOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupListingGroupFilterService::AssetGroupListingGroupFilterOperation\n ->new({\n create => $listing_group_filter\n })});\n}\n\n# Creates a list of MutateOperations that create a new asset_group.\n# A temporary ID will be assigned to this asset group so that it can\n# be referenced by other objects being created in the same Mutate request.\nsub create_asset_and_asset_group_asset_operations {\n my (\n $customer_id,\n $headline_asset_resource_names,\n $description_asset_resource_names,\n $brand_guidelines_enabled\n ) = @_;\n\n my $operations = [];\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets.\n\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # - the resource name of the AssetGroup\n # - the resource name of the Asset\n # - the fieldType of the Asset in this AssetGroup\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n foreach my $resource_name (@$headline_asset_resource_names) {\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $resource_name,\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => HEADLINE\n })})});\n }\n\n # Link the description assets.\n foreach my $resource_name (@$description_asset_resource_names) {\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $resource_name,\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => DESCRIPTION\n })})});\n }\n\n # Create and link the long headline text asset.\n push @$operations,\n @{create_and_link_text_asset($customer_id, \"Travel the World\",\n LONG_HEADLINE)};\n\n # Create and link the business name and logo asset.\n push @$operations,\n @{\n create_and_link_brand_assets(\n $customer_id, $brand_guidelines_enabled,\n \"Interplanetary Cruises\", \"https://gaagl.page.link/1Crm\",\n \"Logo Image\"\n )};\n\n # Create and link the image assets.\n\n # Create and link the marketing image asset.\n push @$operations,\n @{\n create_and_link_image_asset(\n $customer_id, \"https://gaagl.page.link/Eit5\",\n MARKETING_IMAGE, \"Marketing Image\"\n )};\n\n # Create and link the square marketing image asset.\n push @$operations,\n @{\n create_and_link_image_asset(\n $customer_id, \"https://gaagl.page.link/bjYi\",\n SQUARE_MARKETING_IMAGE, \"Square Marketing Image\"\n )};\n\n # After being created the list must be sorted so that all asset\n # operations come before all the asset group asset operations,\n # otherwise the API will reject the request.\n return sort_asset_and_asset_group_asset_operations($operations);\n}\n\n# Creates a list of MutateOperations that create a new linked text asset.\nsub create_and_link_text_asset {\n my ($customer_id, $text, $field_type) = @_;\n\n my $operations = [];\n # Create a new mutate operation for a text asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n textAsset => Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $text\n })})})});\n\n # Create an asset group asset to link the asset to the asset group.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => $field_type\n })})});\n\n $next_temp_id--;\n return $operations;\n}\n\n# Creates a list of MutateOperations that create a new linked image asset.\nsub create_and_link_image_asset {\n my ($customer_id, $url, $field_type, $asset_name) = @_;\n\n my $operations = [];\n # Create a new mutate operation for an image asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n name => $asset_name,\n imageAsset =>\n Google::Ads::GoogleAds::V25::Common::ImageAsset->new({\n data => get_base64_data_from_url($url)})})})});\n\n # Create an asset group asset to link the asset to the asset group.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => $field_type\n })})});\n\n $next_temp_id--;\n return $operations;\n}\n\n# Creates a list of MutateOperations that create linked brand assets.\nsub create_and_link_brand_assets {\n my ($customer_id, $brand_guidelines_enabled, $business_name, $logo_url,\n $logo_name)\n = @_;\n\n my $operations = [];\n\n # Create the text asset.\n my $text_asset_temp_id = $next_temp_id--;\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $text_asset_temp_id\n ),\n textAsset => Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $business_name\n })})})});\n\n # Create the image asset.\n my $image_asset_temp_id = $next_temp_id--;\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $image_asset_temp_id\n ),\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n name => $logo_name,\n imageAsset =>\n Google::Ads::GoogleAds::V25::Common::ImageAsset->new({\n data => get_base64_data_from_url($logo_url)})})})});\n\n if ($brand_guidelines_enabled) {\n # Create CampaignAsset resources to link the Asset resources to the Campaign.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignAssetService::CampaignAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignAsset->new({\n fieldType => BUSINESS_NAME,\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $text_asset_temp_id\n )})})});\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignAssetService::CampaignAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignAsset->new({\n fieldType => LOGO,\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $image_asset_temp_id\n )})})});\n } else {\n # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $text_asset_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => BUSINESS_NAME\n })})});\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $image_asset_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => LOGO\n })})});\n }\n\n return $operations;\n}\n\n# Sorts a list of asset and asset group asset operations.\n#\n# This sorts the list such that all asset operations precede\n# all asset group asset operations. If asset group assets are\n# created before assets then an error will be returned by the API.\nsub sort_asset_and_asset_group_asset_operations {\n my ($operations) = @_;\n\n sub sorter {\n if (defined $a->{assetOperation}) {\n return -1;\n } else {\n return 1;\n }\n }\n my @operations_sorted = sort sorter @$operations;\n return \\@operations_sorted;\n}\n\n# Retrieves the list of customer conversion goals.\nsub get_customer_conversion_goals {\n my ($api_client, $customer_id) = @_;\n\n my $customer_conversion_goals = [];\n # Create a query that retrieves all customer conversion goals.\n my $query =\n \"SELECT customer_conversion_goal.category, customer_conversion_goal.origin \"\n . \"FROM customer_conversion_goal\";\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService->search() method instead of search_stream().\n my $search_response = $api_client->GoogleAdsService()->search({\n customerId => $customer_id,\n query => $query\n });\n\n # Iterate over the results and build the list of conversion goals.\n foreach my $google_ads_row (@{$search_response->{results}}) {\n push @$customer_conversion_goals,\n {\n category => $google_ads_row->{customerConversionGoal}{category},\n origin => $google_ads_row->{customerConversionGoal}{origin}};\n }\n\n return $customer_conversion_goals;\n}\n\n# Creates a list of MutateOperations that override customer conversion goals.\nsub create_conversion_goal_operations {\n my ($customer_id, $customer_conversion_goals) = @_;\n\n my $operations = [];\n # To override the customer conversion goals, we will change the biddability of\n # each of the customer conversion goals so that only the desired conversion goal\n # is biddable in this campaign.\n foreach my $customer_conversion_goal (@$customer_conversion_goals) {\n my $campaign_conversion_goal =\n Google::Ads::GoogleAds::V25::Resources::CampaignConversionGoal->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_conversion_goal(\n $customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n $customer_conversion_goal->{category},\n $customer_conversion_goal->{origin})});\n # Change the biddability for the campaign conversion goal.\n # Set biddability to true for the desired (category, origin).\n # Set biddability to false for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n if ( $customer_conversion_goal->{category} eq PURCHASE\n && $customer_conversion_goal->{origin} eq WEBSITE)\n {\n $campaign_conversion_goal->{biddable} = \"true\";\n } else {\n $campaign_conversion_goal->{biddable} = \"false\";\n }\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignConversionGoalOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignConversionGoalService::CampaignConversionGoalOperation\n ->new({\n update => $campaign_conversion_goal,\n # Set the update mask on the operation. Here the update mask will be\n # a list of all the fields that were set on the update object.\n updateMask => all_set_fields_of($campaign_conversion_goal)})});\n }\n\n return $operations;\n}\n\n# Prints the details of a MutateGoogleAdsResponse.\n# Parses the \"response\" oneof field name and uses it to extract the new entity's\n# name and resource name.\nsub print_response_details {\n my ($mutate_google_ads_response) = @_;\n\n foreach\n my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})\n {\n my $result_type = [keys %$response]->[0];\n\n printf \"Created a(n) %s with '%s'.\\n\",\n ucfirst $result_type =~ s/Result$//r,\n $response->{$result_type}{resourceName};\n }\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\nmy $customer_id = undef;\nmy $merchant_center_account_id = undef;\nmy $final_url = \"http://www.example.com\";\nmy $brand_guidelines_enabled = \"true\";\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"merchant_center_account_id=i\" => \\$merchant_center_account_id,\n \"final_url=s\" => \\$final_url,\n \"brand_guidelines_enabled=s\" => \\$brand_guidelines_enabled\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2)\n if not check_params(\n $customer_id, $merchant_center_account_id,\n $final_url, $brand_guidelines_enabled\n );\n\n# Call the example.\nadd_performance_max_retail_campaign($api_client, $customer_id =~ s/-//gr,\n $merchant_center_account_id, $final_url, $brand_guidelines_enabled);\n\n=pod\n\n=head1 NAME\n\nadd_performance_max_retail_campaign\n\n=head1 DESCRIPTION\n\nThis example shows how to create a Performance Max retail campaign.\n\nThis will be created for \"All products\".\n\nFor more information about Performance Max retail campaigns, see\nhttps://developers.google.com/google-ads/api/docs/performance-max/retail.\n\nPrerequisites:\n- You need to have access to a Merchant Center account. You can find\n instructions to create a Merchant Center account here:\n https://support.google.com/merchants/answer/188924.\n This account must be linked to your Google Ads account. The integration\n instructions can be found at:\n https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center.\n- You need your Google Ads account to track conversions. The different ways\n to track conversions can be found here:\n https://support.google.com/google-ads/answer/1722054.\n- You must have at least one conversion action in the account. For more about\n conversion actions, see\n https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n\n=head1 SYNOPSIS\n\nadd_performance_max_retail_campaign.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -merchant_center_account_id The Merchant Center account ID.\n -final_url [optional] The final URL for the asset group of the campaign.\n\n -brand_guidelines_enabled\t [optional] A boolean value indicating if the campaign is enabled for brand guidelines. Defaults to false.\n=cut\nadd_performance_max_retail_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.432Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":6152,"estimatedTokens":64850}}172{"id":"doc-add_performance_max_for_travel_goals_campaign_go-1b3f4ddb","source":"documentation","title":"Add Performance Max For Travel Goals Campaign | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/samples/add-performance-max-for-travel-goals-campaign","text":"Example:\n```text\n// Copyright 2023 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.travel;\n\nimport static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime;\nimport static com.google.ads.googleads.v25.enums.EuPoliticalAdvertisingStatusEnum.EuPoliticalAdvertisingStatus.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleHelper;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.CallToActionAsset;\nimport com.google.ads.googleads.v25.common.HotelPropertyAsset;\nimport com.google.ads.googleads.v25.common.ImageAsset;\nimport com.google.ads.googleads.v25.common.MaximizeConversionValue;\nimport com.google.ads.googleads.v25.common.TextAsset;\nimport com.google.ads.googleads.v25.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType;\nimport com.google.ads.googleads.v25.enums.AssetFieldTypeEnum.AssetFieldType;\nimport com.google.ads.googleads.v25.enums.AssetGroupStatusEnum.AssetGroupStatus;\nimport com.google.ads.googleads.v25.enums.AssetSetTypeEnum.AssetSetType;\nimport com.google.ads.googleads.v25.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod;\nimport com.google.ads.googleads.v25.enums.CampaignStatusEnum.CampaignStatus;\nimport com.google.ads.googleads.v25.enums.HotelAssetSuggestionStatusEnum.HotelAssetSuggestionStatus;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.Asset;\nimport com.google.ads.googleads.v25.resources.AssetGroup;\nimport com.google.ads.googleads.v25.resources.AssetGroupAsset;\nimport com.google.ads.googleads.v25.resources.AssetSet;\nimport com.google.ads.googleads.v25.resources.AssetSetAsset;\nimport com.google.ads.googleads.v25.resources.Campaign;\nimport com.google.ads.googleads.v25.resources.CampaignBudget;\nimport com.google.ads.googleads.v25.services.AssetGroupAssetOperation;\nimport com.google.ads.googleads.v25.services.AssetGroupOperation;\nimport com.google.ads.googleads.v25.services.AssetOperation;\nimport com.google.ads.googleads.v25.services.AssetSetAssetOperation;\nimport com.google.ads.googleads.v25.services.AssetSetOperation;\nimport com.google.ads.googleads.v25.services.AssetSetServiceClient;\nimport com.google.ads.googleads.v25.services.CampaignBudgetOperation;\nimport com.google.ads.googleads.v25.services.CampaignOperation;\nimport com.google.ads.googleads.v25.services.GoogleAdsServiceClient;\nimport com.google.ads.googleads.v25.services.HotelAssetSuggestion;\nimport com.google.ads.googleads.v25.services.HotelImageAsset;\nimport com.google.ads.googleads.v25.services.HotelTextAsset;\nimport com.google.ads.googleads.v25.services.MutateAssetSetsResponse;\nimport com.google.ads.googleads.v25.services.MutateGoogleAdsResponse;\nimport com.google.ads.googleads.v25.services.MutateOperation;\nimport com.google.ads.googleads.v25.services.MutateOperationResponse;\nimport com.google.ads.googleads.v25.services.MutateOperationResponse.ResponseCase;\nimport com.google.ads.googleads.v25.services.SuggestTravelAssetsRequest;\nimport com.google.ads.googleads.v25.services.SuggestTravelAssetsResponse;\nimport com.google.ads.googleads.v25.services.TravelAssetSuggestionServiceClient;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.io.ByteStreams;\nimport com.google.protobuf.ByteString;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.stream.Collectors;\nimport org.joda.time.DateTime;\n\n/**\n * This example shows how to create a Performance Max for travel goals campaign. It also uses\n * TravelAssetSuggestionService to fetch suggested assets for creating an asset group. In case there\n * are not enough assets for the asset group (required by Performance Max), this example will create\n * more assets to fulfill the requirements.\n *\n * <p>For more information about Performance Max campaigns, see\n * https://developers.google.com/google-ads/api/docs/performance-max/overview.\n *\n * <p>Prerequisites:\n *\n * <ul>\n * <li>You must have at least one conversion action in the account. For more about conversion\n * actions, see\n * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n * </ul>\n *\n * <p>Notes:\n *\n * <ul>\n * <li>This example uses the default customer conversion goals. For an example of setting\n * campaign-specific conversion goals, see {@link\n * com.google.ads.googleads.examples.shoppingads.AddPerformanceMaxRetailCampaign}.\n * <li>To learn how to create asset group signals, see {@link\n * com.google.ads.googleads.examples.advancedoperations.AddPerformanceMaxCampaign}.\n * </ul>\n */\npublic class AddPerformanceMaxForTravelGoalsCampaign {\n\n // Minimum requirements of assets required in a Performance Max asset group.\n // See https://developers.google.com/google-ads/api/docs/performance-max/assets for details.\n private static Map<AssetFieldType, Integer> MIN_REQUIRED_TEXT_ASSET_COUNTS =\n ImmutableMap.<AssetFieldType, Integer>builder()\n .put(AssetFieldType.HEADLINE, 3)\n .put(AssetFieldType.LONG_HEADLINE, 1)\n .put(AssetFieldType.DESCRIPTION, 2)\n .put(AssetFieldType.BUSINESS_NAME, 1)\n .build();\n\n private static Map<AssetFieldType, Integer> MIN_REQUIRED_IMAGE_ASSET_COUNTS =\n ImmutableMap.<AssetFieldType, Integer>builder()\n .put(AssetFieldType.MARKETING_IMAGE, 1)\n .put(AssetFieldType.SQUARE_MARKETING_IMAGE, 1)\n .put(AssetFieldType.LOGO, 1)\n .build();\n\n // Texts and URLs used to create text and image assets when the TravelAssetSuggestionService\n // doesn't return enough assets required for creating an asset group.\n private static Map<AssetFieldType, List<String>> DEFAULT_TEXT_ASSETS_INFO =\n ImmutableMap.<AssetFieldType, List<String>>builder()\n .put(AssetFieldType.HEADLINE, ImmutableList.of(\"Hotel\", \"Travel Reviews\", \"Book travel\"))\n .put(AssetFieldType.LONG_HEADLINE, ImmutableList.of(\"Travel the World\"))\n .put(\n AssetFieldType.DESCRIPTION,\n ImmutableList.of(\"Great deal for your beloved hotel\", \"Best rate guaranteed\"))\n .put(AssetFieldType.BUSINESS_NAME, ImmutableList.of(\"Interplanetary Cruises\"))\n .build();\n private static Map<AssetFieldType, List<String>> DEFAULT_IMAGE_ASSETS_INFO =\n ImmutableMap.<AssetFieldType, List<String>>builder()\n .put(AssetFieldType.MARKETING_IMAGE, ImmutableList.of(\"https://gaagl.page.link/Eit5\"))\n .put(\n AssetFieldType.SQUARE_MARKETING_IMAGE,\n ImmutableList.of(\"https://gaagl.page.link/bjYi\"))\n .put(AssetFieldType.LOGO, ImmutableList.of(\"https://gaagl.page.link/bjYi\"))\n .build();\n\n // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are always\n // negative and unique within one mutate request.\n //\n // <p>See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further\n // details.\n //\n // <p>These temporary IDs are fixed because they are used in multiple places.\n private static final int ASSET_TEMPORARY_ID = -1;\n private static final int BUDGET_TEMPORARY_ID = -2;\n private static final int CAMPAIGN_TEMPORARY_ID = -3;\n private static final int ASSET_GROUP_TEMPORARY_ID = -4;\n\n // There are also entities that will be created in the same request but do not\n // need to be fixed temporary IDs because they are referenced only once.\n private static long temporaryId = ASSET_GROUP_TEMPORARY_ID - 1;\n\n /** Parameters for the example. */\n private static class AddPerformanceMaxForTravelGoalsCampaignParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(\n names = ArgumentNames.PLACE_ID,\n required = true,\n description =\n \"The place ID of a hotel property. A place ID uniquely identifies a place in the Google\"\n + \" Places database. See https://developers.google.com/places/web-service/place-id\"\n + \" to learn more.\")\n private String placeId;\n }\n\n public static void main(String[] args) {\n AddPerformanceMaxForTravelGoalsCampaignParams params =\n new AddPerformanceMaxForTravelGoalsCampaignParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.placeId = \"INSERT_PLACE_ID_HERE\";\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new AddPerformanceMaxForTravelGoalsCampaign()\n .runExample(googleAdsClient, params.customerId, params.placeId);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param placeId the place ID for a hotel property asset.\n */\n private void runExample(GoogleAdsClient googleAdsClient, long customerId, String placeId) {\n // Gets hotel asset suggestion using the TravelAssetSuggestionService.\n HotelAssetSuggestion hotelAssetSuggestion =\n getHotelAssetSuggestion(googleAdsClient, customerId, placeId);\n\n // Performance Max campaigns require that repeated assets such as headlines and descriptions be\n // created before the campaign. For the list of required assets for a Performance Max campaign,\n // see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets.\n //\n // This step is the same for any type of Performance Max campaign.\n\n // Creates the headlines using the hotel asset suggestion.\n List<String> headlineAssetResourceNames =\n createMultipleTextAssets(\n googleAdsClient, customerId, AssetFieldType.HEADLINE, hotelAssetSuggestion);\n\n // Creates the descriptions using the hotel asset suggestion.\n List<String> descriptionAssetResourceNames =\n createMultipleTextAssets(\n googleAdsClient, customerId, AssetFieldType.DESCRIPTION, hotelAssetSuggestion);\n\n // Creates a hotel property asset set, which will be used later to link with a newly created\n // campaign.\n String hotelPropertyAssetSetResourceName = createHotelAssetSet(googleAdsClient, customerId);\n\n // Creates a hotel property asset and link it with the previously created hotel property asset\n // set. This asset will also be linked to an asset group in the later steps. In the real-world\n // scenario, you'd need to create many assets for all your hotel properties. We use one hotel\n // property here for simplicity. Both asset and asset set need to be created before creating a\n // campaign, so we cannot bundle them with other mutate operations below.\n String hotelPropertyAssetResourceName =\n createHotelAsset(googleAdsClient, customerId, placeId, hotelPropertyAssetSetResourceName);\n\n // It's important to create the below entities in this order because they depend on\n // each other.\n // The below methods create and return mutate operations that we later provide to the\n // GoogleAdsService.Mutate method in order to create the entities in a single request.\n // Since the entities for a Performance Max campaign are closely tied to one-another, it's\n // considered a best practice to create them in a single Mutate request so they all complete\n // successfully or fail entirely, leaving no orphaned entities. See:\n // https://developers.google.com/google-ads/api/docs/mutating/overview.\n List<MutateOperation> mutateOperations = new ArrayList<>();\n mutateOperations.add(createCampaignBudgetOperation(customerId));\n mutateOperations.add(createCampaignOperation(customerId, hotelPropertyAssetSetResourceName));\n mutateOperations.addAll(\n createAssetGroupOperations(\n customerId,\n hotelPropertyAssetResourceName,\n headlineAssetResourceNames,\n descriptionAssetResourceNames,\n hotelAssetSuggestion));\n\n // Issues a mutate request to create everything and prints the results.\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);\n System.out.println(\n \"Created the following entities for a campaign budget, a campaign, and an asset group for\"\n + \" Performance Max for travel goals:\");\n printResponseDetails(response);\n }\n }\n\n /**\n * Returns hotel asset suggestion obtained from TravelAssetsSuggestionService.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param placeId the place ID for a hotel property asset.\n * @return a hotel asset suggestion.\n */\n private HotelAssetSuggestion getHotelAssetSuggestion(\n GoogleAdsClient googleAdsClient, long customerId, String placeId) {\n\n try (TravelAssetSuggestionServiceClient travelAssetSuggestionServiceClient =\n googleAdsClient.getLatestVersion().createTravelAssetSuggestionServiceClient()) {\n // Sends a request to suggest assets to be created as an asset group for the Performance Max\n // for travel goals campaign.\n SuggestTravelAssetsResponse suggestTravelAssetsResponse =\n travelAssetSuggestionServiceClient.suggestTravelAssets(\n SuggestTravelAssetsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n // Uses 'en-US' as an example. It can be any language specifications in BCP 47\n // format.\n .setLanguageOption(\"en-US\")\n // The service accepts several place IDs. We use only one here for demonstration.\n .addPlaceIds(placeId)\n .build());\n System.out.printf(\"Fetched a hotel asset suggestion for the place ID '%s'.%n\", placeId);\n return suggestTravelAssetsResponse.getHotelAssetSuggestions(0);\n }\n }\n\n /**\n * Creates multiple text assets and returns the list of resource names. The hotel asset suggestion\n * is used to create a text asset first. If the number of created text assets is still fewer than\n * the minimum required number of assets of the specified asset field type, adds more text assets\n * to fulfill the requirement.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param assetFieldType the asset field type that the text assets will be created for.\n * @param hotelAssetSuggestion the hotel asset suggestion.\n * @return the resource names of the created text assets.\n */\n private List<String> createMultipleTextAssets(\n GoogleAdsClient googleAdsClient,\n long customerId,\n AssetFieldType assetFieldType,\n HotelAssetSuggestion hotelAssetSuggestion) {\n\n // Uses the GoogleAdService to create multiple text assets in a single request.\n List<MutateOperation> mutateOperations = new ArrayList<>();\n\n // First, adds all the text assets of the specified asset field type.\n // Filters to only the specified asset field type.\n // Constructs a mutate operation to create the asset.\n // Adds the operation to the list.\n if (HotelAssetSuggestionStatus.SUCCESS.equals(hotelAssetSuggestion.getStatus())) {\n for (HotelTextAsset asset : hotelAssetSuggestion.getTextAssetsList()) {\n if (asset.getAssetFieldType().equals(assetFieldType)) {\n MutateOperation build =\n MutateOperation.newBuilder()\n .setAssetOperation(\n AssetOperation.newBuilder()\n .setCreate(\n Asset.newBuilder()\n .setTextAsset(\n TextAsset.newBuilder().setText(asset.getText()).build())\n .build()))\n .build();\n mutateOperations.add(build);\n }\n }\n }\n\n // If the added assets are still less than the minimum required assets for the asset field type,\n // add more text assets using the default texts.\n int i = 0;\n while (mutateOperations.size() < MIN_REQUIRED_TEXT_ASSET_COUNTS.get(assetFieldType)) {\n String text = DEFAULT_TEXT_ASSETS_INFO.get(assetFieldType).get(i++);\n // Creates a mutate operation for a text asset, using the default text.\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetOperation(\n AssetOperation.newBuilder()\n .setCreate(\n Asset.newBuilder()\n .setTextAsset(TextAsset.newBuilder().setText(text).build())\n .build()))\n .build());\n }\n\n // Issues a mutate request to add all assets.\n List<String> assetResourceNames;\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);\n assetResourceNames =\n response.getMutateOperationResponsesList().stream()\n .map(resp -> resp.getAssetResult().getResourceName())\n .collect(Collectors.toList());\n System.out.printf(\n \"The following assets were created for the asset field type '%s':%n\", assetFieldType);\n printResponseDetails(response);\n }\n\n return assetResourceNames;\n }\n\n /**\n * Creates a hotel property asset set.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @return the created hotel property asset set resource name.\n */\n private String createHotelAssetSet(GoogleAdsClient googleAdsClient, long customerId) {\n // Creates an asset set operation for a hotel property asset set.\n AssetSetOperation assetSetOperation =\n AssetSetOperation.newBuilder()\n .setCreate(\n AssetSet.newBuilder()\n .setName(\n \"My Hotel propery asset set #\" + CodeSampleHelper.getPrintableDateTime())\n .setType(AssetSetType.HOTEL_PROPERTY))\n .build();\n try (AssetSetServiceClient assetSetServiceClient =\n googleAdsClient.getLatestVersion().createAssetSetServiceClient()) {\n MutateAssetSetsResponse mutateAssetSetsResponse =\n assetSetServiceClient.mutateAssetSets(\n Long.toString(customerId), ImmutableList.of(assetSetOperation));\n String assetSetResourceName = mutateAssetSetsResponse.getResults(0).getResourceName();\n System.out.printf(\"Created an asset set with resource name: '%s'%n\", assetSetResourceName);\n return assetSetResourceName;\n }\n }\n\n /**\n * Creates a hotel property asset using the specified place ID. The place ID must belong to a\n * hotel property. Then, links it to the specified asset set.\n *\n * <p>See https://developers.google.com/places/web-service/place-id to search for a hotel place\n * ID.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param placeId the place ID for a hotel.\n * @param hotelPropertyAssetSetResourceName the hotel asset set resource name.\n * @return the created hotel property asset resource name.\n */\n private String createHotelAsset(\n GoogleAdsClient googleAdsClient,\n long customerId,\n String placeId,\n String hotelPropertyAssetSetResourceName) {\n // Uses the GoogleAdService to create an asset and asset set asset in a single request.\n List<MutateOperation> mutateOperations = new ArrayList<>();\n String assetResourceName = ResourceNames.asset(customerId, ASSET_TEMPORARY_ID);\n // Creates a mutate operation for a hotel property asset.\n Asset hotelPropertyAsset =\n Asset.newBuilder()\n .setResourceName(assetResourceName)\n .setHotelPropertyAsset(HotelPropertyAsset.newBuilder().setPlaceId(placeId))\n .build();\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetOperation(AssetOperation.newBuilder().setCreate(hotelPropertyAsset))\n .build());\n\n // Creates a mutate operation for an asset set asset.\n AssetSetAsset assetSetAsset =\n AssetSetAsset.newBuilder()\n .setAsset(assetResourceName)\n .setAssetSet(hotelPropertyAssetSetResourceName)\n .build();\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetSetAssetOperation(AssetSetAssetOperation.newBuilder().setCreate(assetSetAsset))\n .build());\n // Issues a mutate request to create all entities.\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n MutateGoogleAdsResponse mutateGoogleAdsResponse =\n googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);\n System.out.println(\"Created the following entities for the hotel asset:\");\n printResponseDetails(mutateGoogleAdsResponse);\n // Returns the created asset resource name, which will be used later to create an asset\n // group. Other resource names are not used later.\n return mutateGoogleAdsResponse\n .getMutateOperationResponses(0)\n .getAssetResult()\n .getResourceName();\n }\n }\n\n /**\n * Creates a mutate operation that creates a new campaign budget.\n *\n * <p>A temporary ID will be assigned to this campaign budget so that it can be referenced by\n * other objects being created in the same mutate request.\n *\n * @param customerId the client customer ID.\n * @return a mutate operation that creates a campaign budget.\n */\n private MutateOperation createCampaignBudgetOperation(long customerId) {\n CampaignBudget campaignBudget =\n CampaignBudget.newBuilder()\n .setName(\"Performance Max for travel goals campaign budget #\" + getPrintableDateTime())\n // The budget period already defaults to DAILY.\n .setAmountMicros(50_000_000)\n .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)\n // A Performance Max campaign cannot use a shared campaign budget.\n .setExplicitlyShared(false)\n // Sets a temporary ID in the budget's resource name, so it can be referenced\n // by the campaign in later steps.\n .setResourceName(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignBudgetOperation(\n CampaignBudgetOperation.newBuilder().setCreate(campaignBudget).build())\n .build();\n }\n\n /**\n * Creates a mutate operation that creates a new Performance Max campaign. Links the specified\n * hotel property asset set to this campaign.\n *\n * <p>A temporary ID will be assigned to this campaign so that it can be referenced by other\n * objects being created in the same mutate request.\n *\n * @param customerId the client customer ID.\n * @param hotelPropertyAssetSetResourceName\n * @return the mutate operation that creates a campaign.\n */\n private MutateOperation createCampaignOperation(\n long customerId, String hotelPropertyAssetSetResourceName) {\n Campaign performanceMaxCampaign =\n Campaign.newBuilder()\n .setName(\"Performance Max for travel goals campaign #\" + getPrintableDateTime())\n // Sets the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n .setStatus(CampaignStatus.PAUSED)\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n .setAdvertisingChannelType(AdvertisingChannelType.PERFORMANCE_MAX)\n // To create a Performance Max for travel goals campaign, you need to set\n // `hotel_property_asset_set`.\n .setHotelPropertyAssetSet(hotelPropertyAssetSetResourceName)\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n .setMaximizeConversionValue(\n MaximizeConversionValue.newBuilder().setTargetRoas(3.5).build())\n // Assigns the resource name with a temporary ID.\n .setResourceName(ResourceNames.campaign(customerId, CAMPAIGN_TEMPORARY_ID))\n // Sets the budget using the given budget resource name.\n .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n // Optional fields.\n .setStartDateTime(new DateTime().plusDays(1).toString(\"yyyy-MM-dd 00:00:00\"))\n .setEndDateTime(new DateTime().plusDays(365).toString(\"yyyy-MM-dd 23:59:59\"))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignOperation(\n CampaignOperation.newBuilder().setCreate(performanceMaxCampaign).build())\n .build();\n }\n\n /**\n * Creates a list of mutate operations that create a new asset group, composed of suggested\n * assets. In case the number of suggested assets is not enough for the requirements, creates more\n * assets to meet the requirement.\n *\n * <p>For the list of required assets for a Performance Max campaign, see\n * https://developers.google.com/google-ads/api/docs/performance-max/assets.\n *\n * @param hotelPropertyAssetResourceName the hotel property asset resource name that will be used\n * to create an asset group\n * @param headlineAssetResourceNames a list of headline resource names\n * @param descriptionAssetResourceNames a list of description resource names\n * @param hotelAssetSuggestion the hotel asset suggestion\n * @return a list of mutate operations that create the asset group\n */\n private List<MutateOperation> createAssetGroupOperations(\n long customerId,\n String hotelPropertyAssetResourceName,\n List<String> headlineAssetResourceNames,\n List<String> descriptionAssetResourceNames,\n HotelAssetSuggestion hotelAssetSuggestion) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n\n // Creates a new mutate operation that creates an asset group using suggested information when\n // available.\n String assetGroupName;\n List<String> assetGroupFinalUrls;\n if (HotelAssetSuggestionStatus.SUCCESS.equals(hotelAssetSuggestion.getStatus())) {\n assetGroupName = hotelAssetSuggestion.getHotelName();\n assetGroupFinalUrls = ImmutableList.of(hotelAssetSuggestion.getFinalUrl());\n } else {\n assetGroupName = \"Performance Max for travel goals asset group #\" + getPrintableDateTime();\n assetGroupFinalUrls = ImmutableList.of(\"https://www.example.com\");\n }\n String assetGroupResourceName = ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID);\n AssetGroup assetGroup =\n AssetGroup.newBuilder()\n .setResourceName(assetGroupResourceName)\n .setName(assetGroupName)\n .setCampaign(ResourceNames.campaign(customerId, CAMPAIGN_TEMPORARY_ID))\n .addAllFinalUrls(assetGroupFinalUrls)\n .setStatus(AssetGroupStatus.PAUSED)\n .build();\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupOperation(AssetGroupOperation.newBuilder().setCreate(assetGroup))\n .build());\n\n // An asset group is linked to an asset by creating a new asset group asset\n // and providing:\n // - the resource name of the asset group\n // - the resource name of the asset\n // - the field_type of the asset in this asset group\n //\n // To learn more about asset groups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n // Headline and description assets were created at the first step of this example. So, we\n // just need to link them with the created asset group.\n\n // Builds the AssetGroupAssets to link headline assets to the asset group.\n List<AssetGroupAsset> assetGroupAssets = new ArrayList<>();\n assetGroupAssets.addAll(\n headlineAssetResourceNames.stream()\n .map(\n headlineAssetResourceName ->\n AssetGroupAsset.newBuilder()\n .setAsset(headlineAssetResourceName)\n .setAssetGroup(assetGroupResourceName)\n .setFieldType(AssetFieldType.HEADLINE)\n .build())\n .collect(Collectors.toList()));\n // Builds the AssetGroupAssets to link description assets to the asset group.\n assetGroupAssets.addAll(\n descriptionAssetResourceNames.stream()\n .map(\n descriptionAssetResourceName ->\n AssetGroupAsset.newBuilder()\n .setAsset(descriptionAssetResourceName)\n .setAssetGroup(assetGroupResourceName)\n .setFieldType(AssetFieldType.DESCRIPTION)\n .build())\n .collect(Collectors.toList()));\n\n // Adds operations to create the AssetGroupAssets for headlines and descriptions.\n mutateOperations.addAll(\n assetGroupAssets.stream()\n .map(\n assetGroupAsset ->\n MutateOperation.newBuilder()\n .setAssetGroupAssetOperation(\n AssetGroupAssetOperation.newBuilder()\n .setCreate(assetGroupAsset)\n .build())\n .build())\n .collect(Collectors.toList()));\n\n // Link the previously created hotel property asset to the asset group. In the real-world\n // scenario, you'd need to do this step several times for each hotel property asset.\n AssetGroupAsset hotelProperyAssetGroupAsset =\n AssetGroupAsset.newBuilder()\n .setAsset(hotelPropertyAssetResourceName)\n .setAssetGroup(assetGroupResourceName)\n .setFieldType(AssetFieldType.HOTEL_PROPERTY)\n .build();\n // Adds an operation to link the hotel property asset to the asset group.\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupAssetOperation(\n AssetGroupAssetOperation.newBuilder().setCreate(hotelProperyAssetGroupAsset))\n .build());\n\n // Creates the rest of the required text assets and links them to the asset group.\n mutateOperations.addAll(\n createOperationsForTextAssetsAndAssetGroupAssets(\n customerId, hotelAssetSuggestion, assetGroupResourceName));\n\n // Creates the image assets and links them to the asset group. Some optional image assets\n // suggested by the TravelAssetSuggestionService might be created too.\n mutateOperations.addAll(\n createOperationsForImageAssetsAndAssetGroupAssets(\n customerId, hotelAssetSuggestion, assetGroupResourceName));\n if (HotelAssetSuggestionStatus.SUCCESS.equals(hotelAssetSuggestion.getStatus())) {\n // Creates a new mutate operation for a suggested call-to-action asset and links it\n // to the asset group.\n Asset callToActionAsset =\n Asset.newBuilder()\n .setResourceName(ResourceNames.asset(customerId, temporaryId))\n .setName(\"Suggested call-to-action asset #\" + CodeSampleHelper.getPrintableDateTime())\n .setCallToActionAsset(\n CallToActionAsset.newBuilder()\n .setCallToAction(hotelAssetSuggestion.getCallToAction()))\n .build();\n // Adds an operation to create the call-to-action asset.\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetOperation(AssetOperation.newBuilder().setCreate(callToActionAsset))\n .build());\n\n AssetGroupAsset callToActionAssetGroupAsset =\n AssetGroupAsset.newBuilder()\n .setAsset(callToActionAsset.getResourceName())\n .setAssetGroup(assetGroupResourceName)\n .setFieldType(AssetFieldType.CALL_TO_ACTION_SELECTION)\n .build();\n // Adds an operation to link the call-to-action asset to the asset group.\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupAssetOperation(\n AssetGroupAssetOperation.newBuilder().setCreate(callToActionAssetGroupAsset))\n .build());\n\n temporaryId--;\n }\n return mutateOperations;\n }\n\n /**\n * Creates text assets required for an asset group using the suggested hotel text assets. It adds\n * more text assets to fulfill the requirements if the suggested hotel text assets are not enough.\n *\n * @param customerId the client customer ID.\n * @param hotelAssetSuggestion the hotel asset suggestion.\n * @return a list of mutate operations that create text assets and asset group assets.\n */\n private List<MutateOperation> createOperationsForTextAssetsAndAssetGroupAssets(\n long customerId, HotelAssetSuggestion hotelAssetSuggestion, String assetGroupResourceName) {\n // Creates mutate operations for the suggested text assets except for headlines and\n // descriptions, which were created previously.\n List<MutateOperation> mutateOperations = new ArrayList<>();\n // Creates a map of asset field type to list of text values to create.\n Map<AssetFieldType, List<String>> textByFieldType = new HashMap<>();\n\n if (HotelAssetSuggestionStatus.SUCCESS.equals(hotelAssetSuggestion.getStatus())) {\n // Adds text values of suggested text assets.\n for (HotelTextAsset hotelTextAsset : hotelAssetSuggestion.getTextAssetsList()) {\n AssetFieldType assetFieldType = hotelTextAsset.getAssetFieldType();\n if (AssetFieldType.HEADLINE.equals(assetFieldType)\n || AssetFieldType.DESCRIPTION.equals(assetFieldType)) {\n // Headlines and descriptions were already created at the first step of this code example.\n continue;\n }\n System.out.printf(\n \"A text asset with text '%s' is suggested for the asset field type '%s'.%n\",\n hotelTextAsset.getText(), assetFieldType);\n\n textByFieldType\n .computeIfAbsent(assetFieldType, ft -> new ArrayList<>())\n .add(hotelTextAsset.getText());\n }\n }\n\n // Collects more text values by field type to fulfill the requirements.\n for (Entry<AssetFieldType, Integer> requiredEntry : MIN_REQUIRED_TEXT_ASSET_COUNTS.entrySet()) {\n AssetFieldType assetFieldType = requiredEntry.getKey();\n if (AssetFieldType.HEADLINE.equals(assetFieldType)\n || AssetFieldType.DESCRIPTION.equals(assetFieldType)) {\n // Headlines and descriptions were already created at the first step of this code example.\n continue;\n }\n textByFieldType.computeIfAbsent(assetFieldType, k -> new ArrayList<>());\n int i = 0;\n while (textByFieldType.get(assetFieldType).size() < requiredEntry.getValue()) {\n String textFromDefaults = DEFAULT_TEXT_ASSETS_INFO.get(assetFieldType).get(i++);\n System.out.printf(\n \"A default text '%s' is used to create a text asset for the asset field type '%s'.%n\",\n textFromDefaults, assetFieldType);\n textByFieldType\n .computeIfAbsent(assetFieldType, ft -> new ArrayList<>())\n .add(textFromDefaults);\n }\n }\n\n // Converts the list of text values by field type into AssetOperations and\n // AssetGroupAssetOperations.\n for (Entry<AssetFieldType, List<String>> fieldTypeEntry : textByFieldType.entrySet()) {\n for (String text : fieldTypeEntry.getValue()) {\n // Builds the Asset.\n Asset asset =\n Asset.newBuilder()\n .setResourceName(ResourceNames.asset(customerId, temporaryId--))\n .setTextAsset(TextAsset.newBuilder().setText(text))\n .build();\n // Adds an operation to create the Asset.\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetOperation(AssetOperation.newBuilder().setCreate(asset))\n .build());\n\n // Builds the AssetGroupAsset.\n AssetGroupAsset assetGroupAsset =\n AssetGroupAsset.newBuilder()\n // References the Asset above by resource name.\n .setAsset(asset.getResourceName())\n .setAssetGroup(assetGroupResourceName)\n .setFieldType(fieldTypeEntry.getKey())\n .build();\n // Adds an operation to link the Asset to the AssetGroup.\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupAssetOperation(\n AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset))\n .build());\n }\n }\n return mutateOperations;\n }\n\n /**\n * Creates image assets required for an asset group using the suggested hotel image assets. It\n * adds more image assets to fulfill the requirements if the suggested hotel image assets are not\n * enough.\n *\n * @param customerId the client customer ID.\n * @param hotelAssetSuggestion the hotel asset suggestion.\n * @return a list of mutate operations that create image assets and asset group assets.\n */\n private List<MutateOperation> createOperationsForImageAssetsAndAssetGroupAssets(\n long customerId, HotelAssetSuggestion hotelAssetSuggestion, String assetGroupResourceName) {\n // Creates mutate operations for the suggested image assets.\n List<MutateOperation> mutateOperations = new ArrayList<>();\n // Creates a map of asset field type to list of image URLs for which this method will create\n // assets and asset group assets.\n Map<AssetFieldType, List<String>> imageUrlsByFieldType = new HashMap<>();\n\n if (HotelAssetSuggestionStatus.SUCCESS.equals(hotelAssetSuggestion.getStatus())) {\n // Adds URLs of suggested image assets.\n for (HotelImageAsset hotelImageAsset : hotelAssetSuggestion.getImageAssetsList()) {\n AssetFieldType assetFieldType = hotelImageAsset.getAssetFieldType();\n System.out.printf(\n \"An image asset with URL '%s' is suggested for the asset field type '%s'.%n\",\n hotelImageAsset.getUri(), assetFieldType);\n\n imageUrlsByFieldType\n .computeIfAbsent(assetFieldType, ft -> new ArrayList<>())\n .add(hotelImageAsset.getUri());\n }\n }\n\n // Collects more image URLs by field type to fulfill the requirements.\n for (Entry<AssetFieldType, Integer> requiredEntry :\n MIN_REQUIRED_IMAGE_ASSET_COUNTS.entrySet()) {\n AssetFieldType assetFieldType = requiredEntry.getKey();\n imageUrlsByFieldType.computeIfAbsent(assetFieldType, k -> new ArrayList<>());\n int i = 0;\n while (imageUrlsByFieldType.get(assetFieldType).size() < requiredEntry.getValue()) {\n String imageUrlFromDefaults = DEFAULT_IMAGE_ASSETS_INFO.get(assetFieldType).get(i++);\n System.out.printf(\n \"A default image URL '%s' is used to create an image asset for the asset field type\"\n + \" '%s'.%n\",\n imageUrlFromDefaults, assetFieldType);\n imageUrlsByFieldType\n .computeIfAbsent(assetFieldType, ft -> new ArrayList<>())\n .add(imageUrlFromDefaults);\n }\n }\n\n // Converts the list of URLs by field type into AssetOperations and AssetGroupAssetOperations.\n for (Entry<AssetFieldType, List<String>> fieldTypeEntry : imageUrlsByFieldType.entrySet()) {\n AssetFieldType assetFieldType = fieldTypeEntry.getKey();\n for (String url : fieldTypeEntry.getValue()) {\n // Retrieves the image data from the URL.\n byte[] imageData;\n try {\n imageData = ByteStreams.toByteArray(new URL(url).openStream());\n } catch (IOException e) {\n throw new RuntimeException(\"Failed to retrieve image data from URL: \" + url, e);\n }\n\n // Builds the image asset.\n Asset asset =\n Asset.newBuilder()\n .setResourceName(ResourceNames.asset(customerId, temporaryId--))\n // Provide a unique friendly name to identify your asset.\n // When there is an existing image asset with the same content but a different name,\n // the\n // new name will be dropped silently.\n .setName(\n String.format(\n \"%s#%s\", assetFieldType, CodeSampleHelper.getShortPrintableDateTime()))\n .setImageAsset(ImageAsset.newBuilder().setData(ByteString.copyFrom(imageData)))\n .build();\n // Adds an operation to create the Asset.\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetOperation(AssetOperation.newBuilder().setCreate(asset))\n .build());\n\n // Builds the AssetGroupAsset.\n AssetGroupAsset assetGroupAsset =\n AssetGroupAsset.newBuilder()\n // References the Asset above by resource name.\n .setAsset(asset.getResourceName())\n .setAssetGroup(assetGroupResourceName)\n .setFieldType(assetFieldType)\n .build();\n // Adds an operation to link the Asset to the AssetGroup.\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupAssetOperation(\n AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset))\n .build());\n }\n }\n return mutateOperations;\n }\n\n /**\n * Prints the details of a MutateGoogleAdsResponse. Parses the \"response\" oneof field name and\n * uses it to extract the new entity's name and resource name.\n *\n * @param response the mutate Google Ads response.\n */\n private void printResponseDetails(MutateGoogleAdsResponse response) {\n for (MutateOperationResponse operationResponse : response.getMutateOperationResponsesList()) {\n ResponseCase responseCase = operationResponse.getResponseCase();\n String resourceName;\n switch (responseCase) {\n case ASSET_RESULT:\n resourceName = operationResponse.getAssetResult().getResourceName();\n break;\n case ASSET_GROUP_RESULT:\n resourceName = operationResponse.getAssetGroupResult().getResourceName();\n break;\n case ASSET_GROUP_ASSET_RESULT:\n resourceName = operationResponse.getAssetGroupAssetResult().getResourceName();\n break;\n case ASSET_SET_ASSET_RESULT:\n resourceName = operationResponse.getAssetSetAssetResult().getResourceName();\n break;\n case CAMPAIGN_BUDGET_RESULT:\n resourceName = operationResponse.getCampaignBudgetResult().getResourceName();\n break;\n case CAMPAIGN_RESULT:\n resourceName = operationResponse.getCampaignResult().getResourceName();\n break;\n default:\n throw new IllegalArgumentException(\"Unexpected response case: \" + responseCase);\n }\n System.out.printf(\"Created a(n) %s with resource name: '%s'%n\", responseCase, resourceName);\n }\n }\n}\nAddPerformanceMaxForTravelGoalsCampaign.java\n```\n\nExample:\n```text\n// Copyright 2023 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.Gax.Util;\nusing Google.Ads.GoogleAds.Config;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing Google.Protobuf;\nusing System;\nusing System.Collections.Generic;\nusing static Google.Ads.GoogleAds.V25.Enums.AdvertisingChannelTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AssetFieldTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AssetGroupStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.AssetSetTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.BudgetDeliveryMethodEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.CampaignStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.EuPoliticalAdvertisingStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.HotelAssetSuggestionStatusEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This example shows how to create a Performance Max for travel goals campaign. It also uses\n /// TravelAssetSuggestionService to fetch suggested assets for creating an asset group. In case\n /// there are not enough assets for the asset group (required by Performance Max), this example\n /// will create more assets to fulfill the requirements.\n ///\n /// <p>For more information about Performance Max campaigns, see\n /// https://developers.google.com/google-ads/api/docs/performance-max/overview.</p>\n /// <p>Prerequisites:</p>\n ///\n /// <ul>\n /// <li>You must have at least one conversion action in the account. For more about conversion\n /// actions, see\n /// https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n /// </li>\n /// </ul>\n ///\n /// <p>Notes:</p>\n ///\n /// <ul>\n /// <li>This example uses the default customer conversion goals. For an example of setting\n /// campaign-specific conversion goals, see AddPerformanceMaxRetailCampaign.cs.</li>\n /// <li>To learn how to create asset group signals, see AddPerformanceMaxCampaign.cs.</li>\n /// </ul>\n /// </summary>\n public class AddPerformanceMaxForTravelGoalsCampaign : ExampleBase\n {\n /// <summary>\n /// Command line options for running the\n /// <see cref=\"AddPerformanceMaxForTravelGoalsCampaign\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// The place ID of a hotel property. A place ID uniquely identifies a place in the\n /// Google Places database. See\n /// https://developers.google.com/places/web-service-place-id to learn more.\n /// </summary>\n [Option(\"placeId\", Required = true, HelpText =\n \"The place ID of a hotel property. A place ID uniquely identifies a place in the\" +\n \"Google Places database. See \" +\n \"https://developers.google.com/places/web-service-place-id to learn more.\")]\n public string PlaceId { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n AddPerformanceMaxForTravelGoalsCampaign codeExample =\n new AddPerformanceMaxForTravelGoalsCampaign();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.PlaceId);\n }\n\n // Minimum requirements of assets required in a Performance Max asset group.\n // See https://developers.google.com/google-ads/api/docs/performance-max/assets for details.\n private Dictionary<AssetFieldType, int> MIN_REQUIRED_TEXT_ASSET_COUNTS =\n new Dictionary<AssetFieldType, int>()\n {\n { AssetFieldType.Headline, 3 },\n { AssetFieldType.LongHeadline, 1 },\n { AssetFieldType.Description, 2 },\n { AssetFieldType.BusinessName, 1 },\n };\n\n private Dictionary<AssetFieldType, int> MIN_REQUIRED_IMAGE_ASSET_COUNTS =\n new Dictionary<AssetFieldType, int>()\n {\n { AssetFieldType.MarketingImage, 1 },\n { AssetFieldType.SquareMarketingImage, 1 },\n { AssetFieldType.Logo, 1 },\n };\n\n\n // Texts and URLs used to create text and image assets when the TravelAssetSuggestionService\n // doesn't return enough assets required for creating an asset group.\n private Dictionary<AssetFieldType, List<string>> DEFAULT_TEXT_ASSETS_INFO =\n new Dictionary<AssetFieldType, List<string>>()\n {\n { AssetFieldType.Headline, new List<string>()\n {\n \"Hotel\", \"Travel Reviews\", \"Book travel\"\n }\n },\n { AssetFieldType.LongHeadline, new List<string>() { \"Travel the World\" } },\n { AssetFieldType.Description, new List<string>()\n {\n \"Great deal for your beloved hotel\",\n \"Best rate guaranteed\"\n }\n },\n { AssetFieldType.BusinessName, new List<string>() { \"Interplanetary cruises\" } },\n };\n\n private Dictionary<AssetFieldType, List<string>> DEFAULT_IMAGE_ASSETS_INFO =\n new Dictionary<AssetFieldType, List<string>>()\n {\n { AssetFieldType.MarketingImage, new List<string>()\n {\n \"https://gaagl.page.link/Eit5\"\n }\n },\n { AssetFieldType.SquareMarketingImage, new List<string>()\n {\n \"https://gaagl.page.link/bjYi\"\n }\n },\n { AssetFieldType.Logo, new List<string>()\n {\n \"https://gaagl.page.link/bjYi\"\n }\n },\n };\n\n // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are always\n // negative and unique within one mutate request.\n //\n // <p>See https://developers.google.com/google-ads/api/docs/mutating/best-practices for\n // further details.\n //\n // <p>These temporary IDs are fixed because they are used in multiple places.\n private int ASSET_TEMPORARY_ID = -1;\n private int BUDGET_TEMPORARY_ID = -2;\n private int CAMPAIGN_TEMPORARY_ID = -3;\n private int ASSET_GROUP_TEMPORARY_ID = -4;\n\n // There are also entities that will be created in the same request but do not\n // need to be fixed temporary IDs because they are referenced only once.\n private long temporaryId = -5;\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description => \"This example shows how to create a Performance \" +\n \" Max for travel goals campaign. It also uses TravelAssetSuggestionService to fetch \" +\n \"suggested assets for creating an asset group. In case there are not enough assets \" +\n \"for the asset group (required by Performance Max), this example will create more \" +\n \"assets to fulfill the requirements.\\n\" +\n \"For more information about Performance Max campaigns, see \" +\n \"https://developers.google.com/google-ads/api/docs/performance-max/overview.\\n\" +\n \"Prerequisites:\\n\" +\n \"You must have at least one conversion action in the account. For more about \" +\n \"conversion actions, see \" +\n \"https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\\n\" +\n \"Notes:\\n\" +\n \"- This example uses the default customer conversion goals. For an example of \" +\n \"setting campaign-specific conversion goals, see AddPerformanceMaxRetailCampaign.cs.\\n\" +\n \"- To learn how to create asset group signals, see AddPerformanceMaxCampaign.cs.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID.</param>\n /// <param name=\"placeId\">The place ID of a hotel property.</param>\n public void Run(GoogleAdsClient client, long customerId, string placeId)\n {\n HotelAssetSuggestion hotelAssetSuggestion =\n GetHotelAssetSuggestion(client, customerId, placeId);\n\n // Performance Max campaigns require that repeated assets such as headlines and\n // descriptions be created before the campaign. For the list of required assets for a\n // Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets.\n // This step is the same for any type of Performance Max campaign.\n\n // Creates the headlines using the hotel asset suggestion.\n List<string> headlineAssetResourceNames = CreateMultipleTextAssets(\n client, customerId, AssetFieldType.Headline, hotelAssetSuggestion);\n\n // Creates the descriptions using the hotel asset suggestion.\n List<string> descriptionAssetResourceNames = CreateMultipleTextAssets(\n client, customerId, AssetFieldType.Description, hotelAssetSuggestion);\n\n // Creates a hotel property asset set, which will be used later to link with a newly\n // created campaign.\n string hotelPropertyAssetSetResourceName = CreateHotelAssetSet(client, customerId);\n\n // Creates a hotel property asset and link it with the previously created hotel property\n // asset set. This asset will also be linked to an asset group in the later steps.\n // In the real-world scenario, you'd need to create many assets for all your hotel\n // properties. We use one hotel property here for simplicity.\n // Both asset and asset set need to be created before creating a campaign, so we cannot\n // bundle them with other mutate operations below.\n string hotelPropertyAssetResourceName = CreateHotelAsset(\n client, customerId, placeId, hotelPropertyAssetSetResourceName);\n\n // It's important to create the entities below in this order because they depend on\n // each other.\n // The methods below create and return mutate operations that we later provide to the\n // GoogleAdsService.Mutate method in order to create the entities in a single request.\n // Since the entities for a Performance Max campaign are closely tied to one-another,\n // it's considered a best practice to create them in a single Mutate request so they\n // all complete successfully or fail entirely, leaving no orphaned entities. See:\n // https://developers.google.com/google-ads/api/docs/mutating/overview.\n List<MutateOperation> mutateOperations = new List<MutateOperation>();\n mutateOperations.Add(CreateCampaignBudgetOperation(customerId));\n mutateOperations.Add(CreateCampaignOperation(customerId,\n hotelPropertyAssetSetResourceName));\n mutateOperations.AddRange(\n CreateAssetGroupOperations(\n customerId,\n hotelPropertyAssetResourceName,\n headlineAssetResourceNames,\n descriptionAssetResourceNames,\n hotelAssetSuggestion,\n client.Config\n )\n );\n\n // Issues a mutate request to create everything and prints the results.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.Mutate(customerId.ToString(), mutateOperations);\n Console.WriteLine(\"Created the following entities for a campaign budget, a campaign, \" +\n \"and an asset group for Performance Max for travel goals:\");\n PrintResponseDetails(response);\n }\n\n /// <summary>\n /// Returns hotel asset suggestion obtained from TravelAssetsSuggestionService.\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID.</param>\n /// <param name=\"placeId\">The place ID of a hotel property.</param>\n /// <returns>The hotel asset suggestion.</returns>\n /// </summary>\n private HotelAssetSuggestion GetHotelAssetSuggestion(GoogleAdsClient client,\n long customerId, string placeId)\n {\n // Get the TravelAssetSuggestionService client.\n TravelAssetSuggestionServiceClient travelAssetSuggestionService =\n client.GetService(Services.V25.TravelAssetSuggestionService);\n\n SuggestTravelAssetsRequest request = new SuggestTravelAssetsRequest\n {\n CustomerId = customerId.ToString(),\n LanguageOption = \"en-US\",\n };\n\n request.PlaceIds.Add(placeId);\n\n SuggestTravelAssetsResponse response = travelAssetSuggestionService.SuggestTravelAssets(\n request\n );\n\n Console.WriteLine($\"Fetched a hotel asset suggestion for the place ID {placeId}\");\n return response.HotelAssetSuggestions[0];\n }\n\n\n ///<summary>\n /// Creates multiple text assets and returns the list of resource names. The hotel asset\n /// suggestion is used to create a text asset first. If the number of created text assets is\n /// still fewer than the minimum required number of assets of the specified asset field\n /// type, adds more text assets to fulfill the requirement.\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID.</param>\n /// <param name=\"assetFieldType\">The asset field type that the text assets will be created\n /// for.</param>\n /// <param name=\"hotelAssetSuggestion\">The hotel asset suggestion.</param>\n /// <returns>The resource names of the created text assets.</returns>\n /// </summary>\n private List<string> CreateMultipleTextAssets(GoogleAdsClient client, long customerId,\n AssetFieldType assetFieldType, HotelAssetSuggestion hotelAssetSuggestion)\n {\n // Uses the GoogleAdService to create multiple text assets in a single request.\n List<MutateOperation> mutateOperations = new List<MutateOperation>();\n\n // First, adds all the text assets of the specified asset field type.\n // Filters to only the specified asset field type.\n // Constructs a mutate operation to create the asset.\n // Adds the operation to the list.\n if (hotelAssetSuggestion.Status == HotelAssetSuggestionStatus.Success)\n {\n foreach (HotelTextAsset asset in hotelAssetSuggestion.TextAssets)\n {\n if (asset.AssetFieldType == assetFieldType)\n {\n MutateOperation operation = new MutateOperation\n {\n AssetOperation = new AssetOperation\n {\n Create = new Asset\n {\n TextAsset = new TextAsset\n {\n Text = asset.Text\n }\n }\n }\n };\n mutateOperations.Add(operation);\n }\n }\n }\n\n // If the added assets are still less than the minimum required assets for the asset\n // field type, add more text assets using the default texts.\n int i = 0;\n while (mutateOperations.Count < MIN_REQUIRED_TEXT_ASSET_COUNTS[assetFieldType])\n {\n string text = DEFAULT_TEXT_ASSETS_INFO[assetFieldType][i++];\n MutateOperation operation = new MutateOperation\n {\n AssetOperation = new AssetOperation {\n Create = new Asset {\n TextAsset = new TextAsset {\n Text = text\n }\n }\n }\n };\n mutateOperations.Add(operation);\n }\n\n GoogleAdsServiceClient googleAdsService =\n client.GetService(Services.V25.GoogleAdsService);\n\n MutateGoogleAdsResponse response =\n googleAdsService.Mutate(customerId.ToString(), mutateOperations);\n\n List<string> assetResourceNames = new List<string>();\n\n foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)\n {\n MutateAssetResult assetResult = operationResponse.AssetResult;\n assetResourceNames.Add(assetResult.ResourceName);\n }\n\n Console.WriteLine($\"The following assets were created for the asset field type \" +\n $\"{assetFieldType}\");\n PrintResponseDetails(response);\n\n return assetResourceNames;\n }\n\n /// <summary>\n /// Creates a hotel property asset set.\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID.</param>\n /// <returns> The created hotel property asset set resource name.</returns>\n /// </summary>\n private string CreateHotelAssetSet(GoogleAdsClient client, long customerId)\n {\n AssetSetOperation operation = new AssetSetOperation()\n {\n Create = new AssetSet {\n Name = \"My Hotel property asset set #\" + ExampleUtilities.GetRandomString(),\n Type = AssetSetType.HotelProperty\n }\n };\n\n AssetSetServiceClient assetSetService = client.GetService(Services.V25.AssetSetService);\n\n MutateAssetSetsResponse response = assetSetService.MutateAssetSets(\n customerId.ToString(),\n new List<AssetSetOperation> { operation }\n );\n\n string assetResourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Created an asset set with resource name: {assetResourceName}\");\n return assetResourceName;\n }\n\n /// <summary>\n /// Creates a hotel property asset using the specified place ID. The place ID must belong\n /// to a hotel property. Then, links it to the specified asset set.\n ///\n /// <p>See https://developers.google.com/places/web-service/place-id to search for a hotel\n /// place ID.</p>\n /// <param name=\"client\">The Google Ads API client.</param>\n /// <param name=\"customerId\">The client customer ID.</param>\n /// <param name=\"placeId\">The place ID for a hotel.</param>\n /// <param name=\"hotelPropertyAssetSetResourceName\">The hotel asset set resource\n /// name.</param>\n /// <returns>The created hotel property asset resource name.</returns>\n /// </summary>\n private string CreateHotelAsset(\n GoogleAdsClient client, long customerId, string placeId,\n string hotelPropertyAssetSetResourceName)\n {\n // Uses the GoogleAdService to create an asset and asset set asset in a single request.\n List<MutateOperation> mutateOperations = new List<MutateOperation>();\n string assetResourceName = ResourceNames.Asset(customerId, ASSET_TEMPORARY_ID);\n\n // Creates a mutate operation for a hotel property asset.\n Asset hotelPropertyAsset = new Asset()\n {\n ResourceName = assetResourceName,\n HotelPropertyAsset = new HotelPropertyAsset\n {\n PlaceId = placeId\n }\n };\n mutateOperations.Add(new MutateOperation\n {\n AssetOperation = new AssetOperation\n {\n Create = hotelPropertyAsset\n }\n });\n\n // Creates a mutate operation for an asset set asset.\n AssetSetAsset assetSetAsset = new AssetSetAsset\n {\n Asset = assetResourceName,\n AssetSet = hotelPropertyAssetSetResourceName\n };\n mutateOperations.Add(new MutateOperation\n {\n AssetSetAssetOperation = new AssetSetAssetOperation\n {\n Create = assetSetAsset\n }\n });\n\n // Issues a mutate request to create all entities.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.Mutate(customerId.ToString(), mutateOperations);\n Console.WriteLine(\"Created the following entities for the hotel asset:\");\n PrintResponseDetails(response);\n\n return response.MutateOperationResponses[0].AssetResult.ResourceName;\n }\n\n /// <summary>\n /// Creates a mutate operation that creates a new campaign budget.\n /// <p>A temporary ID will be assigned to this campaign budget so that it can be referenced\n /// by other objects being created in the same mutate request.</p>\n /// <param name=\"customerId\">The client customer ID.</param>\n /// <returns>A mutate operation that creates a campaign budget.</returns>\n /// </summary>\n private MutateOperation CreateCampaignBudgetOperation(long customerId)\n {\n CampaignBudget campaignBudget = new CampaignBudget\n {\n Name = \"Performance Max for travel goals campaign budget #\" +\n ExampleUtilities.GetRandomString(),\n // The budget period already defaults to DAILY.\n AmountMicros = 500000,\n DeliveryMethod = BudgetDeliveryMethod.Standard,\n // A Performance Max campaign cannot use a shared campaign budget.\n ExplicitlyShared = false,\n // Sets a temporary ID in the budget's resource name, so it can be referenced\n // by the campaign in later steps.\n ResourceName = ResourceNames.CampaignBudget(customerId, BUDGET_TEMPORARY_ID)\n };\n\n return new MutateOperation\n {\n CampaignBudgetOperation = new CampaignBudgetOperation\n {\n Create = campaignBudget\n }\n };\n }\n\n /// <summary>\n /// Creates a mutate operation that creates a new Performance Max campaign. Links the\n /// specified hotel property asset set to this campaign.\n /// <p>A temporary ID will be assigned to this campaign so that it can be referenced by\n /// other objects being created in the same mutate request.</p>\n /// <param name=\"customerId\">The client customer ID.</param>\n /// <param name=\"hotelPropertyAssetSetResourceName\"> The resource name of the hotel property\n /// asset set.</param>\n /// <returns>A mutate operation that creates a campaign.</returns>\n /// </summary>\n private MutateOperation CreateCampaignOperation(long customerId,\n string hotelPropertyAssetSetResourceName)\n {\n Campaign performanceMaxCampaign = new Campaign\n {\n Name = \"Performance Max for travel goals campaign #\" +\n ExampleUtilities.GetRandomString(),\n // Sets the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n Status = CampaignStatus.Paused,\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n AdvertisingChannelType = AdvertisingChannelType.PerformanceMax,\n // To create a Performance Max for travel goals campaign, you need to set\n // `hotel_property_asset_set`.\n HotelPropertyAssetSet = hotelPropertyAssetSetResourceName,\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n MaximizeConversionValue = new MaximizeConversionValue\n {\n TargetRoas = 3.5\n },\n // Assigns the resource name with a temporary ID.\n ResourceName = ResourceNames.Campaign(customerId, CAMPAIGN_TEMPORARY_ID),\n // Sets the budget using the given budget resource name.\n CampaignBudget = ResourceNames.CampaignBudget(customerId, BUDGET_TEMPORARY_ID),\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n };\n\n return new MutateOperation\n {\n CampaignOperation = new CampaignOperation\n {\n Create = performanceMaxCampaign\n }\n };\n }\n\n /// <summary>\n /// Creates a mutate operation that creates a new asset group.\n /// <param name=\"customerId\">The client customer ID.</param>\n /// <param name=\"hotelPropertyAssetResourceName\"> The resource name of the hotel property\n /// asset.</param>\n /// <param name=\"headlineAssetResourceNames\">The resource names for headline\n /// assets.</param>\n /// <param name=\"descriptionAssetResourceNames\">The resource names for description\n /// assets.</param>\n /// <param name=\"hotelAssetSuggestion\">The hotel asset suggestion.</param>\n /// <param name=\"config\">The Google Ads configuration.</param>\n /// <returns>A mutate operation that creates an asset group.</returns>\n /// </summary>\n private List<MutateOperation> CreateAssetGroupOperations(\n long customerId,\n string hotelPropertyAssetResourceName,\n List<string> headlineAssetResourceNames,\n List<string> descriptionAssetResourceNames,\n HotelAssetSuggestion hotelAssetSuggestion,\n GoogleAdsConfig config\n )\n {\n List<MutateOperation> mutateOperations = new List<MutateOperation>();\n\n // Creates a new mutate operation that creates an asset group using suggested\n // information when available.\n string assetGroupName;\n List<string> assetGroupFinalUrls = new List<string>();\n if (hotelAssetSuggestion.Status == HotelAssetSuggestionStatus.Success)\n {\n assetGroupName = hotelAssetSuggestion.HotelName;\n assetGroupFinalUrls.Add(hotelAssetSuggestion.FinalUrl);\n }\n else\n {\n assetGroupName = \"Performance Max for travel goals asset group #\"\n + ExampleUtilities.GetRandomString();\n assetGroupFinalUrls.Add(\"https://www.example.com\");\n }\n\n string assetGroupResourceName = ResourceNames.AssetGroup(customerId,\n ASSET_GROUP_TEMPORARY_ID);\n\n AssetGroup assetGroup = new AssetGroup\n {\n ResourceName = assetGroupResourceName,\n Name = assetGroupName,\n Campaign = ResourceNames.Campaign(customerId, CAMPAIGN_TEMPORARY_ID),\n Status = AssetGroupStatus.Paused\n };\n\n assetGroup.FinalUrls.AddRange(assetGroupFinalUrls);\n\n mutateOperations.Add(new MutateOperation\n {\n AssetGroupOperation = new AssetGroupOperation\n {\n Create = assetGroup\n }\n });\n\n // An asset group is linked to an asset by creating a new asset group asset\n // and providing:\n // - the resource name of the asset group\n // - the resource name of the asset\n // - the field_type of the asset in this asset group\n //\n // To learn more about asset groups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n // Headline and description assets were created at the first step of this example.\n // So, we just need to link them with the created asset group.\n List<AssetGroupAsset> assetGroupAssets = new List<AssetGroupAsset>();\n foreach (string headlineAssetResourceName in headlineAssetResourceNames)\n {\n assetGroupAssets.Add(new AssetGroupAsset\n {\n Asset = headlineAssetResourceName,\n AssetGroup = assetGroupResourceName,\n FieldType = AssetFieldType.Headline\n });\n }\n\n foreach (string descriptionAssetResourceName in descriptionAssetResourceNames)\n {\n assetGroupAssets.Add(new AssetGroupAsset\n {\n Asset = descriptionAssetResourceName,\n AssetGroup = assetGroupResourceName,\n FieldType = AssetFieldType.Description\n });\n }\n\n foreach (AssetGroupAsset assetGroupAsset in assetGroupAssets)\n {\n mutateOperations.Add(new MutateOperation\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation\n {\n Create = assetGroupAsset\n }\n });\n }\n\n // Link the previously created hotel property asset to the asset group. In the\n // real-world scenario, you'd need to do this step several times for each hotel property\n // asset.\n AssetGroupAsset hotelPropertyAssetGroupAsset = new AssetGroupAsset\n {\n Asset = hotelPropertyAssetResourceName,\n AssetGroup = assetGroupResourceName,\n FieldType = AssetFieldType.HotelProperty\n };\n\n // Adds an operation to link the hotel property asset to the asset group.\n mutateOperations.Add(new MutateOperation\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation\n {\n Create = hotelPropertyAssetGroupAsset\n }\n });\n\n // Creates the rest of the required text assets and links them to the asset group.\n mutateOperations.AddRange(\n CreateOperationsForTextAssetsAndAssetGroupAssets(\n customerId, hotelAssetSuggestion, assetGroupResourceName\n )\n );\n\n // Creates the image assets and links them to the asset group. Some optional image\n // assets suggested by the TravelAssetSuggestionService might be created too.\n mutateOperations.AddRange(\n CreateOperationsForImageAssetsAndAssetGroupAssets(\n customerId, hotelAssetSuggestion, assetGroupResourceName, config\n )\n );\n\n if (hotelAssetSuggestion.Status == HotelAssetSuggestionStatus.Success)\n {\n // Creates a new mutate operation for a suggested call-to-action asset and links it\n // to the asset group.\n Asset callToActionAsset = new Asset\n {\n ResourceName = ResourceNames.Asset(customerId, temporaryId),\n Name = \"Suggested call-to-action asset #\" + ExampleUtilities.GetRandomString(),\n CallToActionAsset = new CallToActionAsset\n {\n CallToAction = hotelAssetSuggestion.CallToAction\n }\n };\n // Adds an operation to create the call-to-action asset.\n mutateOperations.Add(new MutateOperation\n {\n AssetOperation = new AssetOperation\n {\n Create = callToActionAsset\n }\n });\n\n AssetGroupAsset callToActionAssetGroupAsset = new AssetGroupAsset\n {\n Asset = callToActionAsset.ResourceName,\n AssetGroup = assetGroupResourceName,\n FieldType = AssetFieldType.CallToActionSelection\n };\n // Adds an operation to link the call-to-action asset to the asset group.\n mutateOperations.Add(new MutateOperation\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation\n {\n Create = callToActionAssetGroupAsset\n }\n });\n temporaryId--;\n }\n\n return mutateOperations;\n }\n\n /// <summary>\n /// Creates text assets required for an asset group using the suggested hotel text assets.\n /// It adds more text assets to fulfill the requirements if the suggested hotel text assets\n /// are not enough.\n /// <param name=\"customerId\">The client customer ID.</param>\n /// <param name=\"hotelAssetSuggestion\">The hotel asset suggestion.</param>\n /// <param name=\"assetGroupResourceName\">The resource name of the asset group.</param>\n /// <returns>A list of mutate operations that create text assets and asset group\n /// assets.</returns>\n /// </summary>\n private List<MutateOperation> CreateOperationsForTextAssetsAndAssetGroupAssets(\n long customerId,\n HotelAssetSuggestion hotelAssetSuggestion,\n string assetGroupResourceName\n )\n {\n // Creates mutate operations for the suggested text assets except for headlines and\n // descriptions, which were created previously.\n List<MutateOperation> mutateOperations = new List<MutateOperation>();\n // Creates a map of asset field type to list of text values to create.\n Dictionary<AssetFieldType, List<string>> textByFieldType =\n new Dictionary<AssetFieldType, List<string>>();\n\n if (hotelAssetSuggestion.Status == HotelAssetSuggestionStatus.Success)\n {\n // Adds text values of suggested text assets.\n foreach (HotelTextAsset hotelTextAsset in hotelAssetSuggestion.TextAssets)\n {\n AssetFieldType assetFieldType = hotelTextAsset.AssetFieldType;\n if (assetFieldType == AssetFieldType.Headline ||\n assetFieldType == AssetFieldType.Description)\n {\n // Headlines and descriptions were already created at the first step of this\n // code example.\n continue;\n }\n Console.WriteLine($\"A text asset with text {hotelTextAsset.Text} is \" +\n $\"suggested for the asset field type {assetFieldType}\");\n List<string> existingTexts = null;\n if (!textByFieldType.TryGetValue(assetFieldType, out existingTexts))\n {\n existingTexts = textByFieldType[assetFieldType] = new List<string>();\n }\n\n existingTexts.Add(hotelTextAsset.Text);\n }\n }\n\n // Collects more text values by field type to fulfill the requirements.\n foreach (AssetFieldType assetFieldType in MIN_REQUIRED_TEXT_ASSET_COUNTS.Keys)\n {\n if (assetFieldType == AssetFieldType.Headline ||\n assetFieldType == AssetFieldType.Description)\n {\n // Headlines and descriptions were already created at the first step of\n // this code example.\n continue;\n }\n\n List<string> existingTexts = null;\n if (!textByFieldType.TryGetValue(assetFieldType, out existingTexts))\n {\n existingTexts = textByFieldType[assetFieldType] = new List<string>();\n }\n\n int i = 0;\n while (textByFieldType[assetFieldType].Count <\n MIN_REQUIRED_TEXT_ASSET_COUNTS[assetFieldType])\n {\n string textFromDefaults = DEFAULT_TEXT_ASSETS_INFO[assetFieldType][i++];\n Console.WriteLine($\"A default text '{textFromDefaults}' is used to create a \" +\n $\"text asset for the asset field type '{assetFieldType}'\");\n existingTexts.Add(textFromDefaults);\n }\n }\n\n // Converts the list of text values by field type into AssetOperations and\n // AssetGroupAssetOperations.\n foreach (AssetFieldType assetFieldType in textByFieldType.Keys)\n {\n foreach (string text in textByFieldType[assetFieldType])\n {\n // Builds the asset.\n Asset asset = new Asset\n {\n ResourceName = ResourceNames.Asset(customerId, temporaryId--),\n TextAsset = new TextAsset\n {\n Text = text\n }\n };\n // Adds an operation to create the Asset.\n mutateOperations.Add(new MutateOperation\n {\n AssetOperation = new AssetOperation\n {\n Create = asset\n }\n });\n\n //Builds the AssetGroupAsset.\n AssetGroupAsset assetGroupAsset = new AssetGroupAsset\n {\n Asset = asset.ResourceName,\n AssetGroup = assetGroupResourceName,\n FieldType = assetFieldType\n };\n // Adds an operation to link the Asset to the AssetGroup.\n mutateOperations.Add(new MutateOperation\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation\n {\n Create = assetGroupAsset\n }\n });\n }\n }\n\n return mutateOperations;\n }\n\n /// <summary>\n /// Creates image assets required for an asset group using the suggested hotel image assets.\n /// It adds more image assets to fulfill the requirements if the suggested hotel image\n /// assets are not enough.\n /// <param name=\"customerId\">The client customer ID.</param>\n /// <param name=\"hotelAssetSuggestion\">The hotel asset suggestion.</param>\n /// <param name=\"assetGroupResourceName\">The resource name of the asset group.</param>\n /// <param name=\"config\">The Google Ads config.</param>\n /// <returns>A list of mutate operations that create image assets and asset group\n /// assets.</returns>\n /// </summary>\n private List<MutateOperation> CreateOperationsForImageAssetsAndAssetGroupAssets(\n long customerId,\n HotelAssetSuggestion hotelAssetSuggestion,\n string assetGroupResourceName,\n GoogleAdsConfig config\n )\n {\n // Creates mutate operations for the suggested image assets.\n List<MutateOperation> mutateOperations = new List<MutateOperation>();\n // Creates a map of asset field type to list of image URLs for which this method will\n // create assets and asset group assets.\n Dictionary<AssetFieldType, List<string>> imageUrlsByFieldType =\n new Dictionary<AssetFieldType, List<string>>();\n\n if (hotelAssetSuggestion.Status == HotelAssetSuggestionStatus.Success)\n {\n // Adds URLs of suggested image assets.\n foreach (HotelImageAsset hotelImageAsset in hotelAssetSuggestion.ImageAssets)\n {\n AssetFieldType assetFieldType = hotelImageAsset.AssetFieldType;\n Console.WriteLine($\"An image asset with URL '{hotelImageAsset.Uri} is \" +\n $\"suggested for the asset field type {assetFieldType}\");\n\n List<string> existingImageUrls = null;\n if (!imageUrlsByFieldType.TryGetValue(assetFieldType, out existingImageUrls))\n {\n existingImageUrls = imageUrlsByFieldType[assetFieldType] =\n new List<string>();\n }\n existingImageUrls.Add(hotelImageAsset.Uri);\n }\n }\n\n // Collects more image URLs by field type to fulfill the requirements.\n foreach (AssetFieldType assetFieldType in MIN_REQUIRED_IMAGE_ASSET_COUNTS.Keys)\n {\n List<string> existingImageUrls = null;\n if (!imageUrlsByFieldType.TryGetValue(assetFieldType, out existingImageUrls))\n {\n existingImageUrls = imageUrlsByFieldType[assetFieldType] =\n new List<string>();\n }\n\n int i = 0;\n while (imageUrlsByFieldType[assetFieldType].Count <\n MIN_REQUIRED_IMAGE_ASSET_COUNTS[assetFieldType])\n {\n string imageUrlFromDefaults = DEFAULT_IMAGE_ASSETS_INFO[assetFieldType][i++];\n Console.WriteLine($\"A default image URL '{imageUrlFromDefaults} is used to \" +\n $\"create an image asset for the asset field type {assetFieldType}\");\n existingImageUrls.Add(imageUrlFromDefaults);\n }\n }\n\n // Converts the list of URLs by field type into AssetOperations and\n // AssetGroupAssetOperations.\n foreach (AssetFieldType assetFieldType in imageUrlsByFieldType.Keys)\n {\n foreach (string imageUrl in imageUrlsByFieldType[assetFieldType])\n {\n // Builds the image asset.\n Asset asset = new Asset\n {\n ResourceName = ResourceNames.Asset(customerId, temporaryId--),\n // Provide a unique friendly name to identify your asset.\n // When there is an existing image asset with the same content but a\n // different name, the new name will be dropped silently.\n Name = $\"{assetFieldType}{ExampleUtilities.GetRandomString()}\",\n ImageAsset = new ImageAsset\n {\n Data = ByteString.CopyFrom(\n MediaUtilities.GetAssetDataFromUrl(imageUrl, config)\n )\n }\n };\n\n // Adds an operation to create the asset.\n mutateOperations.Add(new MutateOperation\n {\n AssetOperation = new AssetOperation\n {\n Create = asset\n }\n });\n\n // Builds the AssetGroupAsset\n AssetGroupAsset assetGroupAsset = new AssetGroupAsset\n {\n Asset = asset.ResourceName,\n AssetGroup = assetGroupResourceName,\n FieldType = assetFieldType\n };\n\n // Adds an operation to link the Asset to the AssetGroup.\n mutateOperations.Add(new MutateOperation\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation\n {\n Create = assetGroupAsset\n }\n });\n }\n\n }\n\n return mutateOperations;\n }\n\n /// <summary>\n /// Prints the details of a MutateGoogleAdsResponse. Parses the \"response\" field name and\n /// uses it to extract the new entity's name and resource name.\n /// <param name=\"response\">The mutate Google Ads response.</param>\n /// </summary>\n private void PrintResponseDetails(MutateGoogleAdsResponse response) {\n foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)\n {\n string resourceName;\n\n string entityName = operationResponse.ResponseCase.ToString();\n // Trim the substring \"Result\" from the end of the entity name.\n entityName = entityName.Remove(entityName.Length - 6);\n switch (operationResponse.ResponseCase)\n {\n case MutateOperationResponse.ResponseOneofCase.AssetResult:\n resourceName = operationResponse.AssetResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AssetGroupResult:\n resourceName = operationResponse.AssetGroupResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AssetGroupAssetResult:\n resourceName = operationResponse.AssetGroupAssetResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.AssetSetAssetResult:\n resourceName = operationResponse.AssetSetAssetResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.CampaignBudgetResult:\n resourceName = operationResponse.CampaignBudgetResult.ResourceName;\n break;\n\n case MutateOperationResponse.ResponseOneofCase.CampaignResult:\n resourceName = operationResponse.CampaignResult.ResourceName;\n break;\n\n default:\n resourceName = \"<not found>\";\n break;\n }\n Console.WriteLine(\n $\"Created a(n) {entityName} with resource name: '{resourceName}'.\");\n }\n }\n }\n}AddPerformanceMaxForTravelGoalsCampaign.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2023 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\Travel;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\CallToActionAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\HotelPropertyAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ImageAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\MaximizeConversionValue;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\TextAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdvertisingChannelTypeEnum\\AdvertisingChannelType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetFieldTypeEnum\\AssetFieldType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetGroupStatusEnum\\AssetGroupStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AssetSetTypeEnum\\AssetSetType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\BudgetDeliveryMethodEnum\\BudgetDeliveryMethod;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\CampaignStatusEnum\\CampaignStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\EuPoliticalAdvertisingStatusEnum\\EuPoliticalAdvertisingStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\HotelAssetSuggestionStatusEnum\\HotelAssetSuggestionStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Asset;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AssetGroup;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AssetGroupAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AssetSet;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AssetSetAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Campaign;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CampaignBudget;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetGroupAssetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetGroupOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetSetAssetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AssetSetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignBudgetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\HotelAssetSuggestion;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\HotelImageAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\HotelTextAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAssetSetsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateGoogleAdsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateGoogleAdsResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateOperationResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\SuggestTravelAssetsRequest;\nuse Google\\ApiCore\\ApiException;\nuse Google\\ApiCore\\Serializer;\n\n/**\n * This example shows how to create a Performance Max for travel goals campaign. It also uses\n * TravelAssetSuggestionService to fetch suggested assets for creating an asset group. In case\n * there are not enough assets for the asset group (required by Performance Max), this example will\n * create more assets to fulfill the requirements.\n *\n * For more information about Performance Max campaigns, see\n * https://developers.google.com/google-ads/api/docs/performance-max/overview.\n *\n * Prerequisites:\n * - You must have at least one conversion action in the account. For more about conversion actions,\n * see\n * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n *\n * Notes:\n * - This example uses the default customer conversion goals. For an example of setting\n * campaign-specific conversion goals, see ShoppingAds/AddPerformanceMaxRetailCampaign.php.\n * - To learn how to create asset group signals, see\n * AdvancedOperations/AddPerformanceMaxCampaign.php.\n */\nclass AddPerformanceMaxForTravelGoalsCampaign\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n // Sets a place ID that uniquely identifies a place in the Google Places database.\n // See https://developers.google.com/places/web-service/place-id to learn more.\n // The provided place ID must belong to a hotel property.\n private const PLACE_ID = 'INSERT_PLACE_ID_HERE';\n\n // Minimum requirements of assets required in a Performance Max asset group.\n // See https://developers.google.com/google-ads/api/docs/performance-max/assets for details.\n private const MIN_REQUIRED_TEXT_ASSET_COUNTS = [\n AssetFieldType::HEADLINE => 3,\n AssetFieldType::LONG_HEADLINE => 1,\n AssetFieldType::DESCRIPTION => 2,\n AssetFieldType::BUSINESS_NAME => 1\n ];\n private const MIN_REQUIRED_IMAGE_ASSET_COUNTS = [\n AssetFieldType::MARKETING_IMAGE => 1,\n AssetFieldType::SQUARE_MARKETING_IMAGE => 1,\n AssetFieldType::LOGO => 1\n ];\n // Texts and URLs used to create text and image assets when the TravelAssetSuggestionService\n // doesn't return enough assets required for creating an asset group.\n private const DEFAULT_TEXT_ASSETS_INFO = [\n AssetFieldType::HEADLINE => ['Hotel', 'Travel Reviews', 'Book travel'],\n AssetFieldType::LONG_HEADLINE => ['Travel the World'],\n AssetFieldType::DESCRIPTION => [\n 'Great deal for your beloved hotel',\n 'Best rate guaranteed'\n ],\n AssetFieldType::BUSINESS_NAME => ['Interplanetary Cruises']\n ];\n private const DEFAULT_IMAGE_ASSETS_INFO = [\n AssetFieldType::MARKETING_IMAGE => ['https://gaagl.page.link/Eit5'],\n AssetFieldType::SQUARE_MARKETING_IMAGE => ['https://gaagl.page.link/bjYi'],\n AssetFieldType::LOGO => ['https://gaagl.page.link/bjYi']\n ];\n\n // We specify temporary IDs that are specific to a single mutate request.\n // Temporary IDs are always negative and unique within one mutate request.\n //\n // See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n // for further details.\n //\n // These temporary IDs are fixed because they are used in multiple places.\n private const ASSET_TEMPORARY_ID = -1;\n private const BUDGET_TEMPORARY_ID = -2;\n private const CAMPAIGN_TEMPORARY_ID = -3;\n private const ASSET_GROUP_TEMPORARY_ID = -4;\n\n // There are also entities that will be created in the same request but do not need to be fixed\n // temporary IDs because they are referenced only once.\n /** @var int the negative temporary ID used in bulk mutates. */\n private static $nextTempId = self::ASSET_GROUP_TEMPORARY_ID - 1;\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::PLACE_ID => GetOpt::REQUIRED_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())\n ->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::PLACE_ID] ?: self::PLACE_ID\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param string $placeId the place ID for a hotel property asset\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $placeId\n ) {\n // Gets hotel asset suggestion using the TravelAssetSuggestionService.\n $hotelAssetSuggestion =\n self::getHotelAssetSuggestion($googleAdsClient, $customerId, $placeId);\n\n // Performance Max campaigns require that repeated assets such as headlines\n // and descriptions be created before the campaign.\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets.\n //\n // This step is the same for any types of Performance Max campaigns.\n\n // Creates the headlines using the hotel asset suggestion.\n $headlineAssetResourceNames = self::createMultipleTextAssets(\n $googleAdsClient,\n $customerId,\n AssetFieldType::HEADLINE,\n $hotelAssetSuggestion\n );\n // Creates the descriptions using the hotel asset suggestion.\n $descriptionAssetResourceNames = self::createMultipleTextAssets(\n $googleAdsClient,\n $customerId,\n AssetFieldType::DESCRIPTION,\n $hotelAssetSuggestion\n );\n\n // Creates a hotel property asset set, which will be used later to link with a newly created\n // campaign.\n $hotelPropertyAssetSetResourceName =\n self::createHotelAssetSet($googleAdsClient, $customerId);\n // Creates a hotel property asset and link it with the previously created hotel property\n // asset set. This asset will also be linked to an asset group in the later steps.\n // In the real-world scenario, you'd need to create many assets for all your hotel\n // properties. We use one hotel property here for simplicity.\n // Both asset and asset set need to be created before creating a campaign, so we cannot\n // bundle them with other mutate operations below.\n $hotelPropertyAssetResourceName = self::createHotelAsset(\n $googleAdsClient,\n $customerId,\n $placeId,\n $hotelPropertyAssetSetResourceName\n );\n\n // It's important to create the below entities in this order because they depend on\n // each other.\n // The below methods create and return mutate operations that we later provide to the\n // GoogleAdsService.Mutate method in order to create the entities in a single request.\n // Since the entities for a Performance Max campaign are closely tied to one-another, it's\n // considered a best practice to create them in a single Mutate request so they all complete\n // successfully or fail entirely, leaving no orphaned entities. See:\n // https://developers.google.com/google-ads/api/docs/mutating/overview.\n $operations = [];\n $operations[] = self::createCampaignBudgetOperation($customerId);\n $operations[] =\n self::createCampaignOperation($customerId, $hotelPropertyAssetSetResourceName);\n $operations = array_merge($operations, self::createAssetGroupOperations(\n $customerId,\n $hotelPropertyAssetResourceName,\n $headlineAssetResourceNames,\n $descriptionAssetResourceNames,\n $hotelAssetSuggestion\n ));\n\n // Issues a mutate request to create everything and prints the results.\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n $response = $googleAdsServiceClient->mutate(\n MutateGoogleAdsRequest::build($customerId, $operations)\n );\n print \"Created the following entities for a campaign budget, a campaign, and an asset group\"\n . \" for Performance Max for travel goals:\" . PHP_EOL;\n self::printResponseDetails($response);\n }\n\n /**\n * Returns hotel asset suggestion obtained from TravelAssetsSuggestionService.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param string $placeId the place ID of the hotel property you want to get its suggested\n * assets\n * @return HotelAssetSuggestion a hotel asset suggestion\n */\n private static function getHotelAssetSuggestion(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $placeId\n ): HotelAssetSuggestion {\n // Send a request to suggest assets to be created as an asset group for the Performance Max\n // for travel goals campaign.\n $travelAssetSuggestionServiceClient =\n $googleAdsClient->getTravelAssetSuggestionServiceClient();\n // Uses 'en-US' as an example. It can be any language specifications in BCP 47 format.\n $request = SuggestTravelAssetsRequest::build($customerId, 'en-US');\n // The service accepts several place IDs. We use only one here for demonstration.\n $request->setPlaceIds([$placeId]);\n $response = $travelAssetSuggestionServiceClient->suggestTravelAssets($request);\n printf(\"Fetched a hotel asset suggestion for the place ID '%s'.%s\", $placeId, PHP_EOL);\n return $response->getHotelAssetSuggestions()[0];\n }\n\n /**\n * Creates multiple text assets and returns the list of resource names. The hotel asset\n * suggestion is used to create a text asset first. If the number of created text assets is\n * still fewer than the minimum required number of assets of the specified asset field type,\n * adds more text assets to fulfill the requirement.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $assetFieldType the asset field type that this text assets will be created for\n * @param HotelAssetSuggestion $hotelAssetSuggestion the hotel asset suggestion\n * @return string[] a list of asset resource names\n */\n private static function createMultipleTextAssets(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $assetFieldType,\n HotelAssetSuggestion $hotelAssetSuggestion\n ): array {\n // We use the GoogleAdService to create multiple text assets in a single request.\n // First, adds all the text assets of the specified asset field type.\n $operations = [];\n $numOperationsAdded = 0;\n if ($hotelAssetSuggestion->getStatus() === HotelAssetSuggestionStatus::SUCCESS) {\n foreach ($hotelAssetSuggestion->getTextAssets() as $textAsset) {\n /** @var HotelTextAsset $textAsset */\n if ($textAsset->getAssetFieldType() !== $assetFieldType) {\n continue;\n }\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'text_asset' => new TextAsset(['text' => $textAsset->getText()])\n ])\n ])\n ]);\n $numOperationsAdded++;\n }\n }\n // If the added assets are still less than the minimum required assets for the asset field\n // type, add more text assets using the default texts.\n if ($numOperationsAdded < self::MIN_REQUIRED_TEXT_ASSET_COUNTS[$assetFieldType]) {\n for (\n $i = 0;\n $i < self::MIN_REQUIRED_TEXT_ASSET_COUNTS[$assetFieldType] - $numOperationsAdded;\n $i++\n ) {\n // Creates a mutate operation for a text asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'text_asset' => new TextAsset([\n 'text' => self::DEFAULT_TEXT_ASSETS_INFO[$assetFieldType][$i]\n ])\n ])\n ])\n ]);\n }\n }\n\n // Issues a mutate request to add all assets.\n $googleAdsService = $googleAdsClient->getGoogleAdsServiceClient();\n /** @var MutateGoogleAdsResponse $mutateGoogleAdsResponse */\n $mutateGoogleAdsResponse =\n $googleAdsService->mutate(MutateGoogleAdsRequest::build($customerId, $operations));\n\n $assetResourceNames = [];\n foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {\n /** @var MutateOperationResponse $response */\n $assetResourceNames[] = $response->getAssetResult()->getResourceName();\n }\n printf(\n \"The following assets are created for the asset field type '%s':%s\",\n AssetFieldType::name($assetFieldType),\n PHP_EOL\n );\n self::printResponseDetails($mutateGoogleAdsResponse);\n\n return $assetResourceNames;\n }\n\n /**\n * Creates a hotel property asset set.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @return string the created hotel property asset set resource name\n */\n private static function createHotelAssetSet(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n ): string {\n // Creates an asset set operation for a hotel property asset set.\n $assetSetOperation = new AssetSetOperation([\n // Creates a hotel property asset set.\n 'create' => new AssetSet([\n 'name' => 'My Hotel propery asset set #' . Helper::getPrintableDatetime(),\n 'type' => AssetSetType::HOTEL_PROPERTY\n ])\n ]);\n\n // Issues a mutate request to add a hotel asset set and prints its information.\n $assetSetServiceClient = $googleAdsClient->getAssetSetServiceClient();\n $response = $assetSetServiceClient->mutateAssetSets(\n MutateAssetSetsRequest::build($customerId, [$assetSetOperation])\n );\n $assetSetResourceName = $response->getResults()[0]->getResourceName();\n printf(\"Created an asset set with resource name: '%s'.%s\", $assetSetResourceName, PHP_EOL);\n return $assetSetResourceName;\n }\n\n /**\n * Creates a hotel property asset using the specified place ID. The place ID must belong to\n * a hotel property. Then, links it to the specified asset set.\n *\n * See https://developers.google.com/places/web-service/place-id to search for a hotel place ID.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param string $placeId the place ID for a hotel\n * @param string $assetSetResourceName the asset set resource name\n * @return string the created hotel property asset resource name\n */\n private static function createHotelAsset(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $placeId,\n string $assetSetResourceName\n ): string {\n // We use the GoogleAdService to create an asset and asset set asset in a single\n // request.\n $operations = [];\n $assetResourceName =\n ResourceNames::forAsset($customerId, self::ASSET_TEMPORARY_ID);\n // Creates a mutate operation for a hotel property asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n // Creates a hotel property asset.\n 'create' => new Asset([\n 'resource_name' => $assetResourceName,\n // Creates a hotel property asset for the place ID.\n 'hotel_property_asset' => new HotelPropertyAsset(['place_id' => $placeId]),\n ])\n ])\n ]);\n // Creates a mutate operation for an asset set asset.\n $operations[] = new MutateOperation([\n 'asset_set_asset_operation' => new AssetSetAssetOperation([\n // Creates an asset set asset.\n 'create' => new AssetSetAsset([\n 'asset' => $assetResourceName,\n 'asset_set' => $assetSetResourceName\n ])\n ])\n ]);\n\n // Issues a mutate request to create all entities.\n $googleAdsService = $googleAdsClient->getGoogleAdsServiceClient();\n /** @var MutateGoogleAdsResponse $mutateGoogleAdsResponse */\n $mutateGoogleAdsResponse =\n $googleAdsService->mutate(MutateGoogleAdsRequest::build($customerId, $operations));\n print \"Created the following entities for the hotel asset:\" . PHP_EOL;\n self::printResponseDetails($mutateGoogleAdsResponse);\n\n // Returns the created asset resource name, which will be used later to create an asset\n // group. Other resource names are not used later.\n return $mutateGoogleAdsResponse->getMutateOperationResponses()[0]->getAssetResult()\n ->getResourceName();\n }\n\n /**\n * Creates a mutate operation that creates a new campaign budget.\n *\n * A temporary ID will be assigned to this campaign budget so that it can be\n * referenced by other objects being created in the same mutate request.\n *\n * @param int $customerId the customer ID\n * @return MutateOperation the mutate operation that creates a campaign budget\n */\n private static function createCampaignBudgetOperation(int $customerId): MutateOperation\n {\n // Creates a mutate operation that creates a campaign budget.\n return new MutateOperation([\n 'campaign_budget_operation' => new CampaignBudgetOperation([\n 'create' => new CampaignBudget([\n // Sets a temporary ID in the budget's resource name so it can be referenced\n // by the campaign in later steps.\n 'resource_name' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n 'name' => 'Performance Max for travel goals campaign budget #'\n . Helper::getPrintableDatetime(),\n // The budget period already defaults to DAILY.\n 'amount_micros' => 50000000,\n 'delivery_method' => BudgetDeliveryMethod::STANDARD,\n // A Performance Max campaign cannot use a shared campaign budget.\n 'explicitly_shared' => false\n ])\n ])\n ]);\n }\n\n /**\n * Creates a mutate operation that creates a new Performance Max campaign. Links the specified\n * hotel property asset set to this campaign.\n *\n * A temporary ID will be assigned to this campaign so that it can be referenced by other\n * objects being created in the same mutate request.\n *\n * @param int $customerId the customer ID\n * @param string $hotelPropertyAssetSetResourceName the asset set resource name\n * @return MutateOperation the mutate operation that creates the campaign\n */\n private static function createCampaignOperation(\n int $customerId,\n string $hotelPropertyAssetSetResourceName\n ): MutateOperation {\n // Creates a mutate operation that creates a campaign.\n return new MutateOperation([\n 'campaign_operation' => new CampaignOperation([\n 'create' => new Campaign([\n 'name' => 'Performance Max for travel goals campaign #'\n . Helper::getPrintableDatetime(),\n // Assigns the resource name with a temporary ID.\n 'resource_name' => ResourceNames::forCampaign(\n $customerId,\n self::CAMPAIGN_TEMPORARY_ID\n ),\n // Sets the budget using the given budget resource name.\n 'campaign_budget' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n // The campaign is the only entity in the mutate request that should have its\n // status set.\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving.\n 'status' => CampaignStatus::PAUSED,\n // Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n 'advertising_channel_type' => AdvertisingChannelType::PERFORMANCE_MAX,\n\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n\n // To create a Performance Max for travel goals campaign, you need to set\n // `hotel_property_asset_set`.\n 'hotel_property_asset_set' => $hotelPropertyAssetSetResourceName,\n\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: https://support.google.com/google-ads/answer/7684216.\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n 'maximize_conversion_value' => new MaximizeConversionValue([\n 'target_roas' => 3.5\n ])\n ])\n ])\n ]);\n }\n\n /**\n * Creates a list of mutate operations that create a new asset group, composed of suggested\n * assets. In case the number of suggested assets is not enough for the requirements, it'll\n * create more assets to meet the requirement.\n *\n * For the list of required assets for a Performance Max campaign, see\n * https://developers.google.com/google-ads/api/docs/performance-max/assets.\n *\n * @param int $customerId the customer ID\n * @param string $hotelPropertyAssetResourceName the hotel property asset resource name that\n * will be used to create an asset group\n * @param string[] $headlineAssetResourceNames a list of headline resource names\n * @param string[] $descriptionAssetResourceNames a list of description resource names\n * @param HotelAssetSuggestion $hotelAssetSuggestion the hotel asset suggestion\n * @return MutateOperation[] a list of mutate operations that create the asset group\n */\n private static function createAssetGroupOperations(\n int $customerId,\n string $hotelPropertyAssetResourceName,\n array $headlineAssetResourceNames,\n array $descriptionAssetResourceNames,\n HotelAssetSuggestion $hotelAssetSuggestion\n ): array {\n $operations = [];\n\n // Creates a new mutate operation that creates an asset group using suggested information\n // when available.\n $assetGroupName = $hotelAssetSuggestion->getStatus() === HotelAssetSuggestionStatus::SUCCESS\n ? $hotelAssetSuggestion->getHotelName()\n : 'Performance Max for travel goals asset group #' . Helper::getPrintableDatetime();\n $assetGroupFinalUrls =\n $hotelAssetSuggestion->getStatus() === HotelAssetSuggestionStatus::SUCCESS\n ? [$hotelAssetSuggestion->getFinalUrl()] : ['http://www.example.com'];\n $assetGroupResourceName =\n ResourceNames::forAssetGroup($customerId, self::ASSET_GROUP_TEMPORARY_ID);\n $operations[] = new MutateOperation([\n 'asset_group_operation' => new AssetGroupOperation([\n 'create' => new AssetGroup([\n 'resource_name' => $assetGroupResourceName,\n 'name' => $assetGroupName,\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::CAMPAIGN_TEMPORARY_ID\n ),\n 'final_urls' => $assetGroupFinalUrls,\n 'status' => AssetGroupStatus::PAUSED\n ])\n ])\n ]);\n\n // An asset group is linked to an asset by creating a new asset group asset\n // and providing:\n // - the resource name of the asset group\n // - the resource name of the asset\n // - the field_type of the asset in this asset group\n //\n // To learn more about asset groups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n // Headline and description assets were created at the first step of this example. So, we\n // just need to link them with the created asset group.\n\n // Links the headline assets to the asset group.\n foreach ($headlineAssetResourceNames as $resourceName) {\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $resourceName,\n 'asset_group' => $assetGroupResourceName,\n 'field_type' => AssetFieldType::HEADLINE\n ])\n ])\n ]);\n }\n // Links the description assets to the asset group.\n foreach ($descriptionAssetResourceNames as $resourceName) {\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $resourceName,\n 'asset_group' => $assetGroupResourceName,\n 'field_type' => AssetFieldType::DESCRIPTION\n ])\n ])\n ]);\n }\n\n // Link the previously created hotel property asset to the asset group. In the real-world\n // scenario, you'd need to do this step several times for each hotel property asset.\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $hotelPropertyAssetResourceName,\n 'asset_group' => $assetGroupResourceName,\n 'field_type' => AssetFieldType::HOTEL_PROPERTY\n ])\n ])\n ]);\n\n // Creates the rest of required text assets and link them to the asset group.\n $operations = array_merge(\n $operations,\n self::createTextAssetsForAssetGroup($customerId, $hotelAssetSuggestion)\n );\n // Creates the image assets and link them to the asset group. Some optional image assets\n // suggested by the TravelAssetSuggestionService might be created too.\n $operations = array_merge(\n $operations,\n self::createImageAssetsForAssetGroup($customerId, $hotelAssetSuggestion)\n );\n\n if ($hotelAssetSuggestion->getStatus() === HotelAssetSuggestionStatus::SUCCESS) {\n // Creates a new mutate operation for a suggested call-to-action asset and link it\n // to the asset group.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId),\n 'name' => 'Suggested call-to-action asset #'\n . Helper::getShortPrintableDatetime(),\n 'call_to_action_asset' => new CallToActionAsset([\n 'call_to_action' => $hotelAssetSuggestion->getCallToAction()\n ])\n ])\n ])\n ]);\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => ResourceNames::forAsset($customerId, self::$nextTempId),\n 'asset_group' => $assetGroupResourceName,\n 'field_type' => AssetFieldType::CALL_TO_ACTION_SELECTION\n ])\n ])\n ]);\n self::$nextTempId--;\n }\n\n return $operations;\n }\n\n /**\n * Creates text assets required for an asset group using the suggested hotel text assets. It\n * adds more text assets to fulfill the requirements if the suggested hotel text assets are not\n * enough.\n *\n * @param int $customerId the customer ID\n * @param HotelAssetSuggestion $hotelAssetSuggestion the hotel asset suggestion\n * @return MutateOperation[] a list of mutate operations that create text assets\n */\n private static function createTextAssetsForAssetGroup(\n int $customerId,\n HotelAssetSuggestion $hotelAssetSuggestion\n ): array {\n $operations = [];\n // Creates mutate operations for the suggested text assets except for headlines and\n // descriptions, which were created previously.\n $requiredTextAssetCounts =\n array_fill_keys(array_keys(self::MIN_REQUIRED_TEXT_ASSET_COUNTS), 0);\n if ($hotelAssetSuggestion->getStatus() === HotelAssetSuggestionStatus::SUCCESS) {\n foreach ($hotelAssetSuggestion->getTextAssets() as $textAsset) {\n /** @var HotelTextAsset $textAsset */\n if (\n $textAsset->getAssetFieldType() === AssetFieldType::HEADLINE\n || $textAsset->getAssetFieldType() === AssetFieldType::DESCRIPTION\n ) {\n // Headlines and descriptions were already created at the first step of this\n // code example.\n continue;\n }\n printf(\n \"A text asset with text '%s' is suggested for the asset field type '%s'.%s\",\n $textAsset->getText(),\n AssetFieldType::name($textAsset->getAssetFieldType()),\n PHP_EOL\n );\n $operations = array_merge(\n $operations,\n self::createTextAssetAndAssetGroupAssetOperations(\n $customerId,\n $textAsset->getText(),\n $textAsset->getAssetFieldType()\n )\n );\n $requiredTextAssetCounts[$textAsset->getAssetFieldType()]++;\n }\n }\n // Adds more text assets to fulfill the requirements.\n foreach (self::MIN_REQUIRED_TEXT_ASSET_COUNTS as $assetFieldType => $minCount) {\n if (\n $assetFieldType === AssetFieldType::HEADLINE\n || $assetFieldType === AssetFieldType::DESCRIPTION\n ) {\n // Headlines and descriptions were already created at the first step of this\n // code example.\n continue;\n }\n for ($i = 0; $i < $minCount - $requiredTextAssetCounts[$assetFieldType]; $i++) {\n printf(\n \"A default text '%s' is used to create a text asset for the asset\"\n . \" field type '%s'.%s\",\n self::DEFAULT_TEXT_ASSETS_INFO[$assetFieldType][$i],\n AssetFieldType::name($assetFieldType),\n PHP_EOL\n );\n $operations = array_merge(\n $operations,\n self::createTextAssetAndAssetGroupAssetOperations(\n $customerId,\n self::DEFAULT_TEXT_ASSETS_INFO[$assetFieldType][$i],\n $assetFieldType\n )\n );\n }\n }\n\n return $operations;\n }\n\n /**\n * Creates image assets required for an asset group using the suggested hotel image assets. It\n * adds more image assets to fulfill the requirements if the suggested hotel image assets are\n * not enough.\n *\n * @param int $customerId the customer ID\n * @param HotelAssetSuggestion $hotelAssetSuggestion the hotel asset suggestion\n * @return MutateOperation[] a list of mutate operations that create image assets\n */\n private static function createImageAssetsForAssetGroup(\n int $customerId,\n HotelAssetSuggestion $hotelAssetSuggestion\n ): array {\n $operations = [];\n // Creates mutate operations for the suggested image assets.\n $requiredImageAssetCounts =\n array_fill_keys(array_keys(self::MIN_REQUIRED_IMAGE_ASSET_COUNTS), 0);\n foreach ($hotelAssetSuggestion->getImageAssets() as $imageAsset) {\n /** @var HotelImageAsset $imageAsset */\n printf(\n \"An image asset with URL '%s' is suggested for the asset field type '%s'.%s\",\n $imageAsset->getUri(),\n AssetFieldType::name($imageAsset->getAssetFieldType()),\n PHP_EOL\n );\n $operations = array_merge(\n $operations,\n self::createImageAssetAndAssetGroupAssetOperations(\n $customerId,\n $imageAsset->getUri(),\n $imageAsset->getAssetFieldType(),\n 'Suggested image asset #' . Helper::getShortPrintableDatetime()\n )\n );\n // Keeps track of only required image assets. The service may sometimes suggest\n // optional image assets.\n if (array_key_exists($imageAsset->getAssetFieldType(), $requiredImageAssetCounts)) {\n $requiredImageAssetCounts[$imageAsset->getAssetFieldType()]++;\n }\n }\n // Adds more image assets to fulfill the requirements.\n foreach (self::MIN_REQUIRED_IMAGE_ASSET_COUNTS as $assetFieldType => $minCount) {\n for ($i = 0; $i < $minCount - $requiredImageAssetCounts[$assetFieldType]; $i++) {\n printf(\n \"A default image URL '%s' is used to create an image asset for the\"\n . \" asset field type '%s'.%s\",\n self::DEFAULT_IMAGE_ASSETS_INFO[$assetFieldType][$i],\n AssetFieldType::name($assetFieldType),\n PHP_EOL\n );\n $operations = array_merge(\n $operations,\n self::createImageAssetAndAssetGroupAssetOperations(\n $customerId,\n self::DEFAULT_IMAGE_ASSETS_INFO[$assetFieldType][$i],\n $assetFieldType,\n strtolower(AssetFieldType::name($assetFieldType))\n . Helper::getShortPrintableDatetime()\n )\n );\n }\n }\n\n return $operations;\n }\n\n /**\n * Creates a list of mutate operations that create a new linked text asset.\n *\n * @param int $customerId the customer ID\n * @param string $text the text of the asset to be created\n * @param int $fieldType the field type of the new asset in the asset group asset\n * @return MutateOperation[] a list of mutate operations that create a new linked text asset\n */\n private static function createTextAssetAndAssetGroupAssetOperations(\n int $customerId,\n string $text,\n int $fieldType\n ): array {\n $operations = [];\n // Creates a new mutate operation that creates a text asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId),\n 'text_asset' => new TextAsset(['text' => $text])\n ])\n ])\n ]);\n\n // Creates an asset group asset to link the asset to the asset group.\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => ResourceNames::forAsset($customerId, self::$nextTempId),\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => $fieldType\n ])\n ])\n ]);\n self::$nextTempId--;\n\n return $operations;\n }\n\n /**\n * Creates a list of mutate operations that create a new linked image asset.\n *\n * @param int $customerId the customer ID\n * @param string $url the URL of the image to be retrieved and put into an asset\n * @param int $fieldType the field type of the new asset in the asset group asset\n * @param string $assetName the asset name\n * @return MutateOperation[] a list of mutate operations that create a new linked image asset\n */\n private static function createImageAssetAndAssetGroupAssetOperations(\n int $customerId,\n string $url,\n int $fieldType,\n string $assetName\n ): array {\n $operations = [];\n // Creates a new mutate operation that creates an image asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset([\n 'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId),\n // Provide a unique friendly name to identify your asset.\n // When there is an existing image asset with the same content but a different\n // name, the new name will be dropped silently.\n 'name' => $assetName,\n 'image_asset' => new ImageAsset(['data' => file_get_contents($url)])\n ])\n ])\n ]);\n\n // Creates an asset group asset to link the asset to the asset group.\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => ResourceNames::forAsset($customerId, self::$nextTempId),\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => $fieldType\n ])\n ])\n ]);\n self::$nextTempId--;\n\n return $operations;\n }\n\n /**\n * Prints the details of a MutateGoogleAdsResponse. Parses the \"response\" oneof field name and\n * uses it to extract the new entity's name and resource name.\n *\n * @param MutateGoogleAdsResponse $mutateGoogleAdsResponse the mutate Google Ads response\n */\n private static function printResponseDetails(\n MutateGoogleAdsResponse $mutateGoogleAdsResponse\n ): void {\n foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {\n /** @var MutateOperationResponse $response */\n $getter = Serializer::getGetter($response->getResponse());\n printf(\n \"Created a(n) %s with '%s'.%s\",\n preg_replace(\n '/Result$/',\n '',\n ucfirst(Serializer::toCamelCase($response->getResponse()))\n ),\n $response->$getter()->getResourceName(),\n PHP_EOL\n );\n }\n }\n}\n\nAddPerformanceMaxForTravelGoalsCampaign::main();\nAddPerformanceMaxForTravelGoalsCampaign.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2023 Google LLC\n#\n# 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.\n\"\"\"This example shows how to create a Performance Max for travel goals campaign.\n\nIt also uses TravelAssetSuggestionService to fetch suggested assets for creating\nan asset group. In case there are not enough assets for the asset group\n(required by Performance Max), this example will create more assets to fulfill\nthe requirements.\n\nFor more information about Performance Max campaigns, see\nhttps://developers.google.com/google-ads/api/docs/performance-max/overview.\n\nPrerequisites:\n- You must have at least one conversion action in the account. For more about\n conversion actions, see\n https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n\nNotes:\n- This example uses the default customer conversion goals. For an example of\n setting campaign-specific conversion goals, see\n shopping_ads/add_performance_max_retail_campaign.py.\n- To learn how to create asset group signals, see\n advanced_operations/add_performance_max_campaign.py.\n\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import Dict, List\n\n\nfrom examples.utils.example_helpers import (\n get_printable_datetime,\n get_image_bytes_from_url,\n)\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.enums.types.asset_field_type import (\n AssetFieldTypeEnum,\n)\nfrom google.ads.googleads.v24.enums.types.hotel_asset_suggestion_status import (\n HotelAssetSuggestionStatusEnum,\n)\nfrom google.ads.googleads.v24.resources.types import CampaignBudget\nfrom google.ads.googleads.v24.resources.types.campaign import Campaign\nfrom google.ads.googleads.v24.resources.types.asset import Asset\nfrom google.ads.googleads.v24.resources.types.asset_group import AssetGroup\nfrom google.ads.googleads.v24.resources.types.asset_group_asset import (\n AssetGroupAsset,\n)\nfrom google.ads.googleads.v24.services.services.google_ads_service import (\n GoogleAdsServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.google_ads_service import (\n MutateGoogleAdsResponse,\n MutateOperation,\n)\nfrom google.ads.googleads.v24.services.types.google_ads_service import (\n MutateOperationResponse,\n)\n\nfrom google.ads.googleads.v24.resources.types.asset_set import AssetSet\nfrom google.ads.googleads.v24.resources.types.asset_set_asset import (\n AssetSetAsset,\n)\nfrom google.ads.googleads.v24.services.services.asset_set_service import (\n AssetSetServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.asset_set_service import (\n AssetSetOperation,\n MutateAssetSetsResponse,\n)\nfrom google.ads.googleads.v24.services.services.travel_asset_suggestion_service import (\n TravelAssetSuggestionServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.travel_asset_suggestion_service import (\n HotelAssetSuggestion,\n SuggestTravelAssetsRequest,\n SuggestTravelAssetsResponse,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\nMIN_REQUIRED_TEXT_ASSET_COUNTS: Dict[str, int] = {\n \"HEADLINE\": 3,\n \"LONG_HEADLINE\": 1,\n \"DESCRIPTION\": 2,\n \"BUSINESS_NAME\": 1,\n}\n\nMIN_REQUIRED_IMAGE_ASSET_COUNTS: Dict[str, int] = {\n \"MARKETING_IMAGE\": 1,\n \"SQUARE_MARKETING_IMAGE\": 1,\n \"LOGO\": 1,\n}\n\nDEFAULT_TEXT_ASSETS_INFO: Dict[str, List[str]] = {\n \"HEADLINE\": [\"Hotel\", \"Travel Reviews\", \"Book travel\"],\n \"LONG_HEADLINE\": [\"Travel the World\"],\n \"DESCRIPTION\": [\n \"Great deal for your beloved hotel\",\n \"Best rate guaranteed\",\n ],\n \"BUSINESS_NAME\": [\"Interplanetary Cruises\"],\n}\n\nDEFAULT_IMAGE_ASSETS_INFO: Dict[str, List[str]] = {\n \"MARKETING_IMAGE\": [\"https://gaagl.page.link/Eit5\"],\n \"SQUARE_MARKETING_IMAGE\": [\"https://gaagl.page.link/bjYi\"],\n \"LOGO\": [\"https://gaagl.page.link/bjYi\"],\n}\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\n\n# For further details, see:\n# https://developers.google.com/google-ads/api/docs/mutating/best-practices\n\n# These temporary IDs are global because they are used throughout the module.\nASSET_TEMPORARY_ID: int = -1\nBUDGET_TEMPORARY_ID: int = -2\nCAMPAIGN_TEMPORARY_ID: int = -3\nASSET_GROUP_TEMPORARY_ID: int = -4\n\n# There are also entities that will be created in the same request but do not\n# need to be fixed temporary IDs because they are referenced only once.\nnext_temp_id: int = ASSET_GROUP_TEMPORARY_ID - 1\n\n\ndef main(client: GoogleAdsClient, customer_id: str, place_id: str) -> None:\n \"\"\"The main method that creates all necessary entities for the example.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n place_id: a place ID identifying a place in the Google Places database.\n \"\"\"\n # Gets hotel asset suggestion using the TravelAssetSuggestionService.\n hotel_asset_suggestion: HotelAssetSuggestion = get_hotel_asset_suggestion(\n client, customer_id, place_id\n )\n\n # Performance Max campaigns require that repeated assets such as headlines\n # and descriptions be created before the campaign. For the list of required\n # assets for a Performance Max campaign, see:\n # https://developers.google.com/google-ads/api/docs/performance-max/assets.\n\n # This step is the same for all types of Performance Max campaigns.\n\n # Creates the headlines using the hotel asset suggestion.\n headline_asset_resource_names: List[str] = create_multiple_text_assets(\n client,\n customer_id,\n client.enums.AssetFieldTypeEnum.HEADLINE,\n hotel_asset_suggestion,\n )\n\n # Creates the descriptions using the hotel asset suggestion.\n description_asset_resource_names: List[str] = create_multiple_text_assets(\n client,\n customer_id,\n client.enums.AssetFieldTypeEnum.DESCRIPTION,\n hotel_asset_suggestion,\n )\n\n # Creates a hotel property asset set, which will be used later to link with\n # a newly created campaign.\n hotel_property_asset_set_resource_name: str = create_hotel_asset_set(\n client, customer_id\n )\n\n # Creates a hotel property asset and links it with the previously created\n # hotel property asset set. This asset will also be linked to an asset group\n # in the later steps. In a real-world scenario, you'd need to create assets\n # for each of your hotel properties. We use one hotel property here for\n # simplicity. Both asset and asset set need to be created before creating a\n # campaign, so we cannot bundle them with other mutate operations below.\n hotel_property_asset_resource_name: str = create_hotel_asset(\n client, customer_id, place_id, hotel_property_asset_set_resource_name\n )\n\n # It's important to create the below entities in this order because they\n # depend on each other.\n # The below methods create and return mutate operations that we later\n # provide to the GoogleAdsService.Mutate method in order to create the\n # entities in a single request. Since the entities for a Performance Max\n # campaign are closely tied to one-another, it's considered a best practice\n # to create them in a single Mutate request so they all complete\n # successfully or fail entirely, leaving no orphaned entities. See:\n # https://developers.google.com/google-ads/api/docs/mutating/overview.\n campaign_budget_operation: MutateOperation = (\n create_campaign_budget_operation(client, customer_id)\n )\n campaign_operation: MutateOperation = create_campaign_operation(\n client, customer_id, hotel_property_asset_set_resource_name\n )\n asset_group_operations: List[MutateOperation] = (\n create_asset_group_operations(\n client,\n customer_id,\n hotel_property_asset_resource_name,\n headline_asset_resource_names,\n description_asset_resource_names,\n hotel_asset_suggestion,\n )\n )\n\n # Issues a mutate request to create everything.\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n # The list of operations is a MutableSequence because it is modified by\n # the `extend` method.\n operations: List[MutateOperation] = [\n campaign_budget_operation,\n campaign_operation,\n *asset_group_operations,\n ]\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id,\n mutate_operations=operations,\n )\n\n print(\n \"Created the following entities for a campaign budget, a campaign, and \"\n \"an asset group for Performance Max for travel goals:\"\n )\n\n print_response_details(response)\n\n\ndef get_hotel_asset_suggestion(\n client: GoogleAdsClient, customer_id: str, place_id: str\n) -> HotelAssetSuggestion:\n \"\"\"Returns hotel asset suggestion from TravelAssetsSuggestionService.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n place_id: a place ID identifying a place in the Google Places database.\n\n Returns:\n A HotelAssetSuggestion instance.\n \"\"\"\n request: SuggestTravelAssetsRequest = client.get_type(\n \"SuggestTravelAssetsRequest\"\n )\n request.customer_id = customer_id\n # Uses 'en-US' as an example. It can be any language specifications in\n # BCP 47 format.\n request.language_option = \"en-US\"\n # In this example we only use a single place ID for the purpose of\n # demonstration, but it's possible to append more than one here if needed.\n request.place_ids.append(place_id)\n travel_asset_suggestion_service: TravelAssetSuggestionServiceClient = (\n client.get_service(\"TravelAssetSuggestionService\")\n )\n response: SuggestTravelAssetsResponse = (\n travel_asset_suggestion_service.suggest_travel_assets(request=request)\n )\n print(f\"Fetched a hotel asset suggestion for the place ID: '{place_id}'.\")\n\n # Since we sent a single operation in the request, it's guaranteed that\n # there will only be a single item in the response.\n return response.hotel_asset_suggestions[0]\n\n\ndef create_multiple_text_assets(\n client: GoogleAdsClient,\n customer_id: str,\n asset_field_type: AssetFieldTypeEnum.AssetFieldType,\n hotel_asset_suggestion: HotelAssetSuggestion,\n) -> List[str]:\n \"\"\"Creates multiple text assets and returns the list of resource names.\n\n The hotel asset suggestion is used to create a text asset first. If the\n number of created text assets is still fewer than the minimum required\n number of assets of the specified asset field type, adds more text assets to\n fulfill the requirement.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n asset_field_type: the asset field type enum that the new assets will be\n created as.\n hotel_asset_suggestion: the hotel asset suggestion.\n\n Returns:\n a list of asset resource names.\n \"\"\"\n # We use the GoogleAdService to create multiple text assets in a single\n # request. First, adds all the text assets of the specified asset field\n # type.\n operations: List[MutateOperation] = []\n success_status: (\n HotelAssetSuggestionStatusEnum.HotelAssetSuggestionStatus\n ) = client.enums.HotelAssetSuggestionStatusEnum.SUCCESS\n\n if hotel_asset_suggestion.status == success_status:\n for text_asset in hotel_asset_suggestion.text_assets:\n # If the suggested text asset is not of the type specified, then\n # we skip it and move on to the next text asset.\n if text_asset.asset_field_type != asset_field_type:\n continue\n\n # If the suggested text asset is of the type specified, then we\n # build a mutate operation that creates a new text asset using\n # the text from the suggestion.\n operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = operation.asset_operation.create\n asset.text_asset.text = text_asset.text\n operations.append(operation)\n\n # If the current number of operations is still less than the minimum\n # required assets for the asset field type, add more operations using the\n # default texts.\n minimum_required_text_asset_count: int = MIN_REQUIRED_TEXT_ASSET_COUNTS[\n asset_field_type.name\n ]\n\n if len(operations) < minimum_required_text_asset_count:\n # Calculate the number of additional operations that need to be created.\n difference: int = minimum_required_text_asset_count - len(operations)\n # Retrieve the list of default texts for the given asset type.\n default_texts: List[str] = DEFAULT_TEXT_ASSETS_INFO[\n asset_field_type.name\n ]\n for i in range(difference):\n operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = operation.asset_operation.create\n asset.text_asset.text = default_texts[i]\n operations.append(operation)\n\n # Issues a mutate request to add all assets.\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id, mutate_operations=operations\n )\n\n print(\n \"The following assets were created for the asset field type \"\n f\"'{asset_field_type.name}'\"\n )\n print_response_details(response)\n\n return [\n result.asset_result.resource_name\n for result in response.mutate_operation_responses\n ]\n\n\ndef create_hotel_asset_set(client: GoogleAdsClient, customer_id: str) -> str:\n \"\"\"Creates a hotel property asset set.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n the created hotel property asset set's resource name.\n \"\"\"\n # Creates an asset set operation for a hotel property asset set.\n operation: AssetSetOperation = client.get_type(\"AssetSetOperation\")\n # Creates a hotel property asset set.\n asset_set: AssetSet = operation.create\n asset_set.name = f\"My hotel property asset set #{get_printable_datetime()}\"\n asset_set.type_ = client.enums.AssetSetTypeEnum.HOTEL_PROPERTY\n\n # Issues a mutate request to add a hotel asset set.\n asset_set_service: AssetSetServiceClient = client.get_service(\n \"AssetSetService\"\n )\n response: MutateAssetSetsResponse = asset_set_service.mutate_asset_sets(\n customer_id=customer_id, operations=[operation]\n )\n resource_name: str = response.results[0].resource_name\n print(f\"Created an asset set with resource name: '{resource_name}'\")\n\n return resource_name\n\n\ndef create_hotel_asset(\n client: GoogleAdsClient,\n customer_id: str,\n place_id: str,\n asset_set_resource_name: str,\n) -> str:\n \"\"\"Creates a hotel property asset using the specified place ID.\n\n The place ID must belong to a hotel property. Then, links it to the\n specified asset set.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n place_id: a place ID identifying a place in the Google Places database.\n asset_set_resource_name: an asset set resource name\n\n Returns:\n the created hotel property asset's resource name.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n # We use the GoogleAdService to create an asset and asset set asset in a\n # single request.\n\n asset_resource_name: str = googleads_service.asset_path(\n customer_id, ASSET_TEMPORARY_ID\n )\n\n # Creates a mutate operation for a hotel property asset.\n asset_mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n # Creates a hotel property asset.\n asset: Asset = asset_mutate_operation.asset_operation.create\n asset.resource_name = asset_resource_name\n # Creates a hotel property asset for the place ID.\n asset.hotel_property_asset.place_id = place_id\n\n # Creates a mutate operation for an asset set asset.\n asset_set_asset_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n # Creates an asset set asset.\n\n asset_set_asset: AssetSetAsset = (\n asset_set_asset_mutate_operation.asset_set_asset_operation.create\n )\n asset_set_asset.asset = asset_resource_name\n asset_set_asset.asset_set = asset_set_resource_name\n\n # Issues a mutate request to create all entities.\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id,\n mutate_operations=[\n asset_mutate_operation,\n asset_set_asset_mutate_operation,\n ],\n )\n print(\"Created the following entities for the hotel asset:\")\n print_response_details(response)\n\n return response.mutate_operation_responses[0].asset_result.resource_name\n\n\ndef create_campaign_budget_operation(\n client: GoogleAdsClient, customer_id: str\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new campaign budget.\n\n A temporary ID will be assigned to this campaign budget so that it can be\n referenced by other objects being created in the same mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns: A MutateOperation that creates a new campaign budget.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n # Creates a mutate operation that creates a campaign budget.\n operation: MutateOperation = client.get_type(\"MutateOperation\")\n budget: CampaignBudget = operation.campaign_budget_operation.create\n # Sets a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n budget.resource_name = googleads_service.campaign_budget_path(\n customer_id, BUDGET_TEMPORARY_ID\n )\n budget.name = (\n \"Performance Max for travel goals campaign budget \"\n f\"#{get_printable_datetime()}\"\n )\n # The budget period already defaults to DAILY.\n budget.amount_micros = 50000000\n budget.delivery_method = client.enums.BudgetDeliveryMethodEnum.STANDARD\n # A Performance Max campaign cannot use a shared campaign budget.\n budget.explicitly_shared = False\n\n return operation\n\n\ndef create_campaign_operation(\n client: GoogleAdsClient,\n customer_id: str,\n hotel_property_asset_set_resource_name: str,\n) -> MutateOperation:\n \"\"\"Creates a mutate operation that creates a new Performance Max for travel\n goals campaign.\n\n Links the specified hotel property asset set to this campaign.\n\n A temporary ID will be assigned to this campaign budget so that it can be\n referenced by other objects being created in the same mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n hotel_property_asset_set_resource_name: the resource name for a hotel\n property asset set.\n\n Returns:\n a MutateOperation message that creates a new Performance Max campaign.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n # Creates a mutate operation that creates a campaign.\n operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign: Campaign = operation.campaign_operation.create\n campaign.name = (\n \"Performance Max for travel goals campaign \"\n f\"#{get_printable_datetime()}\"\n )\n # Assigns the resource name with a temporary ID.\n campaign.resource_name = googleads_service.campaign_path(\n customer_id, CAMPAIGN_TEMPORARY_ID\n )\n # Sets the budget using the given budget resource name.\n campaign.campaign_budget = googleads_service.campaign_budget_path(\n customer_id, BUDGET_TEMPORARY_ID\n )\n # The campaign is the only entity in the mutate request that should have its\n # status set.\n # Recommendation: Set the campaign to PAUSED when creating it to prevent\n # the ads from immediately serving.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n # Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.PERFORMANCE_MAX\n )\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n # To create a Performance Max for travel goals campaign, you need to set\n # the `hotel_property_asset_set` field.\n campaign.hotel_property_asset_set = hotel_property_asset_set_resource_name\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: https://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n campaign.maximize_conversion_value.target_roas = 3.5\n\n return operation\n\n\ndef create_asset_group_operations(\n client: GoogleAdsClient,\n customer_id: str,\n hotel_property_asset_resource_name: str,\n headline_asset_resource_names: List[str],\n description_asset_resource_names: List[str],\n hotel_asset_suggestion: HotelAssetSuggestion,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of mutate operations that create a new asset group.\n\n The asset group is composed of suggested assets. In case the number of\n suggested assets is not enough for the requirements, it will create more\n assets to meet the requirement.\n\n For the list of required assets for a Performance Max campaign, see\n https://developers.google.com/google-ads/api/docs/performance-max/assets.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n hotel_property_asset_resource_name: the hotel property asset resource\n name that will be used to create an asset group.\n headline_asset_resource_names: a list of headline asset resource names.\n description_asset_resource_names: a list of description asset resource\n names.\n hotel_asset_suggestion: the hotel asset suggestion.\n\n Returns:\n a list of mutate operations that create the asset group.\n \"\"\"\n global next_temp_id\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n operations: List[MutateOperation] = []\n\n # Creates a new mutate operation that creates an asset group using suggested\n # information when available.\n success_status: (\n HotelAssetSuggestionStatusEnum.HotelAssetSuggestionStatus\n ) = client.enums.HotelAssetSuggestionStatusEnum.SUCCESS\n asset_group_name: str\n asset_group_final_urls: List[str]\n if hotel_asset_suggestion.status == success_status:\n asset_group_name = hotel_asset_suggestion.hotel_name\n asset_group_final_urls = [hotel_asset_suggestion.final_url]\n else:\n asset_group_name = (\n \"Performance Max for travel goals asset group \"\n f\"#{get_printable_datetime()}\"\n )\n asset_group_final_urls = [\"http://www.example.com\"]\n\n asset_group_resource_name: str = googleads_service.asset_group_path(\n customer_id, ASSET_GROUP_TEMPORARY_ID\n )\n asset_group_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n asset_group: AssetGroup = (\n asset_group_mutate_operation.asset_group_operation.create\n )\n asset_group.resource_name = asset_group_resource_name\n asset_group.name = asset_group_name\n asset_group.campaign = googleads_service.campaign_path(\n customer_id, CAMPAIGN_TEMPORARY_ID\n )\n asset_group.final_urls = asset_group_final_urls\n asset_group.status = client.enums.AssetGroupStatusEnum.PAUSED\n # Append the asset group operation to the list of operations.\n operations.append(asset_group_mutate_operation)\n\n # An asset group is linked to an asset by creating a new asset group asset\n # and providing:\n # - the resource name of the asset group\n # - the resource name of the asset\n # - the field_type of the asset in this asset group\n\n # To learn more about asset groups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n # Headline and description assets were created at the first step of this\n # example. So, we just need to link them with the created asset group.\n\n # Links the headline assets to the asset group.\n for resource_name in headline_asset_resource_names:\n headline_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_asset: AssetGroupAsset = (\n headline_operation.asset_group_asset_operation.create\n )\n asset_group_asset.asset = resource_name\n asset_group_asset.asset_group = asset_group_resource_name\n asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.HEADLINE\n operations.append(headline_operation)\n\n # Links the description assets to the asset group.\n for resource_name in description_asset_resource_names:\n description_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n asset_group_asset_desc: AssetGroupAsset = (\n description_operation.asset_group_asset_operation.create\n )\n asset_group_asset_desc.asset = resource_name\n asset_group_asset_desc.asset_group = asset_group_resource_name\n asset_group_asset_desc.field_type = (\n client.enums.AssetFieldTypeEnum.DESCRIPTION\n )\n operations.append(description_operation)\n\n # Link the previously created hotel property asset to the asset group. If\n # there are multiple assets, these steps to create a new operation need to\n # be performed for each asset.\n asset_group_asset_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n asset_group_asset_hotel: AssetGroupAsset = (\n asset_group_asset_mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset_hotel.asset = hotel_property_asset_resource_name\n asset_group_asset_hotel.asset_group = asset_group_resource_name\n asset_group_asset_hotel.field_type = (\n client.enums.AssetFieldTypeEnum.HOTEL_PROPERTY\n )\n operations.append(asset_group_asset_mutate_operation)\n\n # Creates the rest of required text assets and link them to the asset group.\n operations.extend(\n create_text_assets_for_asset_group(\n client, customer_id, hotel_asset_suggestion\n )\n )\n\n # Creates the image assets and link them to the asset group. Some optional\n # image assets suggested by the TravelAssetSuggestionService might be\n # created too.\n operations.extend(\n create_image_assets_for_asset_group(\n client, customer_id, hotel_asset_suggestion\n )\n )\n\n if hotel_asset_suggestion.status == success_status:\n # Creates a new mutate operation for a suggested call-to-action asset\n # and link it to the asset group.\n asset_mutate_operation_cta: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n asset_cta: Asset = asset_mutate_operation_cta.asset_operation.create\n asset_cta.resource_name = googleads_service.asset_path(\n customer_id, next_temp_id\n )\n asset_cta.name = (\n f\"Suggested call-to-action asset #{get_printable_datetime()}\"\n )\n asset_cta.call_to_action_asset.call_to_action = (\n hotel_asset_suggestion.call_to_action\n )\n operations.append(asset_mutate_operation_cta)\n\n # Creates a new mutate operation for a call-to-action asset group.\n asset_group_asset_mutate_operation_cta: MutateOperation = (\n client.get_type(\"MutateOperation\")\n )\n asset_group_asset_cta: AssetGroupAsset = (\n asset_group_asset_mutate_operation_cta.asset_group_asset_operation.create\n )\n asset_group_asset_cta.asset = googleads_service.asset_path(\n customer_id, next_temp_id\n )\n asset_group_asset_cta.asset_group = asset_group_resource_name\n asset_group_asset_cta.field_type = (\n client.enums.AssetFieldTypeEnum.CALL_TO_ACTION_SELECTION\n )\n operations.append(asset_group_asset_mutate_operation_cta)\n\n next_temp_id -= 1\n\n return operations\n\n\ndef create_text_assets_for_asset_group(\n client: GoogleAdsClient,\n customer_id: str,\n hotel_asset_suggestion: HotelAssetSuggestion,\n) -> List[MutateOperation]:\n \"\"\"Creates text assets for an asset group using the given hotel text assets.\n\n It adds more text assets to fulfill the requirements if the suggested hotel\n text assets are not enough.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n hotel_asset_suggestion: the hotel asset suggestion.\n\n Returns:\n a list of mutate operations that create text assets.\n \"\"\"\n operations: List[MutateOperation] = []\n\n # Creates mutate operations for the suggested text assets except for\n # headlines and descriptions, which were created previously.\n required_text_asset_counts: Dict[str, int] = {\n key: 0 for key in MIN_REQUIRED_TEXT_ASSET_COUNTS.keys()\n }\n success_status: (\n HotelAssetSuggestionStatusEnum.HotelAssetSuggestionStatus\n ) = client.enums.HotelAssetSuggestionStatusEnum.SUCCESS\n if hotel_asset_suggestion.status == success_status:\n for text_asset in hotel_asset_suggestion.text_assets:\n text: str = text_asset.text\n asset_field_type: AssetFieldTypeEnum.AssetFieldType = (\n text_asset.asset_field_type\n )\n\n if asset_field_type.name in (\"HEADLINE\", \"DESCRIPTION\"):\n # Headlines and descriptions were already created at the first\n # step of this code example\n continue\n\n print(\n f\"A test asset with text {text} is suggested for the asset \"\n f\"field type `{asset_field_type.name}`\"\n )\n\n operations.extend(\n create_text_asset_and_asset_group_asset_operations(\n client, customer_id, text, asset_field_type\n )\n )\n\n required_text_asset_counts[asset_field_type.name] += 1\n\n # Adds more text assets to fulfill the requirements.\n for (\n field_type_name,\n min_count,\n ) in MIN_REQUIRED_TEXT_ASSET_COUNTS.items():\n if field_type_name in (\"HEADLINE\", \"DESCRIPTION\"):\n # Headlines and descriptions were already created at the first step\n # of this code example.\n continue\n\n difference: int = (\n min_count - required_text_asset_counts[field_type_name]\n )\n if difference > 0:\n for i in range(difference):\n default_text: str = DEFAULT_TEXT_ASSETS_INFO[field_type_name][i]\n field_type_enum: AssetFieldTypeEnum.AssetFieldType = (\n client.enums.AssetFieldTypeEnum[field_type_name]\n )\n\n print(\n f\"A default text {default_text} is used to create a \"\n f\"text asset for the asset field type {field_type_name}\"\n )\n\n operations.extend(\n create_text_asset_and_asset_group_asset_operations(\n client, customer_id, default_text, field_type_enum\n )\n )\n\n return operations\n\n\ndef create_text_asset_and_asset_group_asset_operations(\n client: GoogleAdsClient,\n customer_id: str,\n text: str,\n field_type_enum: AssetFieldTypeEnum.AssetFieldType,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of mutate operations that create a new linked text asset.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n text: the text of an asset to be created.\n field_type_enum: the field type enum of a new asset in the asset group\n asset.\n\n Returns:\n a list of mutate operations that create a new linked text asset.\n \"\"\"\n global next_temp_id\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n operations: List[MutateOperation] = []\n\n # Creates a new mutate operation that creates a text asset.\n asset_mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = asset_mutate_operation.asset_operation.create\n asset.resource_name = googleads_service.asset_path(\n customer_id, next_temp_id\n )\n asset.text_asset.text = text\n operations.append(asset_mutate_operation)\n\n # Creates an asset group asset operation to link the asset to the asset\n # group.\n asset_group_asset_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n asset_group_asset: AssetGroupAsset = (\n asset_group_asset_mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.asset = googleads_service.asset_path(\n customer_id, next_temp_id\n )\n asset_group_asset.asset_group = googleads_service.asset_group_path(\n customer_id, ASSET_GROUP_TEMPORARY_ID\n )\n asset_group_asset.field_type = field_type_enum\n operations.append(asset_group_asset_mutate_operation)\n\n next_temp_id -= 1\n\n return operations\n\n\ndef create_image_assets_for_asset_group(\n client: GoogleAdsClient,\n customer_id: str,\n hotel_asset_suggestion: HotelAssetSuggestion,\n) -> List[MutateOperation]:\n \"\"\"Creates image assets for an asset group with the given hotel suggestions.\n\n It adds more image assets to fulfill the requirements if the suggested hotel\n image assets are not enough.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n hotel_asset_suggestion: the hotel asset suggestion.\n\n Returns:\n a list of mutate operations that create image assets.\n \"\"\"\n operations: List[MutateOperation] = []\n\n # Creates mutate operations for the suggested image assets.\n required_image_asset_counts: Dict[str, int] = {\n key: 0 for key in MIN_REQUIRED_IMAGE_ASSET_COUNTS.keys()\n }\n for image_asset in hotel_asset_suggestion.image_assets:\n url: str = image_asset.uri\n field_type_enum: AssetFieldTypeEnum.AssetFieldType = (\n image_asset.asset_field_type\n )\n name: str = f\"Suggested image asset #{get_printable_datetime()}\"\n\n print(\n f\"An image asset with URL '{url}' is suggested for the asset field \"\n f\"type '{field_type_enum.name}'\"\n )\n\n operations.extend(\n create_image_asset_and_image_asset_group_asset_operations(\n client, customer_id, url, field_type_enum, name\n )\n )\n\n # Keeps track of only required image assets. The\n # TravelAssetSuggestionService may sometimes suggest optional image\n # assets.\n if field_type_enum.name in required_image_asset_counts:\n required_image_asset_counts[field_type_enum.name] += 1\n\n # Adds more image assets to fulfill the requirements.\n for (\n field_type_name,\n min_count,\n ) in MIN_REQUIRED_IMAGE_ASSET_COUNTS.items():\n difference: int = (\n min_count - required_image_asset_counts[field_type_name]\n )\n if difference > 0:\n for i in range(difference):\n default_url: str = DEFAULT_IMAGE_ASSETS_INFO[field_type_name][i]\n name = f\"{field_type_name.lower()} {get_printable_datetime()}\"\n field_type_enum: AssetFieldTypeEnum.AssetFieldType = (\n client.enums.AssetFieldTypeEnum[field_type_name]\n )\n\n print(\n f\"A default image URL {default_url} is used to create an \"\n f\"image asset for the asset field type {field_type_name}\"\n )\n\n operations.extend(\n create_image_asset_and_image_asset_group_asset_operations(\n client, customer_id, default_url, field_type_enum, name\n )\n )\n\n return operations\n\n\ndef create_image_asset_and_image_asset_group_asset_operations(\n client: GoogleAdsClient,\n customer_id: str,\n url: str,\n field_type_enum: AssetFieldTypeEnum.AssetFieldType,\n asset_name: str,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of mutate operations that create a new linked image asset.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n url: the URL of the image to be retrieved and put into an asset.\n field_type_enum: the field type enum of the new asset in the asset group\n asset.\n asset_name: the asset name.\n\n Returns:\n a list of mutate operations that create a new linked image asset.\n \"\"\"\n global next_temp_id\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n operations: List[MutateOperation] = []\n\n # Creates a new mutate operation that creates an image asset.\n asset_mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = asset_mutate_operation.asset_operation.create\n asset.resource_name = googleads_service.asset_path(\n customer_id, next_temp_id\n )\n # Provide a unique friendly name to identify your asset. When there is an\n # existing image asset with the same content but a different name, the new\n # name will be dropped silently.\n asset.name = asset_name\n asset.image_asset.data = get_image_bytes_from_url(url)\n operations.append(asset_mutate_operation)\n\n # Creates an asset group asset operation to link the asset to the asset\n # group.\n asset_group_asset_mutate_operation: MutateOperation = client.get_type(\n \"MutateOperation\"\n )\n asset_group_asset: AssetGroupAsset = (\n asset_group_asset_mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.asset = googleads_service.asset_path(\n customer_id, next_temp_id\n )\n asset_group_asset.asset_group = googleads_service.asset_group_path(\n customer_id, ASSET_GROUP_TEMPORARY_ID\n )\n asset_group_asset.field_type = field_type_enum\n operations.append(asset_group_asset_mutate_operation)\n\n next_temp_id -= 1\n\n return operations\n\n\ndef print_response_details(mutate_response: MutateGoogleAdsResponse) -> None:\n \"\"\"Prints the details of a MutateGoogleAdsResponse message.\n\n Parses the \"response\" oneof field name and uses it to extract the new\n entity's name and resource name.\n\n Args:\n mutate_response: a MutateGoogleAdsResponse message.\n \"\"\"\n result: MutateOperationResponse\n for result in mutate_response.mutate_operation_responses:\n resource_type: str = \"unrecognized\"\n resource_name: str = \"not found\"\n\n if \"asset_result\" in result:\n resource_type = \"Asset\"\n resource_name = result.asset_result.resource_name\n elif \"asset_set_asset_result\" in result:\n resource_type = \"AssetSetAsset\"\n resource_name = result.asset_set_asset_result.resource_name\n elif \"campaign_budget_result\" in result:\n resource_type = \"CampaignBudget\"\n resource_name = result.campaign_budget_result.resource_name\n elif \"campaign_result\" in result:\n resource_type = \"Campaign\"\n resource_name = result.campaign_result.resource_name\n elif \"asset_group_result\" in result:\n resource_type = \"AssetGroup\"\n resource_name = result.asset_group_result.resource_name\n elif \"asset_group_asset_result\" in result:\n resource_type = \"AssetGroupAsset\"\n resource_name = result.asset_group_asset_result.resource_name\n\n print(\n f\"Created a(n) {resource_type} with \"\n f\"resource_name: '{resource_name}'.\"\n )\n\n\nif __name__ == \"__main__\":\n parser: argparse.ArgumentParser = argparse.ArgumentParser(\n description=(\"Creates a Performance Max for travel goals campaign.\")\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-p\",\n \"--place_id\",\n type=str,\n required=True,\n help=(\n \"Sets a place ID that uniquely identifies a place in the Google \"\n \"Places database. The provided place ID must belong to a hotel \"\n \"property. To learn more, see: \"\n \"https://developers.google.com/places/web-service/place-id \"\n ),\n )\n\n args: argparse.Namespace = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(googleads_client, args.customer_id, args.place_id)\n except GoogleAdsException as ex:\n print(\n f'Request with ID \"{ex.request_id}\" failed with status '\n f'\"{ex.error.code().name}\" and includes the following errors:'\n )\n for error in ex.failure.errors:\n print(f'Error with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nadd_performance_max_for_travel_goals_campaign.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2023 Google LLC\n#\n# 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.\n\n# This example shows how to create a Performance Max for travel goals campaign.\n# It also uses TravelAssetSuggestionService to fetch suggested assets for\n# creating an asset group. In case there are not enough assets for the asset\n# group (required by Performance Max), this example will create more assets to\n# fulfill the requirements.\n#\n# For more information about Performance Max campaigns, see\n# https://developers.google.com/google-ads/api/docs/performance-max/overview.\n#\n# Prerequisites:\n# - You must have at least one conversion action in the account.\n# For more about conversion actions, see\n# https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n#\n# Notes:\n# - This example uses the default customer conversion goals.\n# For an example of setting campaign-specific conversion goals, see\n# shopping_ads/add_performance_max_retail_campaign.rb\n# - To learn how to create asset group signals, see\n# advanced_perations/add_performance_max_campaign.rb\n\nrequire 'open-uri'\nrequire 'optparse'\nrequire 'google/ads/google_ads'\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\n#\n# See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n# for further details.\n#\n# These temporary IDs are fixed because they are used in multiple places.\nBUDGET_TEMPORARY_ID = '-1'\nPERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = '-2'\nASSET_GROUP_TEMPORARY_ID = '-3'\n\n# There are also entities that will be created in the same request but do not\n# need to be fixed temporary IDs because they are referenced only once.\ndef next_temp_id\n @id ||= ASSET_GROUP_TEMPORARY_ID.to_i\n @id -= 1\nend\n\n# Minimum requirements of assets required in a Performance Max Asset Group.\n# See https://developers.google.com/google-ads/api/docs/performance-max/assets\n# for details.\nMIN_REQUIRED_TEXT_ASSET_COUNTS = {\n HEADLINE: 3,\n LONG_HEADLINE: 1,\n DESCRIPTION: 2,\n BUSINESS_NAME: 1\n}\nMIN_REQUIRED_IMAGE_ASSET_COUNTS = {\n MARKETING_IMAGE: 1,\n SQUARE_MARKETING_IMAGE: 1,\n LOGO: 1\n}\n\n# Texts and URLs used to create text and image assets when the\n# TravelAssetSuggestionService doesn't return enough assets required for\n# creating an asset group.\nDEFAULT_TEXT_ASSETS_INFO = {\n HEADLINE: ['Hotel', 'Travel Reviews', 'Book travel'],\n LONG_HEADLINE: ['Travel the World'],\n DESCRIPTION: ['Great deal for your beloved hotel', 'Best rate guaranteed'],\n BUSINESS_NAME: ['Interplanetary Cruises']\n}\n\nDEFAULT_IMAGE_ASSETS_INFO = {\n MARKETING_IMAGE: ['https://gaagl.page.link/Eit5'],\n SQUARE_MARKETING_IMAGE: ['https://gaagl.page.link/bjYi'],\n LOGO: ['https://gaagl.page.link/bjYi']\n}\n\ndef add_performance_max_for_travel_goals(customer_id, place_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Creates a hotel property asset set, which will be used later to link with a\n # newly created campaign.\n hotel_property_asset_set_resource_name =\n create_hotel_asset_set(client, customer_id)\n\n # Creates a hotel property asset and link it with the previously created hotel property\n # asset set. This asset will also be linked to an asset group in the later steps.\n # In the real-world scenario, you'd need to create many assets for all your hotel\n # properties. We use one hotel property here for simplicity.\n # Both asset and asset set need to be created before creating a campaign,\n # so we cannot bundle them with other mutate operations below.\n hotel_property_asset_resource_name =\n create_hotel_asset(\n client,\n customer_id,\n place_id,\n hotel_property_asset_set_resource_name\n )\n\n # The below methods create and return MutateOperations that we later\n # provide to the GoogleAdsService.Mutate method in order to create the\n # entities in a single request. Since the entities for a Performance Max\n # campaign are closely tied to one-another, it's considered a best practice\n # to create them in a single Mutate request so they all complete\n # successfully or fail entirely, leaving no orphaned entities. See:\n # https://developers.google.com/google-ads/api/docs/mutating/overview\n campaign_budget_operation =\n create_campaign_budget_operation(client, customer_id)\n\n performance_max_campaign_operation =\n create_performance_max_campaign_operation(\n client,\n customer_id,\n hotel_property_asset_set_resource_name\n )\n\n # Gets hotel asset suggestion using the TravelAssetSuggestionService.\n hotel_asset_suggestion =\n get_hotel_asset_suggestion(client, customer_id, place_id)\n\n # Creates the headlines using the hotel asset suggestion.\n headline_asset_resource_names =\n create_multiple_text_assets(\n client,\n customer_id,\n :HEADLINE,\n hotel_asset_suggestion\n )\n\n # Creates the descriptions using the hotel asset suggestion.\n descriptions_asset_resource_names =\n create_multiple_text_assets(\n client,\n customer_id,\n :DESCRIPTION,\n hotel_asset_suggestion\n )\n\n asset_group_operation =\n create_asset_group_operations(\n client,\n customer_id,\n hotel_property_asset_resource_name,\n headline_asset_resource_names,\n descriptions_asset_resource_names,\n hotel_asset_suggestion\n )\n\n # Send the operations in a single Mutate request.\n response =\n client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: [\n # It's important to create these entities in this order because\n # they depend on each other.\n campaign_budget_operation,\n performance_max_campaign_operation,\n asset_group_operation\n ].flatten\n )\n\n print_response_details(response)\nend\n\n# Creates a hotel property asset set.\ndef create_hotel_asset_set(client, customer_id)\n operation =\n client.operation.create_resource.asset_set do |asset_set|\n asset_set.name = \"My Hotel propery asset set #{Time.now}\"\n asset_set.type = :HOTEL_PROPERTY\n end\n\n # Sends the mutate request.\n response =\n client.service.asset_set.mutate_asset_sets(\n customer_id: customer_id,\n operations: [operation]\n )\n\n # Prints some information about the response.\n response.results.first.resource_name\nend\n\n# Creates a hotel property asset using the specified place ID.\n# The place ID must belong to a hotel property. Then, links it to the\n# specified asset set.\n# See https://developers.google.com/places/web-service/place-id to search for a\n# hotel place ID.\ndef create_hotel_asset(\n client,\n customer_id,\n place_id,\n hotel_property_asset_set_resource_name\n)\n asset_operation =\n client.operation.create_resource.asset do |asset|\n asset.name = 'Ad Media Bundle'\n asset.hotel_property_asset =\n client.resource.hotel_property_asset do |hotel_asset|\n hotel_asset.place_id = place_id\n end\n end\n\n # Send the mutate request.\n response =\n client.service.asset.mutate_assets(\n customer_id: customer_id,\n operations: [asset_operation]\n )\n\n asset_resource_name = response.results.first.resource_name\n\n # Creates a mutate operation for an asset set asset.\n asset_set_asset_operation =\n client.operation.create_resource.asset_set_asset do |asa|\n asa.asset = asset_resource_name\n asa.asset_set = hotel_property_asset_set_resource_name\n end\n\n # Sends the mutate request.\n response =\n client.service.asset_set_asset.mutate_asset_set_assets(\n customer_id: customer_id,\n operations: [asset_set_asset_operation]\n )\n\n asset_resource_name\nend\n\n# Creates a MutateOperation that creates a new Performance Max campaign.\n#\n# A temporary ID will be assigned to this campaign so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_performance_max_campaign_operation(\n client,\n customer_id,\n hotel_property_asset_set_resource_name\n)\n client.operation.mutate do |m|\n m.campaign_operation =\n client.operation.create_resource.campaign do |c|\n c.name = \"Performance Max for Travel Goals #{SecureRandom.uuid}\"\n\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n c.status = :PAUSED\n\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n c.advertising_channel_type = :PERFORMANCE_MAX\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n # To create a Performance Max for travel goals campaign, you need to set hotel_property_asset_set\n c.hotel_property_asset_set = hotel_property_asset_set_resource_name\n\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio\n # in the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n c.bidding_strategy_type = :MAXIMIZE_CONVERSION_VALUE\n c.maximize_conversion_value =\n client.resource.maximize_conversion_value do |mcv|\n mcv.target_roas = 3.5\n end\n\n # Assign the resource name with a temporary ID.\n c.resource_name =\n client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n\n # Set the budget using the given budget resource name.\n c.campaign_budget =\n client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n end\n end\nend\n\ndef create_campaign_budget_operation(client, customer_id)\n client.operation.mutate do |m|\n m.campaign_budget_operation =\n client.operation.create_resource.campaign_budget do |cb|\n cb.name = \"Performance Max campaign budget #{SecureRandom.uuid}\"\n # The budget period already defaults to DAILY.\n cb.amount_micros = 50_000_000\n cb.delivery_method = :STANDARD\n # A Performance Max campaign cannot use a shared campaign budget.\n cb.explicitly_shared = false\n\n # Set a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n cb.resource_name =\n client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n end\n end\nend\n\ndef get_hotel_asset_suggestion(client, customer_id, place_id)\n response =\n client.service.travel_asset_suggestion.suggest_travel_assets(\n customer_id: customer_id,\n language_option: 'en-US',\n place_ids: [place_id]\n )\n\n response.hotel_asset_suggestions.first\nend\n\n# Creates multiple text assets and returns the list of resource names.\n# The hotel asset suggestion is used to create a text asset first.\n# If the number of created text assets is still fewer than the minimum required\n# number of assets of the specified asset field type, adds more text assets to\n# fulfill the requirement.\ndef create_multiple_text_assets(\n client,\n customer_id,\n asset_field_type,\n hotel_asset_suggestion\n)\n # Creates the first text asset using the hotel asset suggestions.\n texts = []\n numText = 0\n if hotel_asset_suggestion.status == :SUCCESS\n hotel_asset_suggestion.text_assets.each do |hotel_text_asset|\n if hotel_text_asset.asset_field_type == asset_field_type\n texts.append(hotel_text_asset.text)\n numText += 1\n end\n end\n end\n\n # If the added assets are still less than the minimum required assets for the\n # asset field type, add more text assets using the default texts.\n if numText < MIN_REQUIRED_TEXT_ASSET_COUNTS[asset_field_type]\n for i in numText..MIN_REQUIRED_TEXT_ASSET_COUNTS[asset_field_type] - 1\n texts.append(DEFAULT_TEXT_ASSETS_INFO[asset_field_type][i])\n end\n end\n\n # Create the operations with the selected texts.\n operations =\n texts.map do |text|\n client.operation.mutate do |m|\n m.asset_operation =\n client.operation.create_resource.asset do |asset|\n asset.text_asset =\n client.resource.text_asset { |text_asset| text_asset.text = text }\n end\n end\n end\n\n # Send the operations in a single Mutate request.\n response =\n client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: operations\n )\n\n asset_resource_names = []\n response.mutate_operation_responses.each do |result|\n asset_resource_names.append(result.asset_result.resource_name) if result.asset_result\n end\n print_response_details(response)\n asset_resource_names\nend\n\n# Creates a list of MutateOperations that create a new asset_group.\n#\n# A temporary ID will be assigned to this asset group so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_asset_group_operations(\n client,\n customer_id,\n hotel_property_asset_resource_name,\n headline_asset_resource_names,\n description_asset_resource_names,\n hotel_asset_suggestion\n)\n operations = []\n\n # Creates a new mutate operation that creates an asset group using suggested\n # information when available.\n asset_group_final_urls =\n (\n if hotel_asset_suggestion.status == :SUCCESS\n hotel_asset_suggestion.final_url\n else\n 'http://www.example.com'\n end\n )\n\n operations << client.operation.mutate do |m|\n m.asset_group_operation =\n client.operation.create_resource.asset_group do |ag|\n ag.name =\n \"Performance Max for Travel Goals asset group #{SecureRandom.uuid}\"\n ag.campaign =\n client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n ag.final_urls << asset_group_final_urls\n ag.status = :PAUSED\n ag.resource_name =\n client.path.asset_group(customer_id, ASSET_GROUP_TEMPORARY_ID)\n end\n end\n\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # the resource name of the AssetGroup\n # the resource name of the Asset\n # the field_type of the Asset in this AssetGroup.\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n headline_asset_resource_names.each do |resource_name|\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation =\n client.operation.create_resource.asset_group_asset do |aga|\n aga.field_type = :HEADLINE\n aga.asset_group =\n client.path.asset_group(customer_id, ASSET_GROUP_TEMPORARY_ID)\n aga.asset = resource_name\n end\n end\n end\n\n # Link the description assets.\n description_asset_resource_names.each do |resource_name|\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation =\n client.operation.create_resource.asset_group_asset do |aga|\n aga.field_type = :DESCRIPTION\n aga.asset_group =\n client.path.asset_group(customer_id, ASSET_GROUP_TEMPORARY_ID)\n aga.asset = resource_name\n end\n end\n end\n\n # Link the previously created hotel property asset to the asset group.\n # In the real-world scenario, you'd need to do this step several times for\n # each hotel property asset.\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation =\n client.operation.create_resource.asset_group_asset do |aga|\n aga.field_type = :HOTEL_PROPERTY\n aga.asset_group =\n client.path.asset_group(customer_id, ASSET_GROUP_TEMPORARY_ID)\n aga.asset = hotel_property_asset_resource_name\n end\n end\n\n # Creates the rest of required text assets and link them to the asset group.\n operations +=\n create_text_assets_for_asset_group(\n client,\n customer_id,\n hotel_asset_suggestion\n )\n\n # Creates the image assets and link them to the asset group. Some optional\n # image assets suggested by the TravelAssetSuggestionService might be created too.\n operations +=\n create_image_assets_for_asset_group(\n client,\n customer_id,\n hotel_asset_suggestion\n )\n\n operations\nend\n\n# Creates text assets required for an asset group using the suggested hotel text\n# assets. It adds more text assets to fulfill the requirements if the suggested\n# hotel text assets are not enough.\ndef create_text_assets_for_asset_group(\n client,\n customer_id,\n hotel_asset_suggestion\n)\n operations = []\n\n required_text_asset_counts = deep_copy(MIN_REQUIRED_TEXT_ASSET_COUNTS)\n required_text_asset_counts.each do |asset_field_type, _count|\n required_text_asset_counts[asset_field_type] = 0\n end\n\n # Creates mutate operations for the suggested text assets except for headlines\n # and descriptions, which were created previously.\n if hotel_asset_suggestion.status == :SUCCESS\n hotel_asset_suggestion.text_assets.each do |hotel_text_asset|\n if hotel_text_asset.asset_field_type == :HEADLINE ||\n hotel_text_asset.asset_field_type == :DESCRIPTION\n # Headlines and descriptions were already created at the first step of\n # this code example.\n next\n end\n\n puts \"A text asset with text '#{hotel_text_asset.text}' is suggested for the asset field type '#{hotel_text_asset.asset_field_type}'.\"\n\n operations +=\n create_and_link_text_asset_operations(\n client,\n customer_id,\n hotel_text_asset.text,\n hotel_text_asset.asset_field_type\n )\n\n required_text_asset_counts[hotel_text_asset.asset_field_type] += 1\n end\n end\n\n # Adds more text assets to fulfill the requirements.\n MIN_REQUIRED_TEXT_ASSET_COUNTS.each do |asset_field_type, count|\n if %i[HEADLINE DESCRIPTION].include?(asset_field_type)\n # Headlines and descriptions were already created at the first step of this\n # code example.\n next\n end\n\n next unless required_text_asset_counts[asset_field_type] < count\n\n for i in required_text_asset_counts[asset_field_type]...count\n default_text = DEFAULT_TEXT_ASSETS_INFO[asset_field_type][i]\n puts \"A default text '#{default_text}' is used to create a text asset for the asset field type '#{asset_field_type}'.\"\n\n operations +=\n create_and_link_text_asset_operations(\n client,\n customer_id,\n default_text,\n asset_field_type\n )\n end\n end\n\n operations\nend\n\n# Creates a list of MutateOperations that create a new linked text asset.\ndef create_and_link_text_asset_operations(client, customer_id, text, field_type)\n operations = []\n temp_id = next_temp_id\n\n # Create the Text Asset.\n operations << client.operation.mutate do |m|\n m.asset_operation =\n client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, temp_id)\n a.text_asset =\n client.resource.text_asset { |text_asset| text_asset.text = text }\n end\n end\n\n # Create an AssetGroupAsset to link the Asset to the AssetGroup.\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation =\n client.operation.create_resource.asset_group_asset do |aga|\n aga.field_type = field_type\n aga.asset_group =\n client.path.asset_group(customer_id, ASSET_GROUP_TEMPORARY_ID)\n aga.asset = client.path.asset(customer_id, temp_id)\n end\n end\n\n operations\nend\n\n# Creates image assets required for an asset group using the suggested hotel\n# image assets. It adds more image assets to fulfill the requirements if the\n# suggested hotel image assets are not enough.\ndef create_image_assets_for_asset_group(\n client,\n customer_id,\n hotel_asset_suggestion\n)\n operations = []\n\n required_image_asset_counts = deep_copy(MIN_REQUIRED_IMAGE_ASSET_COUNTS)\n required_image_asset_counts.each do |asset_field_type, _count|\n required_image_asset_counts[asset_field_type] = 0\n end\n\n # Creates mutate operations for the suggested image assets.\n if hotel_asset_suggestion.status == :SUCCESS\n hotel_asset_suggestion.image_assets.each do |hotel_image_asset|\n puts \"An image asset with URL '#{hotel_image_asset.uri}' is suggested for the asset field type '#{hotel_image_asset.asset_field_type}'.\"\n\n operations +=\n create_and_link_image_asset_operations(\n client,\n customer_id,\n hotel_image_asset.uri,\n hotel_image_asset.asset_field_type,\n \"Suggested image asset for the asset field type '%s'.\" %\n hotel_image_asset.asset_field_type\n )\n\n # Keeps track of only required image assets. The service may sometimes\n # suggest optional image assets.\n unless required_image_asset_counts.has_key?(\n hotel_image_asset.asset_field_type\n )\n next\n end\n\n required_image_asset_counts[hotel_image_asset.asset_field_type] += 1\n end\n end\n\n # Adds more image assets to fulfill the requirements.\n MIN_REQUIRED_IMAGE_ASSET_COUNTS.each do |asset_field_type, count|\n next unless required_image_asset_counts[asset_field_type] < count\n\n for i in required_image_asset_counts[asset_field_type]...count\n default_uri = DEFAULT_IMAGE_ASSETS_INFO[asset_field_type][i]\n puts \"A default image URL '#{default_uri}' is used to create an image asset for the asset field type '#{asset_field_type}'.\"\n\n operations +=\n create_and_link_image_asset_operations(\n client,\n customer_id,\n default_uri,\n asset_field_type,\n \"Default image asset for the asset field type '%s'.\" %\n asset_field_type\n )\n end\n end\n\n operations\nend\n\n# Creates a list of MutateOperations that create a new linked image asset.\ndef create_and_link_image_asset_operations(\n client,\n customer_id,\n url,\n field_type,\n asset_name\n)\n operations = []\n temp_id = next_temp_id\n\n # Create the Image Asset.\n operations << client.operation.mutate do |m|\n m.asset_operation =\n client.operation.create_resource.asset do |a|\n a.resource_name = client.path.asset(customer_id, temp_id)\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n a.name = asset_name\n a.type = :IMAGE\n a.image_asset =\n client.resource.image_asset do |image_asset|\n image_asset.data = get_image_bytes(url)\n end\n end\n end\n\n # Create an AssetGroupAsset to link the Asset to the AssetGroup.\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation =\n client.operation.create_resource.asset_group_asset do |aga|\n aga.field_type = field_type\n aga.asset_group =\n client.path.asset_group(customer_id, ASSET_GROUP_TEMPORARY_ID)\n aga.asset = client.path.asset(customer_id, temp_id)\n end\n end\n\n operations\nend\n\n# Loads image data from a URL.\ndef get_image_bytes(url)\n URI.open(url).read\nend\n\n# Prints the details of a MutateGoogleAdsResponse.\ndef print_response_details(response)\n # Parse the mutate response to print details about the entities that\n # were created by the request.\n suffix = '_result'\n response.mutate_operation_responses.each do |result|\n result\n .to_h\n .select { |_k, v| v }\n .each do |name, value|\n name = name.to_s.delete_suffix(suffix) if name.to_s.end_with?(suffix)\n\n puts \"Created a(n) #{::Google::Ads::GoogleAds::Utils.camelize(name)} \" \\\n \"with #{value.to_s.strip}.\"\n end\n end\nend\n\ndef deep_copy(o)\n Marshal.load(Marshal.dump(o))\nend\n\nif __FILE__ == $0\n options = {}\n\n OptionParser\n .new do |opts|\n opts.banner = format('Usage: %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-P', '--place-id PLACE-ID', String, 'Place ID') do |v|\n options[:place_id] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end\n .parse!\n\n begin\n add_performance_max_for_travel_goals(\n options.fetch(:customer_id).tr('-', ''),\n options[:place_id]\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\nadd_performance_max_for_travel_goals_campaign.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2023, Google LLC\n#\n# 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# http://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.\n#\n# This example shows how to create a Performance Max for travel goals campaign. It also uses\n# TravelAssetSuggestionService to fetch suggested assets for creating an asset group. In case\n# there are not enough assets for the asset group (required by Performance Max), this example will\n# create more assets to fulfill the requirements.\n#\n# For more information about Performance Max campaigns, see\n# https://developers.google.com/google-ads/api/docs/performance-max/overview.\n#\n# Prerequisites:\n# - You must have at least one conversion action in the account. For more about conversion actions,\n# see https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n#\n# Notes:\n# - This example uses the default customer conversion goals. For an example of setting\n# campaign-specific conversion goals, see shopping_ads/add_performance_max_retail_campaign.pl.\n# - To learn how to create asset group signals, see\n# advanced_operations/add_performance_max_campaign.pl.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::Utils::MediaUtils;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignBudget;\nuse Google::Ads::GoogleAds::V25::Resources::Campaign;\nuse Google::Ads::GoogleAds::V25::Resources::Asset;\nuse Google::Ads::GoogleAds::V25::Resources::AssetGroup;\nuse Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset;\nuse Google::Ads::GoogleAds::V25::Resources::AssetSet;\nuse Google::Ads::GoogleAds::V25::Resources::AssetSetAsset;\nuse Google::Ads::GoogleAds::V25::Common::CallToActionAsset;\nuse Google::Ads::GoogleAds::V25::Common::MaximizeConversionValue;\nuse Google::Ads::GoogleAds::V25::Common::TextAsset;\nuse Google::Ads::GoogleAds::V25::Common::HotelPropertyAsset;\nuse Google::Ads::GoogleAds::V25::Common::ImageAsset;\nuse Google::Ads::GoogleAds::V25::Enums::BudgetDeliveryMethodEnum qw(STANDARD);\nuse Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum;\nuse Google::Ads::GoogleAds::V25::Enums::AdvertisingChannelTypeEnum\n qw(PERFORMANCE_MAX);\nuse Google::Ads::GoogleAds::V25::Enums::AssetGroupStatusEnum;\nuse Google::Ads::GoogleAds::V25::Enums::AssetFieldTypeEnum\n qw(HEADLINE DESCRIPTION LONG_HEADLINE BUSINESS_NAME LOGO MARKETING_IMAGE SQUARE_MARKETING_IMAGE HOTEL_PROPERTY CALL_TO_ACTION_SELECTION);\nuse Google::Ads::GoogleAds::V25::Enums::HotelAssetSuggestionStatusEnum\n qw(SUCCESS);\nuse Google::Ads::GoogleAds::V25::Enums::EuPoliticalAdvertisingStatusEnum\n qw(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING);\nuse Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation;\nuse Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation;\nuse Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::AssetGroupService::AssetGroupOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation;\nuse Google::Ads::GoogleAds::V25::Services::AssetSetService::AssetSetOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::AssetSetAssetService::AssetSetAssetOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\n\n# Minimum requirements of assets required in a Performance Max asset group.\n# See https://developers.google.com/google-ads/api/docs/performance-max/assets for details.\nmy $min_required_text_asset_counts = {\n HEADLINE => 3,\n LONG_HEADLINE => 1,\n DESCRIPTION => 2,\n BUSINESS_NAME => 1,\n};\n\nmy $min_required_image_asset_counts = {\n MARKETING_IMAGE => 1,\n SQUARE_MARKETING_IMAGE => 1,\n LOGO => 1,\n};\n\n# Texts and URLs used to create text and image assets when the TravelAssetSuggestionService\n# doesn't return enough assets required for creating an asset group.\nmy $default_text_assets_info = {\n HEADLINE => ['Hotel', 'Travel Reviews', 'Book travel'],\n LONG_HEADLINE => ['Travel the World'],\n DESCRIPTION => ['Great deal for your beloved hotel', 'Best rate guaranteed',],\n BUSINESS_NAME => ['Interplanetary Cruises'],\n};\n\nmy $default_image_assets_info = {\n MARKETING_IMAGE => ['https://gaagl.page.link/Eit5'],\n SQUARE_MARKETING_IMAGE => ['https://gaagl.page.link/bjYi'],\n LOGO => ['https://gaagl.page.link/bjYi'],\n};\n\n# We specify temporary IDs that are specific to a single mutate request.\n# Temporary IDs are always negative and unique within one mutate request.\n#\n# See https://developers.google.com/google-ads/api/docs/mutating/best-practices\n# for further details.\n#\n# These temporary IDs are fixed because they are used in multiple places.\nuse constant ASSET_TEMPORARY_ID => -1;\nuse constant BUDGET_TEMPORARY_ID => -2;\nuse constant CAMPAIGN_TEMPORARY_ID => -3;\nuse constant ASSET_GROUP_TEMPORARY_ID => -4;\n\n# There are also entities that will be created in the same request but do not need to be fixed\n# temporary IDs because they are referenced only once.\nour $next_temp_id = ASSET_GROUP_TEMPORARY_ID - 1;\n\nsub add_performance_max_for_travel_goals_campaign {\n my ($api_client, $customer_id, $place_id) = @_;\n\n my $hotel_asset_suggestion =\n get_hotel_asset_suggestion($api_client, $customer_id, $place_id);\n\n # Performance Max campaigns require that repeated assets such as headlines\n # and descriptions be created before the campaign.\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets.\n #\n # This step is the same for any types of Performance Max campaigns.\n\n # Create the headlines using the hotel asset suggestion.\n my $headline_asset_resource_names =\n create_multiple_text_assets($api_client, $customer_id, HEADLINE,\n $hotel_asset_suggestion);\n\n my $description_asset_resource_names =\n create_multiple_text_assets($api_client, $customer_id, DESCRIPTION,\n $hotel_asset_suggestion);\n\n # Create a hotel property asset set, which will be used later to link with a newly created\n # campaign.\n my $hotel_property_asset_set_resource_name =\n create_hotel_asset_set($api_client, $customer_id);\n\n # Create a hotel property asset and link it with the previously created hotel property\n # asset set. This asset will also be linked to an asset group in the later steps.\n # In the real-world scenario, you'd need to create many assets for all your hotel\n # properties. We use one hotel property here for simplicity.\n # Both asset and asset set need to be created before creating a campaign, so we cannot\n # bundle them with other mutate operations below.\n my $hotel_property_asset_resource_name =\n create_hotel_asset($api_client, $customer_id, $place_id,\n $hotel_property_asset_set_resource_name);\n\n # It's important to create the below entities in this order because they depend on\n # each other.\n # The below methods create and return mutate operations that we later provide to the\n # GoogleAdsService.Mutate method in order to create the entities in a single request.\n # Since the entities for a Performance Max campaign are closely tied to one-another, it's\n # considered a best practice to create them in a single Mutate request so they all complete\n # successfully or fail entirely, leaving no orphaned entities. See:\n # https://developers.google.com/google-ads/api/docs/mutating/overview.\n my $operations = [];\n push @$operations, create_campaign_budget_operation($customer_id);\n push @$operations,\n create_campaign_operation($customer_id,\n $hotel_property_asset_set_resource_name);\n push @$operations,\n @{\n create_asset_group_operations(\n $customer_id, $hotel_property_asset_resource_name,\n $headline_asset_resource_names, $description_asset_resource_names,\n $hotel_asset_suggestion\n )};\n\n # Issue a mutate request to create everything and print its information.\n my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $operations\n });\n printf\n\"Created the following entities for a campaign budget, a campaign, and an asset group\"\n . \" for Performance Max for travel goals:\\n\";\n print_response_details($mutate_google_ads_response);\n}\n\n# Return hotel asset suggestion obtained from TravelAssetsSuggestionService.\nsub get_hotel_asset_suggestion {\n my ($api_client, $customer_id, $place_id) = @_;\n\n # Send a request to suggest assets to be created as an asset group for the Performance Max\n # for travel goals campaign.\n my $suggest_travel_assets_response =\n $api_client->TravelAssetSuggestionService()->suggest_travel_assets({\n customerId => $customer_id,\n # Uses 'en-US' as an example. It can be any language specifications in BCP 47 format.\n languageOption => 'en-US',\n # The service accepts several place IDs. We use only one here for demonstration.\n placeIds => [$place_id],\n });\n\n printf \"Fetched a hotel asset suggestion for the place ID '%s'.\\n\", $place_id;\n return $suggest_travel_assets_response->{hotelAssetSuggestions}[0];\n}\n\n# Create multiple text assets and returns the list of resource names. The hotel asset\n# suggestion is used to create a text asset first. If the number of created text assets is\n# still fewer than the minimum required number of assets of the specified asset field type,\n# adds more text assets to fulfill the requirement.\nsub create_multiple_text_assets {\n my ($api_client, $customer_id, $asset_field_type, $hotel_asset_suggestion) =\n @_;\n # We use the GoogleAdService to create multiple text assets in a single request.\n # First, add all the text assets of the specified asset field type.\n my $operations = [];\n\n if ($hotel_asset_suggestion->{status} eq SUCCESS) {\n foreach my $text_asset (@{$hotel_asset_suggestion->{textAssets}}) {\n if ($text_asset->{assetFieldType} ne $asset_field_type) {\n next;\n }\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n textAsset =>\n Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $text_asset->{text}})})})});\n }\n }\n\n # If the added assets are still less than the minimum required assets for the asset field\n # type, add more text assets using the default texts.\n my $min_count = $min_required_text_asset_counts->{$asset_field_type};\n my $num_operations_added = scalar @$operations;\n for (my $i = 0 ; $i < $min_count - $num_operations_added ; $i++) {\n my $text = $default_text_assets_info->{$asset_field_type}[$i++];\n # Creates a mutate operation for a text asset, using the default text.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n textAsset =>\n Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $text\n })})})});\n }\n\n # Issue a mutate request to add all assets.\n my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $operations\n });\n\n my $asset_resource_names = [];\n foreach\n my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})\n {\n push @$asset_resource_names, $response->{assetResult}{resourceName};\n }\n print_response_details($mutate_google_ads_response);\n\n return $asset_resource_names;\n}\n\n# Create a hotel property asset set.\nsub create_hotel_asset_set {\n my ($api_client, $customer_id) = @_;\n\n my $asset_set_operation =\n Google::Ads::GoogleAds::V25::Services::AssetSetService::AssetSetOperation->\n new({\n # Creates a hotel property asset set.\n create => Google::Ads::GoogleAds::V25::Resources::AssetSet->new({\n name => 'My Hotel propery asset set #' . uniqid(),\n type => HOTEL_PROPERTY\n })});\n # Issues a mutate request to add a hotel asset set and prints its information.\n my $response = $api_client->AssetSetService()->mutate({\n customerId => $customer_id,\n operations => [$asset_set_operation]});\n\n my $asset_set_resource_name = $response->{results}[0]{resourceName};\n printf \"Created an asset set with resource name: '%s'.\\n\",\n $asset_set_resource_name;\n\n return $asset_set_resource_name;\n}\n\n# Create a hotel property asset using the specified place ID. The place ID must belong to\n# a hotel property. Then, links it to the specified asset set.\n#\n# See https://developers.google.com/places/web-service/place-id to search for a hotel place ID.\nsub create_hotel_asset {\n my ($api_client, $customer_id, $place_id, $asset_set_resource_name) = @_;\n\n # We use the GoogleAdService to create an asset and asset set asset in a single request.\n my $operations = [];\n my $asset_resource_name =\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset($customer_id,\n ASSET_TEMPORARY_ID);\n\n # Create a mutate operation for a hotel property asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName => $asset_resource_name,\n hotelPropertyAsset =>\n Google::Ads::GoogleAds::V25::Common::HotelPropertyAsset->new({\n placeId => $place_id\n })})})});\n\n # Create a mutate operation for an asset set asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetSetAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetSetAssetService::AssetSetAssetOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::AssetSetAsset->new({\n asset => $asset_resource_name,\n assetSet => $asset_set_resource_name\n })})});\n\n # Issue a mutate request to create all entities.\n my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $operations\n });\n\n printf \"Created the following entities for the hotel asset:\\n\";\n print_response_details($mutate_google_ads_response);\n\n # Return the created asset resource name, which will be used later to create an asset\n # group. Other resource names are not used later.\n return $mutate_google_ads_response->{mutateOperationResponses}[0]\n {assetResult}{resourceName};\n}\n\n# Create a mutate operation that creates a new campaign budget.\n#\n# A temporary ID will be assigned to this campaign budget so that it can be\n# referenced by other objects being created in the same mutate request.\nsub create_campaign_budget_operation {\n my ($customer_id) = @_;\n\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignBudgetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::CampaignBudget->new(\n {\n # Set a temporary ID in the budget's resource name so it can be\n # referenced by the campaign in later steps.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n name => \"Performance Max for travel goals campaign budget #\" .\n uniqid(),\n # The budget period already defaults to DAILY.\n amountMicros => 50000000,\n deliveryMethod => STANDARD,\n # A Performance Max campaign cannot use a shared campaign budget.\n explicitlyShared => \"false\",\n })})});\n}\n\n# Create a mutate operation that creates a new Performance Max campaign. Links the specified\n# hotel property asset set to this campaign.\n#\n# A temporary ID will be assigned to this campaign so that it can be referenced by other\n# objects being created in the same mutate request.\nsub create_campaign_operation {\n my ($customer_id, $hotel_property_asset_set_resource_name) = @_;\n\n # Create a mutate operation that creates a campaign operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n # Assign the resource name with a temporary ID.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, CAMPAIGN_TEMPORARY_ID\n ),\n name => \"Performance Max for travel goals campaign #'\" . uniqid(),\n # Set the budget using the given budget resource name.\n campaignBudget =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n status =>\n Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n # All Performance Max campaigns have an advertisingChannelType of\n # PERFORMANCE_MAX. The advertisingChannelSubType should not be set.\n advertisingChannelType => PERFORMANCE_MAX,\n\n # To create a Performance Max for travel goals campaign, you need to set\n # `hotelPropertyAssetSet`.\n hotelPropertyAssetSet => $hotel_property_asset_set_resource_name,\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Max Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximizeConversionValue. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Max Conversion Value, see the support article:\n # http://support.google.com/google-ads/answer/7684216.\n # A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n maximizeConversionValue =>\n Google::Ads::GoogleAds::V25::Common::MaximizeConversionValue->\n new({\n targetRoas => 3.5\n })})})});\n}\n\n# Create a list of mutate operations that create a new asset group, composed of suggested\n# assets. In case the number of suggested assets is not enough for the requirements, it'll\n# create more assets to meet the requirement.\n#\n# For the list of required assets for a Performance Max campaign, see\n# https://developers.google.com/google-ads/api/docs/performance-max/assets.\nsub create_asset_group_operations {\n my (\n $customer_id,\n $hotel_property_asset_resource_name,\n $headline_asset_resource_names,\n $description_asset_resource_names,\n $hotel_asset_suggestion\n ) = @_;\n my $operations = [];\n\n # Create a new mutate operation that creates an asset group using suggested information\n # when available.\n my $asset_group_name =\n $hotel_asset_suggestion->{status} eq SUCCESS\n ? $hotel_asset_suggestion->{hotelName}\n : 'Performance Max for travel goals asset group #' . uniqid();\n my $asset_group_final_urls =\n $hotel_asset_suggestion->{status} eq SUCCESS\n ? [$hotel_asset_suggestion->{finalUrl}]\n : ['http://www.example.com'];\n my $asset_group_resource_name =\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group($customer_id,\n ASSET_GROUP_TEMPORARY_ID);\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupService::AssetGroupOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::AssetGroup->new({\n resourceName => $asset_group_resource_name,\n name => $asset_group_name,\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, CAMPAIGN_TEMPORARY_ID\n ),\n finalUrls => $asset_group_final_urls,\n status =>\n Google::Ads::GoogleAds::V25::Enums::AssetGroupStatusEnum::PAUSED\n })})});\n\n # An asset group is linked to an asset by creating a new asset group asset and providing:\n # - the resource name of the asset group\n # - the resource name of the asset\n # - the field_type of the asset in this asset group\n #\n # To learn more about asset groups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n #\n # Headline and description assets were created at the first step of this example. So, we\n # just need to link them with the created asset group.\n #\n # Link the headline assets to the asset group.\n foreach my $resource_name (@$headline_asset_resource_names) {\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $resource_name,\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => HEADLINE\n })})});\n }\n\n # Link the description assets.\n foreach my $resource_name (@$description_asset_resource_names) {\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $resource_name,\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => DESCRIPTION\n })})});\n }\n\n # Link the previously created hotel property asset to the asset group. In the real-world\n # scenario, you'd need to do this step several times for each hotel property asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $hotel_property_asset_resource_name,\n assetGroup => $asset_group_resource_name,\n fieldType => HOTEL_PROPERTY\n })})});\n\n # Create the rest of required text assets and link them to the asset group.\n push @$operations,\n @{create_text_assets_for_asset_group($customer_id, $hotel_asset_suggestion)\n };\n\n # Create the image assets and link them to the asset group. Some optional image assets\n # suggested by the TravelAssetSuggestionService might be created too.\n push @$operations,\n @{create_image_assets_for_asset_group($customer_id, $hotel_asset_suggestion)\n };\n\n if ($hotel_asset_suggestion->{status} eq SUCCESS) {\n # Create a new mutate operation for a suggested call-to-action asset and link it\n # to the asset group.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n name => 'Suggested call-to-action asset #' . uniqid(),\n callToActionAsset =>\n Google::Ads::GoogleAds::V25::Common::CallToActionAsset->new({\n callToAction => $hotel_asset_suggestion->{callToAction}})})}\n )});\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n assetGroup => $asset_group_resource_name,\n fieldType => CALL_TO_ACTION_SELECTION\n })})});\n $next_temp_id--;\n }\n\n return $operations;\n}\n\n# Create text assets required for an asset group using the suggested hotel text assets. It adds\n# more text assets to fulfill the requirements if the suggested hotel text assets are not enough.\nsub create_text_assets_for_asset_group {\n my ($customer_id, $hotel_asset_suggestion) = @_;\n\n # Create mutate operations for the suggested text assets except for headlines and\n # descriptions, which were created previously.\n my $operations = [];\n\n # Create a map of asset field type to number of text values.\n my $required_text_asset_counts = {};\n foreach my $field_type (keys %$min_required_text_asset_counts) {\n $required_text_asset_counts->{$field_type} = 0;\n }\n\n if ($hotel_asset_suggestion->{status} eq SUCCESS) {\n # Add text values of suggested text assets.\n foreach my $hotel_text_asset (@{$hotel_asset_suggestion->{textAssetsList}})\n {\n my $asset_field_type = $hotel_text_asset->{assetFieldType};\n if ($asset_field_type eq HEADLINE or $asset_field_type eq DESCRIPTION) {\n # Headlines and descriptions were already created at the first step of this code example.\n next;\n }\n printf\n\"A text asset with text '%s' is suggested for the asset field type '%s'.\\n\",\n $hotel_text_asset->{text}, $asset_field_type;\n\n push @$operations,\n @{\n create_text_asset_and_asset_group_asset_operations(\n $customer_id, $hotel_text_asset->{text},\n $hotel_text_asset->{assetFieldType})};\n $required_text_asset_counts->{$asset_field_type}++;\n }\n }\n\n # Add more text values by field type to fulfill the requirements.\n foreach my $asset_field_type (keys %$min_required_text_asset_counts) {\n if ($asset_field_type eq HEADLINE or $asset_field_type eq DESCRIPTION) {\n # Headlines and descriptions were already created at the first step of this code example.\n next;\n }\n\n my $min_count = $min_required_text_asset_counts->{$asset_field_type};\n for (\n my $i = 0 ;\n $i < $min_count - $required_text_asset_counts->{$asset_field_type} ;\n $i++\n )\n {\n my $text_from_defaults =\n $default_text_assets_info->{$asset_field_type}[$i++];\n printf\n\"A default text '%s' is used to create a text asset for the asset field type '%s'.\\n\",\n $text_from_defaults, $asset_field_type;\n push @$operations,\n @{\n create_text_asset_and_asset_group_asset_operations($customer_id,\n $text_from_defaults, $asset_field_type)};\n\n }\n }\n\n return $operations;\n}\n\n# Create image assets required for an asset group using the suggested hotel image assets. It\n# adds more image assets to fulfill the requirements if the suggested hotel image assets are\n# not enough.\nsub create_image_assets_for_asset_group {\n my ($customer_id, $hotel_asset_suggestion) = @_;\n\n my $operations = [];\n # Create mutate operations for the suggested image assets.\n # Create a map of asset field type to number of text values.\n my $required_image_asset_counts = {};\n foreach my $field_type (keys %$min_required_image_asset_counts) {\n $required_image_asset_counts->{$field_type} = 0;\n }\n foreach my $hotel_image_asset (@{$hotel_asset_suggestion->{imageAssets}}) {\n printf\n\"An image asset with url '%s' is suggested for the asset field type '%s'.\\n\",\n $hotel_image_asset->{uri}, $hotel_image_asset->{assetFieldType};\n push @$operations,\n @{\n create_image_asset_and_asset_group_asset_operations(\n $customer_id,\n $hotel_image_asset->{uri},\n $hotel_image_asset->{assetFieldType},\n 'Suggested image asset #' . uniqid())};\n # Keeps track of only required image assets. The service may sometimes suggest optional\n # image assets.\n if (\n exists $required_image_asset_counts->\n {$hotel_image_asset->{assetFieldType}})\n {\n $required_image_asset_counts->{$hotel_image_asset->{assetFieldType}}++;\n }\n }\n\n # Add more image assets to fulfill the requirements.\n foreach my $asset_field_type (keys %$min_required_image_asset_counts) {\n my $min_count = $min_required_image_asset_counts->{$asset_field_type};\n for (\n my $i = 0 ;\n $i < $min_count - $required_image_asset_counts->{$asset_field_type} ;\n $i++\n )\n {\n my $image_from_defaults =\n $default_image_assets_info->{$asset_field_type}[$i++];\n printf\n\"A default image URL '%s' is used to create an image asset for the asset field type '%s'.\\n\",\n $image_from_defaults, $asset_field_type;\n push @$operations,\n @{\n create_image_asset_and_asset_group_asset_operations(\n $customer_id, $image_from_defaults,\n $asset_field_type, lc $asset_field_type . uniqid())};\n }\n }\n\n return $operations;\n}\n\n# Create a list of mutate operations that create a new linked text asset.\nsub create_text_asset_and_asset_group_asset_operations {\n my ($customer_id, $text, $field_type) = @_;\n\n my $operations = [];\n # Create a new mutate operation that creates a text asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n textAsset => Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $text\n })})})});\n\n # Create an asset group asset to link the asset to the asset group.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => $field_type\n })})});\n\n $next_temp_id--;\n\n return $operations;\n}\n\n# Create a list of mutate operations that create a new linked image asset.\nsub create_image_asset_and_asset_group_asset_operations {\n my ($customer_id, $url, $field_type, $asset_name) = @_;\n\n my $operations = [];\n # Create a new mutate operation that creates an image asset.\n # Create a new mutate operation that creates a text asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n # Provide a unique friendly name to identify your asset.\n # When there is an existing image asset with the same content but a different\n # name, the new name will be dropped silently.\n name => $asset_name,\n imageAsset =>\n Google::Ads::GoogleAds::V25::Common::ImageAsset->new({\n data => get_base64_data_from_url($url)})})})});\n\n # Create an asset group asset to link the asset to the asset group.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset(\n $customer_id, $next_temp_id\n ),\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => $field_type\n })})});\n\n $next_temp_id--;\n\n return $operations;\n}\n\n# Prints the details of a MutateGoogleAdsResponse.\n# Parses the \"response\" oneof field name and uses it to extract the new entity's\n# name and resource name.\nsub print_response_details {\n my ($mutate_google_ads_response) = @_;\n\n foreach\n my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})\n {\n my $result_type = [keys %$response]->[0];\n\n printf \"Created a(n) %s with '%s'.\\n\",\n ucfirst $result_type =~ s/Result$//r,\n $response->{$result_type}{resourceName};\n }\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\nmy $customer_id = undef;\nmy $place_id = undef;\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"place_id=s\" => \\$place_id,\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2)\n if not check_params($customer_id, $place_id);\n\n# Call the example.\nadd_performance_max_for_travel_goals_campaign($api_client,\n $customer_id =~ s/-//gr, $place_id);\n\n=pod\n\n=head1 NAME\n\nadd_performance_max_for_travel_goals_campaign\n\n=head1 DESCRIPTION\n\nThis example shows how to create a Performance Max for travel goals campaign. It also uses\nTravelAssetSuggestionService to fetch suggested assets for creating an asset group. In case\nthere are not enough assets for the asset group (required by Performance Max), this example will\ncreate more assets to fulfill the requirements.\n\nFor more information about Performance Max campaigns, see\nhttps://developers.google.com/google-ads/api/docs/performance-max/overview.\n\nPrerequisites:\n- You must have at least one conversion action in the account. For more about conversion actions,\nsee https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions.\n\nNotes:\n- This example uses the default customer conversion goals. For an example of setting\n campaign-specific conversion goals, see shopping_ads/add_performance_max_retail_campaign.pl.\n- To learn how to create asset group signals, see\n advanced_operations/add_performance_max_campaign.pl.\n\n=head1 SYNOPSIS\n\nadd_performance_max_for_travel_goals_campaign.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -place_id \t\t\t\t\t The place ID of a hotel property. A place ID uniquely identifies a place in the Google Places database. See https://developers.google.com/places/web-service/place-id to learn more.\n\n=cut\nadd_performance_max_for_travel_goals_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.450Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":5826,"estimatedTokens":62766}}173{"id":"doc-customer_goals_google_ads_api_google_for_develop-be4c36d3","source":"documentation","title":"Customer goals | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/conversions/goals/customer-goals","text":"Example:\n```text\nSELECT\n customer_conversion_goal.resource_name,\n customer_conversion_goal.category,\n customer_conversion_goal.origin,\n customer_conversion_goal.biddable\nFROM customer_conversion_goal\n```\n\nExample:\n```text\nSELECT\n conversion_action.category,\n conversion_action.origin,\n conversion_action.name\nFROM conversion_action\nWHERE conversion_action.category = 'PAGE_VIEW'\n AND conversion_action.origin = 'WEBSITE'\n AND conversion_action.status = 'ENABLED'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.454Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":23,"estimatedTokens":123}}174{"id":"doc-conversion_reporting_google_ads_api_google_for_d-3b3a5a3d","source":"documentation","title":"Conversion reporting | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/conversions/reporting","text":"Example:\n```text\nSELECT\n conversion_action.resource_name,\n conversion_action.name,\n conversion_action.type,\n conversion_action.status\nFROM conversion_action\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n segments.conversion_action,\n metrics.conversions,\n metrics.conversions_value\nFROM campaign\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.454Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":21,"estimatedTokens":80}}175{"id":"doc-lifecycle_goals_google_ads_api_google_for_develo-15e21647","source":"documentation","title":"Lifecycle goals | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/conversions/goals/lifecycle-goals","text":"Example:\n```text\ndef create_goal(client: GoogleAdsClient, customer_id: str) -> None:\n \"\"\"Sends an API request to add a new Goal.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n \"\"\"\n goal_operation: GoalOperation = client.get_type(\"GoalOperation\")\n goal = goal_operation.create\n goal.retention_goal_settings.value_settings.additional_value = 50.0\n goal.retention_goal_settings.value_settings.additional_high_lifetime_value = 100.0\n\n goal_service = client.get_service(\"GoalService\")\n goal_service.mutate_goals(\n customer_id=customer_id, operations=[goal_operation]\n )\n```\n\nExample:\n```text\ndef create_campaign_goal_config(\n client: GoogleAdsClient,\n customer_id: str,\n goal_resource_name: str,\n campaign_resource_name: str\n) -> None:\n \"\"\"Sends an API request to add a new CampaignGoalConfig.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n goal_resource_name: the resource name of an existing Goal.\n campaign_resource_name: the resource name of an existing Campaign.\n \"\"\"\n operation: CampaignGoalConfigOperation = client.get_type(\"CampaignGoalConfigOperation\")\n goal_config = operation.create\n goal_config.campaign = campaign_resource_name\n goal_config.goal = goal_resource_name\n\n # Note that the target_option will be set to TARGET_ALL by default. In order\n # to set it to TARGET_SPECIFIC your account must be on the appropriate\n # allowlist.\n #\n # goal_config.campaign_retention_settings.target_option = (\n # client.enums.CustomerLifecycleOptimizationModeEnum.TARGET_SPECIFIC\n # )\n\n campaign_goal_config_service = client.get_service(\"CampaignGoalConfigService\")\n campaign_goal_config_service.mutate_campaign_goal_configs(\n customer_id=customer_id, operations=[operation]\n )\n```\n\nExample:\n```text\nSELECT\n customer_lifecycle_goal.owner_customer,\n customer_lifecycle_goal.customer_acquisition_goal_value_settings.value,\n customer_lifecycle_goal.customer_acquisition_goal_value_settings.high_lifetime_value\nFROM customer_lifecycle_goal\n```\n\nExample:\n```text\nSELECT\n campaign_lifecycle_goal.campaign,\n campaign_lifecycle_goal.customer_acquisition_goal_settings.optimization_mode,\n campaign_lifecycle_goal.customer_acquisition_goal_settings.value_settings.value,\n campaign_lifecycle_goal.customer_acquisition_goal_settings.value_settings.high_lifetime_value\nFROM campaign_lifecycle_goal\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.455Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":75,"estimatedTokens":635}}176{"id":"doc-create_ad_group_and_ad_group_ad_google_ads_api_g-b21b4746","source":"documentation","title":"Create ad group and ad group ad | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/smart-campaigns/create-ad-group-and-ad","text":"Example:\n```text\nprivate MutateOperation createAdGroupOperation(long customerId) {\n MutateOperation.Builder builder = MutateOperation.newBuilder();\n builder\n .getAdGroupOperationBuilder()\n .getCreateBuilder()\n .setResourceName(ResourceNames.adGroup(customerId, AD_GROUP_TEMPORARY_ID))\n .setName(\"Smart campaign ad group \" + CodeSampleHelper.getShortPrintableDateTime())\n .setCampaign(ResourceNames.campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID))\n .setType(AdGroupType.SMART_CAMPAIGN_ADS);\n return builder.build();\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a MutateOperation that creates a new ad group.\n/// A temporary ID will be used in the campaign resource name for this ad group to\n/// associate it with the Smart campaign created in earlier steps. A temporary ID will\n/// also be used for its own resource name so that we can associate an ad group ad with\n/// it later in the process.\n/// Only one ad group can be created for a given Smart campaign.\n/// </summary>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <returns>A MutateOperation that creates a new ad group.</returns>\nprivate MutateOperation CreateAdGroupOperation(long customerId)\n{\n return new MutateOperation\n {\n AdGroupOperation = new AdGroupOperation\n {\n Create = new AdGroup\n {\n // Set the ad group ID to a temporary ID.\n ResourceName = ResourceNames.AdGroup(customerId, AD_GROUP_TEMPORARY_ID),\n Name = $\"Smart campaign ad group #{ExampleUtilities.GetRandomString()}\",\n // Set the campaign ID to a temporary ID.\n Campaign = ResourceNames.Campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID),\n // The ad group type must be SmartCampaignAds.\n Type = AdGroupType.SmartCampaignAds\n }\n }\n };\n}AddSmartCampaign.cs\n```\n\nExample:\n```text\nprivate static function createAdGroupOperation(int $customerId): MutateOperation\n{\n // Creates the ad group object.\n $adGroup = new AdGroup([\n // Sets the ad group ID to a temporary ID.\n 'resource_name' => ResourceNames::forAdGroup($customerId, self::AD_GROUP_TEMPORARY_ID),\n 'name' => \"Smart campaign ad group #\" . Helper::getPrintableDatetime(),\n // Sets the campaign ID to a temporary ID.\n 'campaign' =>\n ResourceNames::forCampaign($customerId, self::SMART_CAMPAIGN_TEMPORARY_ID),\n // The ad group type must be set to SMART_CAMPAIGN_ADS.\n 'type' => AdGroupType::SMART_CAMPAIGN_ADS\n ]);\n\n // Creates the MutateOperation that creates the ad group.\n return new MutateOperation([\n 'ad_group_operation' => new AdGroupOperation(['create' => $adGroup])\n ]);\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef create_ad_group_operation(\n client: GoogleAdsClient, customer_id: str\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new ad group.\n\n A temporary ID will be used in the campaign resource name for this\n ad group to associate it with the Smart campaign created in earlier steps.\n A temporary ID will also be used for its own resource name so that we can\n associate an ad group ad with it later in the process.\n\n Only one ad group can be created for a given Smart campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a MutateOperation that creates a new ad group.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n ad_group_operation: AdGroupOperation = mutate_operation.ad_group_operation\n ad_group: AdGroup = ad_group_operation.create\n # Set the ad group ID to a temporary ID.\n ad_group.resource_name = client.get_service(\"AdGroupService\").ad_group_path(\n customer_id, _AD_GROUP_TEMPORARY_ID\n )\n ad_group.name = f\"Smart campaign ad group #{uuid4()}\"\n # Set the campaign ID to a temporary ID.\n ad_group.campaign = client.get_service(\"CampaignService\").campaign_path(\n customer_id, _BUDGET_TEMPORARY_ID\n )\n # The ad group type must be set to SMART_CAMPAIGN_ADS.\n ad_group.type_ = client.enums.AdGroupTypeEnum.SMART_CAMPAIGN_ADS\n\n return mutate_operationadd_smart_campaign.py\n```\n\nExample:\n```text\n# Creates a mutate_operation that creates a new ad group.\n# A temporary ID will be used in the campaign resource name for this\n# ad group to associate it with the Smart campaign created in earlier steps.\n# A temporary ID will also be used for its own resource name so that we can\n# associate an ad group ad with it later in the process.\n# Only one ad group can be created for a given Smart campaign.\ndef create_ad_group_operation(client, customer_id)\n mutate_operation = client.operation.mutate do |m|\n m.ad_group_operation = client.operation.create_resource.ad_group do |ag|\n # Set the ad group ID to a temporary ID.\n ag.resource_name = client.path.ad_group(customer_id, AD_GROUP_TEMPORARY_ID)\n ag.name = \"Smart campaign ad group ##{(Time.new.to_f * 1000).to_i}\"\n # Set the campaign ID to a temporary ID.\n ag.campaign = client.path.campaign(customer_id, SMART_CAMPAIGN_TEMPORARY_ID)\n # The ad group type must be set to SMART_CAMPAIGN_ADS.\n ag.type = :SMART_CAMPAIGN_ADS\n end\n end\n\n mutate_operation\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Creates a MutateOperation that creates a new ad group.\n# A temporary ID will be used in the campaign resource name for this ad group to\n# associate it with the Smart campaign created in earlier steps. A temporary ID\n# will also be used for its own resource name so that we can associate an ad group ad\n# with it later in the process.\n# Only one ad group can be created for a given Smart campaign.\nsub _create_ad_group_operation {\n my ($customer_id) = @_;\n\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n adGroupOperation =>\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n # Set the ad group ID to a temporary ID.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, AD_GROUP_TEMPORARY_ID\n ),\n name => \"Smart campaign ad group #\" . uniqid(),\n # Set the campaign ID to a temporary ID.\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, SMART_CAMPAIGN_TEMPORARY_ID\n ),\n # The ad group type must be set to SMART_CAMPAIGN_ADS.\n type => SMART_CAMPAIGN_ADS\n })})});\n}add_smart_campaign.pl\n```\n\nExample:\n```text\nprivate MutateOperation createAdGroupAdOperation(\n long customerId, SmartCampaignAdInfo adSuggestions) {\n MutateOperation.Builder opBuilder = MutateOperation.newBuilder();\n\n // Constructs an Ad instance containing a SmartCampaignAd.\n Ad.Builder adBuilder = Ad.newBuilder();\n adBuilder\n .setType(AdType.SMART_CAMPAIGN_AD)\n // The SmartCampaignAdInfo object includes headlines and descriptions retrieved\n // from the suggestSmartCampaignAd method. It's recommended that users review and approve or\n // update these creatives before they're set on the ad. It's possible that some or all of\n // these assets may contain empty texts, which should not be set on the ad and instead\n // should be replaced with meaningful texts from the user. Below we just accept the\n // creatives that were suggested while filtering out empty assets, but individual workflows\n // will vary here.\n .getSmartCampaignAdBuilder()\n .addAllHeadlines(\n adSuggestions.getHeadlinesList().stream()\n .filter(h -> h.hasText())\n .collect(Collectors.toList()))\n .addAllDescriptions(\n adSuggestions.getDescriptionsList().stream()\n .filter(d -> d.hasText())\n .collect(Collectors.toList()));\n\n // Adds additional headlines + descriptions if we didn't get enough back from the suggestion\n // service.\n int numHeadlines = adBuilder.getSmartCampaignAdBuilder().getHeadlinesCount();\n if (numHeadlines < NUM_REQUIRED_HEADLINES) {\n for (int i = 0; i < NUM_REQUIRED_HEADLINES - numHeadlines; ++i) {\n adBuilder\n .getSmartCampaignAdBuilder()\n .addHeadlines(AdTextAsset.newBuilder().setText(\"Placeholder headline \" + i).build());\n }\n }\n if (adSuggestions.getDescriptionsCount() < NUM_REQUIRED_DESCRIPTIONS) {\n int numDescriptions = adBuilder.getSmartCampaignAdBuilder().getDescriptionsCount();\n for (int i = 0; i < NUM_REQUIRED_DESCRIPTIONS - numDescriptions; ++i) {\n adBuilder\n .getSmartCampaignAdBuilder()\n .addDescriptions(\n AdTextAsset.newBuilder().setText(\"Placeholder description \" + i).build());\n }\n }\n\n opBuilder\n .getAdGroupAdOperationBuilder()\n .getCreateBuilder()\n .setAdGroup(ResourceNames.adGroup(customerId, AD_GROUP_TEMPORARY_ID))\n .setAd(adBuilder);\n return opBuilder.build();\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a MutateOperation that creates a new ad group ad.\n/// A temporary ID will be used in the ad group resource name for this ad group ad to\n/// associate it with the ad group created in earlier steps.\n/// </summary>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <param name=\"adSuggestions\">SmartCampaignAdInfo with ad creative\n/// suggestions.</param>\n/// <returns>A MutateOperation that creates a new ad group ad.</returns>\nprivate MutateOperation CreateAdGroupAdOperation(long customerId, SmartCampaignAdInfo\n adSuggestions)\n{\n AdGroupAd adGroupAd = new AdGroupAd\n {\n AdGroup = ResourceNames.AdGroup(customerId, AD_GROUP_TEMPORARY_ID),\n Ad = new Ad\n {\n SmartCampaignAd = new SmartCampaignAdInfo(),\n },\n };\n\n SmartCampaignAdInfo ad = adGroupAd.Ad.SmartCampaignAd;\n\n // The SmartCampaignAdInfo object includes headlines and descriptions\n // retrieved from the SmartCampaignSuggestService.SuggestSmartCampaignAd\n // method. It's recommended that users review and approve or update these\n // creatives before they're set on the ad. It's possible that some or all of\n // these assets may contain empty texts, which should not be set on the ad\n // and instead should be replaced with meaninful texts from the user. Below\n // we just accept the creatives that were suggested while filtering out empty\n // assets. If no headlines or descriptions were suggested, then we manually\n // add some, otherwise this operation will generate an INVALID_ARGUMENT\n // error. Individual workflows will likely vary here.\n ad.Headlines.Add(adSuggestions.Headlines);\n ad.Descriptions.Add(adSuggestions.Descriptions);\n\n // If there are fewer headlines than are required, we manually add additional\n // headlines to make up for the difference.\n if (adSuggestions.Headlines.Count() < NUM_REQUIRED_HEADLINES)\n {\n for (int i = 0; i < NUM_REQUIRED_HEADLINES - adSuggestions.Headlines.Count(); i++)\n {\n ad.Headlines.Add(new AdTextAsset()\n {\n Text = $\"Placeholder headline {i + 1}\"\n });\n }\n }\n\n // If there are fewer descriptions than are required, we manually add\n // additional descriptions to make up for the difference.\n if (adSuggestions.Descriptions.Count() < NUM_REQUIRED_DESCRIPTIONS)\n {\n for (int i = 0; i < NUM_REQUIRED_DESCRIPTIONS -\n adSuggestions.Descriptions.Count(); i++)\n {\n ad.Descriptions.Add(new AdTextAsset()\n {\n Text = $\"Placeholder description {i + 1}\"\n });\n }\n }\n\n return new MutateOperation\n {\n AdGroupAdOperation = new AdGroupAdOperation\n {\n Create = adGroupAd\n }\n };\n}AddSmartCampaign.cs\n```\n\nExample:\n```text\nprivate static function createAdGroupAdOperation(\n int $customerId,\n ?SmartCampaignAdInfo $adSuggestions\n): MutateOperation {\n if (is_null($adSuggestions)) {\n $smartCampaignAdInfo = new SmartCampaignAdInfo();\n } else {\n // The SmartCampaignAdInfo object includes headlines and descriptions retrieved\n // from the SmartCampaignSuggestService::SuggestSmartCampaignAd method. It's\n // recommended that users review and approve or update these creatives before\n // they're set on the ad. It's possible that some or all of these assets may\n // contain empty texts, which should not be set on the ad and instead should be\n // replaced with meaningful texts from the user. Below we just accept the creatives\n // that were suggested while filtering out empty assets, but individual workflows\n // will vary here.\n $smartCampaignAdInfo = new SmartCampaignAdInfo([\n 'headlines' => array_filter(\n iterator_to_array($adSuggestions->getHeadlines()->getIterator()),\n function ($value) {\n return $value->getText();\n }\n ),\n 'descriptions' => array_filter(\n iterator_to_array($adSuggestions->getDescriptions()->getIterator()),\n function ($value) {\n return $value->getText();\n }\n )\n ]);\n }\n // Creates the ad group ad object.\n $adGroupAd = new AdGroupAd([\n // Sets the ad group ID to a temporary ID.\n 'ad_group' => ResourceNames::forAdGroup($customerId, self::AD_GROUP_TEMPORARY_ID),\n 'ad' => new Ad([\n // Sets the type to SMART_CAMPAIGN_AD.\n 'type' => AdType::SMART_CAMPAIGN_AD,\n 'smart_campaign_ad' => $smartCampaignAdInfo\n ])\n ]);\n\n // The SmartCampaignAdInfo object includes headlines and descriptions retrieved from the\n // SmartCampaignSuggestService.SuggestSmartCampaignAd method. It's recommended that users\n // review and approve or update these ads before they're set on the ad. It's possible that\n // some or all of these assets may contain empty texts, which should not be set on the ad\n // and instead should be replaced with meaningful texts from the user.\n // Below we just accept the ads that were suggested while filtering out empty assets.\n // If no headlines or descriptions were suggested, then we manually add some, otherwise\n // this operation will generate an INVALID_ARGUMENT error. Individual workflows will likely\n // vary here.\n $currentHeadlinesCount = $smartCampaignAdInfo->getHeadlines()->count();\n for ($i = 0; $i < self::NUM_REQUIRED_HEADLINES - $currentHeadlinesCount; $i++) {\n $smartCampaignAdInfo->setHeadlines(\n array_merge(\n iterator_to_array($smartCampaignAdInfo->getHeadlines()),\n [new AdTextAsset(['text' => 'Placeholder headline ' . $i])]\n )\n );\n }\n $currentDescriptionsCount = $smartCampaignAdInfo->getDescriptions()->count();\n for ($i = 0; $i < self::NUM_REQUIRED_DESCRIPTIONS - $currentDescriptionsCount; $i++) {\n $smartCampaignAdInfo->setDescriptions(\n array_merge(\n iterator_to_array($smartCampaignAdInfo->getDescriptions()),\n [new AdTextAsset(['text' => 'Placeholder description ' . $i])]\n )\n );\n }\n\n // Creates the MutateOperation that creates the ad group ad.\n return new MutateOperation([\n 'ad_group_ad_operation' => new AdGroupAdOperation(['create' => $adGroupAd])\n ]);\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef create_ad_group_ad_operation(\n client: GoogleAdsClient,\n customer_id: str,\n ad_suggestions: SmartCampaignAdInfo,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new ad group ad.\n\n A temporary ID will be used in the ad group resource name for this\n ad group ad to associate it with the ad group created in earlier steps.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n ad_suggestions: a SmartCampaignAdInfo object with ad creative\n suggestions.\n\n Returns:\n a MutateOperation that creates a new ad group ad.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n ad_group_ad_operation: AdGroupAdOperation = (\n mutate_operation.ad_group_ad_operation\n )\n ad_group_ad: AdGroupAd = ad_group_ad_operation.create\n # Set the ad group ID to a temporary ID.\n ad_group_ad.ad_group = client.get_service(\"AdGroupService\").ad_group_path(\n customer_id, _AD_GROUP_TEMPORARY_ID\n )\n # Set the type to SMART_CAMPAIGN_AD.\n ad_group_ad.ad.type_ = client.enums.AdTypeEnum.SMART_CAMPAIGN_AD\n ad: SmartCampaignAdInfo = ad_group_ad.ad.smart_campaign_ad\n\n # The SmartCampaignAdInfo object includes headlines and descriptions\n # retrieved from the SmartCampaignSuggestService.SuggestSmartCampaignAd\n # method. It's recommended that users review and approve or update these\n # creatives before they're set on the ad. It's possible that some or all of\n # these assets may contain empty texts, which should not be set on the ad\n # and instead should be replaced with meaningful texts from the user. Below\n # we just accept the creatives that were suggested while filtering out empty\n # assets. If no headlines or descriptions were suggested, then we manually\n # add some, otherwise this operation will generate an INVALID_ARGUMENT\n # error. Individual workflows will likely vary here.\n ad.headlines.extend(\n [asset for asset in ad_suggestions.headlines if asset.text]\n )\n num_missing_headlines: int = _REQUIRED_NUM_HEADLINES - len(ad.headlines)\n\n # If there are fewer headlines than are required, we manually add additional\n # headlines to make up for the difference.\n for i in range(num_missing_headlines):\n headline: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline.text = f\"placeholder headline {i}\"\n ad.headlines.append(headline)\n\n ad.descriptions.extend(\n asset for asset in ad_suggestions.descriptions if asset.text\n )\n num_missing_descriptions: int = _REQUIRED_NUM_DESCRIPTIONS - len(\n ad.descriptions\n )\n\n # If there are fewer descriptions than are required, we manually add\n # additional descriptions to make up for the difference.\n for i in range(num_missing_descriptions):\n description: AdTextAsset = client.get_type(\"AdTextAsset\")\n description.text = f\"placeholder description {i}\"\n ad.descriptions.append(description)\n\n return mutate_operationadd_smart_campaign.py\n```\n\nExample:\n```text\n# Creates a mutate_operation that creates a new ad group ad.\n# A temporary ID will be used in the ad group resource name for this\n# ad group ad to associate it with the ad group created in earlier steps.\ndef create_ad_group_ad_operation(client, customer_id, ad_suggestions)\n mutate_operation = client.operation.mutate do |m|\n m.ad_group_ad_operation = client.operation.create_resource.ad_group_ad do |aga|\n # Set the ad group ID to a temporary ID.\n aga.ad_group = client.path.ad_group(customer_id, AD_GROUP_TEMPORARY_ID)\n aga.ad = client.resource.ad do |ad|\n # Set the type to SMART_CAMPAIGN_AD.\n ad.type = :SMART_CAMPAIGN_AD\n ad.smart_campaign_ad = client.resource.smart_campaign_ad_info do |sca|\n # The SmartCampaignAdInfo object includes headlines and descriptions\n # retrieved from the SmartCampaignSuggestService.SuggestSmartCampaignAd\n # method. It's recommended that users review and approve or update these\n # creatives before they're set on the ad. It's possible that some or all of\n # these assets may contain empty texts, which should not be set on the ad\n # and instead should be replaced with meaningful texts from the user. Below\n # we just accept the creatives that were suggested while filtering out empty\n # assets. If no headlines or descriptions were suggested, then we manually\n # add some, otherwise this operation will generate an INVALID_ARGUMENT\n # error. Individual workflows will likely vary here.\n sca.headlines += ad_suggestions.headlines.filter(&:text) if ad_suggestions\n if sca.headlines.size < REQUIRED_NUM_HEADLINES\n (REQUIRED_NUM_HEADLINES - sca.headlines.size).times do |i|\n sca.headlines << client.resource.ad_text_asset do |asset|\n asset.text = \"placeholder headline #{i}\"\n end\n end\n end\n\n sca.descriptions += ad_suggestions.descriptions.filter(&:text) if ad_suggestions\n if sca.descriptions.size < REQUIRED_NUM_DESCRIPTIONS\n (REQUIRED_NUM_DESCRIPTIONS - sca.descriptions.size).times do |i|\n sca.descriptions << client.resource.ad_text_asset do |asset|\n asset.text = \"placeholder description #{i}\"\n end\n end\n end\n end\n end\n end\n end\n\n mutate_operation\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Creates a MutateOperation that creates a new ad group ad.\n# A temporary ID will be used in the ad group resource name for this ad group ad\n# to associate it with the ad group created in earlier steps.\nsub _create_ad_group_ad_operation {\n my ($customer_id, $ad_suggestions) = @_;\n\n my $mutate_operation =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n adGroupAdOperation =>\n Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::AdGroupAd->new({\n adGroup =>\n # Set the ad group ID to a temporary ID.\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, AD_GROUP_TEMPORARY_ID\n ),\n ad => Google::Ads::GoogleAds::V25::Resources::Ad->new({\n # Set the type to SMART_CAMPAIGN_AD.\n type => SMART_CAMPAIGN_AD,\n smartCampaignAd =>\n Google::Ads::GoogleAds::V25::Common::SmartCampaignAdInfo->\n new({\n headlines => [],\n descriptions => []})})})})});\n\n # The SmartCampaignAdInfo object includes headlines and descriptions\n # retrieved from the SmartCampaignSuggestService.SuggestSmartCampaignAd\n # method. It's recommended that users review and approve or update these\n # creatives before they're set on the ad. It's possible that some or all of\n # these assets may contain empty texts, which should not be set on the ad\n # and instead should be replaced with meaningful texts from the user. Below\n # we just accept the creatives that were suggested while filtering out empty\n # assets. If no headlines or descriptions were suggested, then we manually\n # add some, otherwise this operation will generate an INVALID_ARGUMENT\n # error. Individual workflows will likely vary here.\n my $smart_campaign_ad =\n $mutate_operation->{adGroupAdOperation}{create}{ad}{smartCampaignAd};\n\n foreach my $asset (@{$ad_suggestions->{headlines}}) {\n push @{$smart_campaign_ad->{headlines}}, $asset\n if defined $asset->{text};\n }\n # If there are fewer headlines than are required, we manually add additional\n # headlines to make up for the difference.\n my $num_missing_headlines =\n REQUIRED_NUM_HEADLINES - scalar @{$smart_campaign_ad->{headlines}};\n for (my $i = 0 ; $i < $num_missing_headlines ; $i++) {\n push @{$smart_campaign_ad->{headlines}},\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"placeholder headline \" . $i\n });\n }\n\n foreach my $asset (@{$ad_suggestions->{descriptions}}) {\n push @{$smart_campaign_ad->{descriptions}}, $asset\n if defined $asset->{text};\n }\n # If there are fewer descriptions than are required, we manually add\n # additional descriptions to make up for the difference.\n my $num_missing_descriptions =\n REQUIRED_NUM_DESCRIPTIONS - scalar @{$smart_campaign_ad->{descriptions}};\n for (my $i = 0 ; $i < $num_missing_descriptions ; $i++) {\n push @{$smart_campaign_ad->{descriptions}},\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"placeholder description \" . $i\n });\n }\n\n return $mutate_operation;\n}add_smart_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.457Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":579,"estimatedTokens":6201}}177{"id":"doc-get_started_with_audience_segments_google_ads_ap-ccf3549b","source":"documentation","title":"Get Started with Audience Segments | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audience-segments/getting-started","text":"Example:\n```text\nSELECT\n remarketing_action.id,\n remarketing_action.name,\n remarketing_action.tag_snippets\nFROM remarketing_action\nWHERE remarketing_action.resource_name = 'REMARKETING_ACTION_RESOURCE_NAME'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.458Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":11,"estimatedTokens":57}}178{"id":"doc-campaign_goals_google_ads_api_google_for_develop-1ec52550","source":"documentation","title":"Campaign goals | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/conversions/goals/campaign-goals","text":"Example:\n```text\nSELECT\n campaign_conversion_goal.campaign,\n campaign_conversion_goal.category,\n campaign_conversion_goal.origin,\n campaign_conversion_goal.biddable,\n campaign.id,\n campaign.name\nFROM campaign_conversion_goal\nWHERE campaign.advertising_channel_type = PERFORMANCE_MAX\n```\n\nExample:\n```text\nSELECT\n conversion_goal_campaign_config.campaign,\n conversion_goal_campaign_config.custom_conversion_goal,\n conversion_goal_campaign_config.goal_config_level,\n campaign.id,\n campaign.name\nFROM conversion_goal_campaign_config\n```\n\nExample:\n```text\nSELECT\n custom_conversion_goal.id,\n custom_conversion_goal.name,\n custom_conversion_goal.status,\n custom_conversion_goal.conversion_actions\nFROM custom_conversion_goal\n```\n\nExample:\n```text\nSELECT\n conversion_goal_campaign_config.campaign,\n conversion_goal_campaign_config.custom_conversion_goal,\n conversion_goal_campaign_config.goal_config_level,\n campaign.id,\n campaign.name,\n custom_conversion_goal.name,\n custom_conversion_goal.status,\n custom_conversion_goal.conversion_actions\nFROM conversion_goal_campaign_config\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.459Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":49,"estimatedTokens":279}}179{"id":"doc-monitor_offline_data_diagnostics_google_ads_api_-8fbe2949","source":"documentation","title":"Monitor offline data diagnostics | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/conversions/upload-summaries","text":"Example:\n```text\nSELECT\n customer.id,\n offline_conversion_upload_client_summary.alerts,\n offline_conversion_upload_client_summary.client,\n offline_conversion_upload_client_summary.daily_summaries,\n offline_conversion_upload_client_summary.job_summaries,\n offline_conversion_upload_client_summary.last_upload_date_time,\n offline_conversion_upload_client_summary.pending_event_count,\n offline_conversion_upload_client_summary.pending_rate,\n offline_conversion_upload_client_summary.status,\n offline_conversion_upload_client_summary.success_rate,\n offline_conversion_upload_client_summary.successful_event_count,\n offline_conversion_upload_client_summary.total_event_count\nFROM offline_conversion_upload_client_summary\n```\n\nExample:\n```text\nSELECT\n offline_conversion_upload_conversion_action_summary.conversion_action_name,\n offline_conversion_upload_conversion_action_summary.alerts,\n offline_conversion_upload_conversion_action_summary.client,\n offline_conversion_upload_conversion_action_summary.daily_summaries,\n offline_conversion_upload_conversion_action_summary.job_summaries,\n offline_conversion_upload_conversion_action_summary.last_upload_date_time,\n offline_conversion_upload_conversion_action_summary.pending_event_count,\n offline_conversion_upload_conversion_action_summary.status,\n offline_conversion_upload_conversion_action_summary.successful_event_count,\n offline_conversion_upload_conversion_action_summary.total_event_count\nFROM offline_conversion_upload_conversion_action_summary\nWHERE offline_conversion_upload_conversion_action_summary.conversion_action_id = < INSERT CONVERSION ACTION ID >\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.461Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":36,"estimatedTokens":413}}180{"id":"doc-manage_online_click_conversions_google_ads_api_g-4c81d70d","source":"documentation","title":"Manage online click conversions | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/conversions/upload-online","text":"Example:\n```text\nSELECT\n customer.id,\n customer.conversion_tracking_setting.accepted_customer_data_terms\nFROM customer\n```\n\nExample:\n```text\nprivate String normalizeAndHash(MessageDigest digest, String s, boolean trimIntermediateSpaces)\n throws UnsupportedEncodingException {\n // Normalizes by first converting all characters to lowercase, then trimming spaces.\n String normalized = s.toLowerCase();\n if (trimIntermediateSpaces) {\n // Removes leading, trailing, and intermediate spaces.\n normalized = normalized.replaceAll(\"\\\\s+\", \"\");\n } else {\n // Removes only leading and trailing spaces.\n normalized = normalized.trim();\n }\n // Hashes the normalized string using the hashing algorithm.\n byte[] hash = digest.digest(normalized.getBytes(\"UTF-8\"));\n StringBuilder result = new StringBuilder();\n for (byte b : hash) {\n result.append(String.format(\"%02x\", b));\n }\n\n return result.toString();\n}\n\n/**\n * Returns the result of normalizing and hashing an email address. For this use case, Google Ads\n * requires removal of any '.' characters preceding {@code gmail.com} or {@code googlemail.com}.\n *\n * @param digest the digest to use to hash the normalized string.\n * @param emailAddress the email address to normalize and hash.\n */\nprivate String normalizeAndHashEmailAddress(MessageDigest digest, String emailAddress)\n throws UnsupportedEncodingException {\n String normalizedEmail = emailAddress.toLowerCase();\n String[] emailParts = normalizedEmail.split(\"@\");\n if (emailParts.length > 1 && emailParts[1].matches(\"^(gmail|googlemail)\\\\.com\\\\s*\")) {\n // Removes any '.' characters from the portion of the email address before the domain if the\n // domain is gmail.com or googlemail.com.\n emailParts[0] = emailParts[0].replaceAll(\"\\\\.\", \"\");\n normalizedEmail = String.format(\"%s@%s\", emailParts[0], emailParts[1]);\n }\n return normalizeAndHash(digest, normalizedEmail, true);\n}UploadEnhancedConversionsForWeb.java\n```\n\nExample:\n```text\n/// <summary>\n/// Normalizes the email address and hashes it. For this use case, Google Ads requires\n/// removal of any '.' characters preceding <code>gmail.com</code> or\n/// <code>googlemail.com</code>.\n/// </summary>\n/// <param name=\"emailAddress\">The email address.</param>\n/// <returns>The hash code.</returns>\nprivate string NormalizeAndHashEmailAddress(string emailAddress)\n{\n string normalizedEmail = emailAddress.ToLower();\n string[] emailParts = normalizedEmail.Split('@');\n if (emailParts.Length > 1 && (emailParts[1] == \"gmail.com\" ||\n emailParts[1] == \"googlemail.com\"))\n {\n // Removes any '.' characters from the portion of the email address before\n // the domain if the domain is gmail.com or googlemail.com.\n emailParts[0] = emailParts[0].Replace(\".\", \"\");\n normalizedEmail = $\"{emailParts[0]}@{emailParts[1]}\";\n }\n return NormalizeAndHash(normalizedEmail);\n}\n\n/// <summary>\n/// Normalizes and hashes a string value.\n/// </summary>\n/// <param name=\"value\">The value to normalize and hash.</param>\n/// <returns>The normalized and hashed value.</returns>\nprivate static string NormalizeAndHash(string value)\n{\n return ToSha256String(digest, ToNormalizedValue(value));\n}\n\n/// <summary>\n/// Hash a string value using SHA-256 hashing algorithm.\n/// </summary>\n/// <param name=\"digest\">Provides the algorithm for SHA-256.</param>\n/// <param name=\"value\">The string value (e.g. an email address) to hash.</param>\n/// <returns>The hashed value.</returns>\nprivate static string ToSha256String(SHA256 digest, string value)\n{\n byte[] digestBytes = digest.ComputeHash(Encoding.UTF8.GetBytes(value));\n // Convert the byte array into an unhyphenated hexadecimal string.\n return BitConverter.ToString(digestBytes).Replace(\"-\", string.Empty);\n}\n\n/// <summary>\n/// Removes leading and trailing whitespace and converts all characters to\n/// lower case.\n/// </summary>\n/// <param name=\"value\">The value to normalize.</param>\n/// <returns>The normalized value.</returns>\nprivate static string ToNormalizedValue(string value)\n{\n return value.Trim().ToLower();\n}UploadEnhancedConversionsForWeb.cs\n```\n\nExample:\n```text\nprivate static function normalizeAndHash(\n string $hashAlgorithm,\n string $value,\n bool $trimIntermediateSpaces\n): string {\n // Normalizes by first converting all characters to lowercase, then trimming spaces.\n $normalized = strtolower($value);\n if ($trimIntermediateSpaces === true) {\n // Removes leading, trailing, and intermediate spaces.\n $normalized = str_replace(' ', '', $normalized);\n } else {\n // Removes only leading and trailing spaces.\n $normalized = trim($normalized);\n }\n return hash($hashAlgorithm, strtolower(trim($normalized)));\n}\n\n/**\n * Returns the result of normalizing and hashing an email address. For this use case, Google\n * Ads requires removal of any '.' characters preceding \"gmail.com\" or \"googlemail.com\".\n *\n * @param string $hashAlgorithm the hash algorithm to use\n * @param string $emailAddress the email address to normalize and hash\n * @return string the normalized and hashed email address\n */\nprivate static function normalizeAndHashEmailAddress(\n string $hashAlgorithm,\n string $emailAddress\n): string {\n $normalizedEmail = strtolower($emailAddress);\n $emailParts = explode(\"@\", $normalizedEmail);\n if (\n count($emailParts) > 1\n && preg_match('/^(gmail|googlemail)\\.com\\s*/', $emailParts[1])\n ) {\n // Removes any '.' characters from the portion of the email address before the domain\n // if the domain is gmail.com or googlemail.com.\n $emailParts[0] = str_replace(\".\", \"\", $emailParts[0]);\n $normalizedEmail = sprintf('%s@%s', $emailParts[0], $emailParts[1]);\n }\n return self::normalizeAndHash($hashAlgorithm, $normalizedEmail, true);\n}UploadEnhancedConversionsForWeb.php\n```\n\nExample:\n```text\ndef normalize_and_hash_email_address(email_address):\n \"\"\"Returns the result of normalizing and hashing an email address.\n\n For this use case, Google Ads requires removal of any '.' characters\n preceding \"gmail.com\" or \"googlemail.com\"\n\n Args:\n email_address: An email address to normalize.\n\n Returns:\n A normalized (lowercase, removed whitespace) and SHA-265 hashed string.\n \"\"\"\n normalized_email = email_address.strip().lower()\n email_parts = normalized_email.split(\"@\")\n\n # Check that there are at least two segments\n if len(email_parts) > 1:\n # Removes any '.' and '+' characters from the portion of the email address\n # before the domain\n chars_to_remove = \".+\"\n translation_table = str.maketrans(\"\", \"\", chars_to_remove)\n email_parts[0] = email_parts[0].translate(translation_table)\n normalized_email = \"@\".join(email_parts)\n\n return normalize_and_hash(normalized_email)\n\n\ndef normalize_and_hash(s):\n \"\"\"Normalizes and hashes a string with SHA-256.\n\n Private customer data must be hashed during upload, as described at:\n https://support.google.com/google-ads/answer/9888656\n\n Args:\n s: The string to perform this operation on.\n\n Returns:\n A normalized (lowercase, removed whitespace) and SHA-256 hashed string.\n \"\"\"\n return hashlib.sha256(s.strip().lower().encode()).hexdigest()upload_enhanced_conversions_for_web.py\n```\n\nExample:\n```text\n# Returns the result of normalizing and then hashing the string using the\n# provided digest. Private customer data must be hashed during upload, as\n# described at https://support.google.com/google-ads/answer/9888656.\ndef normalize_and_hash(str)\n # Remove leading and trailing whitespace and ensure all letters are lowercase\n # before hasing.\n Digest::SHA256.hexdigest(str.strip.downcase)\nend\n\n# Returns the result of normalizing and hashing an email address. For this use\n# case, Google Ads requires removal of any '.' characters preceding 'gmail.com'\n# or 'googlemail.com'.\ndef normalize_and_hash_email(email)\n email_parts = email.downcase.split(\"@\")\n # Removes any '.' characters from the portion of the email address before the\n # domain if the domain is gmail.com or googlemail.com.\n if email_parts.last =~ /^(gmail|googlemail)\\.com\\s*/\n email_parts[0] = email_parts[0].gsub('.', '')\n end\n normalize_and_hash(email_parts.join('@'))\nendupload_enhanced_conversions_for_web.rb\n```\n\nExample:\n```text\nsub normalize_and_hash {\n my $value = shift;\n my $trim_intermediate_spaces = shift;\n\n if ($trim_intermediate_spaces) {\n $value =~ s/\\s+//g;\n } else {\n $value =~ s/^\\s+|\\s+$//g;\n }\n return sha256_hex(lc $value);\n}\n\n# Returns the result of normalizing and hashing an email address. For this use\n# case, Google Ads requires removal of any '.' characters preceding 'gmail.com'\n# or 'googlemail.com'.\nsub normalize_and_hash_email_address {\n my $email_address = shift;\n\n my $normalized_email = lc $email_address;\n my @email_parts = split('@', $normalized_email);\n if (scalar @email_parts > 1\n && $email_parts[1] =~ /^(gmail|googlemail)\\.com\\s*/)\n {\n # Remove any '.' characters from the portion of the email address before the\n # domain if the domain is 'gmail.com' or 'googlemail.com'.\n $email_parts[0] =~ s/\\.//g;\n $normalized_email = sprintf '%s@%s', $email_parts[0], $email_parts[1];\n }\n return normalize_and_hash($normalized_email, 1);\n}upload_enhanced_conversions_for_web.pl\n```\n\nExample:\n```text\n// Creates a builder for constructing the enhancement adjustment.\nConversionAdjustment.Builder enhancementBuilder =\n ConversionAdjustment.newBuilder().setAdjustmentType(ConversionAdjustmentType.ENHANCEMENT);\n\n// Extracts user email, phone, and address info from the raw data, normalizes and hashes it,\n// then wraps it in UserIdentifier objects.\n// Creates a separate UserIdentifier object for each. The data in this example is hardcoded, but\n// in your application you might read the raw data from an input file.\n\n// IMPORTANT: Since the identifier attribute of UserIdentifier\n// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a\n// oneof\n// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE of\n// hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting more\n// than one of these attributes on the same UserIdentifier will clear all the other members\n// of the oneof. For example, the following code is INCORRECT and will result in a\n// UserIdentifier with ONLY a hashedPhoneNumber.\n//\n// UserIdentifier incorrectlyPopulatedUserIdentifier =\n// UserIdentifier.newBuilder()\n// .setHashedEmail(\"...\")\n// .setHashedPhoneNumber(\"...\")\n// .build();\n\nImmutableMap.Builder<String, String> rawRecordBuilder =\n ImmutableMap.<String, String>builder()\n .put(\"email\", \"alex.2@example.com\")\n // Email address that includes a period (.) before the Gmail domain.\n .put(\"email\", \"alex.2@example.com\")\n // Address that includes all four required elements: first name, last name, country\n // code, and postal code.\n .put(\"firstName\", \"Alex\")\n .put(\"lastName\", \"Quinn\")\n .put(\"countryCode\", \"US\")\n .put(\"postalCode\", \"94045\")\n // Phone number to be converted to E.164 format, with a leading '+' as required.\n .put(\"phone\", \"+1 800 5550102\")\n // This example lets you put conversion details as arguments, but in reality you might\n // store this data alongside other user data, so we include it in this sample user\n // record.\n .put(\"orderId\", orderId)\n .put(\"conversionActionId\", Long.toString(conversionActionId))\n .put(\"currencyCode\", \"USD\");\n\n// Adds entries for the optional fields.\nif (conversionDateTime != null) {\n rawRecordBuilder.put(\"conversionDateTime\", conversionDateTime);\n}\nif (userAgent != null) {\n rawRecordBuilder.put(\"userAgent\", userAgent);\n}\n\n// Builds the map representing the record.\nMap<String, String> rawRecord = rawRecordBuilder.build();\n\n// Creates a SHA256 message digest for hashing user identifiers in a privacy-safe way, as\n// described at https://support.google.com/google-ads/answer/9888656.\nMessageDigest sha256Digest = MessageDigest.getInstance(\"SHA-256\");\n\n// Creates a list for the user identifiers.\nList<UserIdentifier> userIdentifiers = new ArrayList<>();\n\n// Creates a user identifier using the hashed email address, using the normalize and hash method\n// specifically for email addresses.\nUserIdentifier emailIdentifier =\n UserIdentifier.newBuilder()\n // Optional: specify the user identifier source.\n .setUserIdentifierSource(UserIdentifierSource.FIRST_PARTY)\n // Uses the normalize and hash method specifically for email addresses.\n .setHashedEmail(normalizeAndHashEmailAddress(sha256Digest, rawRecord.get(\"email\")))\n .build();\nuserIdentifiers.add(emailIdentifier);\n\n// Checks if the record has a phone number, and if so, adds a UserIdentifier for it.\nif (rawRecord.containsKey(\"phone\")) {\n UserIdentifier hashedPhoneNumberIdentifier =\n UserIdentifier.newBuilder()\n .setHashedPhoneNumber(normalizeAndHash(sha256Digest, rawRecord.get(\"phone\"), true))\n .build();\n // Adds the hashed phone number identifier to the UserData object's list.\n userIdentifiers.add(hashedPhoneNumberIdentifier);\n}\n\n// Checks if the record has all the required mailing address elements, and if so, adds a\n// UserIdentifier for the mailing address.\nif (rawRecord.containsKey(\"firstName\")) {\n // Checks if the record contains all the other required elements of a mailing address.\n Set<String> missingAddressKeys = new HashSet<>();\n for (String addressKey : new String[] {\"lastName\", \"countryCode\", \"postalCode\"}) {\n if (!rawRecord.containsKey(addressKey)) {\n missingAddressKeys.add(addressKey);\n }\n }\n\n if (!missingAddressKeys.isEmpty()) {\n System.out.printf(\n \"Skipping addition of mailing address information because the following required keys\"\n + \" are missing: %s%n\",\n missingAddressKeys);\n } else {\n // Creates an OfflineUserAddressInfo object that contains all the required elements of a\n // mailing address.\n OfflineUserAddressInfo addressInfo =\n OfflineUserAddressInfo.newBuilder()\n .setHashedFirstName(\n normalizeAndHash(sha256Digest, rawRecord.get(\"firstName\"), false))\n .setHashedLastName(normalizeAndHash(sha256Digest, rawRecord.get(\"lastName\"), false))\n .setCountryCode(rawRecord.get(\"countryCode\"))\n .setPostalCode(rawRecord.get(\"postalCode\"))\n .build();\n UserIdentifier addressIdentifier =\n UserIdentifier.newBuilder().setAddressInfo(addressInfo).build();\n // Adds the address identifier to the UserData object's list.\n userIdentifiers.add(addressIdentifier);\n }\n}\n\n// Adds the user identifiers to the enhancement adjustment.\nenhancementBuilder.addAllUserIdentifiers(userIdentifiers);UploadEnhancedConversionsForWeb.java\n```\n\nExample:\n```text\n// Normalize and hash the raw data, then wrap it in UserIdentifier objects.\n// Create a separate UserIdentifier object for each. The data in this example is\n// hardcoded, but in your application you might read the raw data from an input file.\n//\n// IMPORTANT: Since the identifier attribute of UserIdentifier\n// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)\n// is a oneof\n// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set\n// only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id,\n// or address-info. Setting more than one of these attributes on the same UserIdentifier\n// will clear all the other members of the oneof. For example, the following code is\n// INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number:\n// UserIdentifier incorrectlyPopulatedUserIdentifier = new UserIdentifier()\n// {\n// HashedEmail = \"...\"\n// HashedPhoneNumber = \"...\"\n// }\nUserIdentifier addressIdentifier = new UserIdentifier()\n{\n AddressInfo = new OfflineUserAddressInfo()\n {\n HashedFirstName = NormalizeAndHash(\"Dana\"),\n HashedLastName = NormalizeAndHash(\"Quinn\"),\n HashedStreetAddress = NormalizeAndHash(\"1600 Amphitheatre Pkwy\"),\n City = \"Mountain View\",\n State = \"CA\",\n PostalCode = \"94043\",\n CountryCode = \"US\"\n },\n // Optional: Specifies the user identifier source.\n UserIdentifierSource = UserIdentifierSource.FirstParty\n};\n\n// Creates a user identifier using the hashed email address.\nUserIdentifier emailIdentifier = new UserIdentifier()\n{\n UserIdentifierSource = UserIdentifierSource.FirstParty,\n // Uses the normalize and hash method specifically for email addresses.\n HashedEmail = NormalizeAndHashEmailAddress(\"dana@example.com\")\n};\n\n// Adds the user identifiers to the enhancement adjustment.\nenhancement.UserIdentifiers.AddRange(new[] { addressIdentifier, emailIdentifier });UploadEnhancedConversionsForWeb.cs\n```\n\nExample:\n```text\n// Creates the conversion enhancement.\n$enhancement =\n new ConversionAdjustment(['adjustment_type' => ConversionAdjustmentType::ENHANCEMENT]);\n\n// Extracts user email, phone, and address info from the raw data, normalizes and hashes it,\n// then wraps it in UserIdentifier objects.\n// Creates a separate UserIdentifier object for each. The data in this example is hardcoded,\n// but in your application you might read the raw data from an input file.\n\n// IMPORTANT: Since the identifier attribute of UserIdentifier\n// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a\n// oneof\n// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE\n// of hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting\n// more than one of these attributes on the same UserIdentifier will clear all the other\n// members of the oneof. For example, the following code is INCORRECT and will result in a\n// UserIdentifier with ONLY a hashedPhoneNumber.\n//\n// $incorrectlyPopulatedUserIdentifier = new UserIdentifier([\n// 'hashed_email' => '...',\n// 'hashed_phone_number' => '...'\n// ]);\n\n$rawRecord = [\n // Email address that includes a period (.) before the Gmail domain.\n 'email' => 'alex.2@example.com',\n // Address that includes all four required elements: first name, last name, country\n // code, and postal code.\n 'firstName' => 'Alex',\n 'lastName' => 'Quinn',\n 'countryCode' => 'US',\n 'postalCode' => '94045',\n // Phone number to be converted to E.164 format, with a leading '+' as required.\n 'phone' => '+1 800 5550102',\n // This example lets you input conversion details as arguments, but in reality you might\n // store this data alongside other user data, so we include it in this sample user\n // record.\n 'orderId' => $orderId,\n 'conversionActionId' => $conversionActionId,\n 'conversionDateTime' => $conversionDateTime,\n 'currencyCode' => 'USD'\n];\n\n// Creates a list for the user identifiers.\n$userIdentifiers = [];\n\n// Uses the SHA-256 hash algorithm for hashing user identifiers in a privacy-safe way, as\n// described at https://support.google.com/google-ads/answer/9888656.\n$hashAlgorithm = \"sha256\";\n\n// Creates a user identifier using the hashed email address, using the normalize and hash\n// method specifically for email addresses.\n$emailIdentifier = new UserIdentifier([\n // Uses the normalize and hash method specifically for email addresses.\n 'hashed_email' => self::normalizeAndHashEmailAddress(\n $hashAlgorithm,\n $rawRecord['email']\n ),\n // Optional: Specifies the user identifier source.\n 'user_identifier_source' => UserIdentifierSource::FIRST_PARTY\n]);\n$userIdentifiers[] = $emailIdentifier;\n\n// Checks if the record has a phone number, and if so, adds a UserIdentifier for it.\nif (array_key_exists('phone', $rawRecord)) {\n $hashedPhoneNumberIdentifier = new UserIdentifier([\n 'hashed_phone_number' => self::normalizeAndHash(\n $hashAlgorithm,\n $rawRecord['phone'],\n true\n )\n ]);\n // Adds the hashed email identifier to the user identifiers list.\n $userIdentifiers[] = $hashedPhoneNumberIdentifier;\n}\n\n// Checks if the record has all the required mailing address elements, and if so, adds a\n// UserIdentifier for the mailing address.\nif (array_key_exists('firstName', $rawRecord)) {\n // Checks if the record contains all the other required elements of a mailing\n // address.\n $missingAddressKeys = [];\n foreach (['lastName', 'countryCode', 'postalCode'] as $addressKey) {\n if (!array_key_exists($addressKey, $rawRecord)) {\n $missingAddressKeys[] = $addressKey;\n }\n }\n if (!empty($missingAddressKeys)) {\n printf(\n \"Skipping addition of mailing address information because the \"\n . \"following required keys are missing: %s%s\",\n json_encode($missingAddressKeys),\n PHP_EOL\n );\n } else {\n // Creates an OfflineUserAddressInfo object that contains all the required\n // elements of a mailing address.\n $addressIdentifier = new UserIdentifier([\n 'address_info' => new OfflineUserAddressInfo([\n 'hashed_first_name' => self::normalizeAndHash(\n $hashAlgorithm,\n $rawRecord['firstName'],\n false\n ),\n 'hashed_last_name' => self::normalizeAndHash(\n $hashAlgorithm,\n $rawRecord['lastName'],\n false\n ),\n 'country_code' => $rawRecord['countryCode'],\n 'postal_code' => $rawRecord['postalCode']\n ])\n ]);\n // Adds the address identifier to the user identifiers list.\n $userIdentifiers[] = $addressIdentifier;\n }\n}\n\n// Adds the user identifiers to the conversion.\n$enhancement->setUserIdentifiers($userIdentifiers);UploadEnhancedConversionsForWeb.php\n```\n\nExample:\n```text\n# Extracts user email, phone, and address info from the raw data, normalizes\n# and hashes it, then wraps it in UserIdentifier objects. Creates a separate\n# UserIdentifier object for each. The data in this example is hardcoded, but\n# in your application you might read the raw data from an input file.\n\n# IMPORTANT: Since the identifier attribute of UserIdentifier\n# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)\n# is a oneof\n# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must\n# set only ONE of hashed_email, hashed_phone_number, mobile_id,\n# third_party_user_id, or address_info. Setting more than one of these\n# attributes on the same UserIdentifier will clear all the other members of\n# the oneof. For example, the following code is INCORRECT and will result in\n# a UserIdentifier with ONLY a hashed_phone_number:\n#\n# incorrectly_populated_user_identifier = client.get_type(\"UserIdentifier\")\n# incorrectly_populated_user_identifier.hashed_email = \"...\"\"\n# incorrectly_populated_user_identifier.hashed_phone_number = \"...\"\"\n\nraw_record = {\n # Email address that includes a period (.) before the Gmail domain.\n \"email\": \"alex.2@example.com\",\n # Address that includes all four required elements: first name, last\n # name, country code, and postal code.\n \"first_name\": \"Alex\",\n \"last_name\": \"Quinn\",\n \"country_code\": \"US\",\n \"postal_code\": \"94045\",\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required.\n \"phone\": \"+1 800 5550102\",\n # This example lets you input conversion details as arguments, but in\n # reality you might store this data alongside other user data, so we\n # include it in this sample user record.\n \"order_id\": order_id,\n \"conversion_action_id\": conversion_action_id,\n \"conversion_date_time\": conversion_date_time,\n \"currency_code\": \"USD\",\n \"user_agent\": user_agent,\n}\n\n# Constructs the enhancement adjustment.\nconversion_adjustment = client.get_type(\"ConversionAdjustment\")\nconversion_adjustment.adjustment_type = (\n client.enums.ConversionAdjustmentTypeEnum.ENHANCEMENT\n)\n\n# Creates a user identifier using the hashed email address, using the\n# normalize and hash method specifically for email addresses.\nemail_identifier = client.get_type(\"UserIdentifier\")\n# Optional: Specifies the user identifier source.\nemail_identifier.user_identifier_source = (\n client.enums.UserIdentifierSourceEnum.FIRST_PARTY\n)\n# Uses the normalize and hash method specifically for email addresses.\nemail_identifier.hashed_email = normalize_and_hash_email_address(\n raw_record[\"email\"]\n)\n# Adds the email identifier to the conversion adjustment.\nconversion_adjustment.user_identifiers.append(email_identifier)\n\n# Checks if the record has a phone number, and if so, adds a UserIdentifier\n# for it.\nif raw_record.get(\"phone\") is not None:\n phone_identifier = client.get_type(\"UserIdentifier\")\n phone_identifier.hashed_phone_number = normalize_and_hash(\n raw_record[\"phone\"]\n )\n # Adds the phone identifier to the conversion adjustment.\n conversion_adjustment.user_identifiers.append(phone_identifier)\n\n# Checks if the record has all the required mailing address elements, and if\n# so, adds a UserIdentifier for the mailing address.\nif raw_record.get(\"first_name\") is not None:\n # Checks if the record contains all the other required elements of a\n # mailing address.\n required_keys = [\"last_name\", \"country_code\", \"postal_code\"]\n # Builds a new list of the required keys that are missing from\n # raw_record.\n missing_keys = [\n key for key in required_keys if key not in raw_record.keys()\n ]\n if len(missing_keys) > 0:\n print(\n \"Skipping addition of mailing address information because the\"\n f\"following required keys are missing: {missing_keys}\"\n )\n else:\n # Creates a user identifier using sample values for the user address,\n # hashing where required.\n address_identifier = client.get_type(\"UserIdentifier\")\n address_info = address_identifier.address_info\n address_info.hashed_first_name = normalize_and_hash(\n raw_record[\"first_name\"]\n )\n address_info.hashed_last_name = normalize_and_hash(\n raw_record[\"last_name\"]\n )\n address_info.country_code = raw_record[\"country_code\"]\n address_info.postal_code = raw_record[\"postal_code\"]\n # Adds the address identifier to the conversion adjustment.\n conversion_adjustment.user_identifiers.append(address_identifier)upload_enhanced_conversions_for_web.py\n```\n\nExample:\n```text\n# Extracts user email, phone, and address info from the raw data, normalizes\n# and hashes it, then wraps it in UserIdentifier objects. Creates a separate\n# UserIdentifier object for each. The data in this example is hardcoded, but\n# in your application you might read the raw data from an input file.\n\n# IMPORTANT: Since the identifier attribute of UserIdentifier\n# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)\n# is a oneof\n# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must\n# set only ONE of hashed_email, hashed_phone_number, mobile_id,\n# third_party_user_id, or address_info. Setting more than one of these\n# attributes on the same UserIdentifier will clear all the other members of\n# the oneof. For example, the following code is INCORRECT and will result in\n# a UserIdentifier with ONLY a hashed_phone_number:\n#\n# incorrectly_populated_user_identifier.hashed_email = \"...\"\"\n# incorrectly_populated_user_identifier.hashed_phone_number = \"...\"\"\n\nraw_record = {\n # Email address that includes a period (.) before the Gmail domain.\n \"email\" => \"alex.2@example.com\",\n # Address that includes all four required elements: first name, last\n # name, country code, and postal code.\n \"first_name\" => \"Alex\",\n \"last_name\" => \"Quinn\",\n \"country_code\" => \"US\",\n \"postal_code\" => \"94045\",\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required.\n \"phone\" => \"+1 800 5550102\",\n # This example lets you input conversion details as arguments, but in\n # reality you might store this data alongside other user data, so we\n # include it in this sample user record.\n \"order_id\" => order_id,\n \"conversion_action_id\" => conversion_action_id,\n \"conversion_date_time\" => conversion_date_time,\n \"currency_code\" => \"USD\",\n \"user_agent\" => user_agent,\n}\n\nenhancement = client.resource.conversion_adjustment do |ca|\n ca.conversion_action = client.path.conversion_action(customer_id, conversion_action_id)\n ca.adjustment_type = :ENHANCEMENT\n ca.order_id = order_id\n\n # Sets the conversion date and time if provided. Providing this value is\n # optional but recommended.\n unless conversion_date_time.nil?\n ca.gclid_date_time_pair = client.resource.gclid_date_time_pair do |pair|\n pair.conversion_date_time = conversion_date_time\n end\n end\n\n # Creates a user identifier using the hashed email address, using the\n # normalize and hash method specifically for email addresses.\n ca.user_identifiers << client.resource.user_identifier do |ui|\n # Uses the normalize and hash method specifically for email addresses.\n ui.hashed_email = normalize_and_hash_email(raw_record[\"email\"])\n # Optional: Specifies the user identifier source.\n ui.user_identifier_source = :FIRST_PARTY\n end\n\n # Checks if the record has a phone number, and if so, adds a UserIdentifier\n # for it.\n unless raw_record[\"phone\"].nil?\n ca.user_identifiers << client.resource.user_identifier do |ui|\n ui.hashed_phone_number = normalize_and_hash_email(raw_record[\"phone\"])\n end\n end\n\n # Checks if the record has all the required mailing address elements, and if\n # so, adds a UserIdentifier for the mailing address.\n unless raw_record[\"first_name\"].nil?\n # Checks if the record contains all the other required elements of a\n # mailing address.\n required_keys = [\"last_name\", \"country_code\", \"postal_code\"]\n # Builds a new list of the required keys that are missing from\n # raw_record.\n missing_keys = required_keys - raw_record.keys\n if missing_keys\n puts(\n \"Skipping addition of mailing address information because the\" \\\n \"following required keys are missing: #{missing_keys}\"\n )\n else\n ca.user_identifiers << client.resource.user_identifier do |ui|\n ui.address_info = client.resource.offline_user_address_info do |info|\n # Certain fields must be hashed using SHA256 in order to handle\n # identifiers in a privacy-safe way, as described at\n # https://support.google.com/google-ads/answer/9888656.\n info.hashed_first_name = normalize_and_hash( raw_record[\"first_name\"])\n info.hashed_last_name = normalize_and_hash( raw_record[\"last_name\"])\n info.postal_code = normalize_and_hash(raw_record[\"country_code\"])\n info.country_code = normalize_and_hash(raw_record[\"postal_code\"])\n end\n end\n end\n endupload_enhanced_conversions_for_web.rb\n```\n\nExample:\n```text\n# Construct the enhancement adjustment.\nmy $enhancement =\n Google::Ads::GoogleAds::V25::Services::ConversionAdjustmentUploadService::ConversionAdjustment\n ->new({\n adjustmentType => ENHANCEMENT\n });\n\n# Extract user email, phone, and address info from the raw data,\n# normalize and hash it, then wrap it in UserIdentifier objects.\n# Create a separate UserIdentifier object for each.\n# The data in this example is hardcoded, but in your application\n# you might read the raw data from an input file.\n#\n# IMPORTANT: Since the identifier attribute of UserIdentifier\n# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)\n# is a oneof\n# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set\n# only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id,\n# or address-info. Setting more than one of these attributes on the same UserIdentifier\n# will clear all the other members of the oneof. For example, the following code is\n# INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number:\n#\n# my $incorrect_user_identifier = Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n# hashedEmail => '...',\n# hashedPhoneNumber => '...',\n# });\nmy $raw_record = {\n # Email address that includes a period (.) before the Gmail domain.\n email => 'alex.2@example.com',\n # Address that includes all four required elements: first name, last\n # name, country code, and postal code.\n firstName => 'Alex',\n lastName => 'Quinn',\n countryCode => 'US',\n postalCode => '94045',\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required.\n phone => '+1 800 5550102',\n # This example lets you input conversion details as arguments,\n # but in reality you might store this data alongside other user data,\n # so we include it in this sample user record.\n orderId => $order_id,\n conversionActionId => $conversion_action_id,\n conversionDateTime => $conversion_date_time,\n currencyCode => \"USD\",\n userAgent => $user_agent,\n};\nmy $user_identifiers = [];\n\n# Create a user identifier using the hashed email address, using the normalize\n# and hash method specifically for email addresses.\nmy $hashed_email = normalize_and_hash_email_address($raw_record->{email});\npush(\n @$user_identifiers,\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n hashedEmail => $hashed_email,\n # Optional: Specify the user identifier source.\n userIdentifierSource => FIRST_PARTY\n }));\n\n# Check if the record has a phone number, and if so, add a UserIdentifier for it.\nif (defined $raw_record->{phone}) {\n # Add the hashed phone number identifier to the list of UserIdentifiers.\n push(\n @$user_identifiers,\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n hashedPhoneNumber => normalize_and_hash($raw_record->{phone}, 1)}));\n}\n\n# Confirm the record has all the required mailing address elements, and if so, add\n# a UserIdentifier for the mailing address.\nif (defined $raw_record->{firstName}) {\n my $required_keys = [\"lastName\", \"countryCode\", \"postalCode\"];\n my $missing_keys = [];\n\n foreach my $key (@$required_keys) {\n if (!defined $raw_record->{$key}) {\n push(@$missing_keys, $key);\n }\n }\n\n if (@$missing_keys) {\n print\n \"Skipping addition of mailing address information because the following\"\n . \"keys are missing: \"\n . join(\",\", @$missing_keys);\n } else {\n push(\n @$user_identifiers,\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n addressInfo =>\n Google::Ads::GoogleAds::V25::Common::OfflineUserAddressInfo->new({\n # First and last name must be normalized and hashed.\n hashedFirstName => normalize_and_hash($raw_record->{firstName}),\n hashedLastName => normalize_and_hash($raw_record->{lastName}),\n # Country code and zip code are sent in plain text.\n countryCode => $raw_record->{countryCode},\n postalCode => $raw_record->{postalCode},\n })}));\n }\n}\n\n# Add the user identifiers to the enhancement adjustment.\n$enhancement->{userIdentifiers} = $user_identifiers;upload_enhanced_conversions_for_web.pl\n```\n\nExample:\n```text\n// Sets the conversion action.\nenhancementBuilder.setConversionAction(\n ResourceNames.conversionAction(\n customerId, Long.parseLong(rawRecord.get(\"conversionActionId\"))));\n\n// Sets the order ID. Enhancements MUST use order ID instead of GCLID date/time pair.\nenhancementBuilder.setOrderId(rawRecord.get(\"orderId\"));\n\n// Sets the conversion date and time if provided. Providing this value is optional but\n// recommended.\nif (rawRecord.containsKey(\"conversionDateTime\")) {\n enhancementBuilder.setGclidDateTimePair(\n GclidDateTimePair.newBuilder()\n .setConversionDateTime(rawRecord.get(\"conversionDateTime\")));\n}\n\n// Sets the user agent if provided. This should match the user agent of the request that sent\n// the original conversion so the conversion and its enhancement are either both attributed as\n// same-device or both attributed as cross-device.\nif (rawRecord.containsKey(\"userAgent\")) {\n enhancementBuilder.setUserAgent(rawRecord.get(\"userAgent\"));\n}UploadEnhancedConversionsForWeb.java\n```\n\nExample:\n```text\n// Set the conversion action.\nenhancement.ConversionAction =\n ResourceNames.ConversionAction(customerId, conversionActionId);\n\n// Set the order ID. Enhancements MUST use order ID instead of GCLID date/time pair.\nenhancement.OrderId = orderId;\n\n// Sets the conversion date and time if provided. Providing this value is optional but\n// recommended.\nif (string.IsNullOrEmpty(conversionDateTime))\n{\n enhancement.GclidDateTimePair = new GclidDateTimePair()\n {\n ConversionDateTime = conversionDateTime\n };\n}\n\n// Sets optional fields where a value was provided.\nif (!string.IsNullOrEmpty(userAgent))\n{\n // Sets the user agent. This should match the user agent of the request that\n // sent the original conversion so the conversion and its enhancement are either\n // both attributed as same-device or both attributed as cross-device.\n enhancement.UserAgent = userAgent;\n}\nUploadEnhancedConversionsForWeb.cs\n```\n\nExample:\n```text\n// Sets the conversion action.\n$enhancement->setConversionAction(\n ResourceNames::forConversionAction($customerId, $rawRecord['conversionActionId'])\n);\n\n// Sets the order ID. Enhancements MUST use order ID instead of GCLID date/time pair.\nif (!empty($rawRecord['orderId'])) {\n $enhancement->setOrderId($rawRecord['orderId']);\n}\n\n// Sets the conversion date and time if provided. Providing this value is optional but\n// recommended.\nif (!empty($rawRecord['conversionDateTime'])) {\n // Sets the conversion date and time if provided. Providing this value is optional but\n // recommended.\n $enhancement->setGclidDateTimePair(new GclidDateTimePair([\n 'conversion_date_time' => $rawRecord['conversionDateTime']\n ]));\n}\n\n// Sets the user agent if provided. This should match the user agent of the request that\n// sent the original conversion so the conversion and its enhancement are either both\n// attributed as same-device or both attributed as cross-device.\nif (!empty($rawRecord['userAgent'])) {\n $enhancement->setUserAgent($rawRecord['userAgent']);\n}UploadEnhancedConversionsForWeb.php\n```\n\nExample:\n```text\nconversion_action_service = client.get_service(\"ConversionActionService\")\n# Sets the conversion action.\nconversion_adjustment.conversion_action = (\n conversion_action_service.conversion_action_path(\n customer_id, raw_record[\"conversion_action_id\"]\n )\n)\n\n# Sets the order ID. Enhancements MUST use order ID instead of GCLID\n# date/time pair.\nconversion_adjustment.order_id = order_id\n\n# Sets the conversion date and time if provided. Providing this value is\n# optional but recommended.\nif raw_record.get(\"conversion_date_time\"):\n conversion_adjustment.gclid_date_time_pair.conversion_date_time = (\n raw_record[\"conversion_date_time\"]\n )\n\n# Sets optional fields where a value was provided\nif raw_record.get(\"user_agent\"):\n # Sets the user agent. This should match the user agent of the request\n # that sent the original conversion so the conversion and its\n # enhancement are either both attributed as same-device or both\n # attributed as cross-device.\n conversion_adjustment.user_agent = user_agentupload_enhanced_conversions_for_web.py\n```\n\nExample:\n```text\nca.conversion_action = client.path.conversion_action(customer_id, conversion_action_id)\nca.adjustment_type = :ENHANCEMENT\nca.order_id = order_id\n\n# Sets the conversion date and time if provided. Providing this value is\n# optional but recommended.\nunless conversion_date_time.nil?\n ca.gclid_date_time_pair = client.resource.gclid_date_time_pair do |pair|\n pair.conversion_date_time = conversion_date_time\n end\nend\n\n# Creates a user identifier using the hashed email address, using the\n# normalize and hash method specifically for email addresses.\nca.user_identifiers << client.resource.user_identifier do |ui|\n # Uses the normalize and hash method specifically for email addresses.\n ui.hashed_email = normalize_and_hash_email(raw_record[\"email\"])\n # Optional: Specifies the user identifier source.\n ui.user_identifier_source = :FIRST_PARTY\nend\n\n# Checks if the record has a phone number, and if so, adds a UserIdentifier\n# for it.\nunless raw_record[\"phone\"].nil?\n ca.user_identifiers << client.resource.user_identifier do |ui|\n ui.hashed_phone_number = normalize_and_hash_email(raw_record[\"phone\"])\n end\nend\n\n# Checks if the record has all the required mailing address elements, and if\n# so, adds a UserIdentifier for the mailing address.\nunless raw_record[\"first_name\"].nil?\n # Checks if the record contains all the other required elements of a\n # mailing address.\n required_keys = [\"last_name\", \"country_code\", \"postal_code\"]\n # Builds a new list of the required keys that are missing from\n # raw_record.\n missing_keys = required_keys - raw_record.keys\n if missing_keys\n puts(\n \"Skipping addition of mailing address information because the\" \\\n \"following required keys are missing: #{missing_keys}\"\n )\n else\n ca.user_identifiers << client.resource.user_identifier do |ui|\n ui.address_info = client.resource.offline_user_address_info do |info|\n # Certain fields must be hashed using SHA256 in order to handle\n # identifiers in a privacy-safe way, as described at\n # https://support.google.com/google-ads/answer/9888656.\n info.hashed_first_name = normalize_and_hash( raw_record[\"first_name\"])\n info.hashed_last_name = normalize_and_hash( raw_record[\"last_name\"])\n info.postal_code = normalize_and_hash(raw_record[\"country_code\"])\n info.country_code = normalize_and_hash(raw_record[\"postal_code\"])\n end\n end\n end\nend\n\n# Sets optional fields where a value was provided.\nunless user_agent.nil?\n # Sets the user agent. This should match the user agent of the request\n # that sent the original conversion so the conversion and its enhancement\n # are either both attributed as same-device or both attributed as\n # cross-device.\n ca.user_agent = user_agent\nendupload_enhanced_conversions_for_web.rb\n```\n\nExample:\n```text\n# Set the conversion action.\n$enhancement->{conversionAction} =\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::conversion_action(\n $customer_id, $raw_record->{conversionActionId});\n\n# Set the order ID. Enhancements MUST use order ID instead of GCLID date/time pair.\n$enhancement->{orderId} = $raw_record->{orderId};\n\n# Set the conversion date and time if provided. Providing this value is optional\n# but recommended.\nif (defined $raw_record->{conversionDateTime}) {\n $enhancement->{gclidDateTimePair} =\n Google::Ads::GoogleAds::V25::Services::ConversionAdjustmentUploadService::GclidDateTimePair\n ->new({\n conversionDateTime => $raw_record->{conversionDateTime}});\n}\n\n# Set the user agent if provided. This should match the user agent of the\n# request that sent the original conversion so the conversion and its enhancement\n# are either both attributed as same-device or both attributed as cross-device.\nif (defined $raw_record->{userAgent}) {\n $enhancement->{userAgent} = $raw_record->{userAgent};\n}upload_enhanced_conversions_for_web.pl\n```\n\nExample:\n```text\n// Creates the conversion adjustment upload service client.\ntry (ConversionAdjustmentUploadServiceClient conversionUploadServiceClient =\n googleAdsClient.getLatestVersion().createConversionAdjustmentUploadServiceClient()) {\n // Uploads the enhancement adjustment. Partial failure should always be set to true.\n\n // NOTE: This request contains a single adjustment as a demonstration. However, if you have\n // multiple adjustments to upload, it's best to upload multiple adjustments per request\n // instead of sending a separate request per adjustment. See the following for per-request\n // limits:\n // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_adjustment_upload_service\n UploadConversionAdjustmentsResponse response =\n conversionUploadServiceClient.uploadConversionAdjustments(\n UploadConversionAdjustmentsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .addConversionAdjustments(enhancementBuilder)\n // Enables partial failure (must be true).\n .setPartialFailure(true)\n .build());UploadEnhancedConversionsForWeb.java\n```\n\nExample:\n```text\n// Uploads the enhancement adjustment. Partial failure should always be set to true.\n//\n// NOTE: This request contains a single adjustment as a demonstration.\n// However, if you have multiple adjustments to upload, it's best to upload\n// multiple adjustmenst per request instead of sending a separate request per\n// adjustment. See the following for per-request limits:\n// https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_adjust\nUploadConversionAdjustmentsResponse response =\n conversionAdjustmentUploadService.UploadConversionAdjustments(\n new UploadConversionAdjustmentsRequest()\n {\n CustomerId = customerId.ToString(),\n ConversionAdjustments = { enhancement },\n // Enables partial failure (must be true).\n PartialFailure = true,\n });UploadEnhancedConversionsForWeb.cs\n```\n\nExample:\n```text\n// Issues a request to upload the conversion enhancement.\n$conversionAdjustmentUploadServiceClient =\n $googleAdsClient->getConversionAdjustmentUploadServiceClient();\n// NOTE: This request contains a single adjustment as a demonstration. However, if you have\n// multiple adjustments to upload, it's best to upload multiple adjustments per request\n// instead of sending a separate request per adjustment. See the following for per-request\n// limits:\n// https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_adjustment_upload_service\n$response = $conversionAdjustmentUploadServiceClient->uploadConversionAdjustments(\n // Enables partial failure (must be true).\n UploadConversionAdjustmentsRequest::build($customerId, [$enhancement], true)\n);UploadEnhancedConversionsForWeb.php\n```\n\nExample:\n```text\n# Creates the conversion adjustment upload service client.\nconversion_adjustment_upload_service = client.get_service(\n \"ConversionAdjustmentUploadService\"\n)\n# Uploads the enhancement adjustment. Partial failure should always be set\n# to true.\n# NOTE: This request only uploads a single conversion, but if you have\n# multiple conversions to upload, it's still best to upload them in a single\n# request. See the following for per-request limits for reference:\n# https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\nresponse = conversion_adjustment_upload_service.upload_conversion_adjustments(\n customer_id=customer_id,\n conversion_adjustments=[conversion_adjustment],\n # Enables partial failure (must be true).\n partial_failure=True,\n)upload_enhanced_conversions_for_web.py\n```\n\nExample:\n```text\nresponse = client.service.conversion_adjustment_upload.upload_conversion_adjustments(\n customer_id: customer_id,\n # NOTE: This request only uploads a single conversion, but if you have\n # multiple conversions to upload, it's still best to upload them in a single\n # request. See the following for per-request limits for reference:\n # https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\n conversion_adjustments: [enhancement],\n # Partial failure must be set to true.\n partial_failure: true,\n)upload_enhanced_conversions_for_web.rb\n```\n\nExample:\n```text\n# Upload the enhancement adjustment. Partial failure should always be set to true.\n#\n# NOTE: This request contains a single adjustment as a demonstration.\n# However, if you have multiple adjustments to upload, it's best to\n# upload multiple adjustments per request instead of sending a separate\n# request per adjustment. See the following for per-request limits:\n# https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_adjustment_upload_service\nmy $response =\n $api_client->ConversionAdjustmentUploadService()\n ->upload_conversion_adjustments({\n customerId => $customer_id,\n conversionAdjustments => [$enhancement],\n # Enable partial failure (must be true).\n partialFailure => \"true\"\n });upload_enhanced_conversions_for_web.pl\n```\n\nExample:\n```text\nSELECT\n customer.conversion_tracking_setting.google_ads_conversion_customer,\n customer.conversion_tracking_setting.conversion_tracking_status,\n customer.conversion_tracking_setting.conversion_tracking_id,\n customer.conversion_tracking_setting.cross_account_conversion_tracking_id\nFROM customer\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.466Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":26,"totalLines":1205,"estimatedTokens":12325}}181{"id":"doc-import_conversion_adjustments_google_ads_api_goo-90ca2285","source":"documentation","title":"Import conversion adjustments | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/conversions/upload-adjustments","text":"Example:\n```text\nprivate void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n long conversionActionId,\n String orderId,\n String adjustmentType,\n String adjustmentDateTime,\n @Nullable Float restatementValue)\n throws InvalidProtocolBufferException {\n // Gets the conversion adjustment enum value from the adjustmentType String.\n ConversionAdjustmentType conversionAdjustmentType =\n ConversionAdjustmentType.valueOf(adjustmentType);\n\n // Applies the conversion adjustment to the existing conversion.\n ConversionAdjustment conversionAdjustment =\n ConversionAdjustment.newBuilder()\n .setConversionAction(ResourceNames.conversionAction(customerId, conversionActionId))\n .setAdjustmentType(conversionAdjustmentType)\n // Sets the orderId to identify the conversion to adjust.\n .setOrderId(orderId)\n // As an alternative to setting orderId, you can provide a GclidDateTimePair, but\n // setting orderId instead is strongly recommended.\n // .setGclidDateTimePair(\n // GclidDateTimePair.newBuilder()\n // .setGclid(gclid)\n // .setConversionDateTime(conversionDateTime)\n // .build())\n .setAdjustmentDateTime(adjustmentDateTime)\n .build();\n\n // Sets adjusted value for adjustment type RESTATEMENT.\n if (restatementValue != null\n && conversionAdjustmentType == ConversionAdjustmentType.RESTATEMENT) {\n conversionAdjustment =\n conversionAdjustment.toBuilder()\n .setRestatementValue(\n RestatementValue.newBuilder().setAdjustedValue(restatementValue).build())\n .build();\n }\n\n // Creates the conversion upload service client.\n try (ConversionAdjustmentUploadServiceClient conversionUploadServiceClient =\n googleAdsClient.getLatestVersion().createConversionAdjustmentUploadServiceClient()) {\n // Uploads the click conversion. Partial failure should always be set to true.\n UploadConversionAdjustmentsRequest request =\n UploadConversionAdjustmentsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n // Enables partial failure (must be true).\n .setPartialFailure(true)\n .addConversionAdjustments(conversionAdjustment)\n .build();\n UploadConversionAdjustmentsResponse response =\n conversionUploadServiceClient.uploadConversionAdjustments(request);\n\n // Extracts the partial failure error if present on the response.\n ErrorUtils errorUtils = ErrorUtils.getInstance();\n GoogleAdsFailure googleAdsFailure =\n response.hasPartialFailureError()\n ? errorUtils.getGoogleAdsFailure(response.getPartialFailureError())\n : null;\n\n // Constructs a protocol buffer printer that will print error details in a concise format.\n final Printer errorPrinter = JsonFormat.printer().omittingInsignificantWhitespace();\n // Prints the results for each adjustment, including any partial errors returned.\n for (int opIndex = 0; opIndex < request.getConversionAdjustmentsCount(); opIndex++) {\n ConversionAdjustmentResult result = response.getResults(opIndex);\n if (errorUtils.isPartialFailureResult(result)) {\n // The operation failed. Prints the error details.\n for (GoogleAdsError googleAdsError :\n errorUtils.getGoogleAdsErrors(opIndex, googleAdsFailure)) {\n System.out.printf(\n \"%4d: Partial failure occurred: %s%n\", opIndex, errorPrinter.print(googleAdsError));\n }\n } else {\n System.out.printf(\n \"%4d: Uploaded conversion adjustment for conversion action '%s' and order ID '%s'.%n\",\n opIndex, result.getConversionAction(), result.getOrderId());\n }\n }\n }\n}UploadConversionAdjustment.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long conversionActionId,\n string orderId, string adjustmentDateTime,\n ConversionAdjustmentType adjustmentType,\n double? restatementValue)\n{\n // Get the ConversionAdjustmentUploadService.\n ConversionAdjustmentUploadServiceClient conversionAdjustmentUploadService =\n client.GetService(Services.V25.ConversionAdjustmentUploadService);\n\n // Associate conversion adjustments with the existing conversion action.\n ConversionAdjustment conversionAdjustment = new ConversionAdjustment()\n {\n ConversionAction = ResourceNames.ConversionAction(customerId, conversionActionId),\n AdjustmentType = adjustmentType,\n // Sets the orderId to identify the conversion to adjust.\n OrderId = orderId,\n // As an alternative to setting orderId, you can provide a GclidDateTimePair,\n // but setting orderId instead is strongly recommended.\n //GclidDateTimePair = new GclidDateTimePair()\n //{\n // Gclid = gclid,\n // ConversionDateTime = conversionDateTime,\n //},\n AdjustmentDateTime = adjustmentDateTime,\n };\n\n // Set adjusted value for adjustment type RESTATEMENT.\n if (adjustmentType == ConversionAdjustmentType.Restatement)\n {\n conversionAdjustment.RestatementValue = new RestatementValue()\n {\n AdjustedValue = restatementValue.Value\n };\n }\n\n try\n {\n // Issue a request to upload the conversion adjustment.\n UploadConversionAdjustmentsResponse response =\n conversionAdjustmentUploadService.UploadConversionAdjustments(\n new UploadConversionAdjustmentsRequest()\n {\n CustomerId = customerId.ToString(),\n ConversionAdjustments = { conversionAdjustment },\n // Enables partial failure (must be true).\n PartialFailure = true,\n ValidateOnly = false\n });\n\n // Prints any partial errors returned.\n // To review the overall health of your recent uploads, see:\n // https://developers.google.com/google-ads/api/docs/conversions/upload-summaries\n if (response.PartialFailureError != null)\n {\n // Extracts the partial failure from the response status.\n GoogleAdsFailure partialFailure = response.PartialFailure;\n Console.WriteLine($\"{partialFailure.Errors.Count} partial failure error(s) \" +\n $\"occurred\");\n }\n else\n {\n ConversionAdjustmentResult result = response.Results[0];\n // Print the result.\n Console.WriteLine($\"Uploaded conversion adjustment value of\" +\n $\" '{result.ConversionAction}' for Google Click ID \" +\n $\"'{result.GclidDateTimePair.Gclid}'\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}UploadConversionAdjustment.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $conversionActionId,\n string $orderId,\n string $adjustmentType,\n string $adjustmentDateTime,\n ?float $restatementValue\n) {\n $conversionAdjustmentType = ConversionAdjustmentType::value($adjustmentType);\n\n // Applies the conversion adjustment to the existing conversion.\n $conversionAdjustment = new ConversionAdjustment([\n 'conversion_action' =>\n ResourceNames::forConversionAction($customerId, $conversionActionId),\n 'adjustment_type' => $conversionAdjustmentType,\n // Sets the orderId to identify the conversion to adjust.\n 'order_id' => $orderId,\n // As an alternative to setting orderId, you can provide a 'gclid_date_time_pair', but\n // setting 'order_id' instead is strongly recommended.\n // 'conversion_date_time' must be in \"yyyy-mm-dd hh:mm:ss+|-hh:mm\" format.\n /*\n 'gclid_date_time_pair' => new GclidDateTimePair([\n 'gclid' => 'INSERT_YOUR_GCLID_HERE',\n 'conversion_date_time' => 'INSERT_YOUR_CONVERSION_DATE_TIME_HERE'\n ]),\n */\n 'adjustment_date_time' => $adjustmentDateTime\n ]);\n\n // Sets adjusted value for adjustment type RESTATEMENT.\n if (\n $restatementValue !== null\n && $conversionAdjustmentType === ConversionAdjustmentType::RESTATEMENT\n ) {\n $conversionAdjustment->setRestatementValue(new RestatementValue([\n 'adjusted_value' => $restatementValue\n ]));\n }\n\n // Issues a request to upload the conversion adjustment.\n $conversionAdjustmentUploadServiceClient =\n $googleAdsClient->getConversionAdjustmentUploadServiceClient();\n $response = $conversionAdjustmentUploadServiceClient->uploadConversionAdjustments(\n // Enables partial failure (must be true).\n UploadConversionAdjustmentsRequest::build($customerId, [$conversionAdjustment], true)\n );\n\n // Prints the status message if any partial failure error is returned.\n // Note: The details of each partial failure error are not printed here, you can refer to\n // the example HandlePartialFailure.php to learn more.\n if ($response->hasPartialFailureError()) {\n printf(\n \"Partial failures occurred: '%s'.%s\",\n $response->getPartialFailureError()->getMessage(),\n PHP_EOL\n );\n } else {\n // Prints the result if exists.\n /** @var ConversionAdjustmentResult $uploadedConversionAdjustment */\n $uploadedConversionAdjustment = $response->getResults()[0];\n printf(\n \"Uploaded conversion adjustment of '%s' for order ID '%s'.%s\",\n $uploadedConversionAdjustment->getConversionAction(),\n $uploadedConversionAdjustment->getOrderId(),\n PHP_EOL\n );\n }\n}UploadConversionAdjustment.php\n```\n\nExample:\n```text\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n conversion_action_id: str,\n adjustment_type: str,\n order_id: str,\n adjustment_date_time: str,\n restatement_value: Optional[str] = None,\n) -> None:\n \"\"\"The main method that creates all necessary entities for the example.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n conversion_action_id: the ID of the conversion action to upload the\n adjustment to.\n adjustment_type: the adjustment type, e.g. \" \"RETRACTION, RESTATEMENT.\n order_id: the transaction ID of the conversion to adjust. Strongly\n recommended instead of using gclid and conversion_date_time.\n adjustment_date_time: the date and time of the adjustment.\n restatement_value: the adjusted value for adjustment type RESTATEMENT.\n \"\"\"\n conversion_adjustment_type_enum: ConversionAdjustmentTypeEnum = (\n client.enums.ConversionAdjustmentTypeEnum\n )\n # Determine the adjustment type.\n conversion_adjustment_type: int = conversion_adjustment_type_enum[\n adjustment_type\n ].value\n\n # Applies the conversion adjustment to the existing conversion.\n conversion_adjustment: ConversionAdjustment = client.get_type(\n \"ConversionAdjustment\"\n )\n conversion_action_service: ConversionActionServiceClient = (\n client.get_service(\"ConversionActionService\")\n )\n conversion_adjustment.conversion_action = (\n conversion_action_service.conversion_action_path(\n customer_id, conversion_action_id\n )\n )\n conversion_adjustment.adjustment_type = conversion_adjustment_type\n conversion_adjustment.adjustment_date_time = adjustment_date_time\n\n # Sets the order_id to identify the conversion to adjust.\n conversion_adjustment.order_id = order_id\n\n # As an alternative to setting order_id, you can provide a\n # gclid_date_time_pair, but setting order_id instead is strongly recommended.\n # conversion_adjustment.gclid_date_time_pair.gclid = gclid\n # conversion_adjustment.gclid_date_time_pair.conversion_date_time = (\n # conversion_date_time\n # )\n\n # Sets adjusted value for adjustment type RESTATEMENT.\n if (\n restatement_value\n and conversion_adjustment_type\n == conversion_adjustment_type_enum.RESTATEMENT.value\n ):\n conversion_adjustment.restatement_value.adjusted_value = float(\n restatement_value\n )\n\n # Uploads the click conversion. Partial failure should always be set to\n # true.\n service: ConversionAdjustmentUploadServiceClient = client.get_service(\n \"ConversionAdjustmentUploadService\"\n )\n request: UploadConversionAdjustmentsRequest = client.get_type(\n \"UploadConversionAdjustmentsRequest\"\n )\n request.customer_id = customer_id\n request.conversion_adjustments.append(conversion_adjustment)\n # Enables partial failure (must be true)\n request.partial_failure = True\n\n response: UploadConversionAdjustmentsResponse = (\n service.upload_conversion_adjustments(request=request)\n )\n\n # Extracts the partial failure error if present on the response.\n error_details = None\n if response.partial_failure_error:\n error_details: Iterable[Any] = response.partial_failure_error.details\n\n i: int\n conversion_adjustment_result: ConversionAdjustmentResult\n for i, conversion_adjustment_result in enumerate(response.results):\n # If there's a GoogleAdsFailure in error_details at this position then\n # the uploaded operation failed and we print the error message.\n if error_details and error_details[i]:\n error_detail: Any = error_details[i]\n failure_message: GoogleAdsFailure = client.get_type(\n \"GoogleAdsFailure\"\n )\n # Parse the string into a GoogleAdsFailure message instance.\n # To access class-only methods on the message we retrieve its type.\n google_ads_failure_class: GoogleAdsFailure = type(failure_message)\n failure_object: GoogleAdsFailure = (\n google_ads_failure_class.deserialize(error_detail.value)\n )\n\n error: GoogleAdsError\n for error in failure_object.errors:\n # Construct and print a string that details which element in\n # the operation list failed (by index number) as well as the\n # error message and error code.\n print(\n \"A partial failure at index \"\n f\"{error.location.field_path_elements[0].index} occurred \"\n f\"\\nError message: {error.message}\\nError code: \"\n f\"{error.error_code}\"\n )\n else:\n print(\n \"Uploaded conversion adjustment for conversion action \"\n f\"'{conversion_adjustment_result.conversion_action}' and order \"\n f\"ID '{conversion_adjustment_result.order_id}'.\"\n )upload_conversion_adjustment.py\n```\n\nExample:\n```text\ndef upload_conversion_adjustment(\n customer_id,\n conversion_action_id,\n order_id,\n adjustment_type,\n adjustment_date_time,\n restatement_value\n)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Applies the conversion adjustment to the existing conversion.\n conversion_adjustment = client.resource.conversion_adjustment do |ca|\n ca.conversion_action = client.path.conversion_action(customer_id, conversion_action_id)\n ca.adjustment_type = adjustment_type\n ca.order_id = order_id\n ca.adjustment_date_time = adjustment_date_time\n\n # Set adjusted value for adjustment type RESTATEMENT.\n if adjustment_type == :RESTATEMENT\n ca.restatement_value = client.resource.restatement_value do |ra|\n ra.adjusted_value = restatement_value.to_f\n end\n end\n end\n\n # Issue a request to upload the conversion adjustment(s).\n response = client.service.conversion_adjustment_upload.upload_conversion_adjustments(\n customer_id: customer_id,\n # This example shows just one adjustment but you may upload multiple ones.\n conversion_adjustments: [conversion_adjustment],\n partial_failure: true\n )\n\n if response.partial_failure_error.nil?\n # Process and print all results for multiple adjustments\n response.results.each do |result|\n puts \"Uploaded conversion adjustment for conversion action #{result.conversion_action} \"\\\n \"and order ID #{result.order_id}.\"\n end\n else\n # Print any partial errors returned.\n failures = client.decode_partial_failure_error(response.partial_failure_error)\n puts 'Request failed. Failure details:'\n failures.each do |failure|\n failure.errors.each do |error|\n index = error.location.field_path_elements.first.index\n puts \"\\toperation[#{index}] #{error.error_code.error_code}: #{error.message}\"\n end\n end\n end\nendupload_conversion_adjustment.rb\n```\n\nExample:\n```text\nsub upload_conversion_adjustment {\n my ($api_client, $customer_id, $conversion_action_id, $order_id,\n $adjustment_type, $adjustment_date_time, $restatement_value)\n = @_;\n\n # Applies the conversion adjustment to the existing conversion.\n my $conversion_adjustment =\n Google::Ads::GoogleAds::V25::Services::ConversionAdjustmentUploadService::ConversionAdjustment\n ->new({\n conversionAction =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::conversion_action(\n $customer_id, $conversion_action_id\n ),\n adjustmentType => $adjustment_type,\n # Sets the orderId to identify the conversion to adjust.\n orderId => $order_id,\n # As an alternative to setting orderId, you can provide a 'gclid_date_time_pair',\n # but setting 'order_id' instead is strongly recommended.\n # gclidDateTimePair =>\n # Google::Ads::GoogleAds::V25::Services::ConversionAdjustmentUploadService::GclidDateTimePair\n # ->new({\n # gclid => $gclid,\n # conversionDateTime => $conversion_date_time\n # }\n # ),\n adjustmentDateTime => $adjustment_date_time,\n });\n\n # Set adjusted value for adjustment type RESTATEMENT.\n $conversion_adjustment->{restatementValue} =\n Google::Ads::GoogleAds::V25::Services::ConversionAdjustmentUploadService::RestatementValue\n ->new({\n adjustedValue => $restatement_value\n }) if defined $restatement_value && $adjustment_type eq RESTATEMENT;\n\n # Issue a request to upload the conversion adjustment.\n my $upload_conversion_adjustments_response =\n $api_client->ConversionAdjustmentUploadService()\n ->upload_conversion_adjustments({\n customerId => $customer_id,\n conversionAdjustments => [$conversion_adjustment],\n partialFailure => \"true\"\n });\n\n # Print any partial errors returned.\n if ($upload_conversion_adjustments_response->{partialFailureError}) {\n printf \"Partial error encountered: '%s'.\\n\",\n $upload_conversion_adjustments_response->{partialFailureError}{message};\n }\n\n # Print the result if valid.\n my $uploaded_conversion_adjustment =\n $upload_conversion_adjustments_response->{results}[0];\n if (%$uploaded_conversion_adjustment) {\n printf \"Uploaded conversion adjustment of the conversion action \" .\n \"with resource name '%s' for order ID '%s'.\\n\",\n $uploaded_conversion_adjustment->{conversionAction},\n $uploaded_conversion_adjustment->{orderId};\n }\n\n return 1;\n}upload_conversion_adjustment.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.468Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":488,"estimatedTokens":4926}}182{"id":"doc-video_campaigns_google_ads_api_google_for_develo-661fa6b5","source":"documentation","title":"Video campaigns | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/video/overview","text":"Example:\n```text\nSELECT\n campaign.name,\n campaign.advertising_channel_type,\n ad_group.name,\n ad_group.id,\n metrics.impressions,\n metrics.clicks,\n metrics.ctr\nFROM video\nWHERE campaign.advertising_channel_type = 'VIDEO'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.469Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":15,"estimatedTokens":61}}183{"id":"doc-reporting_for_ai_max_for_search_campaigns_google-b7549de3","source":"documentation","title":"Reporting for AI Max for Search campaigns | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/ai-max-for-search-campaigns/ai-max-reporting","text":"Example:\n```text\nSELECT\n campaign.id,\n campaign.name,\n search_term_view.search_term,\n segments.search_term_match_source,\n metrics.impressions,\n metrics.clicks,\n metrics.conversions\nFROM search_term_view\nWHERE\n segments.search_term_match_source IN ('AI_MAX_KEYWORDLESS', 'AI_MAX_BROAD_MATCH')\n AND segments.date DURING LAST_30_DAYS\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.470Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":17,"estimatedTokens":90}}184{"id":"doc-create_campaign_criteria_google_ads_api_google_f-7a1c2f80","source":"documentation","title":"Create campaign criteria | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/smart-campaigns/create-campaign-criteria","text":"Example:\n```text\n/**\n * Creates {@link com.google.ads.googleads.v25.resources.CampaignCriterion} operations for add\n * each {@link KeywordThemeInfo}.\n */\nprivate Collection<? extends MutateOperation> createCampaignCriterionOperations(\n long customerId,\n List<KeywordThemeInfo> keywordThemeInfos,\n SmartCampaignSuggestionInfo suggestionInfo) {\n List<MutateOperation> keywordThemeOperations =\n keywordThemeInfos.stream()\n .map(\n keywordTheme -> {\n MutateOperation.Builder builder = MutateOperation.newBuilder();\n builder\n .getCampaignCriterionOperationBuilder()\n .getCreateBuilder()\n .setCampaign(ResourceNames.campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID))\n .setKeywordTheme(keywordTheme);\n return builder.build();\n })\n .collect(Collectors.toList());\n\n List<MutateOperation> locationOperations =\n suggestionInfo.getLocationList().getLocationsList().stream()\n .map(\n location -> {\n MutateOperation.Builder builder = MutateOperation.newBuilder();\n builder\n .getCampaignCriterionOperationBuilder()\n .getCreateBuilder()\n .setCampaign(ResourceNames.campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID))\n .setLocation(location);\n return builder.build();\n })\n .collect(Collectors.toList());\n\n return Stream.concat(keywordThemeOperations.stream(), locationOperations.stream())\n .collect(Collectors.toList());\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a list of MutateOperations that create new campaign criteria.\n/// </summary>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <param name=\"keywordThemeInfos\">A list of KeywordThemeInfos.</param>\n/// <param name=\"suggestionInfo\">A SmartCampaignSuggestionInfo instance.</param>\n/// <returns>A list of MutateOperations that create new campaign criteria.</returns>\nprivate IEnumerable<MutateOperation> CreateCampaignCriterionOperations(long customerId,\n IEnumerable<KeywordThemeInfo> keywordThemeInfos, SmartCampaignSuggestionInfo\n suggestionInfo)\n{\n List<MutateOperation> mutateOperations = keywordThemeInfos.Select(\n keywordThemeInfo => new MutateOperation\n {\n CampaignCriterionOperation = new CampaignCriterionOperation\n {\n Create = new CampaignCriterion\n {\n // Set the campaign ID to a temporary ID.\n Campaign = ResourceNames.Campaign(\n customerId, SMART_CAMPAIGN_TEMPORARY_ID),\n // Set the keyword theme to each KeywordThemeInfo in turn.\n KeywordTheme = keywordThemeInfo,\n }\n }\n }).ToList();\n\n // Create a location criterion for each location in the suggestion info.\n mutateOperations.AddRange(\n suggestionInfo.LocationList.Locations.Select(\n locationInfo => new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n // Set the campaign ID to a temporary ID.\n Campaign = ResourceNames.Campaign(customerId,\n SMART_CAMPAIGN_TEMPORARY_ID),\n // Set the location to the given location.\n Location = locationInfo\n }\n }\n }).ToList()\n );\n return mutateOperations;\n}AddSmartCampaign.cs\n```\n\nExample:\n```text\nprivate static function createCampaignCriterionOperations(\n int $customerId,\n array $keywordThemeInfos,\n SmartCampaignSuggestionInfo $smartCampaignSuggestionInfo\n): array {\n $operations = [];\n foreach ($keywordThemeInfos as $info) {\n // Creates the campaign criterion object.\n $campaignCriterion = new CampaignCriterion([\n // Sets the campaign ID to a temporary ID.\n 'campaign' =>\n ResourceNames::forCampaign($customerId, self::SMART_CAMPAIGN_TEMPORARY_ID),\n // Sets the keyword theme to the given KeywordThemeInfo.\n 'keyword_theme' => $info\n ]);\n\n // Creates the MutateOperation that creates the campaign criterion and adds it to the\n // list of operations.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => $campaignCriterion\n ])\n ]);\n }\n\n // Create a location criterion for each location in the suggestion info object to add\n // corresponding location targeting to the Smart campaign.\n foreach ($smartCampaignSuggestionInfo->getLocationList()->getLocations() as $location) {\n // Creates the campaign criterion object.\n $campaignCriterion = new CampaignCriterion([\n // Sets the campaign ID to a temporary ID.\n 'campaign' =>\n ResourceNames::forCampaign($customerId, self::SMART_CAMPAIGN_TEMPORARY_ID),\n // Set the location to the given location.\n 'location' => $location\n ]);\n\n // Creates the MutateOperation that creates the campaign criterion and adds it to the\n // list of operations.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => $campaignCriterion\n ])\n ]);\n }\n\n return $operations;\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef create_campaign_criterion_operations(\n client: GoogleAdsClient,\n customer_id: str,\n keyword_theme_infos: List[KeywordThemeInfo],\n suggestion_info: SmartCampaignSuggestionInfo,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create new campaign criteria.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n keyword_theme_infos: a list of KeywordThemeInfos.\n suggestion_info: A SmartCampaignSuggestionInfo instance.\n\n Returns:\n a list of MutateOperations that create new campaign criteria.\n \"\"\"\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n operations: List[MutateOperation] = []\n info: KeywordThemeInfo\n for info in keyword_theme_infos:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion_operation: CampaignCriterionOperation = (\n mutate_operation.campaign_criterion_operation\n )\n campaign_criterion: CampaignCriterion = (\n campaign_criterion_operation.create\n )\n # Set the campaign ID to a temporary ID.\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, _SMART_CAMPAIGN_TEMPORARY_ID\n )\n # Set the keyword theme to the given KeywordThemeInfo.\n campaign_criterion.keyword_theme.CopyFrom(info)\n # Add the mutate operation to the list of other operations.\n operations.append(mutate_operation)\n\n # Create a location criterion for each location in the suggestion info\n # object to add corresponding location targeting to the Smart campaign\n location_info: LocationInfo\n for location_info in suggestion_info.location_list.locations:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion_operation: CampaignCriterionOperation = (\n mutate_operation.campaign_criterion_operation\n )\n campaign_criterion: CampaignCriterion = (\n campaign_criterion_operation.create\n )\n # Set the campaign ID to a temporary ID.\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, _SMART_CAMPAIGN_TEMPORARY_ID\n )\n # Set the location to the given location.\n campaign_criterion.location.CopyFrom(location_info)\n # Add the mutate operation to the list of other operations.\n operations.append(mutate_operation)\n\n return operationsadd_smart_campaign.py\n```\n\nExample:\n```text\n# Creates a list of mutate_operations that create new campaign criteria.\ndef create_campaign_criterion_operations(\n client,\n customer_id,\n keyword_theme_infos,\n suggestion_info)\n operations = []\n\n keyword_theme_infos.each do |info|\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n # Sets the campaign ID to a temporary ID.\n cc.campaign = client.path.campaign(\n customer_id, SMART_CAMPAIGN_TEMPORARY_ID)\n # Sets the keyword theme to the given keyword_theme_info.\n cc.keyword_theme = info\n end\n end\n end\n\n # Create a location criterion for each location in the suggestion info object\n # to add corresponding location targeting to the Smart campaign\n suggestion_info.location_list.locations.each do |location|\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n # Sets the campaign ID to a temporary ID.\n cc.campaign = client.path.campaign(\n customer_id, SMART_CAMPAIGN_TEMPORARY_ID)\n # Sets the location to the given location.\n cc.location = location\n end\n end\n end\n\n operations\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Creates a list of MutateOperations that create new campaign criteria.\nsub _create_campaign_criterion_operations {\n my ($customer_id, $keyword_theme_infos, $suggestion_info) = @_;\n\n my $campaign_criterion_operations = [];\n\n foreach my $keyword_theme_info (@$keyword_theme_infos) {\n push @$campaign_criterion_operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n # Set the campaign ID to a temporary ID.\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, SMART_CAMPAIGN_TEMPORARY_ID\n ),\n # Set the keyword theme to the given KeywordThemeInfo.\n keywordTheme => $keyword_theme_info\n })})});\n }\n\n # Create a location criterion for each location in the suggestion info object\n # to add corresponding location targeting to the Smart campaign.\n foreach my $location_info (@{$suggestion_info->{locationList}{locations}}) {\n push @$campaign_criterion_operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n # Set the campaign ID to a temporary ID.\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, SMART_CAMPAIGN_TEMPORARY_ID\n ),\n # Set the location to the given location.\n location => $location_info\n })})});\n }\n\n return $campaign_criterion_operations;\n}add_smart_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.471Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":306,"estimatedTokens":2974}}185{"id":"doc-import_call_conversions_google_ads_api_google_fo-7413f492","source":"documentation","title":"Import call conversions | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/conversions/upload-calls","text":"Example:\n```text\nprivate void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n String conversionActionId,\n String callerId,\n String callStartDateTime,\n double conversionValue,\n Long conversionCustomVariableId,\n String conversionCustomVariableValue,\n ConsentStatus adUserDataConsent) {\n // Create a call conversion by specifying currency as USD.\n CallConversion.Builder conversionBuilder =\n CallConversion.newBuilder()\n .setConversionAction(conversionActionId)\n .setCallerId(callerId)\n .setCallStartDateTime(callStartDateTime)\n .setConversionValue(conversionValue)\n .setCurrencyCode(\"USD\");\n\n if (conversionCustomVariableId != null && conversionCustomVariableValue != null) {\n conversionBuilder.addCustomVariables(\n CustomVariable.newBuilder()\n .setConversionCustomVariable(\n ResourceNames.conversionCustomVariable(customerId, conversionCustomVariableId))\n .setValue(conversionCustomVariableValue));\n }\n\n // Sets the consent information, if provided.\n if (adUserDataConsent != null) {\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy for details.\n conversionBuilder.setConsent(Consent.newBuilder().setAdUserData(adUserDataConsent));\n }\n\n CallConversion conversion = conversionBuilder.build();\n\n // Uploads the call conversion to the API.\n try (ConversionUploadServiceClient conversionUploadServiceClient =\n googleAdsClient.getLatestVersion().createConversionUploadServiceClient()) {\n // Partial failure MUST be enabled for this request.\n\n // NOTE: This request contains a single conversion as a demonstration. However, if you have\n // multiple conversions to upload, it's best to upload multiple conversions per request\n // instead of sending a separate request per conversion. See the following for per-request\n // limits:\n // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\n UploadCallConversionsResponse response =\n conversionUploadServiceClient.uploadCallConversions(\n UploadCallConversionsRequest.newBuilder()\n .setCustomerId(String.valueOf(customerId))\n .setCustomerId(Long.toString(customerId))\n .addConversions(conversion)\n .setPartialFailure(true)\n .build());\n\n // Prints any partial failure errors returned.\n if (response.hasPartialFailureError()) {\n GoogleAdsFailure googleAdsFailure =\n ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError());\n googleAdsFailure\n .getErrorsList()\n .forEach(e -> System.out.println(\"Partial failure occurred: \" + e.getMessage()));\n throw new RuntimeException(\n \"Partial failure occurred \" + response.getPartialFailureError().getMessage());\n }\n\n // Prints the result if valid.\n CallConversionResult result = response.getResults(0);\n System.out.printf(\n \"Uploaded call conversion that occurred at '%' for caller ID '%' to the conversion\"\n + \" action with resource name '%'.%n\",\n result.getCallStartDateTime(), result.getCallerId(), result.getConversionAction());\n }\n}UploadCallConversion.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId,\n long conversionActionId, string callerId, string callStartTime,\n string conversionTime, double conversionValue,\n long? conversionCustomVariableId, string conversionCustomVariableValue,\n ConsentStatus? adUserDataConsent)\n{\n // Get the ConversionUploadService.\n ConversionUploadServiceClient conversionUploadService =\n client.GetService(Services.V25.ConversionUploadService);\n\n // Create a call conversion by specifying currency as USD.\n CallConversion callConversion = new CallConversion()\n {\n ConversionAction = ResourceNames.ConversionAction(customerId, conversionActionId),\n CallerId = callerId,\n CallStartDateTime = callStartTime,\n ConversionDateTime = conversionTime,\n ConversionValue = conversionValue,\n CurrencyCode = \"USD\",\n };\n\n if (adUserDataConsent != null)\n {\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy\n // for details.\n callConversion.Consent = new Consent()\n {\n AdUserData = (ConsentStatus)adUserDataConsent\n };\n }\n\n if (conversionCustomVariableId != null &&\n !string.IsNullOrEmpty(conversionCustomVariableValue))\n {\n callConversion.CustomVariables.Add(new CustomVariable()\n {\n ConversionCustomVariable = ResourceNames.ConversionCustomVariable(\n customerId, conversionCustomVariableId.Value),\n Value = conversionCustomVariableValue\n });\n }\n\n UploadCallConversionsRequest request = new UploadCallConversionsRequest()\n {\n CustomerId = customerId.ToString(),\n Conversions = { callConversion },\n PartialFailure = true\n };\n\n try\n {\n // Issues a request to upload the call conversion. The partialFailure parameter\n // is set to true, and validateOnly parameter to false as required by this method\n // call.\n // NOTE: This request contains a single conversion as a demonstration. However, if\n // you have multiple conversions to upload, it's best to upload multiple conversions\n // per request instead of sending a separate request per conversion. See the\n // following for per-request limits:\n // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\n UploadCallConversionsResponse response =\n conversionUploadService.UploadCallConversions(request);\n\n // Since we set partialFailure = true, we can retrieve error messages (if any) from\n // the operation response.\n if (response.PartialFailureError != null)\n {\n Console.WriteLine(\"Call conversion upload failed.\");\n\n // Retrieves the errors from the partial failure and prints them.\n List<GoogleAdsError> errors =\n response.PartialFailure.GetErrorsByOperationIndex(0);\n foreach (GoogleAdsError error in errors)\n {\n Console.WriteLine($\"Operation failed with error: {error}.\");\n }\n }\n else\n {\n // Prints the result.\n CallConversionResult uploadedCallConversion = response.Results[0];\n Console.WriteLine($\"Uploaded call conversion that occurred at \" +\n $\"'{uploadedCallConversion.CallStartDateTime}' for caller ID \" +\n $\"'{uploadedCallConversion.CallerId}' to the conversion action with \" +\n $\"resource name '{uploadedCallConversion.ConversionAction}'.\");\n }\n\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}UploadCallConversion.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $conversionActionId,\n string $callerId,\n string $callStartDateTime,\n string $conversionDateTime,\n float $conversionValue,\n ?string $conversionCustomVariableId,\n ?string $conversionCustomVariableValue,\n ?int $adUserDataConsent\n) {\n // Creates a call conversion by specifying currency as USD.\n $callConversion = new CallConversion([\n 'conversion_action' =>\n ResourceNames::forConversionAction($customerId, $conversionActionId),\n 'caller_id' => $callerId,\n 'call_start_date_time' => $callStartDateTime,\n 'conversion_date_time' => $conversionDateTime,\n 'conversion_value' => $conversionValue,\n 'currency_code' => 'USD'\n ]);\n if (!is_null($conversionCustomVariableId) && !is_null($conversionCustomVariableValue)) {\n $callConversion->setCustomVariables([new CustomVariable([\n 'conversion_custom_variable' => ResourceNames::forConversionCustomVariable(\n $customerId,\n $conversionCustomVariableId\n ),\n 'value' => $conversionCustomVariableValue\n ])]);\n }\n // Sets the consent information, if provided.\n if (!empty($adUserDataConsent)) {\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy for details.\n $callConversion->setConsent(new Consent(['ad_user_data' => $adUserDataConsent]));\n }\n\n // Issues a request to upload the call conversion.\n $conversionUploadServiceClient = $googleAdsClient->getConversionUploadServiceClient();\n // NOTE: This request contains a single conversion as a demonstration. However, if you have\n // multiple conversions to upload, it's best to upload multiple conversions per request\n // instead of sending a separate request per conversion. See the following for per-request\n // limits:\n // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\n $response = $conversionUploadServiceClient->uploadCallConversions(\n // Partial failure MUST be enabled for this request.\n UploadCallConversionsRequest::build($customerId, [$callConversion], true)\n );\n\n // Prints the status message if any partial failure error is returned.\n // Note: The details of each partial failure error are not printed here, you can refer to\n // the example HandlePartialFailure.php to learn more.\n if ($response->hasPartialFailureError()) {\n printf(\n \"Partial failures occurred: '%s'.%s\",\n $response->getPartialFailureError()->getMessage(),\n PHP_EOL\n );\n } else {\n // Prints the result if exists.\n /** @var CallConversionResult $uploadedCallConversion */\n $uploadedCallConversion = $response->getResults()[0];\n printf(\n \"Uploaded call conversion that occurred at '%s' for caller ID '%s' to the \"\n . \"conversion action with resource name '%s'.%s\",\n $uploadedCallConversion->getCallStartDateTime(),\n $uploadedCallConversion->getCallerId(),\n $uploadedCallConversion->getConversionAction(),\n PHP_EOL\n );\n }\n}UploadCallConversion.php\n```\n\nExample:\n```text\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n conversion_action_id: str,\n caller_id: str,\n call_start_date_time: str,\n conversion_date_time: str,\n conversion_value: float,\n conversion_custom_variable_id: Optional[str],\n conversion_custom_variable_value: Optional[str],\n ad_user_data_consent: Optional[str],\n):\n \"\"\"Imports offline call conversion values for calls related to your ads.\n\n Args:\n client: An initialized GoogleAdsClient instance.\n customer_id: The client customer ID string.\n conversion_action_id: The ID of the conversion action to upload to.\n caller_id: The caller ID from which this call was placed. Caller ID is\n expected to be in E.164 format with preceding '+' sign,\n e.g. '+18005550100'.\n call_start_date_time: The date and time at which the call occurred. The\n format is 'yyyy-mm-dd hh:mm:ss+|-hh:mm',\n e.g. '2021-01-01 12:32:45-08:00'.\n conversion_date_time: The the date and time of the conversion (should be\n after the click time). The format is 'yyyy-mm-dd hh:mm:ss+|-hh:mm',\n e.g. '2021-01-01 12:32:45-08:00'.\n conversion_value: The conversion value in the desired currency.\n conversion_custom_variable_id: The ID of the conversion custom\n variable to associate with the upload.\n conversion_custom_variable_value: The str value of the conversion custom\n variable to associate with the upload.\n ad_user_data_consent: The consent status for ad user data for all\n members in the job.\n \"\"\"\n # Get the ConversionUploadService client.\n conversion_upload_service: ConversionUploadServiceClient = (\n client.get_service(\"ConversionUploadService\")\n )\n\n # Create a call conversion in USD currency.\n call_conversion: CallConversion = client.get_type(\"CallConversion\")\n call_conversion.conversion_action = client.get_service(\n \"ConversionActionService\"\n ).conversion_action_path(customer_id, conversion_action_id)\n call_conversion.caller_id = caller_id\n call_conversion.call_start_date_time = call_start_date_time\n call_conversion.conversion_date_time = conversion_date_time\n call_conversion.conversion_value = conversion_value\n call_conversion.currency_code = \"USD\"\n\n if conversion_custom_variable_id and conversion_custom_variable_value:\n conversion_custom_variable: CustomVariable = client.get_type(\n \"CustomVariable\"\n )\n conversion_custom_variable.conversion_custom_variable = (\n conversion_custom_variable_id\n )\n conversion_custom_variable.value = conversion_custom_variable_value\n call_conversion.custom_variables.append(conversion_custom_variable)\n\n # Specifies whether user consent was obtained for the data you are\n # uploading. For more details. see:\n # https://www.google.com/about/company/user-consent-policy\n if ad_user_data_consent:\n call_conversion.consent.ad_user_data = client.enums.ConsentStatusEnum[\n ad_user_data_consent\n ]\n\n # Issue a request to upload the call conversion.\n # Partial failure MUST be enabled for this request.\n request: UploadCallConversionsRequest = client.get_type(\n \"UploadCallConversionsRequest\"\n )\n request.customer_id = customer_id\n request.conversions = [call_conversion]\n request.partial_failure = True\n # NOTE: This request only uploads a single conversion, but if you have\n # multiple conversions to upload, it's most efficient to upload them in a\n # single request. See the following for per-request limits for reference:\n # https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\n upload_call_conversions_response: UploadCallConversionsResponse = (\n conversion_upload_service.upload_call_conversions(request=request)\n )\n\n # Print any partial errors returned.\n if upload_call_conversions_response.partial_failure_error:\n print(\n \"Partial error occurred: \"\n f\"'{upload_call_conversions_response.partial_failure_error.message}'\"\n )\n\n # Print the result if valid.\n uploaded_call_conversion: CallConversionResult = (\n upload_call_conversions_response.results[0]\n )\n if uploaded_call_conversion.call_start_date_time:\n print(\n \"Uploaded call conversion that occurred at \"\n f\"'{uploaded_call_conversion.call_start_date_time}' \"\n f\"for caller ID '{uploaded_call_conversion.caller_id}' \"\n \"to the conversion action with resource name \"\n f\"'{uploaded_call_conversion.conversion_action}'.\"\n )upload_call_conversion.py\n```\n\nExample:\n```text\ndef upload_call_conversion(\n customer_id,\n conversion_action_id,\n caller_id,\n call_start_date_time,\n conversion_date_time,\n conversion_value,\n conversion_custom_variable_id,\n conversion_custom_variable_value,\n ad_user_data_consent)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Create a call conversion by specifying currency as USD.\n call_conversion = client.resource.call_conversion do |c|\n c.conversion_action = client.path.conversion_action(\n customer_id, conversion_action_id)\n c.caller_id = caller_id\n c.call_start_date_time = call_start_date_time\n c.conversion_date_time = conversion_date_time\n c.conversion_value = conversion_value\n c.currency_code = \"USD\"\n if conversion_custom_variable_id && conversion_custom_variable_value\n c.custom_variables << client.resource.custom_variable do |cv|\n cv.conversion_custom_variable = client.path.conversion_custom_variable(\n customer_id, conversion_custom_variable_id)\n cv.value = conversion_custom_variable_value\n end\n end\n\n unless ad_user_data_consent.nil?\n c.consent = client.resource.consent do |c|\n # Specifies whether user consent was obtained for the data you are\n # uploading. For more details, see:\n # https://www.google.com/about/company/user-consent-policy\n c.ad_user_data = ad_user_data_consent\n end\n end\n end\n\n # Issues a request to upload the call conversion.\n response = client.service.conversion_upload.upload_call_conversions(\n customer_id: customer_id,\n # NOTE: This request only uploads a single conversion, but if you have\n # multiple conversions to upload, it's most efficient to upload them in a\n # single request. See the following for per-request limits for reference:\n # https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\n conversions: [call_conversion],\n partial_failure: true\n )\n\n # Prints errors if any partial failure error is returned.\n if response.partial_failure_error\n failures = client.decode_partial_failure_error(response.partial_failure_error)\n failures.each do |failure|\n failure.errors.each do |error|\n human_readable_error_path = error\n .location\n .field_path_elements\n .map { |location_info|\n if location_info.index\n \"#{location_info.field_name}[#{location_info.index}]\"\n else\n \"#{location_info.field_name}\"\n end\n }.join(\" > \")\n\n errmsg = \"error occured while adding operations \" \\\n \"#{human_readable_error_path}\" \\\n \" with value: #{error.trigger.string_value}\" \\\n \" because #{error.message.downcase}\"\n puts errmsg\n end\n end\n else\n # Print the result if valid.\n uploaded_call_conversion = response.results.first\n puts \"Uploaded call conversion that occurred at \" \\\n \"#{uploaded_call_conversion.call_start_date_time} \" \\\n \"for caller ID \" \\\n \"#{uploaded_call_conversion.caller_id} \" \\\n \"to the conversion action with resource name \" \\\n \"#{uploaded_call_conversion.conversion_action}\"\n end\nendupload_call_conversion.rb\n```\n\nExample:\n```text\nsub upload_call_conversion {\n my (\n $api_client, $customer_id,\n $conversion_action_id, $caller_id,\n $call_start_date_time, $conversion_date_time,\n $conversion_value, $conversion_custom_variable_id,\n $conversion_custom_variable_value, $ad_user_data_consent\n ) = @_;\n\n # Create a call conversion by specifying currency as USD.\n my $call_conversion =\n Google::Ads::GoogleAds::V25::Services::ConversionUploadService::CallConversion\n ->new({\n conversionAction =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::conversion_action(\n $customer_id, $conversion_action_id\n ),\n callerId => $caller_id,\n callStartDateTime => $call_start_date_time,\n conversionDateTime => $conversion_date_time,\n conversionValue => $conversion_value,\n currencyCode => \"USD\"\n });\n\n if ($conversion_custom_variable_id && $conversion_custom_variable_value) {\n $call_conversion->{customVariables} = [\n Google::Ads::GoogleAds::V25::Services::ConversionUploadService::CustomVariable\n ->new({\n conversionCustomVariable =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::conversion_custom_variable(\n $customer_id, $conversion_custom_variable_id\n ),\n value => $conversion_custom_variable_value\n })];\n }\n\n # Set the consent information, if provided.\n if ($ad_user_data_consent) {\n # Specify whether user consent was obtained for the data you are uploading.\n # See https://www.google.com/about/company/user-consent-policy for details.\n $call_conversion->{consent} =\n Google::Ads::GoogleAds::V25::Common::Consent->new({\n adUserData => $ad_user_data_consent\n });\n }\n\n # Issue a request to upload the call conversion.\n # NOTE: This request contains a single conversion as a demonstration.\n # However, if you have multiple conversions to upload, it's best to\n # upload multiple conversions per request instead of sending a separate\n # request per conversion. See the following for per-request limits:\n # https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\n my $upload_call_conversions_response =\n $api_client->ConversionUploadService()->upload_call_conversions({\n customerId => $customer_id,\n conversions => [$call_conversion],\n partialFailure => \"true\"\n });\n\n # Print any partial errors returned.\n if ($upload_call_conversions_response->{partialFailureError}) {\n printf \"Partial error encountered: '%s'.\\n\",\n $upload_call_conversions_response->{partialFailureError}{message};\n }\n\n # Print the result if valid.\n my $uploaded_call_conversion =\n $upload_call_conversions_response->{results}[0];\n if (%$uploaded_call_conversion) {\n printf \"Uploaded call conversion that occurred at '%s' \" .\n \"for caller ID '%s' to the conversion action with resource name '%s'.\\n\",\n $uploaded_call_conversion->{callStartDateTime},\n $uploaded_call_conversion->{callerId},\n $uploaded_call_conversion->{conversionAction};\n }\n\n return 1;\n}upload_call_conversion.pl\n```\n\nExample:\n```text\n# This code example uploads a call conversion.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n#\n# CONVERSION_ACTION_RESOURCE_NAME: Resource name of the conversion action\n# associated with this conversion.\n# CALLER_ID: The caller id from which this call was placed. Caller id is\n# expected to be in E.164 format with preceding '+' sign, for example,\n# \"+18005550100\".\n# CALL_START_DATE_TIME: The date time at which the call occurred. The format\n# is \"yyyy-mm-dd hh:mm:ss+|-hh:mm\", for example,\n# \"2019-01-01 12:32:45-08:00\".\n# CONVERSION_DATE_TIME: The date time at which the conversion occurred. The\n# format is \"yyyy-mm-dd hh:mm:ss+|-hh:mm\", for example,\n# \"2019-01-01 12:32:45-08:00\".\n# CONVERSION_VALUE: The value of the conversion for the advertiser.\n# CURRENCY_CODE: The currency code of the conversion value. This is the\n# ISO 4217 3-character currency code. For example: USD, EUR.\n# CONVERSION_CUSTOM_VARIABLE: The name of the conversion custom variable.\n# CONVERSION_CUSTOM_VARIABLE_VALUE: The value of the conversion custom\n# variable.\ncurl -f --request POST \\\n\"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}:uploadCallConversions\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data @- <<EOF\n{\n \"conversions\": [\n {\n \"conversionAction\": \"${CONVERSION_ACTION_RESOURCE_NAME}\",\n \"callerId\": \"${CALLER_ID}\",\n \"callStartDateTime\": \"${CALL_START_DATE_TIME}\",\n \"conversionDateTime\": \"${CONVERSION_DATE_TIME}\",\n \"conversionValue\": ${CONVERSION_VALUE},\n \"currencyCode\": \"${CURRENCY_CODE}\",\n \"customVariables\": [\n {\n \"conversionCustomVariable\": \"${CONVERSION_CUSTOM_VARIABLE}\",\n \"value\": \"${CONVERSION_CUSTOM_VARIABLE_VALUE}\"\n }\n ],\n \"consent\": {\n \"adUserData\": \"GRANTED\"\n }\n }\n\n ]\n}\nEOFupload_call_conversion.sh\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.476Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":599,"estimatedTokens":6090}}186{"id":"doc-create_a_smart_campaign_and_a_smart_campaign_set-6c2e7efe","source":"documentation","title":"Create a Smart campaign and a Smart campaign setting | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/smart-campaigns/create-campaign","text":"Example:\n```text\nprivate MutateOperation createSmartCampaignOperation(long customerId) {\n MutateOperation.Builder builder = MutateOperation.newBuilder();\n builder\n .getCampaignOperationBuilder()\n .getCreateBuilder()\n .setName(\"Smart campaign \" + CodeSampleHelper.getShortPrintableDateTime())\n .setStatus(CampaignStatus.PAUSED)\n .setAdvertisingChannelType(AdvertisingChannelType.SMART)\n .setAdvertisingChannelSubType(AdvertisingChannelSubType.SMART_CAMPAIGN)\n // Assigns the resource name with a temporary ID.\n .setResourceName(ResourceNames.campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID))\n .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING);\n return builder.build();\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a MutateOperation that creates a new Smart campaign.\n/// A temporary ID will be assigned to this campaign so that it can be referenced by other\n/// objects being created in the same Mutate request.\n/// </summary>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <returns>A MutateOperation that creates a campaign.</returns>\nprivate MutateOperation CreateSmartCampaignOperation(long customerId)\n{\n return new MutateOperation\n {\n CampaignOperation = new CampaignOperation\n {\n Create = new Campaign\n {\n Name = $\"Smart campaign #{ExampleUtilities.GetRandomString()}\",\n // Set the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n Status = CampaignStatus.Paused,\n // AdvertisingChannelType must be SMART.\n AdvertisingChannelType = AdvertisingChannelType.Smart,\n // AdvertisingChannelSubType must be SMART_CAMPAIGN.\n AdvertisingChannelSubType = AdvertisingChannelSubType.SmartCampaign,\n // Assign the resource name with a temporary ID.\n ResourceName =\n ResourceNames.Campaign(customerId, SMART_CAMPAIGN_TEMPORARY_ID),\n // Set the budget using the given budget resource name.\n CampaignBudget =\n ResourceNames.CampaignBudget(customerId, BUDGET_TEMPORARY_ID),\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n }\n }\n };\n}AddSmartCampaign.cs\n```\n\nExample:\n```text\nprivate static function createSmartCampaignOperation(int $customerId): MutateOperation\n{\n // Creates the campaign object.\n $campaign = new Campaign([\n 'name' => \"Smart campaign #\" . Helper::getPrintableDatetime(),\n // Sets the campaign status as PAUSED. The campaign is the only entity in the mutate\n // request that should have its' status set.\n 'status' => CampaignStatus::PAUSED,\n // The advertising channel type is required to be SMART.\n 'advertising_channel_type' => AdvertisingChannelType::SMART,\n // The advertising channel sub type is required to be SMART_CAMPAIGN.\n 'advertising_channel_sub_type' => AdvertisingChannelSubType::SMART_CAMPAIGN,\n // Assigns the resource name with a temporary ID.\n 'resource_name' =>\n ResourceNames::forCampaign($customerId, self::SMART_CAMPAIGN_TEMPORARY_ID),\n // Sets the budget using the given budget resource name.\n 'campaign_budget' =>\n ResourceNames::forCampaignBudget($customerId, self::BUDGET_TEMPORARY_ID),\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n ]);\n\n // Creates the MutateOperation that creates the campaign.\n return new MutateOperation([\n 'campaign_operation' => new CampaignOperation(['create' => $campaign])\n ]);\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef create_smart_campaign_operation(\n client: GoogleAdsClient, customer_id: str\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new Smart campaign.\n\n A temporary ID will be assigned to this campaign so that it can\n be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a MutateOperation that creates a campaign.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_operation: CampaignOperation = mutate_operation.campaign_operation\n campaign: Campaign = campaign_operation.create\n campaign.name = f\"Smart campaign #{uuid4()}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its' status set.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n # Campaign.AdvertisingChannelType is required to be SMART.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.SMART\n )\n # Campaign.AdvertisingChannelSubType is required to be SMART_CAMPAIGN.\n campaign.advertising_channel_sub_type = (\n client.enums.AdvertisingChannelSubTypeEnum.SMART_CAMPAIGN\n )\n # Assign the resource name with a temporary ID.\n campaign.resource_name = client.get_service(\n \"CampaignService\"\n ).campaign_path(customer_id, _SMART_CAMPAIGN_TEMPORARY_ID)\n # Set the budget using the given budget resource name.\n campaign.campaign_budget = client.get_service(\n \"CampaignBudgetService\"\n ).campaign_budget_path(customer_id, _BUDGET_TEMPORARY_ID)\n\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n return mutate_operationadd_smart_campaign.py\n```\n\nExample:\n```text\n# Creates a mutate_operation that creates a new Smart campaign.\n# A temporary ID will be assigned to this campaign so that it can\n# be referenced by other objects being created in the same mutate request.\ndef create_smart_campaign_operation(\n client,\n customer_id)\n mutate_operation = client.operation.mutate do |m|\n m.campaign_operation = client.operation.create_resource.campaign do |c|\n c.name = \"Smart campaign ##{(Time.new.to_f * 1000).to_i}\"\n # Sets the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its' status set.\n c.status = :PAUSED\n # campaign.advertising_channel_type is required to be SMART.\n c.advertising_channel_type = :SMART\n # campaign.advertising_channel_sub_type is required to be SMART_CAMPAIGN.\n c.advertising_channel_sub_type = :SMART_CAMPAIGN\n # Assigns the resource name with a temporary ID.\n c.resource_name = client.path.campaign(customer_id, SMART_CAMPAIGN_TEMPORARY_ID)\n c.campaign_budget = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n end\n end\n\n mutate_operation\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Creates a MutateOperation that creates a new Smart campaign.\n# A temporary ID will be assigned to this campaign so that it can be referenced\n# by other objects being created in the same Mutate request.\nsub _create_smart_campaign_operation {\n my ($customer_id) = @_;\n\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n name => \"Smart campaign #\" . uniqid(),\n # Set the campaign status as PAUSED. The campaign is the only\n # entity in the mutate request that should have its status set.\n status => PAUSED,\n # AdvertisingChannelType must be SMART.\n advertisingChannelType => SMART,\n # AdvertisingChannelSubType must be SMART_CAMPAIGN.\n advertisingChannelSubType =>\n Google::Ads::GoogleAds::V25::Enums::AdvertisingChannelSubTypeEnum::SMART_CAMPAIGN,\n # Assign the resource name with a temporary ID.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, SMART_CAMPAIGN_TEMPORARY_ID\n ),\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n # Set the budget using the given budget resource name.\n campaignBudget =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n )})})});\n}add_smart_campaign.pl\n```\n\nExample:\n```text\nprivate MutateOperation createSmartCampaignSettingOperation(\n long customerId, String businessProfileLocation, String businessName) {\n MutateOperation.Builder builder = MutateOperation.newBuilder();\n SmartCampaignSetting.Builder settingBuilder =\n builder\n .getSmartCampaignSettingOperationBuilder()\n .getUpdateBuilder()\n // Sets a temporary ID in the campaign setting's resource name to associate it with\n // the campaign created in the previous step.\n .setResourceName(\n ResourceNames.smartCampaignSetting(customerId, SMART_CAMPAIGN_TEMPORARY_ID));\n // Configures the SmartCampaignSetting using many of the same details used to\n // generate a budget suggestion.\n settingBuilder\n .setFinalUrl(LANDING_PAGE_URL)\n .setAdvertisingLanguageCode(LANGUAGE_CODE)\n .getPhoneNumberBuilder()\n .setCountryCode(COUNTRY_CODE)\n .setPhoneNumber(PHONE_NUMBER);\n\n // It's required that either a business profile location resource name or a business name is\n // added to the SmartCampaignSetting.\n if (businessProfileLocation != null) {\n settingBuilder.setBusinessProfileLocation(businessProfileLocation);\n } else {\n settingBuilder.setBusinessName(businessName);\n }\n builder\n .getSmartCampaignSettingOperationBuilder()\n .setUpdateMask(FieldMasks.allSetFieldsOf(settingBuilder.build()));\n return builder.build();\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a MutateOperation to create a new SmartCampaignSetting. SmartCampaignSettings\n/// are unique in that they only support UPDATE operations, which are used to update and\n/// create them. Below we will use a temporary ID in the resource name to associate it with\n/// the campaign created in the previous step.\n/// </summary>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <param name=\"businessProfileLocation\">The identifier of a Business Profile location.</param>\n/// <param name=\"businessName\">The name of a Business Profile business.</param>\n/// <returns>A MutateOperation that creates a SmartCampaignSetting.</returns>\nprivate MutateOperation CreateSmartCampaignSettingOperation(long customerId,\n string businessProfileLocation, string businessName)\n{\n SmartCampaignSetting smartCampaignSetting = new SmartCampaignSetting\n {\n // Set a temporary ID in the campaign setting's resource name to associate it with\n // the campaign created in the previous step.\n ResourceName =\n ResourceNames.SmartCampaignSetting(customerId, SMART_CAMPAIGN_TEMPORARY_ID),\n // Below we configure the SmartCampaignSetting using many of the same details used\n // to generate a budget suggestion.\n PhoneNumber = new SmartCampaignSetting.Types.PhoneNumber\n {\n CountryCode = COUNTRY_CODE,\n PhoneNumber_ = PHONE_NUMBER\n },\n FinalUrl = LANDING_PAGE_URL,\n AdvertisingLanguageCode = LANGUAGE_CODE\n };\n\n // Either a business profile location or a business name must be added to the\n // SmartCampaignSetting.\n if (!string.IsNullOrEmpty(businessProfileLocation))\n {\n // Transform Google Business Location ID to a compatible format before\n // passing it onto the API.\n smartCampaignSetting.BusinessProfileLocation = businessProfileLocation;\n }\n else\n {\n smartCampaignSetting.BusinessName = businessName;\n }\n\n return new MutateOperation\n {\n SmartCampaignSettingOperation = new SmartCampaignSettingOperation\n {\n Update = smartCampaignSetting,\n // Set the update mask on the operation. This is required since the smart\n // campaign setting is created in an UPDATE operation. Here the update mask\n // will be a list of all the fields that were set on the SmartCampaignSetting.\n UpdateMask = FieldMasks.AllSetFieldsOf(smartCampaignSetting)\n }\n };\n}AddSmartCampaign.cs\n```\n\nExample:\n```text\nprivate static function createSmartCampaignSettingOperation(\n int $customerId,\n ?string $businessProfileLocationResourceName,\n ?string $businessName\n): MutateOperation {\n // Creates the smart campaign setting object.\n $smartCampaignSetting = new SmartCampaignSetting([\n // Sets a temporary ID in the campaign setting's resource name to associate it with\n // the campaign created in the previous step.\n 'resource_name' => ResourceNames::forSmartCampaignSetting(\n $customerId,\n self::SMART_CAMPAIGN_TEMPORARY_ID\n ),\n // Below we configure the SmartCampaignSetting using many of the same details used to\n // generate a budget suggestion.\n 'phone_number' => new PhoneNumber([\n 'country_code' => self::COUNTRY_CODE,\n 'phone_number' => self::PHONE_NUMBER\n ]),\n 'final_url' => self::LANDING_PAGE_URL,\n 'advertising_language_code' => self::LANGUAGE_CODE,\n ]);\n\n // It's required that either a business profile location resource name or a business name is\n // added to the SmartCampaignSetting.\n if ($businessProfileLocationResourceName) {\n $smartCampaignSetting->setBusinessProfileLocation($businessProfileLocationResourceName);\n } else {\n $smartCampaignSetting->setBusinessName($businessName);\n }\n\n // Creates the MutateOperation that creates the smart campaign setting with an update.\n return new MutateOperation([\n 'smart_campaign_setting_operation' => new SmartCampaignSettingOperation([\n 'update' => $smartCampaignSetting,\n // Sets the update mask on the operation. This is required since the smart campaign\n // setting is created in an UPDATE operation. Here the update mask will be a list\n // of all the fields that were set on the SmartCampaignSetting.\n 'update_mask' => FieldMasks::allSetFieldsOf($smartCampaignSetting)\n ])\n ]);\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef create_smart_campaign_setting_operation(\n client: GoogleAdsClient,\n customer_id: str,\n business_profile_location: Optional[str],\n business_name: Optional[str],\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation to create a new SmartCampaignSetting.\n\n SmartCampaignSettings are unique in that they only support UPDATE\n operations, which are used to update and create them. Below we will\n use a temporary ID in the resource name to associate it with the\n campaign created in the previous step.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n business_profile_location: the resource name of a Business Profile\n location.\n business_name: the name of a Business Profile.\n\n Returns:\n a MutateOperation that creates a SmartCampaignSetting.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n smart_campaign_setting_operation: SmartCampaignSettingOperation = (\n mutate_operation.smart_campaign_setting_operation\n )\n smart_campaign_setting: SmartCampaignSetting = (\n smart_campaign_setting_operation.update\n )\n # Set a temporary ID in the campaign setting's resource name to associate it\n # with the campaign created in the previous step.\n smart_campaign_setting.resource_name = client.get_service(\n \"SmartCampaignSettingService\"\n ).smart_campaign_setting_path(customer_id, _SMART_CAMPAIGN_TEMPORARY_ID)\n\n # Below we configure the SmartCampaignSetting using many of the same\n # details used to generate a budget suggestion.\n smart_campaign_setting.phone_number.country_code = _COUNTRY_CODE\n smart_campaign_setting.phone_number.phone_number = _PHONE_NUMBER\n smart_campaign_setting.final_url = _LANDING_PAGE_URL\n smart_campaign_setting.advertising_language_code = _LANGUAGE_CODE\n\n # Set either of the business_profile_location or business_name, depending on\n # whichever is provided.\n if business_profile_location:\n smart_campaign_setting.business_profile_location = (\n business_profile_location\n )\n else:\n smart_campaign_setting.business_name = business_name\n\n # Set the update mask on the operation. This is required since the smart\n # campaign setting is created in an UPDATE operation. Here the update\n # mask will be a list of all the fields that were set on the\n # SmartCampaignSetting.\n client.copy_from(\n smart_campaign_setting_operation.update_mask,\n protobuf_helpers.field_mask(None, smart_campaign_setting._pb),\n )\n\n return mutate_operationadd_smart_campaign.py\n```\n\nExample:\n```text\n# Creates a mutate_operation to create a new smart_campaign_setting.\n# smart_campaign_settings are unique in that they only support UPDATE\n# operations, which are used to update and create them. Below we will\n# use a temporary ID in the resource name to associate it with the\n# campaign created in the previous step.\ndef create_smart_campaign_setting_operation(\n client,\n customer_id,\n business_profile_location,\n business_name)\n mutate_operation = client.operation.mutate do |m|\n m.smart_campaign_setting_operation =\n client.operation.update_resource.smart_campaign_setting(\n # Sets a temporary ID in the campaign setting's resource name to\n # associate it with the campaign created in the previous step.\n client.path.smart_campaign_setting(\n customer_id, SMART_CAMPAIGN_TEMPORARY_ID)\n ) do |scs|\n # Below we configure the smart_campaign_setting using many of the same\n # details used to generate a budget suggestion.\n scs.phone_number = client.resource.phone_number do |p|\n p.country_code = COUNTRY_CODE\n p.phone_number = PHONE_NUMBER\n end\n scs.final_url = LANDING_PAGE_URL\n scs.advertising_language_code = LANGUAGE_CODE\n # It's required that either a business location ID or a business name is\n # added to the smart_campaign_setting.\n if business_profile_location\n scs.business_profile_location = business_profile_location\n else\n scs.business_name = business_name\n end\n end\n end\n\n mutate_operation\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Creates a MutateOperation to create a new SmartCampaignSetting.\n# SmartCampaignSettings are unique in that they only support UPDATE operations,\n# which are used to update and create them. Below we will use a temporary ID in\n# the resource name to associate it with the campaign created in the previous step.\nsub _create_smart_campaign_setting_operation {\n my ($customer_id, $business_profile_location, $business_name) = @_;\n\n my $smart_campaign_setting =\n Google::Ads::GoogleAds::V25::Resources::SmartCampaignSetting->new({\n # Set a temporary ID in the campaign setting's resource name to associate it\n # with the campaign created in the previous step.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::smart_campaign_setting(\n $customer_id, SMART_CAMPAIGN_TEMPORARY_ID\n ),\n # Below we configure the SmartCampaignSetting using many of the same\n # details used to generate a budget suggestion.\n phoneNumber => Google::Ads::GoogleAds::V25::Resources::PhoneNumber->new({\n countryCode => COUNTRY_CODE,\n phoneNumber => PHONE_NUMBER\n }\n ),\n finalUrl => LANDING_PAGE_URL,\n advertisingLanguageCode => LANGUAGE_CODE\n });\n\n # It's required that either a business profile location or a business name is\n # added to the SmartCampaignSetting.\n if (defined $business_profile_location) {\n $smart_campaign_setting->{businessProfileLocation} =\n $business_profile_location;\n } else {\n $smart_campaign_setting->{businessName} = $business_name;\n }\n\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n smartCampaignSettingOperation =>\n Google::Ads::GoogleAds::V25::Services::SmartCampaignSettingService::SmartCampaignSettingOperation\n ->new({\n update => $smart_campaign_setting,\n # Set the update mask on the operation. This is required since the\n # smart campaign setting is created in an UPDATE operation. Here the\n # update mask will be a list of all the fields that were set on the\n # SmartCampaignSetting.\n updateMask => all_set_fields_of($smart_campaign_setting)})});\n}add_smart_campaign.pl\n```\n\nExample:\n```text\nSmartCampaignSetting smartCampaignSetting =\n SmartCampaignSetting.newBuilder()\n .setBusinessProfileLocation(businessProfileLocation)\n // Sets the ad optimized business profile setting to an empty\n // instance of AdOptimizedBusinessProfileSetting.\n .setAdOptimizedBusinessProfileSetting(\n AdOptimizedBusinessProfileSetting.newBuilder().build())\n .build();\n```\n\nExample:\n```text\nSmartCampaignSetting smartCampaignSetting = new SmartCampaignSetting()\n{\n BusinessProfileLocation = businessProfileLocation,\n /// Sets the ad optimized business profile setting to an empty\n /// instance of AdOptimizedBusinessProfileSetting.\n AdOptimizedBusinessProfileSetting =\n new SmartCampaignSetting.Types.AdOptimizedBusinessProfileSetting()\n};\n```\n\nExample:\n```text\n$smartCampaignSetting = new SmartCampaignSetting([\n 'business_profile_location' => business_profile_location,\n // Sets the ad optimized business profile setting to an empty instance\n // of AdOptimizedBusinessProfileSetting.\n 'ad_optimized_business_profile_setting' => new AdOptimizedBusinessProfileSetting(),\n]);\n```\n\nExample:\n```text\nsmart_campaign_setting = client.get_type(\"SmartCampaignSetting\")\nsmart_campaign_setting.business_profile_location = business_profile_location\n# Sets the ad optimized business profile setting to an empty instance of\n# AdOptimizedBusinessProfileSetting.\nclient.copy_from(\n smart_campaign_setting.ad_optimized_business_profile_setting,\n client.get_type(\"AdOptimizedBusinessProfileSetting\")\n)\n```\n\nExample:\n```text\nsmart_campaign_setting = client.resource.smart_campaign_setting do |s|\n s.business_profile_location = business_profile_location\n # Sets the ad optimized business profile setting to an empty instance of\n # AdOptimizedBusinessProfileSetting.\n s.ad_optimized_business_profile_setting = client.resource.ad_optimized_business_profile_setting\nend\n```\n\nExample:\n```text\nmy $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n businessProfileLocation => $business_profile_location,\n # Sets the ad optimized business profile setting to an empty instance of\n # AdOptimizedBusinessProfileSetting.\n adOptimizedBusinessProfileSetting =>\n Google::Ads::GoogleAds::V25::Common::AdOptimizedBusinessProfileSetting->new()\n});\n```\n\nExample:\n```text\nSmartCampaignSetting smartCampaignSetting =\n SmartCampaignSetting.newBuilder()\n .setBusinessProfileLocation(businessProfileLocation)\n // Sets the AdOptimizedBusinessProfileSetting.include_lead_form field to true.\n .setAdOptimizedBusinessProfileSetting(\n AdOptimizedBusinessProfileSetting.newBuilder().setIncludeLeadForm(true).build())\n .build();\n```\n\nExample:\n```text\nSmartCampaignSetting smartCampaignSetting = new SmartCampaignSetting()\n{\n BusinessProfileLocation = businessProfileLocation,\n /// Sets the AdOptimizedBusinessProfileSetting.include_lead_form\n /// field to true.\n AdOptimizedBusinessProfileSetting =\n new SmartCampaignSetting.Types.AdOptimizedBusinessProfileSetting\n {\n IncludeLeadForm = true\n }\n};\n```\n\nExample:\n```text\n$smartCampaignSetting = new SmartCampaignSetting([\n 'business_profile_location' => business_profile_location,\n // Sets the AdOptimizedBusinessProfileSetting.include_lead_form field\n // to true.\n 'ad_optimized_business_profile_setting' => new AdOptimizedBusinessProfileSetting([\n 'include_lead_form' => true\n ]),\n]);\n```\n\nExample:\n```text\nsmart_campaign_setting = client.get_type(\"SmartCampaignSetting\")\nsmart_campaign_setting.business_profile_location = business_profile_location\n# Sets the AdOptimizedBusinessProfileSetting.include_lead_form field to\n# true.\nsmart_campaign_setting.ad_optimized_business_profile_setting.include_lead_form = True\n```\n\nExample:\n```text\nsmart_campaign_setting = client.resource.smart_campaign_setting do |s|\n s.business_profile_location = business_profile_location\n # Sets the AdOptimizedBusinessProfileSetting.include_lead_form field to\n # true.\n s.ad_optimized_business_profile_setting = client.resource.ad_optimized_business_profile_setting do |a|\n a.include_lead_form = true\n end\nend\n```\n\nExample:\n```text\nmy $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n businessProfileLocation => $business_profile_location,\n # Sets the AdOptimizedBusinessProfileSetting.include_lead_form field to\n # true.\n adOptimizedBusinessProfileSetting =>\n Google::Ads::GoogleAds::V25::Common::AdOptimizedBusinessProfileSetting->new({\n includeLeadForm => \"true\"\n })\n});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.479Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":24,"totalLines":659,"estimatedTokens":6776}}187{"id":"doc-manage_offline_conversions_google_ads_api_google-353b7baa","source":"documentation","title":"Manage offline conversions | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/conversions/upload-offline","text":"Example:\n```text\nSELECT\n customer.id,\n customer.conversion_tracking_setting.accepted_customer_data_terms,\n customer.conversion_tracking_setting.enhanced_conversions_for_leads_enabled\nFROM customer\n```\n\nExample:\n```text\nSELECT\n customer.id,\n conversion_action.id,\n conversion_action.name,\n conversion_action.type,\n conversion_action.resource_name\nFROM conversion_action\nWHERE conversion_action.type = 'UPLOAD_CLICKS'\n AND conversion_action.status = 'ENABLED'\n```\n\nExample:\n```text\nprivate String normalizeAndHash(MessageDigest digest, String s)\n throws UnsupportedEncodingException {\n // Normalizes by first converting all characters to lowercase, then trimming spaces.\n String normalized = s.toLowerCase();\n // Removes leading, trailing, and intermediate spaces.\n normalized = normalized.replaceAll(\"\\\\s+\", \"\");\n // Hashes the normalized string using the hashing algorithm.\n byte[] hash = digest.digest(normalized.getBytes(\"UTF-8\"));\n StringBuilder result = new StringBuilder();\n for (byte b : hash) {\n result.append(String.format(\"%02x\", b));\n }\n\n return result.toString();\n}\n\n/**\n * Returns the result of normalizing and hashing an email address. For this use case, Google Ads\n * requires removal of any '.' characters preceding {@code gmail.com} or {@code googlemail.com}.\n *\n * @param digest the digest to use to hash the normalized string.\n * @param emailAddress the email address to normalize and hash.\n */\nprivate String normalizeAndHashEmailAddress(MessageDigest digest, String emailAddress)\n throws UnsupportedEncodingException {\n String normalizedEmail = emailAddress.toLowerCase();\n String[] emailParts = normalizedEmail.split(\"@\");\n if (emailParts.length > 1 && emailParts[1].matches(\"^(gmail|googlemail)\\\\.com\\\\s*\")) {\n // Removes any '.' characters from the portion of the email address before the domain if the\n // domain is gmail.com or googlemail.com.\n emailParts[0] = emailParts[0].replaceAll(\"\\\\.\", \"\");\n normalizedEmail = String.format(\"%s@%s\", emailParts[0], emailParts[1]);\n }\n return normalizeAndHash(digest, normalizedEmail);\n}UploadEnhancedConversionsForLeads.java\n```\n\nExample:\n```text\n/// <summary>\n/// Normalizes the email address and hashes it. For this use case, Google Ads requires\n/// removal of any '.' characters preceding <code>gmail.com</code> or\n/// <code>googlemail.com</code>.\n/// </summary>\n/// <param name=\"emailAddress\">The email address.</param>\n/// <returns>The hash code.</returns>\nprivate string NormalizeAndHashEmailAddress(string emailAddress)\n{\n string normalizedEmail = emailAddress.ToLower();\n string[] emailParts = normalizedEmail.Split('@');\n if (emailParts.Length > 1 && (emailParts[1] == \"gmail.com\" ||\n emailParts[1] == \"googlemail.com\"))\n {\n // Removes any '.' characters from the portion of the email address before\n // the domain if the domain is gmail.com or googlemail.com.\n emailParts[0] = emailParts[0].Replace(\".\", \"\");\n normalizedEmail = $\"{emailParts[0]}@{emailParts[1]}\";\n }\n return NormalizeAndHash(normalizedEmail);\n}\n\n/// <summary>\n/// Normalizes and hashes a string value.\n/// </summary>\n/// <param name=\"value\">The value to normalize and hash.</param>\n/// <returns>The normalized and hashed value.</returns>\nprivate static string NormalizeAndHash(string value)\n{\n return ToSha256String(digest, ToNormalizedValue(value));\n}\n\n/// <summary>\n/// Hash a string value using SHA-256 hashing algorithm.\n/// </summary>\n/// <param name=\"digest\">Provides the algorithm for SHA-256.</param>\n/// <param name=\"value\">The string value (e.g. an email address) to hash.</param>\n/// <returns>The hashed value.</returns>\nprivate static string ToSha256String(SHA256 digest, string value)\n{\n byte[] digestBytes = digest.ComputeHash(Encoding.UTF8.GetBytes(value));\n // Convert the byte array into an unhyphenated hexadecimal string.\n return BitConverter.ToString(digestBytes).Replace(\"-\", string.Empty);\n}\n\n/// <summary>\n/// Removes leading and trailing whitespace and converts all characters to\n/// lower case.\n/// </summary>\n/// <param name=\"value\">The value to normalize.</param>\n/// <returns>The normalized value.</returns>\nprivate static string ToNormalizedValue(string value)\n{\n return value.Trim().ToLower();\n}UploadEnhancedConversionsForLeads.cs\n```\n\nExample:\n```text\nprivate static function normalizeAndHash(string $hashAlgorithm, string $value): string\n{\n // Normalizes by first converting all characters to lowercase, then trimming spaces.\n $normalized = strtolower($value);\n // Removes leading, trailing, and intermediate spaces.\n $normalized = str_replace(' ', '', $normalized);\n return hash($hashAlgorithm, strtolower(trim($normalized)));\n}\n\n/**\n * Returns the result of normalizing and hashing an email address. For this use case, Google\n * Ads requires removal of any '.' characters preceding \"gmail.com\" or \"googlemail.com\".\n *\n * @param string $hashAlgorithm the hash algorithm to use\n * @param string $emailAddress the email address to normalize and hash\n * @return string the normalized and hashed email address\n */\nprivate static function normalizeAndHashEmailAddress(\n string $hashAlgorithm,\n string $emailAddress\n): string {\n $normalizedEmail = strtolower($emailAddress);\n $emailParts = explode(\"@\", $normalizedEmail);\n if (\n count($emailParts) > 1\n && preg_match('/^(gmail|googlemail)\\.com\\s*/', $emailParts[1])\n ) {\n // Removes any '.' characters from the portion of the email address before the domain\n // if the domain is gmail.com or googlemail.com.\n $emailParts[0] = str_replace(\".\", \"\", $emailParts[0]);\n $normalizedEmail = sprintf('%s@%s', $emailParts[0], $emailParts[1]);\n }\n return self::normalizeAndHash($hashAlgorithm, $normalizedEmail);\n}UploadEnhancedConversionsForLeads.php\n```\n\nExample:\n```text\ndef normalize_and_hash_email_address(email_address: str) -> str:\n \"\"\"Returns the result of normalizing and hashing an email address.\n\n For this use case, Google Ads requires removal of any '.' characters\n preceding \"gmail.com\" or \"googlemail.com\"\n\n Args:\n email_address: An email address to normalize.\n\n Returns:\n A normalized (lowercase, removed whitespace) and SHA-265 hashed string.\n \"\"\"\n normalized_email: str = email_address.strip().lower()\n email_parts: list[str] = normalized_email.split(\"@\")\n\n # Check that there are at least two segments\n if len(email_parts) > 1:\n # Removes any '.' and '+' characters from the portion of the email address\n # before the domain\n chars_to_remove = \".+\"\n translation_table = str.maketrans(\"\", \"\", chars_to_remove)\n email_parts[0] = email_parts[0].translate(translation_table)\n normalized_email = \"@\".join(email_parts)\n\n return normalize_and_hash(normalized_email)\n\n\ndef normalize_and_hash(s: str) -> str:\n \"\"\"Normalizes and hashes a string with SHA-256.\n\n Private customer data must be hashed during upload, as described at:\n https://support.google.com/google-ads/answer/7474263\n\n Args:\n s: The string to perform this operation on.\n\n Returns:\n A normalized (lowercase, removed whitespace) and SHA-256 hashed string.\n \"\"\"\n return hashlib.sha256(s.strip().lower().encode()).hexdigest()upload_enhanced_conversions_for_leads.py\n```\n\nExample:\n```text\n# Returns the result of normalizing and then hashing the string using the\n# provided digest. Private customer data must be hashed during upload, as\n# described at https://support.google.com/google-ads/answer/7474263.\ndef normalize_and_hash(str)\n # Remove leading and trailing whitespace and ensure all letters are lowercase\n # before hashing.\n Digest::SHA256.hexdigest(str.strip.downcase)\nend\n\n# Returns the result of normalizing and hashing an email address. For this use\n# case, Google Ads requires removal of any '.' characters preceding 'gmail.com'\n# or 'googlemail.com'.\ndef normalize_and_hash_email(email)\n email_parts = email.downcase.split(\"@\")\n # Removes any '.' characters from the portion of the email address before the\n # domain if the domain is gmail.com or googlemail.com.\n if email_parts.last =~ /^(gmail|googlemail)\\.com\\s*/\n email_parts[0] = email_parts[0].gsub('.', '')\n end\n normalize_and_hash(email_parts.join('@'))\nendupload_enhanced_conversions_for_leads.rb\n```\n\nExample:\n```text\nsub normalize_and_hash {\n my $value = shift;\n\n # Removes leading, trailing, and intermediate spaces.\n $value =~ s/\\s+//g;\n return sha256_hex(lc $value);\n}\n\n# Returns the result of normalizing and hashing an email address. For this use\n# case, Google Ads requires removal of any '.' characters preceding 'gmail.com'\n# or 'googlemail.com'.\nsub normalize_and_hash_email_address {\n my $email_address = shift;\n\n my $normalized_email = lc $email_address;\n my @email_parts = split('@', $normalized_email);\n if (scalar @email_parts > 1\n && $email_parts[1] =~ /^(gmail|googlemail)\\.com\\s*/)\n {\n # Remove any '.' characters from the portion of the email address before the\n # domain if the domain is 'gmail.com' or 'googlemail.com'.\n $email_parts[0] =~ s/\\.//g;\n $normalized_email = sprintf '%s@%s', $email_parts[0], $email_parts[1];\n }\n return normalize_and_hash($normalized_email);\n}upload_enhanced_conversions_for_leads.pl\n```\n\nExample:\n```text\n// Sets one of the sessionAttributesEncoded or sessionAttributesKeyValuePairs if either is\n// provided. The session attribute fields are only available to allowlisted users.\n// To include these fields in conversion imports, upgrade to the Data Manager API.\nif (rawRecord.containsKey(\"sessionAttributesEncoded\")) {\n clickConversionBuilder.setSessionAttributesEncoded(\n ByteString.copyFromUtf8(rawRecord.get(\"sessionAttributesEncoded\")));\n} else if (rawRecord.containsKey(\"sessionAttributesMap\")) {\n List<String> pairings =\n Arrays.stream(rawRecord.get(\"sessionAttributesMap\").split(\" \"))\n .map(String::trim)\n .collect(Collectors.toList());\n SessionAttributesKeyValuePairs.Builder sessionAttributePairs =\n SessionAttributesKeyValuePairs.newBuilder();\n for (String pair : pairings) {\n String[] parts = pair.split(\"=\", 2);\n if (parts.length != 2) {\n throw new IllegalArgumentException(\n \"Failed to read the sessionAttributesMap. SessionAttributesMap must use a \"\n + \"space-delimited list of session attribute key value pairs. Each pair should be\"\n + \" separated by an equal sign, for example: 'gad_campaignid=12345 gad_source=1'\");\n }\n sessionAttributePairs.addKeyValuePairs(\n SessionAttributeKeyValuePair.newBuilder()\n .setSessionAttributeKey(parts[0])\n .setSessionAttributeValue(parts[1])\n .build());\n }\n clickConversionBuilder.setSessionAttributesKeyValuePairs(sessionAttributePairs.build());\n}UploadEnhancedConversionsForLeads.java\n```\n\nExample:\n```text\n// The session attribute fields are only available to allowlisted users. To\n// include these fields in conversion imports, upgrade to the Data Manager API.\nif (!string.IsNullOrEmpty(sessionAttributesEncoded))\n{\n clickConversion.SessionAttributesEncoded =\n ByteString.CopyFrom(sessionAttributesEncoded, Encoding.Unicode);\n}\nelse if (!string.IsNullOrEmpty(sessionAttributes))\n{\n IEnumerable<SessionAttributeKeyValuePair> parsedSessionAttributes =\n sessionAttributes.Split(';').Select(pair => {\n string[] split = pair.Split('=');\n return new SessionAttributeKeyValuePair()\n {\n SessionAttributeKey = split[0],\n SessionAttributeValue = split[1]\n };\n });\n\n clickConversion.SessionAttributesKeyValuePairs =\n new SessionAttributesKeyValuePairs();\n clickConversion.SessionAttributesKeyValuePairs.KeyValuePairs\n .AddRange(parsedSessionAttributes);\n}UploadEnhancedConversionsForLeads.cs\n```\n\nExample:\n```text\nThis example is not yet available in PHP; you can take a look at the other languages.\n```\n\nExample:\n```text\n# Set one of the session_attributes_encoded or\n# session_attributes_key_value_pairs fields if either are provided.\n# The session attribute fields are only available to allowlisted users.\n# To include these fields in conversion imports, upgrade to the Data Manager API.\nif session_attributes_encoded:\n click_conversion.session_attributes_encoded = session_attributes_encoded\nelif session_attributes_dict:\n for key, value in session_attributes_dict.items():\n pair: SessionAttributeKeyValuePair = client.get_type(\n \"SessionAttributeKeyValuePair\"\n )\n pair.session_attribute_key = key\n pair.session_attribute_value = value\n click_conversion.session_attributes_key_value_pairs.key_value_pairs.append(\n pair\n )upload_enhanced_conversions_for_leads.py\n```\n\nExample:\n```text\n# Set one of the session_attributes_encoded or\n# session_attributes_key_value_pairs fields if either are provided.\n# The session attribute fields are only available to allowlisted users.\n# To include these fields in conversion imports, upgrade to the Data Manager API.\nif session_attributes_encoded != nil\n cc.class.module_eval { attr_accessor :session_attributes_encoded}\n cc.session_attributes_encoded = session_attributes_encoded\nelsif session_attributes_hash != nil\n # Add new attribute to click conversion object\n cc.class.module_eval { attr_accessor :session_attributes_key_value_pairs}\n cc.session_attributes_key_value_pairs = ::Google::Ads::GoogleAds::V19::Services::SessionAttributesKeyValuePairs.new\n\n # Loop thru inputted session_attributes_hash to populate session_attributes_key_value_pairs\n session_attributes_hash.each do |key, value|\n pair = ::Google::Ads::GoogleAds::V19::Services::SessionAttributeKeyValuePair.new\n pair.session_attribute_key = key\n pair.session_attribute_value = value\n cc.session_attributes_key_value_pairs.key_value_pairs << pair\n end\nend upload_enhanced_conversions_for_leads.rb\n```\n\nExample:\n```text\n# Set one of the session_attributes_encoded or session_attributes_key_value_pairs\n# fields if either are provided.\nif (defined $session_attributes_encoded) {\n $click_conversion->{sessionAttributesEncoded} = $session_attributes_encoded;\n} elsif (defined $session_attributes_hash) {\n while (my ($key, $value) = each %$session_attributes_hash) {\n my $pair =\n Google::Ads::GoogleAds::V25::Services::ConversionUploadService::SessionAttributeKeyValuePair\n ->new({sessionAttributeKey => $key, sessionAttributeValue => $value});\n push @{$click_conversion->{sessionAttributesKeyValuePairs}{keyValuePairs}\n }, $pair;\n }\n}upload_enhanced_conversions_for_leads.pl\n```\n\nExample:\n```text\n// Creates an empty builder for constructing the click conversion.\nClickConversion.Builder clickConversionBuilder = ClickConversion.newBuilder();\n\n// Extracts user email and phone from the raw data, normalizes and hashes it, then wraps it in\n// UserIdentifier objects.\n// Creates a separate UserIdentifier object for each. The data in this example is hardcoded, but\n// in your application you might read the raw data from an input file.\n\n// IMPORTANT: Since the identifier attribute of UserIdentifier\n// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a\n// oneof\n// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE of\n// hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting more\n// than one of these attributes on the same UserIdentifier will clear all the other members\n// of the oneof. For example, the following code is INCORRECT and will result in a\n// UserIdentifier with ONLY a hashedPhoneNumber.\n//\n// UserIdentifier incorrectlyPopulatedUserIdentifier =\n// UserIdentifier.newBuilder()\n// .setHashedEmail(\"...\")\n// .setHashedPhoneNumber(\"...\")\n// .build();\n\nImmutableMap.Builder<String, String> rawRecordBuilder =\n ImmutableMap.<String, String>builder()\n .put(\"email\", \"alex.2@example.com\")\n // Phone number to be converted to E.164 format, with a leading '+' as required.\n .put(\"phone\", \"+1 800 5550102\")\n // This example lets you put conversion details as arguments, but in reality you might\n // store this data alongside other user data, so we include it in this sample user\n // record.\n .put(\"conversionActionId\", Long.toString(conversionActionId))\n .put(\"conversionDateTime\", conversionDateTime)\n .put(\"conversionValue\", Double.toString(conversionValue))\n .put(\"currencyCode\", \"USD\");\n\n// Adds entries for the optional fields.\nif (orderId != null) {\n rawRecordBuilder.put(\"orderId\", orderId);\n}\nif (gclid != null) {\n rawRecordBuilder.put(\"gclid\", gclid);\n}\nif (adUserDataConsent != null) {\n rawRecordBuilder.put(\"adUserDataConsent\", adUserDataConsent.name());\n}\nif (sessionAttributesEncoded != null) {\n rawRecordBuilder.put(\"sessionAttributesEncoded\", sessionAttributesEncoded);\n}\nif (sessionAttributesMap != null) {\n rawRecordBuilder.put(\"sessionAttributesMap\", sessionAttributesMap);\n}\n\n// Builds the map representing the record.\nMap<String, String> rawRecord = rawRecordBuilder.build();\n\n// Creates a SHA256 message digest for hashing user identifiers in a privacy-safe way, as\n// described at https://support.google.com/google-ads/answer/9888656.\nMessageDigest sha256Digest = MessageDigest.getInstance(\"SHA-256\");\n\n// Creates a list for the user identifiers.\nList<UserIdentifier> userIdentifiers = new ArrayList<>();\n\n// Creates a user identifier using the hashed email address, using the normalize and hash method\n// specifically for email addresses.\nUserIdentifier emailIdentifier =\n UserIdentifier.newBuilder()\n // Optional: specify the user identifier source.\n .setUserIdentifierSource(UserIdentifierSource.FIRST_PARTY)\n // Uses the normalize and hash method specifically for email addresses.\n .setHashedEmail(normalizeAndHashEmailAddress(sha256Digest, rawRecord.get(\"email\")))\n .build();\nuserIdentifiers.add(emailIdentifier);\n\n// Creates a user identifier using normalized and hashed phone info.\nUserIdentifier hashedPhoneNumberIdentifier =\n UserIdentifier.newBuilder()\n .setHashedPhoneNumber(normalizeAndHash(sha256Digest, rawRecord.get(\"phone\")))\n .build();\n// Adds the hashed phone number identifier to the UserData object's list.\nuserIdentifiers.add(hashedPhoneNumberIdentifier);\n\n// Adds the user identifiers to the conversion.\nclickConversionBuilder.addAllUserIdentifiers(userIdentifiers);UploadEnhancedConversionsForLeads.java\n```\n\nExample:\n```text\n// Adds a user identifier using the hashed email address, using the normalize\n// and hash method specifically for email addresses.\nclickConversion.UserIdentifiers.Add(new UserIdentifier()\n{\n HashedEmail = NormalizeAndHashEmailAddress(\"alex.2@example.com\"),\n // Optional: Specifies the user identifier source.\n UserIdentifierSource = UserIdentifierSource.FirstParty\n});\n\n// Adds a user identifier using normalized and hashed phone info.\nclickConversion.UserIdentifiers.Add(new UserIdentifier()\n{\n HashedPhoneNumber = NormalizeAndHash(\"+1 800 5550102\"),\n // Optional: Specifies the user identifier source.\n UserIdentifierSource = UserIdentifierSource.FirstParty\n});\n\n// Adds a user identifier with all the required mailing address elements.\nclickConversion.UserIdentifiers.Add(new UserIdentifier()\n{\n AddressInfo = new OfflineUserAddressInfo()\n {\n // FirstName and LastName must be normalized and hashed.\n HashedFirstName = NormalizeAndHash(\"Alex\"),\n HashedLastName = NormalizeAndHash(\"Quinn\"),\n // CountryCode and PostalCode are sent in plain text.\n CountryCode = \"US\",\n PostalCode = \"94045\"\n }\n});UploadEnhancedConversionsForLeads.cs\n```\n\nExample:\n```text\n// Creates a click conversion with the specified attributes.\n$clickConversion = new ClickConversion();\n\n// Extract user email and phone from the raw data, normalize and hash it, then wrap it in\n// UserIdentifier objects. Creates a separate UserIdentifier object for each.\n// The data in this example is hardcoded, but in your application you might read the raw\n// data from an input file.\n\n// IMPORTANT: Since the identifier attribute of UserIdentifier\n// (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a\n// oneof\n// (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE\n// of hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting\n// more than one of these attributes on the same UserIdentifier will clear all the other\n// members of the oneof. For example, the following code is INCORRECT and will result in a\n// UserIdentifier with ONLY a hashedPhoneNumber.\n//\n// $incorrectlyPopulatedUserIdentifier = new UserIdentifier([\n// 'hashed_email' => '...',\n// 'hashed_phone_number' => '...'\n// ]);\n\n$rawRecord = [\n // Email address that includes a period (.) before the Gmail domain.\n 'email' => 'alex.2@example.com',\n // Phone number to be converted to E.164 format, with a leading '+' as required.\n 'phone' => '+1 800 5550102',\n // This example lets you input conversion details as arguments, but in reality you might\n // store this data alongside other user data, so we include it in this sample user\n // record.\n 'orderId' => $orderId,\n 'gclid' => $gclid,\n 'conversionActionId' => $conversionActionId,\n 'conversionDateTime' => $conversionDateTime,\n 'conversionValue' => $conversionValue,\n 'currencyCode' => 'USD',\n 'adUserDataConsent' => $adUserDataConsent,\n 'sessionAttributesEncoded' => $sessionAttributesEncoded,\n 'sessionAttributesDict' => $sessionAttributesDict\n];\n\n// Creates a list for the user identifiers.\n$userIdentifiers = [];\n\n// Uses the SHA-256 hash algorithm for hashing user identifiers in a privacy-safe way, as\n// described at https://support.google.com/google-ads/answer/9888656.\n$hashAlgorithm = \"sha256\";\n\n// Creates a user identifier using the hashed email address, using the normalize and hash\n// method specifically for email addresses.\n$emailIdentifier = new UserIdentifier([\n // Uses the normalize and hash method specifically for email addresses.\n 'hashed_email' => self::normalizeAndHashEmailAddress(\n $hashAlgorithm,\n $rawRecord['email']\n ),\n // Optional: Specifies the user identifier source.\n 'user_identifier_source' => UserIdentifierSource::FIRST_PARTY\n]);\n$userIdentifiers[] = $emailIdentifier;\n\n// Checks if the record has a phone number, and if so, adds a UserIdentifier for it.\nif (array_key_exists('phone', $rawRecord)) {\n $hashedPhoneNumberIdentifier = new UserIdentifier([\n 'hashed_phone_number' => self::normalizeAndHash(\n $hashAlgorithm,\n $rawRecord['phone'],\n true\n )\n ]);\n // Adds the hashed email identifier to the user identifiers list.\n $userIdentifiers[] = $hashedPhoneNumberIdentifier;\n}\n\n// Adds the user identifiers to the conversion.\n$clickConversion->setUserIdentifiers($userIdentifiers);UploadEnhancedConversionsForLeads.php\n```\n\nExample:\n```text\n# Extract user email and phone from the raw data, normalize and hash it,\n# then wrap it in UserIdentifier objects. Create a separate UserIdentifier\n# object for each. The data in this example is hardcoded, but in your\n# application you might read the raw data from an input file.\n\n# IMPORTANT: Since the identifier attribute of UserIdentifier\n# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)\n# is a oneof\n# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must\n# set only ONE of hashed_email, hashed_phone_number, mobile_id,\n# third_party_user_id, or address_info. Setting more than one of these\n# attributes on the same UserIdentifier will clear all the other members of\n# the oneof. For example, the following code is INCORRECT and will result in\n# a UserIdentifier with ONLY a hashed_phone_number:\n#\n# incorrectly_populated_user_identifier = client.get_type(\"UserIdentifier\")\n# incorrectly_populated_user_identifier.hashed_email = \"...\"\"\n# incorrectly_populated_user_identifier.hashed_phone_number = \"...\"\"\n\nraw_record: Dict[str, Union[str, float]] = {\n # Email address that includes a period (.) before the Gmail domain.\n \"email\": \"alex.2@example.com\",\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required.\n \"phone\": \"+1 800 5550102\",\n # This example lets you input conversion details as arguments,\n # but in reality you might store this data alongside other user data,\n # so we include it in this sample user record.\n \"order_id\": order_id,\n \"gclid\": gclid,\n \"conversion_action_id\": conversion_action_id,\n \"conversion_date_time\": conversion_date_time,\n \"conversion_value\": conversion_value,\n \"currency_code\": \"USD\",\n \"ad_user_data_consent\": ad_user_data_consent,\n}\n\n# Constructs the click conversion.\nclick_conversion: ClickConversion = client.get_type(\"ClickConversion\")\n# Creates a user identifier using the hashed email address, using the\n# normalize and hash method specifically for email addresses.\nemail_identifier: UserIdentifier = client.get_type(\"UserIdentifier\")\n# Optional: Specifies the user identifier source.\nemail_identifier.user_identifier_source = (\n client.enums.UserIdentifierSourceEnum.FIRST_PARTY\n)\n# Uses the normalize and hash method specifically for email addresses.\nemail_identifier.hashed_email = normalize_and_hash_email_address(\n raw_record[\"email\"]\n)\n# Adds the user identifier to the conversion.\nclick_conversion.user_identifiers.append(email_identifier)\n\n# Checks if the record has a phone number, and if so, adds a UserIdentifier\n# for it.\nif raw_record.get(\"phone\") is not None:\n phone_identifier: UserIdentifier = client.get_type(\"UserIdentifier\")\n phone_identifier.hashed_phone_number = normalize_and_hash(\n raw_record[\"phone\"]\n )\n # Adds the phone identifier to the conversion adjustment.\n click_conversion.user_identifiers.append(phone_identifier)upload_enhanced_conversions_for_leads.py\n```\n\nExample:\n```text\n# Extract user email and phone from the raw data, normalize and hash it,\n# then wrap it in UserIdentifier objects. Create a separate UserIdentifier\n# object for each. The data in this example is hardcoded, but in your\n# application you might read the raw data from an input file.\n\n# IMPORTANT: Since the identifier attribute of UserIdentifier\n# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)\n# is a oneof\n# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must\n# set only ONE of hashed_email, hashed_phone_number, mobile_id,\n# third_party_user_id, or address_info. Setting more than one of these\n# attributes on the same UserIdentifier will clear all the other members of\n# the oneof. For example, the following code is INCORRECT and will result in\n# a UserIdentifier with ONLY a hashed_phone_number:\n#\n# incorrectly_populated_user_identifier.hashed_email = \"...\"\"\n# incorrectly_populated_user_identifier.hashed_phone_number = \"...\"\"\n\nraw_record = {\n # Email address that includes a period (.) before the Gmail domain.\n \"email\" => \"alex.2@example.com\",\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required.\n \"phone\" => \"+1 800 5550102\",\n # This example lets you input conversion details as arguments,\n # but in reality you might store this data alongside other user data,\n # so we include it in this sample user record.\n \"order_id\" => order_id,\n \"gclid\" => gclid,\n \"conversion_action_id\" => conversion_action_id,\n \"conversion_date_time\" => conversion_date_time,\n \"conversion_value\" => conversion_value,\n \"currency_code\" => \"USD\",\n \"ad_user_data_consent\" => ad_user_data_consent,\n \"session_attributes_encoded\" => session_attributes_encoded,\n \"session_attributes_hash\" => session_attributes_hash\n}\n\nclick_conversion = client.resource.click_conversion do |cc|\n cc.conversion_action = client.path.conversion_action(customer_id, conversion_action_id)\n cc.conversion_date_time = conversion_date_time\n cc.conversion_value = conversion_value.to_f\n cc.currency_code = 'USD'\n\n unless order_id.nil?\n cc.order_id = order_id\n end\n\n unless raw_record[\"gclid\"].nil?\n cc.gclid = gclid\n end\n\n # Specifies whether user consent was obtained for the data you are\n # uploading. For more details, see:\n # https://www.google.com/about/company/user-consent-policy\n unless raw_record[\"ad_user_data_consent\"].nil?\n cc.consent = client.resource.consent do |c|\n c.ad_user_data = ad_user_data_consent\n end\n end\n\n # Set one of the session_attributes_encoded or\n # session_attributes_key_value_pairs fields if either are provided.\n # The session attribute fields are only available to allowlisted users.\n # To include these fields in conversion imports, upgrade to the Data Manager API.\n if session_attributes_encoded != nil\n cc.class.module_eval { attr_accessor :session_attributes_encoded}\n cc.session_attributes_encoded = session_attributes_encoded\n elsif session_attributes_hash != nil\n # Add new attribute to click conversion object\n cc.class.module_eval { attr_accessor :session_attributes_key_value_pairs}\n cc.session_attributes_key_value_pairs = ::Google::Ads::GoogleAds::V19::Services::SessionAttributesKeyValuePairs.new\n\n # Loop thru inputted session_attributes_hash to populate session_attributes_key_value_pairs\n session_attributes_hash.each do |key, value|\n pair = ::Google::Ads::GoogleAds::V19::Services::SessionAttributeKeyValuePair.new\n pair.session_attribute_key = key\n pair.session_attribute_value = value\n cc.session_attributes_key_value_pairs.key_value_pairs << pair\n end\n end \n\n # Creates a user identifier using the hashed email address, using the\n # normalize and hash method specifically for email addresses.\n # If using a phone number, use the normalize_and_hash method instead.\n cc.user_identifiers << client.resource.user_identifier do |ui|\n ui.hashed_email = normalize_and_hash_email(raw_record[\"email\"])\n # Optional: Specifies the user identifier source.\n ui.user_identifier_source = :FIRST_PARTY\n end\n\n # Checks if the record has a phone number, and if so, adds a UserIdentifier\n # for it.\n unless raw_record[\"phone\"].nil?\n cc.user_identifiers << client.resource.user_identifier do |ui|\n ui.hashed_phone_number = normalize_and_hash(raw_record[\"phone\"])\n end\n end\nendupload_enhanced_conversions_for_leads.rb\n```\n\nExample:\n```text\n# Create an empty click conversion.\nmy $click_conversion =\n Google::Ads::GoogleAds::V25::Services::ConversionUploadService::ClickConversion\n ->new({});\n\n# Extract user email and phone from the raw data, normalize and hash it,\n# then wrap it in UserIdentifier objects. Create a separate UserIdentifier\n# object for each.\n# The data in this example is hardcoded, but in your application\n# you might read the raw data from an input file.\n#\n# IMPORTANT: Since the identifier attribute of UserIdentifier\n# (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)\n# is a oneof\n# (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set\n# only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id,\n# or address-info. Setting more than one of these attributes on the same UserIdentifier\n# will clear all the other members of the oneof. For example, the following code is\n# INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number:\n#\n# my $incorrect_user_identifier = Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n# hashedEmail => '...',\n# hashedPhoneNumber => '...',\n# });\nmy $raw_record = {\n # Email address that includes a period (.) before the Gmail domain.\n email => 'alex.2@example.com',\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required.\n phone => '+1 800 5550102',\n # This example lets you input conversion details as arguments,\n # but in reality you might store this data alongside other user data,\n # so we include it in this sample user record.\n orderId => $order_id,\n gclid => $gclid,\n conversionActionId => $conversion_action_id,\n conversionDateTime => $conversion_date_time,\n conversionValue => $conversion_value,\n currencyCode => \"USD\",\n adUserDataConsent => $ad_user_data_consent\n};\nmy $user_identifiers = [];\n\n# Create a user identifier using the hashed email address, using the normalize\n# and hash method specifically for email addresses.\nmy $hashed_email = normalize_and_hash_email_address($raw_record->{email});\npush(\n @$user_identifiers,\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n hashedEmail => $hashed_email,\n # Optional: Specify the user identifier source.\n userIdentifierSource => FIRST_PARTY\n }));\n\n# Create a user identifier using normalized and hashed phone info.\nmy $hashed_phone = normalize_and_hash($raw_record->{phone});\npush(\n @$user_identifiers,\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n hashedPhone => $hashed_phone,\n # Optional: Specify the user identifier source.\n userIdentifierSource => FIRST_PARTY\n }));\n\n# Add the user identifiers to the conversion.\n$click_conversion->{userIdentifiers} = $user_identifiers;upload_enhanced_conversions_for_leads.pl\n```\n\nExample:\n```text\n// Adds details of the conversion.\nclickConversionBuilder.setConversionAction(\n ResourceNames.conversionAction(\n customerId, Long.parseLong(rawRecord.get(\"conversionActionId\"))));\nclickConversionBuilder.setConversionDateTime(rawRecord.get(\"conversionDateTime\"));\nclickConversionBuilder.setConversionValue(Double.parseDouble(rawRecord.get(\"conversionValue\")));\nclickConversionBuilder.setCurrencyCode(rawRecord.get(\"currencyCode\"));\n\n// Sets the order ID if provided.\nif (rawRecord.containsKey(\"orderId\")) {\n clickConversionBuilder.setOrderId(rawRecord.get(\"orderId\"));\n}\n\n// Sets the Google click ID (gclid) if provided.\nif (rawRecord.containsKey(\"gclid\")) {\n clickConversionBuilder.setGclid(rawRecord.get(\"gclid\"));\n}\n\n// Sets the consent information, if provided.\nif (rawRecord.containsKey(\"adUserDataConsent\")) {\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy for details.\n clickConversionBuilder.setConsent(\n Consent.newBuilder()\n .setAdUserData(ConsentStatus.valueOf(rawRecord.get(\"adUserDataConsent\"))));\n}\n\n// Sets one of the sessionAttributesEncoded or sessionAttributesKeyValuePairs if either is\n// provided. The session attribute fields are only available to allowlisted users.\n// To include these fields in conversion imports, upgrade to the Data Manager API.\nif (rawRecord.containsKey(\"sessionAttributesEncoded\")) {\n clickConversionBuilder.setSessionAttributesEncoded(\n ByteString.copyFromUtf8(rawRecord.get(\"sessionAttributesEncoded\")));\n} else if (rawRecord.containsKey(\"sessionAttributesMap\")) {\n List<String> pairings =\n Arrays.stream(rawRecord.get(\"sessionAttributesMap\").split(\" \"))\n .map(String::trim)\n .collect(Collectors.toList());\n SessionAttributesKeyValuePairs.Builder sessionAttributePairs =\n SessionAttributesKeyValuePairs.newBuilder();\n for (String pair : pairings) {\n String[] parts = pair.split(\"=\", 2);\n if (parts.length != 2) {\n throw new IllegalArgumentException(\n \"Failed to read the sessionAttributesMap. SessionAttributesMap must use a \"\n + \"space-delimited list of session attribute key value pairs. Each pair should be\"\n + \" separated by an equal sign, for example: 'gad_campaignid=12345 gad_source=1'\");\n }\n sessionAttributePairs.addKeyValuePairs(\n SessionAttributeKeyValuePair.newBuilder()\n .setSessionAttributeKey(parts[0])\n .setSessionAttributeValue(parts[1])\n .build());\n }\n clickConversionBuilder.setSessionAttributesKeyValuePairs(sessionAttributePairs.build());\n}\n\n// Calls build to build the conversion.\nClickConversion clickConversion = clickConversionBuilder.build();UploadEnhancedConversionsForLeads.java\n```\n\nExample:\n```text\n// Adds details of the conversion.\nclickConversion.ConversionAction =\n ResourceNames.ConversionAction(customerId, conversionActionId);\nclickConversion.ConversionDateTime = conversionDateTime;\nclickConversion.ConversionValue = conversionValue;\nclickConversion.CurrencyCode = \"USD\";\n\n// Sets the order ID if provided.\nif (!string.IsNullOrEmpty(orderId))\n{\n clickConversion.OrderId = orderId;\n}\n\n// Sets the Google click ID (gclid) if provided.\nif (!string.IsNullOrEmpty(gclid))\n{\n clickConversion.Gclid = gclid;\n}\n\n// The session attribute fields are only available to allowlisted users. To\n// include these fields in conversion imports, upgrade to the Data Manager API.\nif (!string.IsNullOrEmpty(sessionAttributesEncoded))\n{\n clickConversion.SessionAttributesEncoded =\n ByteString.CopyFrom(sessionAttributesEncoded, Encoding.Unicode);\n}\nelse if (!string.IsNullOrEmpty(sessionAttributes))\n{\n IEnumerable<SessionAttributeKeyValuePair> parsedSessionAttributes =\n sessionAttributes.Split(';').Select(pair => {\n string[] split = pair.Split('=');\n return new SessionAttributeKeyValuePair()\n {\n SessionAttributeKey = split[0],\n SessionAttributeValue = split[1]\n };\n });\n\n clickConversion.SessionAttributesKeyValuePairs =\n new SessionAttributesKeyValuePairs();\n clickConversion.SessionAttributesKeyValuePairs.KeyValuePairs\n .AddRange(parsedSessionAttributes);\n}\nUploadEnhancedConversionsForLeads.cs\n```\n\nExample:\n```text\n// Adds details of the conversion.\n$clickConversion->setConversionAction(\n ResourceNames::forConversionAction($customerId, $rawRecord['conversionActionId'])\n);\n$clickConversion->setConversionDateTime($rawRecord['conversionDateTime']);\n$clickConversion->setConversionValue($rawRecord['conversionValue']);\n$clickConversion->setCurrencyCode($rawRecord['currencyCode']);\n\n// Sets the order ID if provided.\nif (!empty($rawRecord['orderId'])) {\n $clickConversion->setOrderId($rawRecord['orderId']);\n}\n\n// Sets the Google click ID (gclid) if provided.\nif (!empty($rawRecord['gclid'])) {\n $clickConversion->setGclid($rawRecord['gclid']);\n}\n\n// Sets the ad user data consent if provided.\nif (!empty($rawRecord['adUserDataConsent'])) {\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy for details.\n $clickConversion->setConsent(\n new Consent(['ad_user_data' => $rawRecord['adUserDataConsent']])\n );\n}\n\n// Set one of the sessionAttributesEncoded or\n// SessionAttributeKeyValuePair fields if either are provided. The session attribute\n// fields are only available to allowlisted users. To include these fields in conversion\n// imports, upgrade to the Data Manager API.\nif (!empty($sessionAttributesEncoded)) {\n $clickConversion->setSessionAttributesEncoded($sessionAttributesEncoded);\n} elseif (!empty($sessionAttributesDict)) {\n // Create a new container object to hold key-value pairs.\n $sessionAttributesKeyValuePairs = new SessionAttributesKeyValuePairs();\n // Initialize an array to hold individual key-value pair messages.\n $keyValuePairs = [];\n // Append each key-value pair provided to the $keyValuePairs array\n foreach ($sessionAttributesDict as $key => $value) {\n $pair = new SessionAttributeKeyValuePair();\n $pair->setSessionAttributeKey($key);\n $pair->setSessionAttributeValue($value);\n $keyValuePairs[] = $pair;\n }\n // Set the the full list of key-value pairs on the container object.\n $sessionAttributesKeyValuePairs->setKeyValuePairs($keyValuePairs);\n // Attach the container of key-value pairs to the ClickConversion object.\n $clickConversion->setSessionAttributesKeyValuePairs($sessionAttributesKeyValuePairs);\n}UploadEnhancedConversionsForLeads.php\n```\n\nExample:\n```text\n# Add details of the conversion.\n# Gets the conversion action resource name.\nconversion_action_service: ConversionActionServiceClient = (\n client.get_service(\"ConversionActionService\")\n)\nclick_conversion.conversion_action = (\n conversion_action_service.conversion_action_path(\n customer_id, raw_record[\"conversion_action_id\"]\n )\n)\nclick_conversion.conversion_date_time = raw_record[\"conversion_date_time\"]\nclick_conversion.conversion_value = raw_record[\"conversion_value\"]\nclick_conversion.currency_code = raw_record[\"currency_code\"]\n\n# Sets the order ID if provided.\nif raw_record.get(\"order_id\"):\n click_conversion.order_id = raw_record[\"order_id\"]\n\n# Sets the gclid if provided.\nif raw_record.get(\"gclid\"):\n click_conversion.gclid = raw_record[\"gclid\"]\n\n# Specifies whether user consent was obtained for the data you are\n# uploading. For more details, see:\n# https://www.google.com/about/company/user-consent-policy\nif raw_record[\"ad_user_data_consent\"]:\n click_conversion.consent.ad_user_data = client.enums.ConsentStatusEnum[\n raw_record[\"ad_user_data_consent\"]\n ]\n\n# Set one of the session_attributes_encoded or\n# session_attributes_key_value_pairs fields if either are provided.\n# The session attribute fields are only available to allowlisted users.\n# To include these fields in conversion imports, upgrade to the Data Manager API.\nif session_attributes_encoded:\n click_conversion.session_attributes_encoded = session_attributes_encoded\nelif session_attributes_dict:\n for key, value in session_attributes_dict.items():\n pair: SessionAttributeKeyValuePair = client.get_type(\n \"SessionAttributeKeyValuePair\"\n )\n pair.session_attribute_key = key\n pair.session_attribute_value = value\n click_conversion.session_attributes_key_value_pairs.key_value_pairs.append(\n pair\n )upload_enhanced_conversions_for_leads.py\n```\n\nExample:\n```text\ncc.conversion_action = client.path.conversion_action(customer_id, conversion_action_id)\ncc.conversion_date_time = conversion_date_time\ncc.conversion_value = conversion_value.to_f\ncc.currency_code = 'USD'\n\nunless order_id.nil?\n cc.order_id = order_id\nend\n\nunless raw_record[\"gclid\"].nil?\n cc.gclid = gclid\nend\n\n# Specifies whether user consent was obtained for the data you are\n# uploading. For more details, see:\n# https://www.google.com/about/company/user-consent-policy\nunless raw_record[\"ad_user_data_consent\"].nil?\n cc.consent = client.resource.consent do |c|\n c.ad_user_data = ad_user_data_consent\n end\nend\n\n# Set one of the session_attributes_encoded or\n# session_attributes_key_value_pairs fields if either are provided.\n# The session attribute fields are only available to allowlisted users.\n# To include these fields in conversion imports, upgrade to the Data Manager API.\nif session_attributes_encoded != nil\n cc.class.module_eval { attr_accessor :session_attributes_encoded}\n cc.session_attributes_encoded = session_attributes_encoded\nelsif session_attributes_hash != nil\n # Add new attribute to click conversion object\n cc.class.module_eval { attr_accessor :session_attributes_key_value_pairs}\n cc.session_attributes_key_value_pairs = ::Google::Ads::GoogleAds::V19::Services::SessionAttributesKeyValuePairs.new\n\n # Loop thru inputted session_attributes_hash to populate session_attributes_key_value_pairs\n session_attributes_hash.each do |key, value|\n pair = ::Google::Ads::GoogleAds::V19::Services::SessionAttributeKeyValuePair.new\n pair.session_attribute_key = key\n pair.session_attribute_value = value\n cc.session_attributes_key_value_pairs.key_value_pairs << pair\n end\nend upload_enhanced_conversions_for_leads.rb\n```\n\nExample:\n```text\n# Add details of the conversion.\n$click_conversion->{conversionAction} =\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::conversion_action(\n $customer_id, $raw_record->{conversionActionId});\n$click_conversion->{conversionDateTime} = $raw_record->{conversionDateTime};\n$click_conversion->{conversionValue} = $raw_record->{conversionValue};\n$click_conversion->{currencyCode} = $raw_record->{currencyCode};\n\n# Set the order ID if provided.\nif (defined $raw_record->{orderId}) {\n $click_conversion->{orderId} = $raw_record->{orderId};\n}\n\n# Set the Google click ID (gclid) if provided.\nif (defined $raw_record->{gclid}) {\n $click_conversion->{gclid} = $raw_record->{gclid};\n}\n\n# Set the consent information, if provided.\nif (defined $raw_record->{adUserDataConsent}) {\n $click_conversion->{consent} =\n Google::Ads::GoogleAds::V25::Common::Consent->new({\n adUserData => $raw_record->{adUserDataConsent}});\n}\n\n# Set one of the session_attributes_encoded or session_attributes_key_value_pairs\n# fields if either are provided.\nif (defined $session_attributes_encoded) {\n $click_conversion->{sessionAttributesEncoded} = $session_attributes_encoded;\n} elsif (defined $session_attributes_hash) {\n while (my ($key, $value) = each %$session_attributes_hash) {\n my $pair =\n Google::Ads::GoogleAds::V25::Services::ConversionUploadService::SessionAttributeKeyValuePair\n ->new({sessionAttributeKey => $key, sessionAttributeValue => $value});\n push @{$click_conversion->{sessionAttributesKeyValuePairs}{keyValuePairs}\n }, $pair;\n }\n}upload_enhanced_conversions_for_leads.pl\n```\n\nExample:\n```text\n// Creates the conversion upload service client.\ntry (ConversionUploadServiceClient conversionUploadServiceClient =\n googleAdsClient.getLatestVersion().createConversionUploadServiceClient()) {\n // Uploads the click conversion. Partial failure should always be set to true.\n\n // NOTE: This request contains a single conversion as a demonstration. However, if you have\n // multiple conversions to upload, it's best to upload multiple conversions per request\n // instead of sending a separate request per conversion. See the following for per-request\n // limits:\n // https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\n UploadClickConversionsResponse response =\n conversionUploadServiceClient.uploadClickConversions(\n UploadClickConversionsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .addConversions(clickConversion)\n // Enables partial failure (must be true).\n .setPartialFailure(true)\n .build());UploadEnhancedConversionsForLeads.java\n```\n\nExample:\n```text\n// Uploads the click conversion. Partial failure should always be set to true.\n// NOTE: This request contains a single conversion as a demonstration.\n// However, if you have multiple conversions to upload, it's best to upload multiple\n// conversions per request instead of sending a separate request per conversion.\n// See the following for per-request limits:\n// https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload\nUploadClickConversionsResponse response =\n conversionUploadService.UploadClickConversions(\n new UploadClickConversionsRequest()\n {\n CustomerId = customerId.ToString(),\n Conversions = { clickConversion },\n // Enables partial failure (must be true).\n PartialFailure = true\n });\nUploadEnhancedConversionsForLeads.cs\n```\n\nExample:\n```text\n// Issues a request to upload the click conversion.\n$conversionUploadServiceClient = $googleAdsClient->getConversionUploadServiceClient();\n// NOTE: This request contains a single conversion as a demonstration. However, if you have\n// multiple conversions to upload, it's best to upload multiple conversions per request\n// instead of sending a separate request per conversion. See the following for per-request\n// limits:\n// https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\n$response = $conversionUploadServiceClient->uploadClickConversions(\n // Enables partial failure (must be true).\n UploadClickConversionsRequest::build($customerId, [$clickConversion], true)\n);UploadEnhancedConversionsForLeads.php\n```\n\nExample:\n```text\n# Creates the conversion upload service client.\nconversion_upload_service: ConversionUploadServiceClient = (\n client.get_service(\"ConversionUploadService\")\n)\n# Uploads the click conversion. Partial failure should always be set to\n# True.\n# NOTE: This request only uploads a single conversion, but if you have\n# multiple conversions to upload, it's most efficient to upload them in a\n# single request. See the following for per-request limits for reference:\n# https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\nresponse: UploadClickConversionsResponse = (\n conversion_upload_service.upload_click_conversions(\n customer_id=customer_id,\n conversions=[click_conversion],\n # Enables partial failure (must be true).\n partial_failure=True,\n )\n)upload_enhanced_conversions_for_leads.py\n```\n\nExample:\n```text\nresponse = client.service.conversion_upload.upload_click_conversions(\n customer_id: customer_id,\n conversions: [click_conversion],\n # Partial failure must be true.\n partial_failure: true,\n)\n\nif response.partial_failure_error\n puts \"Partial failure encountered: #{response.partial_failure_error.message}\"\nelse\n result = response.results.first\n puts \"Uploaded click conversion that happened at #{result.conversion_date_time} \" \\\n \"to #{result.conversion_action}.\"\nendupload_enhanced_conversions_for_leads.rb\n```\n\nExample:\n```text\n# Upload the click conversion. Partial failure should always be set to true.\n#\n# NOTE: This request contains a single conversion as a demonstration.\n# However, if you have multiple conversions to upload, it's best to\n# upload multiple conversions per request instead of sending a separate\n# request per conversion. See the following for per-request limits:\n# https://developers.google.com/google-ads/api/docs/best-practices/quotas#conversion_upload_service\nmy $response =\n $api_client->ConversionUploadService()->upload_click_conversions({\n customerId => $customer_id,\n conversions => [$click_conversion],\n # Enable partial failure (must be true).\n partialFailure => \"true\"\n });upload_enhanced_conversions_for_leads.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.483Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":32,"totalLines":1244,"estimatedTokens":12658}}188{"id":"doc-getting_started_google_ads_api_google_for_develo-03827000","source":"documentation","title":"Getting started | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/conversions/getting-started","text":"Example:\n```text\nSELECT\n customer.conversion_tracking_setting.google_ads_conversion_customer,\n customer.conversion_tracking_setting.conversion_tracking_status,\n customer.conversion_tracking_setting.conversion_tracking_id,\n customer.conversion_tracking_setting.cross_account_conversion_tracking_id\nFROM customer\n```\n\nExample:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient, long customerId) {\n\n // Creates a ConversionAction.\n ConversionAction conversionAction =\n ConversionAction.newBuilder()\n // Note that conversion action names must be unique. If a conversion action already\n // exists with the specified conversion_action_name the create operation will fail with\n // a ConversionActionError.DUPLICATE_NAME error.\n .setName(\"Earth to Mars Cruises Conversion #\" + getPrintableDateTime())\n .setCategory(ConversionActionCategory.DEFAULT)\n .setType(ConversionActionType.WEBPAGE)\n .setStatus(ConversionActionStatus.ENABLED)\n .setViewThroughLookbackWindowDays(15L)\n .setValueSettings(\n ValueSettings.newBuilder()\n .setDefaultValue(23.41)\n .setAlwaysUseDefaultValue(true)\n .build())\n .build();\n\n // Creates the operation.\n ConversionActionOperation operation =\n ConversionActionOperation.newBuilder().setCreate(conversionAction).build();\n\n try (ConversionActionServiceClient conversionActionServiceClient =\n googleAdsClient.getLatestVersion().createConversionActionServiceClient()) {\n MutateConversionActionsResponse response =\n conversionActionServiceClient.mutateConversionActions(\n Long.toString(customerId), Collections.singletonList(operation));\n System.out.printf(\"Added %d conversion actions:%n\", response.getResultsCount());\n for (MutateConversionActionResult result : response.getResultsList()) {\n System.out.printf(\n \"New conversion action added with resource name: '%s'%n\", result.getResourceName());\n }\n }\n}AddConversionAction.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId)\n{\n // Get the ConversionActionService.\n ConversionActionServiceClient conversionActionService =\n client.GetService(Services.V25.ConversionActionService);\n\n // Note that conversion action names must be unique.\n // If a conversion action already exists with the specified name the create operation\n // will fail with a ConversionAction.DUPLICATE_NAME error.\n string ConversionActionName = \"Earth to Mars Cruises Conversion #\"\n + ExampleUtilities.GetRandomString();\n\n // Add a conversion action.\n ConversionAction conversionAction = new ConversionAction()\n {\n Name = ConversionActionName,\n Category = ConversionActionCategory.Default,\n Type = ConversionActionType.Webpage,\n Status = ConversionActionStatus.Enabled,\n ViewThroughLookbackWindowDays = 15,\n ValueSettings = new ConversionAction.Types.ValueSettings()\n {\n DefaultValue = 23.41,\n AlwaysUseDefaultValue = true\n }\n };\n\n // Create the operation.\n ConversionActionOperation operation = new ConversionActionOperation()\n {\n Create = conversionAction\n };\n\n try\n {\n // Create the conversion action.\n MutateConversionActionsResponse response =\n conversionActionService.MutateConversionActions(customerId.ToString(),\n new ConversionActionOperation[] { operation });\n\n // Display the results.\n foreach (MutateConversionActionResult newConversionAction in response.Results)\n {\n Console.WriteLine($\"New conversion action with resource name = \" +\n $\"'{newConversionAction.ResourceName}' was added.\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}AddConversionAction.cs\n```\n\nExample:\n```text\npublic static function runExample(GoogleAdsClient $googleAdsClient, int $customerId)\n{\n // Creates a conversion action.\n $conversionAction = new ConversionAction([\n // Note that conversion action names must be unique.\n // If a conversion action already exists with the specified conversion_action_name\n // the create operation will fail with a ConversionActionError.DUPLICATE_NAME error.\n 'name' => 'Earth to Mars Cruises Conversion #' . Helper::getPrintableDatetime(),\n 'category' => ConversionActionCategory::PBDEFAULT,\n 'type' => ConversionActionType::WEBPAGE,\n 'status' => ConversionActionStatus::ENABLED,\n 'view_through_lookback_window_days' => 15,\n 'value_settings' => new ValueSettings([\n 'default_value' => 23.41,\n 'always_use_default_value' => true\n ])\n ]);\n\n // Creates a conversion action operation.\n $conversionActionOperation = new ConversionActionOperation();\n $conversionActionOperation->setCreate($conversionAction);\n\n // Issues a mutate request to add the conversion action.\n $conversionActionServiceClient = $googleAdsClient->getConversionActionServiceClient();\n $response = $conversionActionServiceClient->mutateConversionActions(\n MutateConversionActionsRequest::build($customerId, [$conversionActionOperation])\n );\n\n printf(\"Added %d conversion actions:%s\", $response->getResults()->count(), PHP_EOL);\n\n foreach ($response->getResults() as $addedConversionAction) {\n /** @var ConversionAction $addedConversionAction */\n printf(\n \"New conversion action added with resource name: '%s'%s\",\n $addedConversionAction->getResourceName(),\n PHP_EOL\n );\n }\n}AddConversionAction.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient, customer_id: str) -> None:\n conversion_action_service: ConversionActionServiceClient = (\n client.get_service(\"ConversionActionService\")\n )\n\n # Create the operation.\n conversion_action_operation: ConversionActionOperation = client.get_type(\n \"ConversionActionOperation\"\n )\n\n # Create conversion action.\n conversion_action: ConversionAction = conversion_action_operation.create\n\n # Note that conversion action names must be unique. If a conversion action\n # already exists with the specified conversion_action_name, the create\n # operation will fail with a ConversionActionError.DUPLICATE_NAME error.\n conversion_action.name = f\"Earth to Mars Cruises Conversion {uuid.uuid4()}\"\n conversion_action.type_ = (\n client.enums.ConversionActionTypeEnum.UPLOAD_CLICKS\n )\n conversion_action.category = (\n client.enums.ConversionActionCategoryEnum.DEFAULT\n )\n conversion_action.status = client.enums.ConversionActionStatusEnum.ENABLED\n conversion_action.view_through_lookback_window_days = 15\n\n # Create a value settings object.\n value_settings: ConversionAction.ValueSettings = (\n conversion_action.value_settings\n )\n value_settings.default_value = 15.0\n value_settings.always_use_default_value = True\n\n # Add the conversion action.\n conversion_action_response: MutateConversionActionsResponse = (\n conversion_action_service.mutate_conversion_actions(\n customer_id=customer_id,\n operations=[conversion_action_operation],\n )\n )\n\n print(\n \"Created conversion action \"\n f'\"{conversion_action_response.results[0].resource_name}\".'\n )add_conversion_action.py\n```\n\nExample:\n```text\ndef add_conversion_action(customer_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n\n # Add a conversion action.\n conversion_action = client.resource.conversion_action do |ca|\n ca.name = \"Earth to Mars Cruises Conversion #{(Time.new.to_f * 100).to_i}\"\n ca.type = :UPLOAD_CLICKS\n ca.category = :DEFAULT\n ca.status = :ENABLED\n ca.view_through_lookback_window_days = 15\n\n # Create a value settings object.\n ca.value_settings = client.resource.value_settings do |vs|\n vs.default_value = 15\n vs.always_use_default_value = true\n end\n end\n\n # Create the operation.\n conversion_action_operation = client.operation.create_resource.conversion_action(conversion_action)\n\n # Add the ad group ad.\n response = client.service.conversion_action.mutate_conversion_actions(\n customer_id: customer_id,\n operations: [conversion_action_operation],\n )\n\n puts \"New conversion action with resource name = #{response.results.first.resource_name}.\"\nendadd_conversion_action.rb\n```\n\nExample:\n```text\nsub add_conversion_action {\n my ($api_client, $customer_id) = @_;\n\n # Note that conversion action names must be unique.\n # If a conversion action already exists with the specified conversion_action_name,\n # the create operation fails with error ConversionActionError.DUPLICATE_NAME.\n my $conversion_action_name = \"Earth to Mars Cruises Conversion #\" . uniqid();\n\n # Create a conversion action.\n my $conversion_action =\n Google::Ads::GoogleAds::V25::Resources::ConversionAction->new({\n name => $conversion_action_name,\n category => DEFAULT,\n type => WEBPAGE,\n status => ENABLED,\n viewThroughLookbackWindowDays => 15,\n valueSettings =>\n Google::Ads::GoogleAds::V25::Resources::ValueSettings->new({\n defaultValue => 23.41,\n alwaysUseDefaultValue => \"true\"\n })});\n\n # Create a conversion action operation.\n my $conversion_action_operation =\n Google::Ads::GoogleAds::V25::Services::ConversionActionService::ConversionActionOperation\n ->new({create => $conversion_action});\n\n # Add the conversion action.\n my $conversion_actions_response =\n $api_client->ConversionActionService()->mutate({\n customerId => $customer_id,\n operations => [$conversion_action_operation]});\n\n printf \"New conversion action added with resource name: '%s'.\\n\",\n $conversion_actions_response->{results}[0]{resourceName};\n\n return 1;\n}add_conversion_action.pl\n```\n\nExample:\n```text\nSELECT\n conversion_action.resource_name,\n conversion_action.name,\n conversion_action.status\nFROM conversion_action\nWHERE conversion_action.type = 'INSERT_CONVERSION_ACTION_TYPE'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.486Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":291,"estimatedTokens":2663}}189{"id":"doc-create_a_performance_max_campaign_budget_google_-52cf09fe","source":"documentation","title":"Create a Performance Max Campaign Budget | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/create-budget","text":"Example:\n```text\n/** Creates a MutateOperation that creates a new CampaignBudget. */\nprivate MutateOperation createCampaignBudgetOperation(long customerId) {\n CampaignBudget campaignBudget =\n CampaignBudget.newBuilder()\n .setName(\"Performance Max campaign budget #\" + getPrintableDateTime())\n // The budget period already defaults to DAILY.\n .setAmountMicros(50_000_000)\n .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)\n // A Performance Max campaign cannot use a shared campaign budget.\n .setExplicitlyShared(false)\n // Set a temporary ID in the budget's resource name, so it can be referenced\n // by the campaign in later steps.\n .setResourceName(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignBudgetOperation(\n CampaignBudgetOperation.newBuilder().setCreate(campaignBudget).build())\n .build();\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a MutateOperation that creates a new CampaignBudget.\n///\n/// A temporary ID will be assigned to this campaign budget so that it can be\n/// referenced by other objects being created in the same Mutate request.\n/// </summary>\n/// <param name=\"budgetResourceName\">The temporary resource name of the budget to\n/// create.</param>\n/// <returns>A MutateOperation that creates a CampaignBudget.</returns>\nprivate MutateOperation CreateCampaignBudgetOperation(string budgetResourceName)\n{\n MutateOperation operation = new MutateOperation\n {\n CampaignBudgetOperation = new CampaignBudgetOperation\n {\n Create = new CampaignBudget\n {\n Name = \"Performance Max campaign budget #\"\n + ExampleUtilities.GetRandomString(),\n\n // The budget period already defaults to DAILY.\n AmountMicros = 50000000,\n\n // A Performance Max campaign cannot use a shared campaign budget.\n ExplicitlyShared = false,\n\n // Set a temporary ID in the budget's resource name so it can be referenced\n // by the campaign in later steps.\n ResourceName = budgetResourceName\n }\n }\n };\n\n return operation;\n}\nAddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createCampaignBudgetOperation(int $customerId): MutateOperation\n{\n // Creates a mutate operation that creates a campaign budget operation.\n return new MutateOperation([\n 'campaign_budget_operation' => new CampaignBudgetOperation([\n 'create' => new CampaignBudget([\n // Sets a temporary ID in the budget's resource name so it can be referenced\n // by the campaign in later steps.\n 'resource_name' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n 'name' => 'Performance Max campaign budget #' . Helper::getPrintableDatetime(),\n // The budget period already defaults to DAILY.\n 'amount_micros' => 50000000,\n 'delivery_method' => BudgetDeliveryMethod::STANDARD,\n // A Performance Max campaign cannot use a shared campaign budget.\n 'explicitly_shared' => false\n ])\n ])\n ]);\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\ndef create_campaign_budget_operation(\n client: GoogleAdsClient,\n customer_id: str,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new CampaignBudget.\n\n A temporary ID will be assigned to this campaign budget so that it can be\n referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a MutateOperation that creates a CampaignBudget.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_budget_operation: CampaignBudgetOperation = (\n mutate_operation.campaign_budget_operation\n )\n campaign_budget: CampaignBudget = campaign_budget_operation.create\n campaign_budget.name = f\"Performance Max campaign budget #{uuid4()}\"\n # The budget period already defaults to DAILY.\n campaign_budget.amount_micros = 50000000\n campaign_budget.delivery_method = (\n client.enums.BudgetDeliveryMethodEnum.STANDARD\n )\n # A Performance Max campaign cannot use a shared campaign budget.\n campaign_budget.explicitly_shared = False\n\n # Set a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n campaign_budget.resource_name = client.get_service(\n \"CampaignBudgetService\"\n ).campaign_budget_path(customer_id, _BUDGET_TEMPORARY_ID)\n\n return mutate_operationadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates a MutateOperation that creates a new CampaignBudget.\n#\n# A temporary ID will be assigned to this campaign budget so that it can be\n# referenced by other objects being created in the same Mutate request.\ndef create_campaign_budget_operation(client, customer_id)\n client.operation.mutate do |m|\n m.campaign_budget_operation = client.operation.create_resource.campaign_budget do |cb|\n cb.name = \"Performance Max campaign budget #{SecureRandom.uuid}\"\n # The budget period already defaults to DAILY.\n cb.amount_micros = 50_000_000\n cb.delivery_method = :STANDARD\n # A Performance Max campaign cannot use a shared campaign budget.\n cb.explicitly_shared = false\n\n # Set a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n cb.resource_name = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n end\n end\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_campaign_budget_operation {\n my ($customer_id) = @_;\n\n # Create a mutate operation that creates a campaign budget operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignBudgetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::CampaignBudget->new(\n {\n # Set a temporary ID in the budget's resource name so it can be\n # referenced by the campaign in later steps.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n name => \"Performance Max campaign budget #\" . uniqid(),\n # The budget period already defaults to DAILY.\n amountMicros => 50000000,\n deliveryMethod => STANDARD,\n # A Performance Max campaign cannot use a shared campaign budget.\n explicitlyShared => \"false\",\n })})});\n}add_performance_max_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.487Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":187,"estimatedTokens":1802}}190{"id":"doc-campaign_drafts_google_ads_api_google_for_develo-36e7eebb","source":"documentation","title":"Campaign drafts | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaign-drafts","text":"Example:\n```text\nSELECT campaign_draft.draft_campaign\nFROM campaign_draft\nWHERE campaign_draft.resource_name = \"CAMPAIGN_DRAFT_RESOURCE_NAME_HERE\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.488Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":8,"estimatedTokens":41}}191{"id":"doc-asset_requirements_google_ads_api_google_for_dev-7150f89b","source":"documentation","title":"Asset Requirements | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/asset-requirements","text":"Example:\n```text\nSELECT\n asset.resource_name,\n asset.name,\n asset.image_asset.full_size.width_pixels,\n asset.image_asset.full_size.height_pixels\nFROM asset\nWHERE asset.type = 'IMAGE'\n AND asset.image_asset.file_size <= 5120000\n AND asset.image_asset.full_size.width_pixels = 1200\n AND asset.image_asset.full_size.height_pixels = 628\n AND asset.name LIKE '%KEYWORD%'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.489Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":16,"estimatedTokens":98}}192{"id":"doc-location_targeting_google_ads_api_google_for_dev-4ba45c39","source":"documentation","title":"Location targeting | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/targeting/location-targeting","text":"Example:\n```text\nprivate static CampaignCriterion buildLocationIdCriterion(\n long locationId, String campaignResourceName) {\n Builder criterionBuilder = CampaignCriterion.newBuilder().setCampaign(campaignResourceName);\n\n criterionBuilder\n .getLocationBuilder()\n .setGeoTargetConstant(ResourceNames.geoTargetConstant(locationId));\n\n return criterionBuilder.build();\n}AddCampaignTargetingCriteria.java\n```\n\nExample:\n```text\nprivate CampaignCriterion buildLocationCriterion(long locationId,\n string campaignResourceName)\n{\n GeoTargetConstantName location = new GeoTargetConstantName(locationId.ToString());\n return new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n Location = new LocationInfo()\n {\n GeoTargetConstant = location.ToString()\n }\n };\n}AddCampaignTargetingCriteria.cs\n```\n\nExample:\n```text\nprivate static function createLocationCampaignCriterionOperation(\n int $locationId,\n string $campaignResourceName\n) {\n // Constructs a campaign criterion for the specified campaign ID using the specified\n // location ID.\n $campaignCriterion = new CampaignCriterion([\n // Creates a location using the specified location ID.\n 'location' => new LocationInfo([\n // Besides using location ID, you can also search by location names using\n // GeoTargetConstantServiceClient::suggestGeoTargetConstants() and directly\n // apply GeoTargetConstant::$resourceName here. An example can be found\n // in GetGeoTargetConstantByNames.php.\n 'geo_target_constant' => ResourceNames::forGeoTargetConstant($locationId)\n ]),\n 'campaign' => $campaignResourceName\n ]);\n\n return new CampaignCriterionOperation(['create' => $campaignCriterion]);\n}AddCampaignTargetingCriteria.php\n```\n\nExample:\n```text\ndef create_location_op(\n client: GoogleAdsClient,\n customer_id: str,\n campaign_id: str,\n location_id: str,\n) -> CampaignCriterionOperation:\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n geo_target_constant_service: GeoTargetConstantServiceClient = (\n client.get_service(\"GeoTargetConstantService\")\n )\n\n # Create the campaign criterion.\n campaign_criterion_operation: CampaignCriterionOperation = client.get_type(\n \"CampaignCriterionOperation\"\n )\n campaign_criterion: CampaignCriterion = campaign_criterion_operation.create\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, campaign_id\n )\n\n # Besides using location_id, you can also search by location names from\n # GeoTargetConstantService.suggest_geo_target_constants() and directly\n # apply GeoTargetConstant.resource_name here. An example can be found\n # in get_geo_target_constant_by_names.py.\n campaign_criterion.location.geo_target_constant = (\n geo_target_constant_service.geo_target_constant_path(location_id)\n )\n\n return campaign_criterion_operationadd_campaign_targeting_criteria.py\n```\n\nExample:\n```text\ndef create_location(client, customer_id, campaign_id, location_id)\n client.operation.create_resource.campaign_criterion do |criterion|\n criterion.campaign = client.path.campaign(customer_id, campaign_id)\n\n criterion.location = client.resource.location_info do |li|\n # Besides using location_id, you can also search by location names from\n # GeoTargetConstantService.suggest_geo_target_constants() and directly\n # apply GeoTargetConstant.resource_name here. An example can be found\n # in get_geo_target_constant_by_names.rb.\n li.geo_target_constant = client.path.geo_target_constant(location_id)\n end\n end\nendadd_campaign_targeting_criteria.rb\n```\n\nExample:\n```text\nsub create_location_campaign_criterion_operation {\n my ($location_id, $campaign_resource_name) = @_;\n\n # Construct a campaign criterion for the specified campaign using the\n # specified location ID.\n my $campaign_criterion =\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n # Create a location using the specified location ID.\n location => Google::Ads::GoogleAds::V25::Common::LocationInfo->new({\n # Besides using location ID, you can also search by location names\n # using GeoTargetConstantService::suggest() and directly apply\n # GeoTargetConstant->{resourceName} here. An example can be found\n # in get_geo_target_constants_by_names.pl.\n geoTargetConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n $location_id)}\n ),\n campaign => $campaign_resource_name\n });\n\n return\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create => $campaign_criterion\n });\n}add_campaign_targeting_criteria.pl\n```\n\nExample:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient) {\n try (GeoTargetConstantServiceClient geoTargetClient =\n googleAdsClient.getLatestVersion().createGeoTargetConstantServiceClient()) {\n\n SuggestGeoTargetConstantsRequest.Builder requestBuilder =\n SuggestGeoTargetConstantsRequest.newBuilder();\n\n // Locale is using ISO 639-1 format. If an invalid locale is given, 'en' is used by default.\n requestBuilder.setLocale(\"en\");\n\n // A list of country codes can be referenced here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n requestBuilder.setCountryCode(\"FR\");\n\n requestBuilder\n .getLocationNamesBuilder()\n .addAllNames(ImmutableList.of(\"Paris\", \"Quebec\", \"Spain\", \"Deutschland\"));\n\n SuggestGeoTargetConstantsResponse response =\n geoTargetClient.suggestGeoTargetConstants(requestBuilder.build());\n\n for (GeoTargetConstantSuggestion suggestion :\n response.getGeoTargetConstantSuggestionsList()) {\n System.out.printf(\n \"%s (%s,%s,%s,%s) is found in locale (%s) with reach (%d) for search term (%s).%n\",\n suggestion.getGeoTargetConstant().getResourceName(),\n suggestion.getGeoTargetConstant().getName(),\n suggestion.getGeoTargetConstant().getCountryCode(),\n suggestion.getGeoTargetConstant().getTargetType(),\n suggestion.getGeoTargetConstant().getStatus().name(),\n suggestion.getLocale(),\n suggestion.getReach(),\n suggestion.getSearchTerm());\n }\n }\n}GetGeoTargetConstantsByNames.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client)\n{\n // Get the GeoTargetConstantServiceClient.\n GeoTargetConstantServiceClient geoService =\n client.GetService(Services.V25.GeoTargetConstantService);\n\n // Locale is using ISO 639-1 format. If an invalid locale is given,\n // 'en' is used by default.\n string locale = \"en\";\n\n // A list of country codes can be referenced here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n string countryCode = \"FR\";\n\n string[] locations = { \"Paris\", \"Quebec\", \"Spain\", \"Deutschland\" };\n\n SuggestGeoTargetConstantsRequest request = new SuggestGeoTargetConstantsRequest()\n {\n Locale = locale,\n CountryCode = countryCode,\n LocationNames = new SuggestGeoTargetConstantsRequest.Types.LocationNames()\n };\n\n request.LocationNames.Names.AddRange(locations);\n\n try\n {\n SuggestGeoTargetConstantsResponse response =\n geoService.SuggestGeoTargetConstants(request);\n\n foreach (GeoTargetConstantSuggestion suggestion\n in response.GeoTargetConstantSuggestions)\n {\n Console.WriteLine(\n $\"{suggestion.GeoTargetConstant.ResourceName} \" +\n $\"({suggestion.GeoTargetConstant.Name}, \" +\n $\"{suggestion.GeoTargetConstant.CountryCode}, \" +\n $\"{suggestion.GeoTargetConstant.TargetType}, \" +\n $\"{suggestion.GeoTargetConstant.Status}) is found in locale \" +\n $\"({suggestion.Locale}) with reach ({suggestion.Reach}) \" +\n $\"for search term ({suggestion.SearchTerm}).\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}GetGeoTargetConstantsByNames.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n array $locationNames,\n string $locale,\n string $countryCode\n) {\n $geoTargetConstantServiceClient = $googleAdsClient->getGeoTargetConstantServiceClient();\n\n $response = $geoTargetConstantServiceClient->suggestGeoTargetConstants(\n new SuggestGeoTargetConstantsRequest([\n 'locale' => $locale,\n 'country_code' => $countryCode,\n 'location_names' => new LocationNames(['names' => $locationNames])\n ])\n );\n\n // Iterates over all geo target constant suggestion objects and prints the requested field\n // values for each one.\n foreach ($response->getGeoTargetConstantSuggestions() as $geoTargetConstantSuggestion) {\n /** @var GeoTargetConstantSuggestion $geoTargetConstantSuggestion */\n printf(\n \"Found '%s' ('%s','%s','%s',%s) in locale '%s' with reach %d\"\n . \" for the search term '%s'.%s\",\n $geoTargetConstantSuggestion->getGeoTargetConstant()->getResourceName(),\n $geoTargetConstantSuggestion->getGeoTargetConstant()->getName(),\n $geoTargetConstantSuggestion->getGeoTargetConstant()->getCountryCode(),\n $geoTargetConstantSuggestion->getGeoTargetConstant()->getTargetType(),\n GeoTargetConstantStatus::name(\n $geoTargetConstantSuggestion->getGeoTargetConstant()->getStatus()\n ),\n $geoTargetConstantSuggestion->getLocale(),\n $geoTargetConstantSuggestion->getReach(),\n $geoTargetConstantSuggestion->getSearchTerm(),\n PHP_EOL\n );\n }\n}GetGeoTargetConstantsByNames.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient) -> None:\n gtc_service: GeoTargetConstantServiceClient = client.get_service(\n \"GeoTargetConstantService\"\n )\n\n gtc_request: SuggestGeoTargetConstantsRequest = client.get_type(\n \"SuggestGeoTargetConstantsRequest\"\n )\n gtc_request.locale = LOCALE\n gtc_request.country_code = COUNTRY_CODE\n\n # The location names to get suggested geo target constants.\n # Type hint for gtc_request.location_names.names is not straightforward\n # as it's part of a complex protobuf object.\n gtc_request.location_names.names.extend(\n [\"Paris\", \"Quebec\", \"Spain\", \"Deutschland\"]\n )\n\n results: SuggestGeoTargetConstantsResponse = (\n gtc_service.suggest_geo_target_constants(gtc_request)\n )\n\n suggestion: GeoTargetConstantSuggestion\n for suggestion in results.geo_target_constant_suggestions:\n geo_target_constant: GeoTargetConstant = suggestion.geo_target_constant\n print(\n f\"{geo_target_constant.resource_name} \"\n f\"({geo_target_constant.name}, \"\n f\"{geo_target_constant.country_code}, \"\n f\"{geo_target_constant.target_type}, \"\n f\"{geo_target_constant.status.name}) \"\n f\"is found in locale ({suggestion.locale}) \"\n f\"with reach ({suggestion.reach}) \"\n f\"from search term ({suggestion.search_term}).\"\n )get_geo_target_constants_by_names.py\n```\n\nExample:\n```text\ndef get_geo_target_constants_by_names\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n gtc_service = client.service.geo_target_constant\n\n location_names = client.resource.location_names do |ln|\n ['Paris', 'Quebec', 'Spain', 'Deutschland'].each do |name|\n ln.names << name\n end\n end\n\n # Locale is using ISO 639-1 format. If an invalid locale is given,\n # 'en' is used by default.\n locale = 'en'\n\n # A list of country codes can be referenced here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n country_code = 'FR'\n\n response = gtc_service.suggest_geo_target_constants(\n locale: locale,\n country_code: country_code,\n location_names: location_names\n )\n\n response.geo_target_constant_suggestions.each do |suggestion|\n puts sprintf(\"%s (%s,%s,%s,%s) is found in locale (%s) with reach (%d)\" \\\n \" from search term (%s).\", suggestion.geo_target_constant.resource_name,\n suggestion.geo_target_constant.name,\n suggestion.geo_target_constant.country_code,\n suggestion.geo_target_constant.target_type,\n suggestion.geo_target_constant.status,\n suggestion.locale,\n suggestion.reach,\n suggestion.search_term)\n end\nendget_geo_target_constants_by_names.rb\n```\n\nExample:\n```text\nsub get_geo_target_constants_by_names {\n my ($api_client, $location_names, $locale, $country_code) = @_;\n\n my $suggest_response = $api_client->GeoTargetConstantService()->suggest({\n locale => $locale,\n countryCode => $country_code,\n locationNames =>\n Google::Ads::GoogleAds::V25::Services::GeoTargetConstantService::LocationNames\n ->new({\n names => $location_names\n })});\n\n # Iterate over all geo target constant suggestion objects and print the requested\n # field values for each one.\n foreach my $geo_target_constant_suggestion (\n @{$suggest_response->{geoTargetConstantSuggestions}})\n {\n printf \"Found '%s' ('%s','%s','%s',%s) in locale '%s' with reach %d\" .\n \" for the search term '%s'.\\n\",\n $geo_target_constant_suggestion->{geoTargetConstant}{resourceName},\n $geo_target_constant_suggestion->{geoTargetConstant}{name},\n $geo_target_constant_suggestion->{geoTargetConstant}{countryCode},\n $geo_target_constant_suggestion->{geoTargetConstant}{targetType},\n $geo_target_constant_suggestion->{geoTargetConstant}{status},\n $geo_target_constant_suggestion->{locale},\n $geo_target_constant_suggestion->{reach},\n $geo_target_constant_suggestion->{searchTerm};\n }\n\n return 1;\n}get_geo_target_constants_by_names.pl\n```\n\nExample:\n```text\nprivate static CampaignCriterion buildProximityLocation(String campaignResourceName) {\n Builder builder = CampaignCriterion.newBuilder().setCampaign(campaignResourceName);\n\n ProximityInfo.Builder proximityBuilder = builder.getProximityBuilder();\n proximityBuilder.setRadius(10.0).setRadiusUnits(ProximityRadiusUnits.MILES);\n\n AddressInfo.Builder addressBuilder = proximityBuilder.getAddressBuilder();\n addressBuilder\n .setStreetAddress(\"38 avenue de l'Opéra\")\n .setCityName(\"Paris\")\n .setPostalCode(\"75002\")\n .setCountryCode(\"FR\");\n\n return builder.build();\n}AddCampaignTargetingCriteria.java\n```\n\nExample:\n```text\nprivate CampaignCriterion buildProximityCriterion(string campaignResourceName)\n{\n ProximityInfo proximity = new ProximityInfo()\n {\n Address = new AddressInfo()\n {\n StreetAddress = \"38 avenue de l'Opéra\",\n CityName = \"Paris\",\n PostalCode = \"75002\",\n CountryCode = \"FR\"\n },\n Radius = 10d,\n // Default is kilometers.\n RadiusUnits = ProximityRadiusUnits.Miles\n };\n\n return new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n Proximity = proximity\n };\n}AddCampaignTargetingCriteria.cs\n```\n\nExample:\n```text\nprivate static function createProximityCampaignCriterionOperation(string $campaignResourceName)\n{\n // Constructs a campaign criterion as a proximity.\n $campaignCriterion = new CampaignCriterion([\n 'proximity' => new ProximityInfo([\n 'address' => new AddressInfo([\n 'street_address' => '38 avenue de l\\'Opéra',\n 'city_name' => 'Paris',\n 'postal_code' => '75002',\n 'country_code' => 'FR',\n ]),\n 'radius' => 10.0,\n // Default is kilometers.\n 'radius_units' => ProximityRadiusUnits::MILES\n ]),\n 'campaign' => $campaignResourceName\n ]);\n\n return new CampaignCriterionOperation(['create' => $campaignCriterion]);\n}AddCampaignTargetingCriteria.php\n```\n\nExample:\n```text\ndef create_proximity_op(\n client: GoogleAdsClient, customer_id: str, campaign_id: str\n) -> CampaignCriterionOperation:\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n # Create the campaign criterion.\n campaign_criterion_operation: CampaignCriterionOperation = client.get_type(\n \"CampaignCriterionOperation\"\n )\n campaign_criterion: CampaignCriterion = campaign_criterion_operation.create\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, campaign_id\n )\n campaign_criterion.proximity.address.street_address = \"38 avenue de l'Opera\"\n campaign_criterion.proximity.address.city_name = \"Paris\"\n campaign_criterion.proximity.address.postal_code = \"75002\"\n campaign_criterion.proximity.address.country_code = \"FR\"\n campaign_criterion.proximity.radius = 10\n # Default is kilometers.\n campaign_criterion.proximity.radius_units = (\n client.enums.ProximityRadiusUnitsEnum.MILES\n )\n\n return campaign_criterion_operationadd_campaign_targeting_criteria.py\n```\n\nExample:\n```text\ndef create_proximity(client, customer_id, campaign_id)\n client.operation.create_resource.campaign_criterion do |criterion|\n criterion.campaign = client.path.campaign(customer_id, campaign_id)\n\n criterion.proximity = client.resource.proximity_info do |proximity|\n proximity.address = client.resource.address_info do |address|\n address.street_address = \"38 avenue de l'Opéra\"\n address.city_name = \"Paris\"\n address.postal_code = \"75002\"\n address.country_code = \"FR\"\n end\n\n proximity.radius = 10\n proximity.radius_units = :MILES\n end\n end\nendadd_campaign_targeting_criteria.rb\n```\n\nExample:\n```text\nsub create_proximity_campaign_criterion_operation {\n my ($campaign_resource_name) = @_;\n\n # Construct a campaign criterion as a proximity.\n my $campaign_criterion =\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n proximity => Google::Ads::GoogleAds::V25::Common::ProximityInfo->new({\n address => Google::Ads::GoogleAds::V25::Common::AddressInfo->new({\n streetAddress => \"38 avenue de l'Opéra\",\n cityName => \"cityName\",\n postalCode => \"75002\",\n countryCode => \"FR\"\n }\n ),\n radius => 10.0,\n # Default is kilometers.\n radiusUnits => MILES\n }\n ),\n campaign => $campaign_resource_name\n });\n\n return\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create => $campaign_criterion\n });\n}add_campaign_targeting_criteria.pl\n```\n\nExample:\n```text\nSELECT\n campaign_criterion.campaign,\n campaign_criterion.location.geo_target_constant,\n campaign_criterion.proximity.geo_point.longitude_in_micro_degrees,\n campaign_criterion.proximity.geo_point.latitude_in_micro_degrees,\n campaign_criterion.proximity.radius,\n campaign_criterion.negative\nFROM campaign_criterion\nWHERE\n campaign_criterion.campaign = 'customers/{customer_id}/campaigns/{campaign_id}'\n AND campaign_criterion.type IN (LOCATION, PROXIMITY)\n```\n\nExample:\n```text\n// Conceptual structure for a Campaign update operation\noperations {\n update {\n resource_name: \"customers/{customer_id}/campaigns/{campaign_id}\"\n geo_target_type_setting {\n positive_geo_target_type: PRESENCE\n // negative_geo_target_type remains at its default PRESENCE if not specified\n }\n }\n update_mask {\n paths: \"geo_target_type_setting.positive_geo_target_type\"\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.491Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":20,"totalLines":576,"estimatedTokens":5023}}193{"id":"doc-targeting_settings_google_ads_api_google_for_dev-5a7338a9","source":"documentation","title":"Targeting settings | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/targeting/targeting-settings","text":"Example:\n```text\nString searchQuery =\n \"SELECT ad_group.id, ad_group.name, ad_group.targeting_setting.target_restrictions \"\n + \"FROM ad_group \"\n + \"WHERE ad_group.id = \"\n + adGroupId;UpdateAudienceTargetRestriction.java\n```\n\nExample:\n```text\nstring query = $@\"\n SELECT ad_group.id, ad_group.name, ad_group.targeting_setting.target_restrictions\n FROM ad_group\n WHERE ad_group.id = {adGroupId}\";UpdateAudienceTargetRestriction.cs\n```\n\nExample:\n```text\n$query = \"SELECT ad_group.id, ad_group.name, \" .\n \"ad_group.targeting_setting.target_restrictions \" .\n \"FROM ad_group \" .\n \"WHERE ad_group.id = $adGroupId\";UpdateAudienceTargetRestriction.php\n```\n\nExample:\n```text\nquery: str = f\"\"\"\n SELECT\n ad_group.id,\n ad_group.name,\n ad_group.targeting_setting.target_restrictions\n FROM ad_group\n WHERE ad_group.id = {ad_group_id}\"\"\"update_audience_target_restriction.py\n```\n\nExample:\n```text\nquery = <<~QUERY\n SELECT ad_group.id, ad_group.name,\n ad_group.targeting_setting.target_restrictions\n FROM ad_group\n WHERE ad_group.id = #{ad_group_id}\nQUERYupdate_audience_target_restriction.rb\n```\n\nExample:\n```text\nmy $query =\n \"SELECT ad_group.id, ad_group.name, \" .\n \"ad_group.targeting_setting.target_restrictions FROM ad_group \" .\n \"WHERE ad_group.id = $ad_group_id\";update_audience_target_restriction.pl\n```\n\nExample:\n```text\nfor (TargetRestriction targetRestriction : targetRestrictions) {\n TargetingDimension targetingDimension = targetRestriction.getTargetingDimension();\n boolean bidOnly = targetRestriction.getBidOnly();\n System.out.printf(\n \"- Targeting restriction with targeting dimension '%s' and bid only set to '%b'.%n\",\n targetingDimension, bidOnly);\n // Adds the target restriction to the TargetingSetting object as is if the targeting\n // dimension has a value other than AUDIENCE because those should not change.\n if (!targetingDimension.equals(TargetingDimension.AUDIENCE)) {\n targetingSettingBuilder.addTargetRestrictions(targetRestriction);\n } else if (!bidOnly) {\n shouldUpdateTargetingSetting = true;\n // Adds an AUDIENCE target restriction with bid_only set to true to the targeting\n // setting object. This has the effect of setting the AUDIENCE target restriction to\n // \"Observation\". For more details about the targeting setting, visit\n // https://support.google.com/google-ads/answer/7365594.\n targetingSettingBuilder.addTargetRestrictions(\n TargetRestriction.newBuilder()\n .setTargetingDimensionValue(TargetingDimension.AUDIENCE_VALUE)\n .setBidOnly(true));\n }\n}UpdateAudienceTargetRestriction.java\n```\n\nExample:\n```text\nforeach (TargetRestriction targetRestriction in targetRestrictions)\n{\n TargetingDimension targetingDimension =\n targetRestriction.TargetingDimension;\n bool bidOnly = targetRestriction.BidOnly;\n\n Console.WriteLine(\"\\tTargeting restriction with targeting dimension \" +\n $\"'{targetingDimension}' and bid only set to '{bidOnly}'.\");\n\n // Add the target restriction to the TargetingSetting object as is if the\n // targeting dimension has a value other than AUDIENCE because those should\n // not change.\n if (targetingDimension != TargetingDimension.Audience)\n {\n targetingSetting.TargetRestrictions.Add(targetRestriction);\n }\n else if (!bidOnly)\n {\n shouldUpdateTargetingSetting = true;\n\n // Add an AUDIENCE target restriction with bid_only set to true to the\n // targeting setting object. This has the effect of setting the AUDIENCE\n // target restriction to \"Observation\". For more details about the\n // targeting setting, visit\n // https://support.google.com/google-ads/answer/7365594.\n targetingSetting.TargetRestrictions.Add(new TargetRestriction\n {\n TargetingDimension = TargetingDimension.Audience,\n BidOnly = true\n });\n }\n}UpdateAudienceTargetRestriction.cs\n```\n\nExample:\n```text\nforeach (\n $adGroup->getTargetingSetting()->getTargetRestrictions() as $targetRestriction\n) {\n // Prints the results.\n $targetingDimension = $targetRestriction->getTargetingDimension();\n $bidOnly = $targetRestriction->getBidOnly();\n printf(\n \"- Targeting restriction with targeting dimension '%s' and bid only set to \" .\n \"'%s'.%s\",\n TargetingDimension::name($targetingDimension),\n $bidOnly ? 'true' : 'false',\n PHP_EOL\n );\n\n // Adds the target restriction to the TargetingSetting object as is if the targeting\n // dimension has a value other than AUDIENCE because those should not change.\n if ($targetingDimension !== TargetingDimension::AUDIENCE) {\n $targetRestrictions[] = $targetRestriction;\n } elseif (!$bidOnly) {\n $shouldUpdateTargetingSetting = true;\n\n // Adds an AUDIENCE target restriction with bid_only set to true to the\n // targeting setting object. This has the effect of setting the AUDIENCE\n // target restriction to \"Observation\".\n // For more details about the targeting setting, visit\n // https://support.google.com/google-ads/answer/7365594.\n $targetRestrictions[] = new TargetRestriction([\n 'targeting_dimension' => TargetingDimension::AUDIENCE,\n 'bid_only' => true\n ]);\n }\n}UpdateAudienceTargetRestriction.php\n```\n\nExample:\n```text\ntarget_restriction: TargetRestriction\nfor target_restriction in target_restrictions:\n targeting_dimension: TargetingDimensionEnum.TargetingDimension = (\n target_restriction.targeting_dimension\n )\n bid_only: bool = target_restriction.bid_only\n\n print(\n \"\\tTargeting restriction with targeting dimension \"\n f\"'{targeting_dimension.name}' \"\n f\"and bid only set to '{bid_only}'.\"\n )\n\n # Add the target restriction to the TargetingSetting object as\n # is if the targeting dimension has a value other than audience\n # because those should not change.\n if targeting_dimension != targeting_dimension_enum.AUDIENCE:\n targeting_setting.target_restrictions.append(target_restriction)\n elif not bid_only:\n should_update_targeting_setting: bool = True\n\n # Add an audience target restriction with bid_only set to\n # true to the targeting setting object. This has the effect\n # of setting the audience target restriction to\n # \"Observation\". For more details about the targeting\n # setting, visit\n # https://support.google.com/google-ads/answer/7365594.\n new_target_restriction: TargetRestriction = (\n targeting_setting.target_restrictions.add()\n )\n new_target_restriction.targeting_dimension = (\n targeting_dimension_enum.AUDIENCE\n )\n new_target_restriction.bid_only = Trueupdate_audience_target_restriction.py\n```\n\nExample:\n```text\nad_group.targeting_setting.target_restrictions.each do |r|\n # Prints the results.\n targeting_dimension = r.targeting_dimension\n bid_only = r.bid_only\n puts \"- Targeting restriction with targeting dimension \" \\\n \"#{targeting_dimension} and bid only set to #{bid_only}.\"\n\n # Adds the target restriction to the TargetingSetting object as is if the\n # targeting dimension has a value other than AUDIENCE because those should\n # not change.\n if targeting_dimension != :AUDIENCE\n target_restrictions << r\n elsif !bid_only\n should_update_targeting_setting = true\n\n # Adds an AUDIENCE target restriction with bid_only set to true to the\n # targeting setting object. This has the effect of setting the AUDIENCE\n # target restriction to \"Observation\".\n # For more details about the targeting setting, visit\n # https://support.google.com/google-ads/answer/7365594.\n target_restrictions << client.resource.target_restriction do |tr|\n tr.targeting_dimension = :AUDIENCE\n tr.bid_only = true\n end\n end\nendupdate_audience_target_restriction.rb\n```\n\nExample:\n```text\nforeach my $target_restriction (@target_restrictions) {\n my $targeting_dimension = $target_restriction->{targetingDimension};\n\n printf\n \"\\tTargeting restriction with targeting dimension '%s' and bid \" .\n \"only set to '%s'.\\n\",\n $targeting_dimension,\n $target_restriction->{bidOnly} ? \"TRUE\" : \"FALSE\";\n\n # Add the target restriction to the TargetingSetting object as is if the\n # targeting dimension has a value other than AUDIENCE because those\n # should not change.\n if ($targeting_dimension ne AUDIENCE) {\n $target_restriction->{bidOnly} =\n $target_restriction->{bidOnly} ? \"true\" : \"false\";\n push @{$targeting_setting->{targetRestrictions}}, $target_restriction;\n } elsif (!$target_restriction->{bidOnly}) {\n $should_update_target_setting = 1;\n\n # Add an AUDIENCE target restriction with bid_only set to true to the\n # targeting setting object. This has the effect of setting the\n # AUDIENCE target restriction to \"Observation\". For more details about\n # the targeting setting, visit\n # https://support.google.com/google-ads/answer/7365594.\n my $new_restriction =\n Google::Ads::GoogleAds::V25::Common::TargetRestriction->new({\n targetingDimension => AUDIENCE,\n bidOnly => \"true\"\n });\n push @{$targeting_setting->{targetRestrictions}}, $new_restriction;\n }\n}update_audience_target_restriction.pl\n```\n\nExample:\n```text\nprivate void updateTargetingSetting(\n GoogleAdsClient googleAdsClient,\n long customerId,\n long adGroupId,\n TargetingSetting targetingSetting) {\n // Creates the ad group service client.\n try (AdGroupServiceClient adGroupServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupServiceClient()) {\n // Creates an ad group object with the proper resource name and updated targeting setting.\n AdGroup adGroup =\n AdGroup.newBuilder()\n .setResourceName(ResourceNames.adGroup(customerId, adGroupId))\n .setTargetingSetting(targetingSetting)\n .build();\n // Constructs an operation that will update the ad group, using the FieldMasks utility to\n // derive the update mask. This mask tells the Google Ads API which attributes of the\n // ad group you want to change.\n AdGroupOperation operation =\n AdGroupOperation.newBuilder()\n .setUpdate(adGroup)\n .setUpdateMask(FieldMasks.allSetFieldsOf(adGroup))\n .build();\n // Sends the operation in a mutate request.\n MutateAdGroupsResponse response =\n adGroupServiceClient.mutateAdGroups(\n Long.toString(customerId), ImmutableList.of(operation));\n // Prints the resource name of the updated object.\n System.out.printf(\n \"Updated targeting setting of ad group with resource name '%s'; set the AUDIENCE \"\n + \"target restriction to 'Observation'.%n\",\n response.getResults(0).getResourceName());\n }\n}UpdateAudienceTargetRestriction.java\n```\n\nExample:\n```text\nprivate void UpdateTargetingSetting(GoogleAdsClient client, long customerId, long\n adGroupId, TargetingSetting targetingSetting)\n{\n // Get the AdGroupService client.\n AdGroupServiceClient adGroupServiceClient =\n client.GetService(Services.V25.AdGroupService);\n\n // Create an ad group object with the updated targeting setting.\n AdGroup adGroup = new AdGroup\n {\n ResourceName = ResourceNames.AdGroup(customerId, adGroupId),\n TargetingSetting = targetingSetting\n };\n\n // Construct an operation that will update the ad group, using the FieldMasks utility\n // to derive the update mask. This mask tells the Google Ads API which attributes of the\n // ad group you want to change.\n AdGroupOperation operation = new AdGroupOperation\n {\n Update = adGroup,\n UpdateMask = FieldMasks.AllSetFieldsOf(adGroup)\n };\n\n // Send the operation in a mutate request.\n MutateAdGroupsResponse response =\n adGroupServiceClient.MutateAdGroups(customerId.ToString(), new[] { operation });\n // Print the resource name of the updated object.\n Console.WriteLine(\"Updated targeting setting of ad group with resource name \" +\n $\"'{response.Results.First().ResourceName}'; set the AUDIENCE target restriction \" +\n \"to 'Observation'.\");\n}UpdateAudienceTargetRestriction.cs\n```\n\nExample:\n```text\nprivate static function updateTargetingSetting(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n TargetingSetting $targetingSetting\n) {\n // Creates an ad group object with the proper resource name and updated targeting setting.\n $adGroup = new AdGroup([\n 'resource_name' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'targeting_setting' => $targetingSetting\n ]);\n\n // Constructs an operation that will update the ad group with the specified resource name,\n // using the FieldMasks utility to derive the update mask. This mask tells the Google Ads\n // API which attributes of the ad group you want to change.\n $adGroupOperation = new AdGroupOperation();\n $adGroupOperation->setUpdate($adGroup);\n $adGroupOperation->setUpdateMask(FieldMasks::allSetFieldsOf($adGroup));\n\n // Issues a mutate request to update the ad group.\n $adGroupServiceClient = $googleAdsClient->getAdGroupServiceClient();\n $response = $adGroupServiceClient->mutateAdGroups(\n MutateAdGroupsRequest::build($customerId, [$adGroupOperation])\n );\n\n // Prints the resource name of the updated ad group.\n printf(\n \"Updated targeting setting of ad group with resource name '%s'; set the AUDIENCE \" .\n \"target restriction to 'Observation'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n}UpdateAudienceTargetRestriction.php\n```\n\nExample:\n```text\ndef update_targeting_setting(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n targeting_setting: TargetingSetting,\n) -> None:\n \"\"\"Updates the given TargetingSetting of an ad group.\n\n Args:\n client: The Google Ads client.\n customer_id: The Google Ads customer ID.\n ad_group_id: The ad group ID for which to update the audience targeting\n restriction.\n targeting_setting: The updated targeting setting.\n \"\"\"\n # Get the AdGroupService client.\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n\n # Construct an operation that will update the ad group.\n ad_group_operation: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n\n # Populate the ad group object with the updated targeting setting.\n ad_group: AdGroup = ad_group_operation.update\n ad_group.resource_name = ad_group_service.ad_group_path(\n customer_id, ad_group_id\n )\n ad_group.targeting_setting.target_restrictions.extend(\n targeting_setting.target_restrictions\n )\n # Use the field_mask utility to derive the update mask. This mask tells the\n # Google Ads API which attributes of the ad group you want to change.\n client.copy_from(\n ad_group_operation.update_mask,\n protobuf_helpers.field_mask(None, ad_group._pb),\n )\n\n # Send the operation in a mutate request and print the resource name of the\n # updated object.\n mutate_ad_groups_response: MutateAdGroupsResponse = (\n ad_group_service.mutate_ad_groups(\n customer_id=customer_id, operations=[ad_group_operation]\n )\n )\n print(\n \"Updated targeting setting of ad group with resource name \"\n f\"'{mutate_ad_groups_response.results[0].resource_name}'; set the \"\n \"audience target restriction to 'Observation'.\"\n )update_audience_target_restriction.py\n```\n\nExample:\n```text\ndef update_targeting_setting(\n client,\n customer_id,\n ad_group_id,\n targeting_setting)\n # Constructs an operation that will update the ad group with the specified\n # resource name.\n ad_group_resource_name = client.path.ad_group(customer_id, ad_group_id)\n operation = client.operation.update_resource.ad_group(ad_group_resource_name) do |ag|\n ag.targeting_setting = targeting_setting\n end\n\n # Issues a mutate request to update the ad group.\n response = client.service.ad_group.mutate_ad_groups(\n customer_id: customer_id,\n operations: [operation],\n )\n\n # Prints the resource name of the updated ad group.\n puts \"Updated targeting setting of ad group with resource name \" \\\n \"#{response.results.first.resource_name}; set the AUDIENCE target \" \\\n \"restriction to 'Observation'.\"\nendupdate_audience_target_restriction.rb\n```\n\nExample:\n```text\nsub update_targeting_setting {\n my ($api_client, $customer_id, $ad_group_id, $targeting_setting) = @_;\n\n # Construct an ad group object with the updated targeting setting.\n my $ad_group = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n targetingSetting => $targeting_setting\n });\n\n # Create an operation that will update the ad group, using the FieldMasks\n # utility to derive the update mask. This mask tells the Google Ads API which\n # attributes of the ad group you want to change.\n my $ad_group_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({\n update => $ad_group,\n updateMask => all_set_fields_of($ad_group)});\n\n # Send the operation in a mutate request and print the resource name of the\n # updated resource.\n my $ad_groups_response = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_operation]});\n\n printf \"Updated targeting setting of ad group with resourceName \" .\n \"'%s'; set the AUDIENCE target restriction to 'Observation'.\\n\",\n $ad_groups_response->{results}[0]{resourceName};\n}update_audience_target_restriction.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.494Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":482,"estimatedTokens":4488}}194{"id":"doc-shared_sets_google_ads_api_google_for_developers-038615e1","source":"documentation","title":"Shared sets | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/targeting/shared-sets","text":"Example:\n```text\n// Copyright 2018 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.advancedoperations;\n\nimport static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.KeywordInfo;\nimport com.google.ads.googleads.v25.enums.KeywordMatchTypeEnum.KeywordMatchType;\nimport com.google.ads.googleads.v25.enums.SharedSetTypeEnum.SharedSetType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.CampaignSharedSet;\nimport com.google.ads.googleads.v25.resources.SharedCriterion;\nimport com.google.ads.googleads.v25.resources.SharedSet;\nimport com.google.ads.googleads.v25.services.CampaignSharedSetOperation;\nimport com.google.ads.googleads.v25.services.CampaignSharedSetServiceClient;\nimport com.google.ads.googleads.v25.services.MutateCampaignSharedSetsResponse;\nimport com.google.ads.googleads.v25.services.MutateSharedCriteriaResponse;\nimport com.google.ads.googleads.v25.services.MutateSharedCriterionResult;\nimport com.google.ads.googleads.v25.services.MutateSharedSetsResponse;\nimport com.google.ads.googleads.v25.services.SharedCriterionOperation;\nimport com.google.ads.googleads.v25.services.SharedCriterionServiceClient;\nimport com.google.ads.googleads.v25.services.SharedSetOperation;\nimport com.google.ads.googleads.v25.services.SharedSetServiceClient;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/** Creates a shared list of negative broad match keywords. It then attaches them to a campaign. */\npublic class CreateAndAttachSharedKeywordSet {\n\n private static class CreateAndAttachSharedKeywordSetParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(names = ArgumentNames.CAMPAIGN_ID, required = true)\n private Long campaignId;\n }\n\n public static void main(String[] args) throws IOException {\n CreateAndAttachSharedKeywordSetParams params = new CreateAndAttachSharedKeywordSetParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.campaignId = Long.parseLong(\"INSERT_CAMPAIGN_ID_HERE\");\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new CreateAndAttachSharedKeywordSet()\n .runExample(googleAdsClient, params.customerId, params.campaignId);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param campaignId the campaign ID.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void runExample(GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n\n // Creates a keywords list to create a shared set of.\n List<String> keywords = Arrays.asList(\"mars cruise\", \"mars hotels\");\n\n // Creates shared negative keyword set.\n SharedSet sharedSet =\n SharedSet.newBuilder()\n .setName(\"API Negative keyword list - \" + getPrintableDateTime())\n .setType(SharedSetType.NEGATIVE_KEYWORDS)\n .build();\n\n SharedSetOperation operation = SharedSetOperation.newBuilder().setCreate(sharedSet).build();\n\n String sharedSetResourceName;\n try (SharedSetServiceClient sharedSetServiceClient =\n googleAdsClient.getLatestVersion().createSharedSetServiceClient()) {\n MutateSharedSetsResponse response =\n sharedSetServiceClient.mutateSharedSets(\n Long.toString(customerId), ImmutableList.of(operation));\n sharedSetResourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Created shared set %s%n\", sharedSetResourceName);\n }\n\n List<SharedCriterionOperation> sharedCriterionOperations = new ArrayList<>();\n for (String keyword : keywords) {\n SharedCriterion sharedCriterion =\n SharedCriterion.newBuilder()\n .setKeyword(\n KeywordInfo.newBuilder()\n .setText(keyword)\n .setMatchType(KeywordMatchType.BROAD)\n .build())\n .setSharedSet(sharedSetResourceName)\n .build();\n\n SharedCriterionOperation sharedCriterionOperation =\n SharedCriterionOperation.newBuilder().setCreate(sharedCriterion).build();\n sharedCriterionOperations.add(sharedCriterionOperation);\n }\n\n try (SharedCriterionServiceClient sharedCriterionServiceClient =\n googleAdsClient.getLatestVersion().createSharedCriterionServiceClient()) {\n MutateSharedCriteriaResponse response =\n sharedCriterionServiceClient.mutateSharedCriteria(\n Long.toString(customerId), sharedCriterionOperations);\n System.out.printf(\"Added %d shared criteria:%n\", response.getResultsCount());\n for (MutateSharedCriterionResult result : response.getResultsList()) {\n System.out.printf(\"\\t%s%n\", result.getResourceName());\n }\n }\n\n String campaignResourceName = ResourceNames.campaign(customerId, campaignId);\n CampaignSharedSet campaignSharedSet =\n CampaignSharedSet.newBuilder()\n .setCampaign(campaignResourceName)\n .setSharedSet(sharedSetResourceName)\n .build();\n\n CampaignSharedSetOperation campaignSharedSetOperation =\n CampaignSharedSetOperation.newBuilder().setCreate(campaignSharedSet).build();\n\n try (CampaignSharedSetServiceClient campaignSharedSetServiceClient =\n googleAdsClient.getLatestVersion().createCampaignSharedSetServiceClient()) {\n MutateCampaignSharedSetsResponse response =\n campaignSharedSetServiceClient.mutateCampaignSharedSets(\n Long.toString(customerId), ImmutableList.of(campaignSharedSetOperation));\n String campaignSharedSetResourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Created campaign shared set %s%n\", campaignSharedSetResourceName);\n }\n }\n}\nCreateAndAttachSharedKeywordSet.java\n```\n\nExample:\n```text\n// Copyright 2019 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System;\nusing System.Collections.Generic;\nusing static Google.Ads.GoogleAds.V25.Enums.KeywordMatchTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.SharedSetTypeEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example creates a shared list of negative broad match keywords. It then\n /// attaches them to a campaign.\n /// </summary>\n public class CreateAndAttachSharedKeywordSet : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"CreateAndAttachSharedKeywordSet\"/>\n /// example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// The ID of the campaign for which shared criterion is updated.\n /// </summary>\n [Option(\"campaignId\", Required = true, HelpText =\n \"The ID of the campaign for which shared criterion is updated.\")]\n public long CampaignId { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n CreateAndAttachSharedKeywordSet codeExample = new CreateAndAttachSharedKeywordSet();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.CampaignId);\n }\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example creates a shared list of negative broad match keywords. It then \" +\n \"attaches them to a campaign.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"campaignId\">The ID of the campaign for which shared criterion is updated.\n /// </param>\n public void Run(GoogleAdsClient client, long customerId, long campaignId)\n {\n SharedSetServiceClient sharedSetService = client.GetService(\n Services.V25.SharedSetService);\n SharedCriterionServiceClient sharedCriterionService =\n client.GetService(Services.V25.SharedCriterionService);\n CampaignSharedSetServiceClient campaignSharedSetService =\n client.GetService(Services.V25.CampaignSharedSetService);\n\n try\n {\n // Keywords to create a shared set of.\n string[] keywords = new string[] { \"mars cruise\", \"mars hotels\" };\n\n // Create shared negative keyword set.\n SharedSet sharedSet = new SharedSet()\n {\n Name = \"API Negative keyword list - \" + ExampleUtilities.GetRandomString(),\n Type = SharedSetType.NegativeKeywords,\n };\n SharedSetOperation operation = new SharedSetOperation()\n {\n Create = sharedSet\n };\n\n MutateSharedSetsResponse sharedSetResponse = sharedSetService.MutateSharedSets(\n customerId.ToString(), new SharedSetOperation[] { operation });\n\n string sharedSetResourceName = sharedSetResponse.Results[0].ResourceName;\n Console.WriteLine($\"Created shared set {sharedSetResourceName}.\");\n\n // Create negative keywords in the shared set.\n List<SharedCriterionOperation> criterionOperations =\n new List<SharedCriterionOperation>();\n\n foreach (string keyword in keywords)\n {\n SharedCriterion sharedCriterion = new SharedCriterion()\n {\n Keyword = new KeywordInfo()\n {\n Text = keyword,\n MatchType = KeywordMatchType.Broad\n },\n SharedSet = sharedSetResourceName\n };\n criterionOperations.Add(new SharedCriterionOperation()\n {\n Create = sharedCriterion\n });\n }\n\n MutateSharedCriteriaResponse criteriaResponse =\n sharedCriterionService.MutateSharedCriteria(\n customerId.ToString(), criterionOperations);\n\n foreach (MutateSharedCriterionResult result in criteriaResponse.Results)\n {\n Console.WriteLine($\"Created shared criterion {result.ResourceName}.\");\n }\n\n // Attach shared set to campaign.\n CampaignSharedSet campaignSet = new CampaignSharedSet()\n {\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n SharedSet = sharedSetResourceName\n };\n\n CampaignSharedSetOperation sharedSetoperation = new CampaignSharedSetOperation()\n {\n Create = campaignSet\n };\n MutateCampaignSharedSetsResponse response =\n campaignSharedSetService.MutateCampaignSharedSets(customerId.ToString(),\n new CampaignSharedSetOperation[] { sharedSetoperation });\n\n Console.WriteLine(\"Created campaign shared set {0}.\",\n response.Results[0].ResourceName);\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n }\n}\nCreateAndAttachSharedKeywordSet.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2018 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\AdvancedOperations;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\KeywordInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\KeywordMatchTypeEnum\\KeywordMatchType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\SharedSetTypeEnum\\SharedSetType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CampaignSharedSet;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\SharedCriterion;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\SharedSet;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CampaignSharedSetOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateCampaignSharedSetsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateSharedCriteriaRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateSharedSetsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\SharedCriterionOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\SharedSetOperation;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * This example creates a shared list of negative broad match keywords. It then attaches them to a\n * campaign.\n */\nclass CreateAndAttachSharedKeywordSet\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const CAMPAIGN_ID = 'INSERT_CAMPAIGN_ID_HERE';\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::CAMPAIGN_ID => GetOpt::REQUIRED_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::CAMPAIGN_ID] ?: self::CAMPAIGN_ID\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $campaignId the ID of the campaign\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n ) {\n // Create shared negative keyword set.\n $sharedSet = new SharedSet([\n 'name' => 'API Negative keyword list - ' . Helper::getPrintableDatetime(),\n 'type' => SharedSetType::NEGATIVE_KEYWORDS,\n ]);\n\n $sharedSetOperation = new SharedSetOperation();\n $sharedSetOperation->setCreate($sharedSet);\n\n $sharedSetServiceClient = $googleAdsClient->getSharedSetServiceClient();\n $response = $sharedSetServiceClient->mutateSharedSets(MutateSharedSetsRequest::build(\n $customerId,\n [$sharedSetOperation]\n ));\n\n $sharedSetResourceName = $response->getResults()[0]->getResourceName();\n print 'Created shared set ' . $sharedSetResourceName . PHP_EOL;\n\n // Creates shared set criteria.\n $sharedCriterionOperations = [];\n // Keywords to create a shared set of.\n $keywords = ['mars cruise', 'mars hotels'];\n foreach ($keywords as $keyword) {\n $sharedCriterion = new SharedCriterion([\n 'keyword' => new KeywordInfo([\n 'text' => $keyword,\n 'match_type' => KeywordMatchType::BROAD\n ]),\n 'shared_set' => $sharedSetResourceName\n ]);\n\n $sharedCriterionOperation = new SharedCriterionOperation();\n $sharedCriterionOperation->setCreate($sharedCriterion);\n $sharedCriterionOperations[] = $sharedCriterionOperation;\n }\n\n $sharedCriterionServiceClient = $googleAdsClient->getSharedCriterionServiceClient();\n $response = $sharedCriterionServiceClient->mutateSharedCriteria(\n MutateSharedCriteriaRequest::build($customerId, $sharedCriterionOperations)\n );\n\n printf(\"Added %d shared criteria:%s\", $response->getResults()->count(), PHP_EOL);\n foreach ($response->getResults() as $addedSharedCriterion) {\n /** @var SharedCriterion $addedSharedCriterion */\n print \"\\t\" . $addedSharedCriterion->getResourceName() . PHP_EOL;\n }\n\n // Creates campaign shared set.\n $campaignSharedSet = new CampaignSharedSet([\n 'campaign' => ResourceNames::forCampaign($customerId, $campaignId),\n 'shared_set' => $sharedSetResourceName\n ]);\n\n $campaignSharedSetOperation = new CampaignSharedSetOperation();\n $campaignSharedSetOperation->setCreate($campaignSharedSet);\n\n $campaignSharedSetServiceClient = $googleAdsClient->getCampaignSharedSetServiceClient();\n $response = $campaignSharedSetServiceClient->mutateCampaignSharedSets(\n MutateCampaignSharedSetsRequest::build($customerId, [$campaignSharedSetOperation])\n );\n\n print 'Created campaign shared set: ' . $response->getResults()[0]->getResourceName()\n . PHP_EOL;\n }\n}\n\nCreateAndAttachSharedKeywordSet::main();\nCreateAndAttachSharedKeywordSet.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2018 Google LLC\n#\n# 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.\n\"\"\"Demonstrates how to create a shared list of negative broad match keywords.\n\nNote that the keywords will be attached to the specified campaign.\n\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import List\nimport uuid\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.errors.types.errors import GoogleAdsError\nfrom google.ads.googleads.v24.resources.types.campaign_shared_set import (\n CampaignSharedSet,\n)\nfrom google.ads.googleads.v24.resources.types.shared_criterion import (\n SharedCriterion,\n)\nfrom google.ads.googleads.v24.resources.types.shared_set import SharedSet\nfrom google.ads.googleads.v24.services.services.campaign_service import (\n CampaignServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.campaign_shared_set_service import (\n CampaignSharedSetServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.campaign_shared_set_service import (\n CampaignSharedSetOperation,\n MutateCampaignSharedSetsResponse,\n)\nfrom google.ads.googleads.v24.services.services.shared_criterion_service import (\n SharedCriterionServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.shared_set_service import (\n SharedSetServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.shared_criterion_service import (\n MutateSharedCriteriaResponse,\n MutateSharedCriterionResult,\n SharedCriterionOperation,\n)\nfrom google.ads.googleads.v24.services.types.shared_set_service import (\n MutateSharedSetsResponse,\n SharedSetOperation,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef main(client: GoogleAdsClient, customer_id: str, campaign_id: str) -> None:\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n shared_set_service: SharedSetServiceClient = client.get_service(\n \"SharedSetService\"\n )\n shared_criterion_service: SharedCriterionServiceClient = client.get_service(\n \"SharedCriterionService\"\n )\n campaign_shared_set_service: CampaignSharedSetServiceClient = (\n client.get_service(\"CampaignSharedSetService\")\n )\n\n # Create shared negative keyword set.\n shared_set_operation: SharedSetOperation = client.get_type(\n \"SharedSetOperation\"\n )\n shared_set: SharedSet = shared_set_operation.create\n shared_set.name = f\"API Negative keyword list - {uuid.uuid4()}\"\n shared_set.type_ = client.enums.SharedSetTypeEnum.NEGATIVE_KEYWORDS\n\n try:\n shared_set_response: MutateSharedSetsResponse = (\n shared_set_service.mutate_shared_sets(\n customer_id=customer_id, operations=[shared_set_operation]\n )\n )\n shared_set_resource_name: str = shared_set_response.results[\n 0\n ].resource_name\n\n print(f'Created shared set \"{shared_set_resource_name}\".')\n except GoogleAdsException as ex:\n handle_googleads_exception(ex)\n\n # Keywords to create a shared set of.\n keywords: List[str] = [\"mars cruise\", \"mars hotels\"]\n shared_criteria_operations: List[SharedCriterionOperation] = []\n for keyword in keywords:\n shared_criterion_operation: SharedCriterionOperation = client.get_type(\n \"SharedCriterionOperation\"\n )\n shared_criterion: SharedCriterion = shared_criterion_operation.create\n shared_criterion.keyword.text = keyword\n shared_criterion.keyword.match_type = (\n client.enums.KeywordMatchTypeEnum.BROAD\n )\n shared_criterion.shared_set = shared_set_resource_name\n shared_criteria_operations.append(shared_criterion_operation)\n try:\n response: MutateSharedCriteriaResponse = (\n shared_criterion_service.mutate_shared_criteria(\n customer_id=customer_id, operations=shared_criteria_operations\n )\n )\n\n shared_criterion_result: MutateSharedCriterionResult\n for shared_criterion_result in response.results:\n print(\n \"Created shared criterion \"\n f'\"{shared_criterion_result.resource_name}\".'\n )\n except GoogleAdsException as ex:\n handle_googleads_exception(ex)\n\n campaign_set_operation: CampaignSharedSetOperation = client.get_type(\n \"CampaignSharedSetOperation\"\n )\n campaign_set: CampaignSharedSet = campaign_set_operation.create\n campaign_set.campaign = campaign_service.campaign_path(\n customer_id, campaign_id\n )\n campaign_set.shared_set = shared_set_resource_name\n\n try:\n campaign_shared_set_response: MutateCampaignSharedSetsResponse = (\n campaign_shared_set_service.mutate_campaign_shared_sets(\n customer_id=customer_id, operations=[campaign_set_operation]\n )\n )\n\n print(\n \"Created campaign shared set \"\n f'\"{campaign_shared_set_response.results[0].resource_name}\".'\n )\n except GoogleAdsException as ex:\n handle_googleads_exception(ex)\n\n\ndef handle_googleads_exception(exception: GoogleAdsException) -> None:\n print(\n f'Request with ID \"{exception.request_id}\" failed with status '\n f'\"{exception.error.code().name}\" and includes the following errors:'\n )\n error: GoogleAdsError\n for error in exception.failure.errors:\n print(f'\\tError with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\n\n\nif __name__ == \"__main__\":\n parser = argparse.ArgumentParser(\n description=(\n \"Adds a list of negative broad match keywords to the \"\n \"provided campaign, for the specified customer.\"\n )\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-i\", \"--campaign_id\", type=str, required=True, help=\"The campaign ID.\"\n )\n args = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n main(googleads_client, args.customer_id, args.campaign_id)\ncreate_and_attach_shared_keyword_set.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2018 Google LLC\n#\n# 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.\n#\n# This example creates a shared list of negative broad match keywords. It then\n# attaches them to a campaign.\n\nrequire \"optparse\"\nrequire \"google/ads/google_ads\"\nrequire \"date\"\n\ndef create_and_attach_shared_keyword_set(customer_id, campaign_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Keywords to create a shared set of.\n keywords = [\"mars cruise\", \"mars hotels\"]\n\n # Create shared negative keyword set.\n shared_set = client.resource.shared_set do |ss|\n ss.name = \"API Negative keyword list - #{(Time.new.to_f * 1000).to_i}\"\n ss.type = :NEGATIVE_KEYWORDS\n end\n\n operation = client.operation.create_resource.shared_set(shared_set)\n\n response = client.service.shared_set.mutate_shared_sets(\n customer_id: customer_id,\n operations: [operation],\n )\n\n shared_set_resource_name = response.results.first.resource_name\n puts \"Created shared set #{shared_set_resource_name}\"\n\n shared_criteria = keywords.map do |keyword|\n client.resource.shared_criterion do |sc|\n sc.keyword = client.resource.keyword_info do |kw|\n kw.text = keyword\n kw.match_type = :BROAD\n end\n sc.shared_set = shared_set_resource_name\n end\n end\n\n operations = shared_criteria.map do |criterion|\n client.operation.create_resource.shared_criterion(criterion)\n end\n\n response = client.service.shared_criterion.mutate_shared_criteria(\n customer_id: customer_id,\n operations: operations,\n )\n\n response.results.each do |result|\n puts \"Created shared criterion #{result.resource_name}\"\n end\n\n campaign_set = client.resource.campaign_shared_set do |css|\n css.campaign = client.path.campaign(customer_id, campaign_id)\n css.shared_set = shared_set_resource_name\n end\n\n operation = client.operation.create_resource.campaign_shared_set(campaign_set)\n\n response = client.service.campaign_shared_set.mutate_campaign_shared_sets(\n customer_id: customer_id,\n operations: [operation],\n )\n\n puts \"Created campaign shared set #{response.results.first.resource_name}\"\nend\n\nif __FILE__ == $PROGRAM_NAME\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:campaign_id] = 'INSERT_CAMPAIGN_ID_HERE'\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: ruby %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-c', '--campaign-id CAMPAIGN-ID', String, 'Campaign ID') do |v|\n options[:campaign_id] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n create_and_attach_shared_keyword_set(options.fetch(:customer_id).tr(\"-\", \"\"),\n options[:campaign_id])\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\ncreate_and_attach_shared_keyword_set.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2019, Google LLC\n#\n# 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# http://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.\n#\n# This example creates a shared list of negative broad match keywords. It then\n# attaches them to a campaign.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::SharedSet;\nuse Google::Ads::GoogleAds::V25::Resources::SharedCriterion;\nuse Google::Ads::GoogleAds::V25::Resources::CampaignSharedSet;\nuse Google::Ads::GoogleAds::V25::Common::KeywordInfo;\nuse Google::Ads::GoogleAds::V25::Enums::SharedSetTypeEnum qw(NEGATIVE_KEYWORDS);\nuse Google::Ads::GoogleAds::V25::Enums::KeywordMatchTypeEnum qw(BROAD);\nuse Google::Ads::GoogleAds::V25::Services::SharedSetService::SharedSetOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::SharedCriterionService::SharedCriterionOperation;\nuse\n Google::Ads::GoogleAds::V25::Services::CampaignSharedSetService::CampaignSharedSetOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\nmy $campaign_id = \"INSERT_CAMPAIGN_ID_HERE\";\n\nsub create_and_attach_shared_keyword_set {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n # Create shared negative keyword set.\n my $shared_set = Google::Ads::GoogleAds::V25::Resources::SharedSet->new({\n name => \"API Negative keyword list - \" . uniqid(),\n type => NEGATIVE_KEYWORDS\n });\n\n my $shared_set_operation =\n Google::Ads::GoogleAds::V25::Services::SharedSetService::SharedSetOperation\n ->new({\n create => $shared_set\n });\n\n my $shared_sets_response = $api_client->SharedSetService()->mutate({\n customerId => $customer_id,\n operations => [$shared_set_operation]});\n\n my $shared_set_resource_name =\n $shared_sets_response->{results}[0]{resourceName};\n printf \"Created shared set: '%s'.\\n\", $shared_set_resource_name;\n\n # Create shared set criterion.\n my $shared_criterion_operations = [];\n # Keywords to create a shared set of.\n my $keywords = ['mars cruise', 'mars hotels'];\n foreach my $keyword (@$keywords) {\n my $shared_criterion =\n Google::Ads::GoogleAds::V25::Resources::SharedCriterion->new({\n keyword => Google::Ads::GoogleAds::V25::Common::KeywordInfo->new({\n text => $keyword,\n matchType => BROAD\n }\n ),\n sharedSet => $shared_set_resource_name\n });\n\n my $shared_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::SharedCriterionService::SharedCriterionOperation\n ->new({\n create => $shared_criterion\n });\n push @$shared_criterion_operations, $shared_criterion_operation;\n }\n\n my $shared_criteria_response = $api_client->SharedCriterionService()->mutate({\n customerId => $customer_id,\n operations => $shared_criterion_operations\n });\n\n my $shared_criterion_results = $shared_criteria_response->{results};\n printf \"Added %d shared criterion:\\n\", scalar @$shared_criterion_results;\n foreach my $shared_criterion_result (@$shared_criterion_results) {\n printf \"\\t%s\\n\", $shared_criterion_result->{resourceName};\n }\n\n # Create campaign shared set.\n my $campaign_shared_set =\n Google::Ads::GoogleAds::V25::Resources::CampaignSharedSet->new({\n campaign => Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $campaign_id\n ),\n sharedSet => $shared_set_resource_name\n });\n\n my $campaign_shared_set_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignSharedSetService::CampaignSharedSetOperation\n ->new({\n create => $campaign_shared_set\n });\n\n my $campaign_shared_sets_response =\n $api_client->CampaignSharedSetService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_shared_set_operation]});\n\n printf \"Created campaign shared set: '%s'.\\n\",\n $campaign_shared_sets_response->{results}[0]{resourceName};\n return 1;\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\"customer_id=s\" => \\$customer_id, \"campaign_id=i\" => \\$campaign_id);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id, $campaign_id);\n\n# Call the example.\ncreate_and_attach_shared_keyword_set($api_client, $customer_id =~ s/-//gr,\n $campaign_id);\n\n=pod\n\n=head1 NAME\n\ncreate_and_attach_shared_keyword_set\n\n=head1 DESCRIPTION\n\nThis example creates a shared list of negative broad match keywords. It then attaches\nthem to a campaign.\n\n=head1 SYNOPSIS\n\ncreate_and_attach_shared_keyword_set.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -campaign_id The campaign ID.\n\n=cut\ncreate_and_attach_shared_keyword_set.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.497Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":1087,"estimatedTokens":10304}}195{"id":"doc-get_started_with_performance_max_google_ads_api_-0d46701c","source":"documentation","title":"Get started with Performance Max | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/getting-started","text":"Example:\n```text\n/** Creates a MutateOperation that creates a new CampaignBudget. */\nprivate MutateOperation createCampaignBudgetOperation(long customerId) {\n CampaignBudget campaignBudget =\n CampaignBudget.newBuilder()\n .setName(\"Performance Max campaign budget #\" + getPrintableDateTime())\n // The budget period already defaults to DAILY.\n .setAmountMicros(50_000_000)\n .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)\n // A Performance Max campaign cannot use a shared campaign budget.\n .setExplicitlyShared(false)\n // Set a temporary ID in the budget's resource name, so it can be referenced\n // by the campaign in later steps.\n .setResourceName(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignBudgetOperation(\n CampaignBudgetOperation.newBuilder().setCreate(campaignBudget).build())\n .build();\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a MutateOperation that creates a new CampaignBudget.\n///\n/// A temporary ID will be assigned to this campaign budget so that it can be\n/// referenced by other objects being created in the same Mutate request.\n/// </summary>\n/// <param name=\"budgetResourceName\">The temporary resource name of the budget to\n/// create.</param>\n/// <returns>A MutateOperation that creates a CampaignBudget.</returns>\nprivate MutateOperation CreateCampaignBudgetOperation(string budgetResourceName)\n{\n MutateOperation operation = new MutateOperation\n {\n CampaignBudgetOperation = new CampaignBudgetOperation\n {\n Create = new CampaignBudget\n {\n Name = \"Performance Max campaign budget #\"\n + ExampleUtilities.GetRandomString(),\n\n // The budget period already defaults to DAILY.\n AmountMicros = 50000000,\n\n // A Performance Max campaign cannot use a shared campaign budget.\n ExplicitlyShared = false,\n\n // Set a temporary ID in the budget's resource name so it can be referenced\n // by the campaign in later steps.\n ResourceName = budgetResourceName\n }\n }\n };\n\n return operation;\n}\nAddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createCampaignBudgetOperation(int $customerId): MutateOperation\n{\n // Creates a mutate operation that creates a campaign budget operation.\n return new MutateOperation([\n 'campaign_budget_operation' => new CampaignBudgetOperation([\n 'create' => new CampaignBudget([\n // Sets a temporary ID in the budget's resource name so it can be referenced\n // by the campaign in later steps.\n 'resource_name' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n 'name' => 'Performance Max campaign budget #' . Helper::getPrintableDatetime(),\n // The budget period already defaults to DAILY.\n 'amount_micros' => 50000000,\n 'delivery_method' => BudgetDeliveryMethod::STANDARD,\n // A Performance Max campaign cannot use a shared campaign budget.\n 'explicitly_shared' => false\n ])\n ])\n ]);\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\ndef create_campaign_budget_operation(\n client: GoogleAdsClient,\n customer_id: str,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new CampaignBudget.\n\n A temporary ID will be assigned to this campaign budget so that it can be\n referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a MutateOperation that creates a CampaignBudget.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_budget_operation: CampaignBudgetOperation = (\n mutate_operation.campaign_budget_operation\n )\n campaign_budget: CampaignBudget = campaign_budget_operation.create\n campaign_budget.name = f\"Performance Max campaign budget #{uuid4()}\"\n # The budget period already defaults to DAILY.\n campaign_budget.amount_micros = 50000000\n campaign_budget.delivery_method = (\n client.enums.BudgetDeliveryMethodEnum.STANDARD\n )\n # A Performance Max campaign cannot use a shared campaign budget.\n campaign_budget.explicitly_shared = False\n\n # Set a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n campaign_budget.resource_name = client.get_service(\n \"CampaignBudgetService\"\n ).campaign_budget_path(customer_id, _BUDGET_TEMPORARY_ID)\n\n return mutate_operationadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates a MutateOperation that creates a new CampaignBudget.\n#\n# A temporary ID will be assigned to this campaign budget so that it can be\n# referenced by other objects being created in the same Mutate request.\ndef create_campaign_budget_operation(client, customer_id)\n client.operation.mutate do |m|\n m.campaign_budget_operation = client.operation.create_resource.campaign_budget do |cb|\n cb.name = \"Performance Max campaign budget #{SecureRandom.uuid}\"\n # The budget period already defaults to DAILY.\n cb.amount_micros = 50_000_000\n cb.delivery_method = :STANDARD\n # A Performance Max campaign cannot use a shared campaign budget.\n cb.explicitly_shared = false\n\n # Set a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n cb.resource_name = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n end\n end\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_campaign_budget_operation {\n my ($customer_id) = @_;\n\n # Create a mutate operation that creates a campaign budget operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignBudgetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::CampaignBudget->new(\n {\n # Set a temporary ID in the budget's resource name so it can be\n # referenced by the campaign in later steps.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n name => \"Performance Max campaign budget #\" . uniqid(),\n # The budget period already defaults to DAILY.\n amountMicros => 50000000,\n deliveryMethod => STANDARD,\n # A Performance Max campaign cannot use a shared campaign budget.\n explicitlyShared => \"false\",\n })})});\n}add_performance_max_campaign.pl\n```\n\nExample:\n```text\n/** Creates a MutateOperation that creates a new Performance Max campaign. */\nprivate MutateOperation createPerformanceMaxCampaignOperation(\n long customerId, boolean brandGuidelinesEnabled) {\n TextGuidelines textGuidelines =\n TextGuidelines.newBuilder()\n // Specifies a list of terms that should not be used in any auto-generated\n // text assets.\n .addAllTermExclusions(ImmutableList.of(\"cheap\", \"free\"))\n // Specifies freeform messaging restriction prompts that will apply to all\n // auto-generated text assets.\n .addMessagingRestrictions(\n MessagingRestriction.newBuilder()\n .setRestrictionText(\"Don't mention competitor names\")\n .setRestrictionType(\n MessagingRestrictionType.RESTRICTION_BASED_EXCLUSION)\n .build())\n .build();\n Campaign performanceMaxCampaign =\n Campaign.newBuilder()\n .setName(\"Performance Max campaign #\" + getPrintableDateTime())\n // Sets the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n .setStatus(CampaignStatus.PAUSED)\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n .setAdvertisingChannelType(AdvertisingChannelType.PERFORMANCE_MAX)\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n .setMaximizeConversionValue(\n MaximizeConversionValue.newBuilder().setTargetRoas(3.5).build())\n // Sets if the campaign is enabled for brand guidelines. For more information on brand\n // guidelines, see https://support.google.com/google-ads/answer/14934472.\n .setBrandGuidelinesEnabled(brandGuidelinesEnabled)\n // Sets the text guidelines.\n .setTextGuidelines(textGuidelines)\n // Assigns the resource name with a temporary ID.\n .setResourceName(\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID))\n // Sets the budget using the given budget resource name.\n .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID))\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n // Optional fields.\n .setStartDateTime(new DateTime().plusDays(1).toString(\"yyyy-MM-dd 00:00:00\"))\n .setEndDateTime(new DateTime().plusDays(365).toString(\"yyyy-MM-dd 23:59:59\"))\n // Configures the optional opt-in/out status for asset automation settings.\n .addAllAssetAutomationSettings(ImmutableList.of(\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_IMAGE_EXTRACTION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(\n AssetAutomationType.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_ENHANCED_YOUTUBE_VIDEOS)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build(),\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.GENERATE_IMAGE_ENHANCEMENT)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN).build()))\n .build();\n\n return MutateOperation.newBuilder()\n .setCampaignOperation(\n CampaignOperation.newBuilder().setCreate(performanceMaxCampaign).build())\n .build();\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n/// Creates a MutateOperation that creates a new Performance Max campaign.\n/// <param name=\"campaignResourceName\">The campaign resource name.</param>\n/// <param name=\"campaignBudgetResourceName\">The campaign budget resource name.</param>\n/// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n/// <returns>A MutateOperations that will create this new campaign.</returns>\nprivate MutateOperation CreatePerformanceMaxCampaignOperation(\n string campaignResourceName,\n string campaignBudgetResourceName,\n bool brandGuidelinesEnabled)\n{\n Campaign.Types.TextGuidelines textGuidelines =\n new Campaign.Types.TextGuidelines();\n textGuidelines.TermExclusions.AddRange([\"cheap\", \"free\"]);\n textGuidelines.MessagingRestrictions.Add(\n new Campaign.Types.MessagingRestriction()\n {\n RestrictionText = \"Don't mention competitor names\",\n RestrictionType = MessagingRestrictionType.RestrictionBasedExclusion\n }\n );\n\n Campaign campaign = new Campaign()\n {\n Name = \"Performance Max campaign #\" + ExampleUtilities.GetRandomString(),\n\n // Set the campaign status as PAUSED. The campaign is the only entity in\n // the mutate request that should have its status set.\n Status = CampaignStatus.Paused,\n\n // All Performance Max campaigns have an AdvertisingChannelType of\n // PerformanceMax. The AdvertisingChannelSubType should not be set.\n AdvertisingChannelType = AdvertisingChannelType.PerformanceMax,\n\n // Bidding strategy must be set directly on the campaign. Setting a\n // portfolio bidding strategy by resource name is not supported. Max\n // Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns. BiddingStrategyType is\n // read-only and cannot be set by the API. An optional ROAS (Return on\n // Advertising Spend) can be set to enable the MaximizeConversionValue\n // bidding strategy. The ROAS value must be specified as a ratio in the API.\n // It is calculated by dividing \"total value\" by \"total spend\".\n //\n // For more information on Maximize Conversion Value, see the support\n // article:\n // http://support.google.com/google-ads/answer/7684216.\n //\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n MaximizeConversionValue = new MaximizeConversionValue()\n {\n TargetRoas = 3.5\n },\n\n // Use the temporary resource name created earlier\n ResourceName = campaignResourceName,\n\n // Set the budget using the given budget resource name.\n CampaignBudget = campaignBudgetResourceName,\n\n // Set if the campaign is enabled for brand guidelines. For more information\n // on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n BrandGuidelinesEnabled = brandGuidelinesEnabled,\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n\n TextGuidelines = textGuidelines,\n\n // Optional fields\n StartDateTime = DateTime.Now.AddDays(1).ToString(\"yyyyMMdd 00:00:00\"),\n EndDateTime = DateTime.Now.AddDays(365).ToString(\"yyyyMMdd 23:59:59\")\n };\n\n campaign.AssetAutomationSettings.AddRange(new[]{\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateImageExtraction,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.FinalUrlExpansionTextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.TextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateEnhancedYoutubeVideos,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n new Campaign.Types.AssetAutomationSetting\n {\n AssetAutomationType = AssetAutomationType.GenerateImageEnhancement,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n },\n });\n\n MutateOperation operation = new MutateOperation()\n {\n CampaignOperation = new CampaignOperation()\n {\n Create = campaign\n }\n };\n\n return operation;\n}\nAddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createPerformanceMaxCampaignOperation(\n int $customerId,\n bool $brandGuidelinesEnabled\n): MutateOperation {\n // Creates a mutate operation that creates a campaign operation.\n return new MutateOperation([\n 'campaign_operation' => new CampaignOperation([\n 'create' => new Campaign([\n 'name' => 'Performance Max campaign #' . Helper::getPrintableDatetime(),\n // Assigns the resource name with a temporary ID.\n 'resource_name' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n // Sets the budget using the given budget resource name.\n 'campaign_budget' => ResourceNames::forCampaignBudget(\n $customerId,\n self::BUDGET_TEMPORARY_ID\n ),\n // The campaign is the only entity in the mutate request that should have its\n // status set.\n // Recommendation: Set the campaign to PAUSED when creating it to prevent\n // the ads from immediately serving.\n 'status' => CampaignStatus::PAUSED,\n // All Performance Max campaigns have an advertising_channel_type of\n // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n 'advertising_channel_type' => AdvertisingChannelType::PERFORMANCE_MAX,\n\n // Bidding strategy must be set directly on the campaign.\n // Setting a portfolio bidding strategy by resource name is not supported.\n // Max Conversion and Maximize Conversion Value are the only strategies\n // supported for Performance Max campaigns.\n // An optional ROAS (Return on Advertising Spend) can be set for\n // maximize_conversion_value. The ROAS value must be specified as a ratio in\n // the API. It is calculated by dividing \"total value\" by \"total spend\".\n // For more information on Maximize Conversion Value, see the support\n // article: http://support.google.com/google-ads/answer/7684216.\n // A target_roas of 3.5 corresponds to a 350% return on ad spend.\n 'maximize_conversion_value' => new MaximizeConversionValue([\n 'target_roas' => 3.5\n ]),\n\n 'asset_automation_settings' => [\n new AssetAutomationSetting([\n 'asset_automation_type' => AssetAutomationType::TEXT_ASSET_AUTOMATION,\n 'asset_automation_status' => AssetAutomationStatus::OPTED_IN\n ]),\n new AssetAutomationSetting([\n 'asset_automation_type' => AssetAutomationType::URL_EXPANSION,\n 'asset_automation_status' => AssetAutomationStatus::OPTED_IN\n ])\n ],\n\n\n // Sets if the campaign is enabled for brand guidelines. For more information\n // on brand guidelines, see\n // https://support.google.com/google-ads/answer/14934472.\n 'brand_guidelines_enabled' => $brandGuidelinesEnabled,\n\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,\n\n // Optional fields.\n 'start_date_time' => date('Y-m-d 00:00:00', strtotime('+1 day')),\n 'end_date_time' => date('Y-m-d 23:59:59', strtotime('+365 days'))\n ])\n ])\n ]);\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\ndef create_performance_max_campaign_operation(\n client: GoogleAdsClient,\n customer_id: str,\n brand_guidelines_enabled: bool,\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new Performance Max campaign.\n\n A temporary ID will be assigned to this campaign so that it can\n be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n\n Returns:\n a MutateOperation that creates a campaign.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign: Campaign = mutate_operation.campaign_operation.create\n campaign.name = f\"Performance Max campaign #{uuid4()}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.PERFORMANCE_MAX\n )\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n campaign.bidding_strategy_type = (\n client.enums.BiddingStrategyTypeEnum.MAXIMIZE_CONVERSION_VALUE\n )\n campaign.maximize_conversion_value.target_roas = 3.5\n\n # Set if the campaign is enabled for brand guidelines. For more information\n # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n campaign.brand_guidelines_enabled = brand_guidelines_enabled\n\n # Assign the resource name with a temporary ID.\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n campaign.resource_name = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n # Set the budget using the given budget resource name.\n campaign.campaign_budget = campaign_service.campaign_budget_path(\n customer_id, _BUDGET_TEMPORARY_ID\n )\n\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n # Optional fields\n campaign.start_date_time = (datetime.now() + timedelta(1)).strftime(\n \"%Y%m%d 00:00:00\"\n )\n campaign.end_date_time = (datetime.now() + timedelta(365)).strftime(\n \"%Y%m%d 23:59:59\"\n )\n\n campaign.text_guidelines.term_exclusions = [\"cheap\", \"free\"]\n messaging_restriction = campaign.MessagingRestriction()\n messaging_restriction.restriction_text = \"Don't mention competitor names\"\n messaging_restriction.restriction_type = (\n client.enums.MessagingRestrictionTypeEnum.RESTRICTION_BASED_EXCLUSION\n )\n campaign.text_guidelines.messaging_restrictions.append(\n messaging_restriction\n )\n\n # Configures the optional opt-in/out status for asset automation settings.\n for asset_automation_type_enum in [\n client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_EXTRACTION,\n client.enums.AssetAutomationTypeEnum.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,\n client.enums.AssetAutomationTypeEnum.TEXT_ASSET_AUTOMATION,\n client.enums.AssetAutomationTypeEnum.GENERATE_ENHANCED_YOUTUBE_VIDEOS,\n client.enums.AssetAutomationTypeEnum.GENERATE_IMAGE_ENHANCEMENT,\n ]:\n asset_automattion_setting: Campaign.AssetAutomationSetting = (\n client.get_type(\"Campaign\").AssetAutomationSetting()\n )\n asset_automattion_setting.asset_automation_type = (\n asset_automation_type_enum\n )\n asset_automattion_setting.asset_automation_status = (\n client.enums.AssetAutomationStatusEnum.OPTED_IN\n )\n campaign.asset_automation_settings.append(asset_automattion_setting)\n\n return mutate_operationadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates a MutateOperation that creates a new Performance Max campaign.\n#\n# A temporary ID will be assigned to this campaign so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_performance_max_campaign_operation(\n client,\n customer_id,\n brand_guidelines_enabled)\n client.operation.mutate do |m|\n m.campaign_operation = client.operation.create_resource.campaign do |c|\n c.name = \"Performance Max campaign #{SecureRandom.uuid}\"\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n c.status = :PAUSED\n # All Performance Max campaigns have an advertising_channel_type of\n # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.\n c.advertising_channel_type = :PERFORMANCE_MAX\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximize_conversion_value. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A target_roas of 3.5 corresponds to a 350% return on ad spend.\n c.bidding_strategy_type = :MAXIMIZE_CONVERSION_VALUE\n c.maximize_conversion_value = client.resource.maximize_conversion_value do |mcv|\n mcv.target_roas = 3.5\n end\n\n # Configures the optional opt-in/out status for asset automation settings.\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_IMAGE_EXTRACTION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :TEXT_ASSET_AUTOMATION\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_ENHANCED_YOUTUBE_VIDEOS\n aas.asset_automation_status = :OPTED_IN\n end\n c.asset_automation_settings << client.resource.asset_automation_setting do |aas|\n aas.asset_automation_type = :GENERATE_IMAGE_ENHANCEMENT\n aas.asset_automation_status = :OPTED_IN\n end\n\n # Set if the campaign is enabled for brand guidelines. For more\n # information on brand guidelines, see\n # https://support.google.com/google-ads/answer/14934472.\n c.brand_guidelines_enabled = brand_guidelines_enabled\n\n # Assign the resource name with a temporary ID.\n c.resource_name = client.path.campaign(customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n # Set the budget using the given budget resource name.\n c.campaign_budget = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n # Optional fields\n c.start_date_time = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d %H:%M:%S')\n c.end_date_time = DateTime.parse(Date.today.next_year.to_s).strftime('%Y%m%d %H:%M:%S')\n end\n end\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_performance_max_campaign_operation {\n my ($customer_id, $brand_guidelines_enabled) = @_;\n # Configures the optional opt-in/out status for asset automation settings.\n # When we create the campaign object, we set campaign->{assetAutomationSettings}\n # equal to $asset_automation_settings.\n my $asset_automation_settings = [];\n my $asset_automation_types = [\n GENERATE_IMAGE_EXTRACTION, FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,\n TEXT_ASSET_AUTOMATION, GENERATE_ENHANCED_YOUTUBE_VIDEOS,\n GENERATE_IMAGE_ENHANCEMENT\n ];\n foreach my $asset_automation_type (@$asset_automation_types) {\n push @$asset_automation_settings,\n Google::Ads::GoogleAds::V25::Resources::AssetAutomationSetting->new({\n assetAutomationStatus => OPTED_IN,\n assetAutomationType => $asset_automation_type\n });\n }\n\n my $text_guidelines =\n Google::Ads::GoogleAds::V25::Resources::TextGuidelines->new({\n termExclusions => [\"cheap\", \"free\"],\n messagingRestrictions => [\n Google::Ads::GoogleAds::V25::Resources::MessagingRestriction->new({\n restrictionText => \"Don't mention competitor names\",\n restrictionType => RESTRICTION_BASED_EXCLUSION\n })]});\n\n # Create a mutate operation that creates a campaign operation.\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n # Assign the resource name with a temporary ID.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n name => \"Performance Max campaign #\" . uniqid(),\n # Set the budget using the given budget resource name.\n campaignBudget =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n ),\n # Set the campaign status as PAUSED. The campaign is the only entity in\n # the mutate request that should have its status set.\n status =>\n Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n # All Performance Max campaigns have an advertisingChannelType of\n # PERFORMANCE_MAX. The advertisingChannelSubType should not be set.\n advertisingChannelType => PERFORMANCE_MAX,\n\n # Bidding strategy must be set directly on the campaign.\n # Setting a portfolio bidding strategy by resource name is not supported.\n # Max Conversion and Maximize Conversion Value are the only strategies\n # supported for Performance Max campaigns.\n # An optional ROAS (Return on Advertising Spend) can be set for\n # maximizeConversionValue. The ROAS value must be specified as a ratio in\n # the API. It is calculated by dividing \"total value\" by \"total spend\".\n # For more information on Maximize Conversion Value, see the support\n # article: http://support.google.com/google-ads/answer/7684216.\n # A targetRoas of 3.5 corresponds to a 350% return on ad spend.\n maximizeConversionValue =>\n Google::Ads::GoogleAds::V25::Common::MaximizeConversionValue->\n new({\n targetRoas => 3.5\n }\n ),\n\n # Set if the campaign is enabled for brand guidelines. For more information\n # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.\n brandGuidelinesEnabled => $brand_guidelines_enabled,\n\n # Configures the optional opt-in/out status for asset automation settings.\n assetAutomationSettings => $asset_automation_settings,\n\n # Set the text guidelines.\n textGuidelines => $text_guidelines,\n\n # Optional fields.\n startDateTime =>\n strftime(\"%Y%m%d 00:00:00\", localtime(time + 60 * 60 * 24)),\n endDateTime => strftime(\n \"%Y%m%d 23:59:59\",\n localtime(time + 60 * 60 * 24 * 365)\n ),\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n })})});\n}add_performance_max_campaign.pl\n```\n\nExample:\n```text\n/** Retrieves the list of customer conversion goals. */\nprivate static List<CustomerConversionGoal> getCustomerConversionGoals(\n GoogleAdsClient googleAdsClient, long customerId) {\n String query =\n \"SELECT customer_conversion_goal.category, customer_conversion_goal.origin \"\n + \"FROM customer_conversion_goal\";\n\n List<CustomerConversionGoal> customerConversionGoals = new ArrayList<>();\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // The number of conversion goals is typically less than 50, so we use\n // GoogleAdsService.search instead of search_stream.\n SearchPagedResponse response =\n googleAdsServiceClient.search(Long.toString(customerId), query);\n for (GoogleAdsRow googleAdsRow : response.iterateAll()) {\n customerConversionGoals.add(googleAdsRow.getCustomerConversionGoal());\n }\n }\n\n return customerConversionGoals;\n}\n\n/** Creates a list of MutateOperations that override customer conversion goals. */\nprivate static List<MutateOperation> createConversionGoalOperations(\n long customerId, List<CustomerConversionGoal> customerConversionGoals) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n // To override the customer conversion goals, we will change the\n // biddability of each of the customer conversion goals so that only\n // the desired conversion goal is biddable in this campaign.\n for (CustomerConversionGoal customerConversionGoal : customerConversionGoals) {\n ConversionActionCategory category = customerConversionGoal.getCategory();\n ConversionOrigin origin = customerConversionGoal.getOrigin();\n String campaignConversionGoalResourceName =\n ResourceNames.campaignConversionGoal(\n customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID, category, origin);\n CampaignConversionGoal.Builder campaignConversionGoalBuilder =\n CampaignConversionGoal.newBuilder().setResourceName(campaignConversionGoalResourceName);\n // Change the biddability for the campaign conversion goal.\n // Set biddability to True for the desired (category, origin).\n // Set biddability to False for all other conversion goals.\n // Note:\n // 1- It is assumed that this Conversion Action\n // (category=PURCHASE, origin=WEBSITE) exists in this account.\n // 2- More than one goal can be biddable if desired. This example\n // shows only one.\n if (category == ConversionActionCategory.PURCHASE && origin == ConversionOrigin.WEBSITE) {\n campaignConversionGoalBuilder.setBiddable(true);\n } else {\n campaignConversionGoalBuilder.setBiddable(false);\n }\n CampaignConversionGoal campaignConversionGoal = campaignConversionGoalBuilder.build();\n CampaignConversionGoalOperation campaignConversionGoalOperation =\n CampaignConversionGoalOperation.newBuilder()\n .setUpdate(campaignConversionGoal)\n .setUpdateMask(FieldMasks.allSetFieldsOf(campaignConversionGoal))\n .build();\n mutateOperations.add(\n MutateOperation.newBuilder()\n .setCampaignConversionGoalOperation(campaignConversionGoalOperation)\n .build());\n }\n return mutateOperations;\n}\nAddPerformanceMaxRetailCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a MutateOperation that links an asset to an asset group.\n/// </summary>\n/// <param name=\"fieldType\">The field type of the asset to be linked.</param>\n/// <param name=\"linkedEntityResourceName\">The resource name of the entity (asset group or\n/// campaign) to link the asset to.</param>\n/// <param name=\"assetResourceName\">The resource name of the text asset to be\n/// linked.</param>\n/// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n/// <returns>A MutateOperation that links an asset to an asset group.</returns>\nprivate MutateOperation CreateLinkAssetOperation(\n AssetFieldType fieldType,\n string linkedEntityResourceName,\n string assetResourceName,\n bool brandGuidelinesEnabled = false)\n{ if (brandGuidelinesEnabled)\n {\n return new MutateOperation()\n {\n CampaignAssetOperation = new CampaignAssetOperation()\n {\n Create = new CampaignAsset()\n {\n FieldType = fieldType,\n Campaign = linkedEntityResourceName,\n Asset = assetResourceName\n }\n }\n };\n } else\n { return new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = fieldType,\n AssetGroup = linkedEntityResourceName,\n Asset = assetResourceName\n }\n }\n };\n }\n}\nAddPerformanceMaxRetailCampaign.cs\n```\n\nExample:\n```text\nprivate static function getCustomerConversionGoals(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n): array {\n $customerConversionGoals = [];\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n // Creates a query that retrieves all customer conversion goals.\n $query = 'SELECT customer_conversion_goal.category, customer_conversion_goal.origin ' .\n 'FROM customer_conversion_goal';\n // The number of conversion goals is typically less than 50 so we use a search request\n // instead of search stream.\n $response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n // Iterates over all rows in all pages and builds the list of conversion goals.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $customerConversionGoals[] = [\n 'category' => $googleAdsRow->getCustomerConversionGoal()->getCategory(),\n 'origin' => $googleAdsRow->getCustomerConversionGoal()->getOrigin()\n ];\n }\n\n return $customerConversionGoals;\n}\n\n/**\n * Creates a list of MutateOperations that override customer conversion goals.\n *\n * @param int $customerId the customer ID\n * @param array $customerConversionGoals the list of customer conversion goals that will be\n * overridden\n * @return MutateOperation[] a list of MutateOperations that update campaign conversion goals\n */\nprivate static function createConversionGoalOperations(\n int $customerId,\n array $customerConversionGoals\n): array {\n $operations = [];\n\n // To override the customer conversion goals, we will change the biddability of each of the\n // customer conversion goals so that only the desired conversion goal is biddable in this\n // campaign.\n foreach ($customerConversionGoals as $customerConversionGoal) {\n $campaignConversionGoal = new CampaignConversionGoal([\n 'resource_name' => ResourceNames::forCampaignConversionGoal(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n ConversionActionCategory::name($customerConversionGoal['category']),\n ConversionOrigin::name($customerConversionGoal['origin'])\n )\n ]);\n // Changes the biddability for the campaign conversion goal.\n // Sets biddability to true for the desired (category, origin).\n // Sets biddability to false for all other conversion goals.\n // Note:\n // 1- It is assumed that this Conversion Action\n // (category=PURCHASE, origin=WEBSITE) exists in this account.\n // 2- More than one goal can be biddable if desired. This example\n // shows only one.\n if (\n $customerConversionGoal[\"category\"] === ConversionActionCategory::PURCHASE\n && $customerConversionGoal[\"origin\"] === ConversionOrigin::WEBSITE\n ) {\n $campaignConversionGoal->setBiddable(true);\n } else {\n $campaignConversionGoal->setBiddable(false);\n }\n\n $operations[] = new MutateOperation([\n 'campaign_conversion_goal_operation' => new CampaignConversionGoalOperation([\n 'update' => $campaignConversionGoal,\n // Sets the update mask on the operation. Here the update mask will be a list\n // of all the fields that were set on the update object.\n 'update_mask' => FieldMasks::allSetFieldsOf($campaignConversionGoal)\n ])\n ]);\n }\n\n return $operations;\n}AddPerformanceMaxRetailCampaign.php\n```\n\nExample:\n```text\ndef get_customer_conversion_goals(\n client: GoogleAdsClient, customer_id: str\n) -> List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n]:\n \"\"\"Retrieves the list of customer conversion goals.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a list of dicts containing the category and origin of customer\n conversion goals.\n \"\"\"\n ga_service: GoogleAdsServiceClient = client.get_service(\"GoogleAdsService\")\n customer_conversion_goals: List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n ] = []\n query: str = \"\"\"\n SELECT\n customer_conversion_goal.category,\n customer_conversion_goal.origin\n FROM customer_conversion_goal\n \"\"\"\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService.search instead of search_stream.\n search_request: SearchGoogleAdsRequest = client.get_type(\n \"SearchGoogleAdsRequest\"\n )\n search_request.customer_id = customer_id\n search_request.query = query\n results: SearchGoogleAdsResponse = ga_service.search(request=search_request)\n\n # Iterate over the results and build the list of conversion goals.\n for row in results:\n customer_conversion_goals.append(\n {\n \"category\": row.customer_conversion_goal.category,\n \"origin\": row.customer_conversion_goal.origin,\n }\n )\n return customer_conversion_goals\n\n\ndef create_conversion_goal_operations(\n client: GoogleAdsClient,\n customer_id: str,\n customer_conversion_goals: List[\n Dict[\n str,\n Union[\n ConversionActionCategoryEnum.ConversionActionCategory,\n ConversionOriginEnum.ConversionOrigin,\n ],\n ]\n ],\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that override customer conversion goals.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n customer_conversion_goals: the list of customer conversion goals that\n will be overridden.\n\n Returns:\n MutateOperations that update campaign conversion goals.\n \"\"\"\n campaign_conversion_goal_service: CampaignConversionGoalServiceClient = (\n client.get_service(\"CampaignConversionGoalService\")\n )\n operations: List[MutateOperation] = []\n\n # To override the customer conversion goals, we will change the\n # biddability of each of the customer conversion goals so that only\n # the desired conversion goal is biddable in this campaign.\n for customer_goal_dict in customer_conversion_goals:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_conversion_goal: CampaignConversionGoal = (\n mutate_operation.campaign_conversion_goal_operation.update\n )\n\n category_enum_value: (\n ConversionActionCategoryEnum.ConversionActionCategory\n ) = customer_goal_dict[\"category\"]\n origin_enum_value: ConversionOriginEnum.ConversionOrigin = (\n customer_goal_dict[\"origin\"]\n )\n\n campaign_conversion_goal.resource_name = (\n campaign_conversion_goal_service.campaign_conversion_goal_path(\n customer_id,\n _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n category_enum_value.name,\n origin_enum_value.name,\n )\n )\n # Change the biddability for the campaign conversion goal.\n # Set biddability to True for the desired (category, origin).\n # Set biddability to False for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n if (\n category_enum_value\n == client.enums.ConversionActionCategoryEnum.PURCHASE\n and origin_enum_value == client.enums.ConversionOriginEnum.WEBSITE\n ):\n biddable = True\n else:\n biddable = False\n campaign_conversion_goal.biddable = biddable\n field_mask = protobuf_helpers.field_mask(\n None, campaign_conversion_goal._pb\n )\n client.copy_from(\n mutate_operation.campaign_conversion_goal_operation.update_mask,\n field_mask,\n )\n operations.append(mutate_operation)\n\n return operationsadd_performance_max_retail_campaign.py\n```\n\nExample:\n```text\ndef _get_customer_conversion_goals(client, customer_id)\n query = <<~EOD\n SELECT\n customer_conversion_goal.category,\n customer_conversion_goal.origin\n FROM customer_conversion_goal\n EOD\n\n customer_conversion_goals = []\n\n ga_service = client.service.google_ads\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService.search instead of search_stream.\n response = ga_service.search(\n customer_id: customer_id,\n query: query,\n )\n\n # Iterate over the results and build the list of conversion goals.\n response.each do |row|\n customer_conversion_goals << {\n \"category\" => row.customer_conversion_goal.category,\n \"origin\" => row.customer_conversion_goal.origin\n }\n end\n\n customer_conversion_goals\nend\n\ndef create_conversion_goal_operations(client, customer_id, customer_conversion_goals)\n campaign_conversion_goal_service = client.service.campaign_conversion_goal\n\n operations = []\n\n # To override the customer conversion goals, we will change the\n # biddability of each of the customer conversion goals so that only\n # the desired conversion goal is biddable in this campaign.\n customer_conversion_goals.each do |customer_conversion_goal|\n operations << client.operation.mutate do |m|\n m.campaign_conversion_goal_operation = client.operation.campaign_conversion_goal do |op|\n op.update = client.resource.campaign_conversion_goal do |ccg|\n ccg.resource_name = client.path.campaign_conversion_goal(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n customer_conversion_goal[\"category\"].to_s,\n customer_conversion_goal[\"origin\"].to_s)\n # Change the biddability for the campaign conversion goal.\n # Set biddability to True for the desired (category, origin).\n # Set biddability to False for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n ccg.biddable = (customer_conversion_goal[\"category\"] == :PURCHASE &&\n customer_conversion_goal[\"origin\"] == :WEBSITE)\n end\n op.update_mask = Google::Ads::GoogleAds::FieldMaskUtil.all_set_fields_of(op.update)\n end\n end\n end\n\n operations\nendadd_performance_max_retail_campaign.rb\n```\n\nExample:\n```text\nsub get_customer_conversion_goals {\n my ($api_client, $customer_id) = @_;\n\n my $customer_conversion_goals = [];\n # Create a query that retrieves all customer conversion goals.\n my $query =\n \"SELECT customer_conversion_goal.category, customer_conversion_goal.origin \"\n . \"FROM customer_conversion_goal\";\n # The number of conversion goals is typically less than 50 so we use\n # GoogleAdsService->search() method instead of search_stream().\n my $search_response = $api_client->GoogleAdsService()->search({\n customerId => $customer_id,\n query => $query\n });\n\n # Iterate over the results and build the list of conversion goals.\n foreach my $google_ads_row (@{$search_response->{results}}) {\n push @$customer_conversion_goals,\n {\n category => $google_ads_row->{customerConversionGoal}{category},\n origin => $google_ads_row->{customerConversionGoal}{origin}};\n }\n\n return $customer_conversion_goals;\n}\n\n# Creates a list of MutateOperations that override customer conversion goals.\nsub create_conversion_goal_operations {\n my ($customer_id, $customer_conversion_goals) = @_;\n\n my $operations = [];\n # To override the customer conversion goals, we will change the biddability of\n # each of the customer conversion goals so that only the desired conversion goal\n # is biddable in this campaign.\n foreach my $customer_conversion_goal (@$customer_conversion_goals) {\n my $campaign_conversion_goal =\n Google::Ads::GoogleAds::V25::Resources::CampaignConversionGoal->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_conversion_goal(\n $customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,\n $customer_conversion_goal->{category},\n $customer_conversion_goal->{origin})});\n # Change the biddability for the campaign conversion goal.\n # Set biddability to true for the desired (category, origin).\n # Set biddability to false for all other conversion goals.\n # Note:\n # 1- It is assumed that this Conversion Action\n # (category=PURCHASE, origin=WEBSITE) exists in this account.\n # 2- More than one goal can be biddable if desired. This example\n # shows only one.\n if ( $customer_conversion_goal->{category} eq PURCHASE\n && $customer_conversion_goal->{origin} eq WEBSITE)\n {\n $campaign_conversion_goal->{biddable} = \"true\";\n } else {\n $campaign_conversion_goal->{biddable} = \"false\";\n }\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n campaignConversionGoalOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignConversionGoalService::CampaignConversionGoalOperation\n ->new({\n update => $campaign_conversion_goal,\n # Set the update mask on the operation. Here the update mask will be\n # a list of all the fields that were set on the update object.\n updateMask => all_set_fields_of($campaign_conversion_goal)})});\n }\n\n return $operations;\n}add_performance_max_retail_campaign.pl\n```\n\nExample:\n```text\n/** Creates a list of MutateOperations that create new campaign criteria. */\nprivate List<MutateOperation> createCampaignCriterionOperations(long customerId) {\n String campaignResourceName =\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID);\n List<CampaignCriterion> campaignCriteria = new ArrayList<>();\n // Sets the LOCATION campaign criteria.\n // Targets all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n //\n // We will add one positive location target for New York City (ID=1023191)\n // and one negative location target for Brooklyn (ID=1022762).\n // First, adds the positive (negative = False) for New York City.\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n .setLocation(\n LocationInfo.newBuilder()\n .setGeoTargetConstant(ResourceNames.geoTargetConstant(1023191))\n .build())\n .setNegative(false)\n .build());\n // Next adds the negative target for Brooklyn.\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n .setLocation(\n LocationInfo.newBuilder()\n .setGeoTargetConstant(ResourceNames.geoTargetConstant(1022762))\n .build())\n .setNegative(true)\n .build());\n // Sets the LANGUAGE campaign criterion.\n campaignCriteria.add(\n CampaignCriterion.newBuilder()\n .setCampaign(campaignResourceName)\n // Sets the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n .setLanguage(\n LanguageInfo.newBuilder()\n .setLanguageConstant(ResourceNames.languageConstant(1000)) // English\n .build())\n .build());\n // Returns a list of mutate operations with one operation per criterion.\n return campaignCriteria.stream()\n .map(\n criterion ->\n MutateOperation.newBuilder()\n .setCampaignCriterionOperation(\n CampaignCriterionOperation.newBuilder().setCreate(criterion).build())\n .build())\n .collect(Collectors.toList());\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a list of MutateOperations that create new campaign criteria.\n/// </summary>\n/// <param name=\"campaignResourceName\">The campaign resource name.</param>\n/// <returns>A list of MutateOperations that create new campaign criteria.</returns>\nprivate List<MutateOperation> CreateCampaignCriterionOperations(\n string campaignResourceName)\n{\n List<MutateOperation> operations = new List<MutateOperation>();\n\n // Set the LOCATION campaign criteria.\n // Target all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n //\n // We will add one positive location target for New York City (ID=1023191)\n // and one negative location target for Brooklyn (ID=1022762).\n // First, add the positive (negative = False) for New York City.\n MutateOperation operation1 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n Location = new LocationInfo()\n {\n GeoTargetConstant = ResourceNames.GeoTargetConstant(1023191)\n },\n\n Negative = false\n }\n }\n };\n\n operations.Add(operation1);\n\n // Next add the negative target for Brooklyn.\n MutateOperation operation2 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n Location = new LocationInfo()\n {\n GeoTargetConstant = ResourceNames.GeoTargetConstant(1022762)\n },\n\n Negative = true\n }\n }\n };\n\n operations.Add(operation2);\n\n // Set the LANGUAGE campaign criterion.\n MutateOperation operation3 = new MutateOperation()\n {\n CampaignCriterionOperation = new CampaignCriterionOperation()\n {\n Create = new CampaignCriterion()\n {\n Campaign = campaignResourceName,\n\n // Set the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n Language = new LanguageInfo()\n {\n LanguageConstant = ResourceNames.LanguageConstant(1000) // English\n },\n }\n }\n };\n\n operations.Add(operation3);\n\n return operations;\n}\nAddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createCampaignCriterionOperations(int $customerId): array\n{\n $operations = [];\n // Set the LOCATION campaign criteria.\n // Target all of New York City except Brooklyn.\n // Location IDs are listed here:\n // https://developers.google.com/google-ads/api/reference/data/geotargets\n // and they can also be retrieved using the GeoTargetConstantService as shown\n // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n //\n // We will add one positive location target for New York City (ID=1023191)\n // and one negative location target for Brooklyn (ID=1022762).\n // First, adds the positive (negative = false) for New York City.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'location' => new LocationInfo([\n 'geo_target_constant' => ResourceNames::forGeoTargetConstant(1023191)\n ]),\n 'negative' => false\n ])\n ])\n ]);\n\n // Next adds the negative target for Brooklyn.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'location' => new LocationInfo([\n 'geo_target_constant' => ResourceNames::forGeoTargetConstant(1022762)\n ]),\n 'negative' => true\n ])\n ])\n ]);\n\n // Sets the LANGUAGE campaign criterion.\n $operations[] = new MutateOperation([\n 'campaign_criterion_operation' => new CampaignCriterionOperation([\n 'create' => new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n // Set the language.\n // For a list of all language codes, see:\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n 'language' => new LanguageInfo([\n 'language_constant' => ResourceNames::forLanguageConstant(1000) // English\n ])\n ])\n ])\n ]);\n\n return $operations;\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\ndef create_campaign_criterion_operations(\n client: GoogleAdsClient,\n customer_id: str,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create new campaign criteria.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n a list of MutateOperations that create new campaign criteria.\n \"\"\"\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n geo_target_constant_service: GeoTargetConstantServiceClient = (\n client.get_service(\"GeoTargetConstantService\")\n )\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n operations: List[MutateOperation] = []\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n #\n # We will add one positive location target for New York City (ID=1023191)\n # and one negative location target for Brooklyn (ID=1022762).\n # First, add the positive (negative = False) for New York City.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion: CampaignCriterion = (\n mutate_operation.campaign_criterion_operation.create\n )\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n campaign_criterion.location.geo_target_constant = (\n geo_target_constant_service.geo_target_constant_path(\"1023191\")\n )\n campaign_criterion.negative = False\n operations.append(mutate_operation)\n\n # Next add the negative target for Brooklyn.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion: CampaignCriterion = (\n mutate_operation.campaign_criterion_operation.create\n )\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n campaign_criterion.location.geo_target_constant = (\n geo_target_constant_service.geo_target_constant_path(\"1022762\")\n )\n campaign_criterion.negative = True\n operations.append(mutate_operation)\n\n # Set the LANGUAGE campaign criterion.\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_criterion: CampaignCriterion = (\n mutate_operation.campaign_criterion_operation.create\n )\n campaign_criterion.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n campaign_criterion.language.language_constant = (\n googleads_service.language_constant_path(\"1000\")\n ) # English\n operations.append(mutate_operation)\n\n return operationsadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates a list of MutateOperations that create new campaign criteria.\ndef create_campaign_criterion_operations(client, customer_id)\n operations = []\n\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting\n #\n # We will add one positive location target for New York City (ID=1023191)\n # and one negative location target for Brooklyn (ID=1022762).\n # First, add the positive (negative = false) for New York City.\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n cc.location = client.resource.location_info do |li|\n li.geo_target_constant = client.path.geo_target_constant(\"1023191\")\n end\n cc.negative = false\n end\n end\n\n # Next add the negative target for Brooklyn.\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n cc.location = client.resource.location_info do |li|\n li.geo_target_constant = client.path.geo_target_constant(\"1022762\")\n end\n cc.negative = true\n end\n end\n\n # Set the LANGUAGE campaign criterion.\n operations << client.operation.mutate do |m|\n m.campaign_criterion_operation =\n client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(\n customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n cc.language = client.resource.language_info do |li|\n li.language_constant = client.path.language_constant(\"1000\") # English\n end\n end\n end\n\n operations\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_campaign_criterion_operations {\n my ($customer_id) = @_;\n\n my $operations = [];\n # Set the LOCATION campaign criteria.\n # Target all of New York City except Brooklyn.\n # Location IDs are listed here:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # and they can also be retrieved using the GeoTargetConstantService as shown\n # here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting.\n #\n # We will add one positive location target for New York City (ID=1023191)\n # and one negative location target for Brooklyn (ID=1022762).\n # First, add the positive (negative = false) for New York City.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n location =>\n Google::Ads::GoogleAds::V25::Common::LocationInfo->new({\n geoTargetConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n 1023191)}\n ),\n negative => \"false\"\n })})});\n\n # Next add the negative target for Brooklyn.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n location =>\n Google::Ads::GoogleAds::V25::Common::LocationInfo->new({\n geoTargetConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n 1022762)}\n ),\n negative => \"true\"\n })})});\n\n # Set the LANGUAGE campaign criterion.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignCriterionOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n # Set the language.\n # For a list of all language codes, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7.\n language =>\n Google::Ads::GoogleAds::V25::Common::LanguageInfo->new({\n languageConstant =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::language_constant(\n 1000) # English\n })})})});\n\n return $operations;\n}add_performance_max_campaign.pl\n```\n\nExample:\n```text\n/** Creates multiple text assets and returns the list of resource names. */\nprivate List<String> createMultipleTextAssets(\n GoogleAdsClient googleAdsClient, long customerId, List<String> texts) {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n for (String text : texts) {\n Asset asset = Asset.newBuilder().setTextAsset(TextAsset.newBuilder().setText(text)).build();\n AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build();\n mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build());\n }\n\n List<String> assetResourceNames = new ArrayList<>();\n // Creates the service client.\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Sends the operations in a single Mutate request.\n MutateGoogleAdsResponse response =\n googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);\n for (MutateOperationResponse result : response.getMutateOperationResponsesList()) {\n if (result.hasAssetResult()) {\n assetResourceNames.add(result.getAssetResult().getResourceName());\n }\n }\n printResponseDetails(response);\n }\n return assetResourceNames;\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates multiple text assets and returns the list of resource names.\n/// </summary>\n/// <param name=\"client\">The Google Ads Client.</param>\n/// <param name=\"customerId\">The customer's ID.</param>\n/// <param name=\"texts\">The texts to add.</param>\n/// <returns>A list of asset resource names.</returns>\nprivate List<string> CreateMultipleTextAssets(\n GoogleAdsClient client,\n long customerId,\n string[] texts)\n{\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n MutateGoogleAdsRequest request = new MutateGoogleAdsRequest()\n {\n CustomerId = customerId.ToString()\n };\n\n foreach (string text in texts)\n {\n request.MutateOperations.Add(\n new MutateOperation()\n {\n AssetOperation = new AssetOperation()\n {\n Create = new Asset()\n {\n TextAsset = new TextAsset()\n {\n Text = text\n }\n }\n }\n }\n );\n }\n\n // Send the operations in a single Mutate request.\n MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);\n\n List<string> assetResourceNames = new List<string>();\n\n foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)\n {\n MutateAssetResult assetResult = operationResponse.AssetResult;\n assetResourceNames.Add(assetResult.ResourceName);\n }\n\n PrintResponseDetails(response);\n\n return assetResourceNames;\n}\nAddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createMultipleTextAssets(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n array $texts\n): array {\n // Here again, we use the GoogleAdService to create multiple text assets in a single\n // request.\n $operations = [];\n foreach ($texts as $text) {\n // Creates a mutate operation for a text asset.\n $operations[] = new MutateOperation([\n 'asset_operation' => new AssetOperation([\n 'create' => new Asset(['text_asset' => new TextAsset(['text' => $text])])\n ])\n ]);\n }\n\n // Issues a mutate request to add all assets.\n $googleAdsService = $googleAdsClient->getGoogleAdsServiceClient();\n /** @var MutateGoogleAdsResponse $mutateGoogleAdsResponse */\n $mutateGoogleAdsResponse =\n $googleAdsService->mutate(MutateGoogleAdsRequest::build($customerId, $operations));\n\n $assetResourceNames = [];\n foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {\n /** @var MutateOperationResponse $response */\n $assetResourceNames[] = $response->getAssetResult()->getResourceName();\n }\n self::printResponseDetails($mutateGoogleAdsResponse);\n\n return $assetResourceNames;\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\ndef create_multiple_text_assets(\n client: GoogleAdsClient, customer_id: str, texts: List[str]\n) -> List[str]:\n \"\"\"Creates multiple text assets and returns the list of resource names.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n texts: a list of strings, each of which will be used to create a text\n asset.\n\n Returns:\n asset_resource_names: a list of asset resource names.\n \"\"\"\n # Here again we use the GoogleAdService to create multiple text\n # assets in a single request.\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n operations: List[MutateOperation] = []\n for text in texts:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset: Asset = mutate_operation.asset_operation.create\n asset.text_asset.text = text\n operations.append(mutate_operation)\n\n # Send the operations in a single Mutate request.\n response: MutateGoogleAdsResponse = googleads_service.mutate(\n customer_id=customer_id,\n mutate_operations=operations,\n )\n asset_resource_names: List[str] = []\n for result in response.mutate_operation_responses:\n if result._pb.HasField(\"asset_result\"):\n asset_resource_names.append(result.asset_result.resource_name)\n print_response_details(response)\n return asset_resource_namesadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates multiple text assets and returns the list of resource names.\ndef create_multiple_text_assets(client, customer_id, texts)\n operations = texts.map do |text|\n client.operation.mutate do |m|\n m.asset_operation = client.operation.create_resource.asset do |asset|\n asset.text_asset = client.resource.text_asset do |text_asset|\n text_asset.text = text\n end\n end\n end\n end\n\n # Send the operations in a single Mutate request.\n response = client.service.google_ads.mutate(\n customer_id: customer_id,\n mutate_operations: operations,\n )\n\n asset_resource_names = []\n response.mutate_operation_responses.each do |result|\n if result.asset_result\n asset_resource_names.append(result.asset_result.resource_name)\n end\n end\n print_response_details(response)\n asset_resource_names\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_multiple_text_assets {\n my ($api_client, $customer_id, $texts) = @_;\n\n # Here again we use the GoogleAdService to create multiple text assets in a\n # single request.\n my $operations = [];\n foreach my $text (@$texts) {\n # Create a mutate operation for a text asset.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->\n new({\n create => Google::Ads::GoogleAds::V25::Resources::Asset->new({\n textAsset =>\n Google::Ads::GoogleAds::V25::Common::TextAsset->new({\n text => $text\n })})})});\n }\n\n # Issue a mutate request to add all assets.\n my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({\n customerId => $customer_id,\n mutateOperations => $operations\n });\n\n my $asset_resource_names = [];\n foreach\n my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})\n {\n push @$asset_resource_names, $response->{assetResult}{resourceName};\n }\n print_response_details($mutate_google_ads_response);\n\n return $asset_resource_names;\n}add_performance_max_campaign.pl\n```\n\nExample:\n```text\n/** Creates a list of MutateOperations that create a new AssetGroup. */\nprivate List<MutateOperation> createAssetGroupOperations(\n long customerId,\n String assetGroupResourceName,\n List<String> headlineAssetResourceNames,\n List<String> descriptionAssetResourceNames,\n boolean brandGuidelinesEnabled)\n throws IOException {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n String campaignResourceName =\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID);\n // Creates the AssetGroup.\n AssetGroup assetGroup =\n AssetGroup.newBuilder()\n .setName(\"Performance Max asset group #\" + getPrintableDateTime())\n .setCampaign(campaignResourceName)\n .addFinalUrls(\"http://www.example.com\")\n .addFinalMobileUrls(\"http://www.example.com\")\n .setStatus(AssetGroupStatus.PAUSED)\n .setResourceName(assetGroupResourceName)\n .build();\n AssetGroupOperation assetGroupOperation =\n AssetGroupOperation.newBuilder().setCreate(assetGroup).build();\n mutateOperations.add(\n MutateOperation.newBuilder().setAssetGroupOperation(assetGroupOperation).build());\n\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // the resource name of the AssetGroup\n // the resource name of the Asset\n // the field_type of the Asset in this AssetGroup.\n\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n // Links the previously created multiple text assets.\n\n // Links the headline assets.\n for (String resourceName : headlineAssetResourceNames) {\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.HEADLINE, resourceName, assetGroupResourceName));\n }\n\n // Links the description assets.\n for (String resourceName : descriptionAssetResourceNames) {\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.DESCRIPTION, resourceName, assetGroupResourceName));\n }\n\n // Creates and links the long headline text asset.\n List<MutateOperation> createAndLinkTextAssetOperations =\n createAndLinkTextAsset(customerId, \"Travel the World\", AssetFieldType.LONG_HEADLINE);\n mutateOperations.addAll(createAndLinkTextAssetOperations);\n\n // Creates and links the business name and logo assets.\n List<MutateOperation> createAndLinkBrandAssets =\n createAndLinkBrandAssets(\n customerId,\n brandGuidelinesEnabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\");\n mutateOperations.addAll(createAndLinkBrandAssets);\n\n // Creates and links the image assets.\n\n // Creates and links the Marketing Image Asset.\n createAndLinkTextAssetOperations =\n createAndLinkImageAsset(\n customerId,\n \"https://gaagl.page.link/Eit5\",\n AssetFieldType.MARKETING_IMAGE,\n \"Marketing Image\");\n mutateOperations.addAll(createAndLinkTextAssetOperations);\n\n // Creates and links the Square Marketing Image Asset.\n createAndLinkTextAssetOperations =\n createAndLinkImageAsset(\n customerId,\n \"https://gaagl.page.link/bjYi\",\n AssetFieldType.SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\");\n mutateOperations.addAll(createAndLinkTextAssetOperations);\n\n return mutateOperations;\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a list of MutateOperations that create a new asset_group.\n/// </summary>\n/// <param name=\"campaignResourceName\">The campaign resource name.</param>\n/// <param name=\"assetGroupResourceName\">The asset group resource name.</param>\n/// <param name=\"headlineAssetResourceNames\">The headline asset resource names.</param>\n/// <param name=\"descriptionAssetResourceNames\">The description asset resource\n/// names.</param>\n/// <param name=\"resourceNameGenerator\">A generator for unique temporary ID's.</param>\n/// <param name=\"config\">The Google Ads config.</param>\n/// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n/// <returns>A list of MutateOperations that create the new asset group.</returns>\nprivate List<MutateOperation> CreateAssetGroupOperations(\n string campaignResourceName,\n string assetGroupResourceName,\n List<string> headlineAssetResourceNames,\n List<string> descriptionAssetResourceNames,\n AssetTemporaryResourceNameGenerator resourceNameGenerator,\n GoogleAdsConfig config,\n bool brandGuidelinesEnabled)\n{\n List<MutateOperation> operations = new List<MutateOperation>();\n\n // Create the AssetGroup\n operations.Add(\n new MutateOperation()\n {\n AssetGroupOperation = new AssetGroupOperation()\n {\n Create = new AssetGroup()\n {\n Name = \"Performance Max asset group #\" +\n ExampleUtilities.GetRandomString(),\n Campaign = campaignResourceName,\n FinalUrls = { \"http://www.example.com\" },\n FinalMobileUrls = { \"http://www.example.com\" },\n Status = AssetGroupStatus.Paused,\n ResourceName = assetGroupResourceName\n }\n }\n }\n );\n\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // the resource name of the AssetGroup\n // the resource name of the Asset\n // the field_type of the Asset in this AssetGroup.\n //\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n // Link the previously created multiple text assets.\n\n // Link the headline assets.\n foreach (string resourceName in headlineAssetResourceNames)\n {\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.Headline,\n AssetGroup = assetGroupResourceName,\n Asset = resourceName\n }\n }\n }\n );\n }\n\n // Link the description assets.\n foreach (string resourceName in descriptionAssetResourceNames)\n {\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.Description,\n AssetGroup = assetGroupResourceName,\n Asset = resourceName\n }\n }\n }\n );\n }\n\n // Create and link the brand assets.\n operations.AddRange(\n CreateAndLinkBrandAssets(\n assetGroupResourceName,\n campaignResourceName,\n resourceNameGenerator,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\",\n config,\n brandGuidelinesEnabled\n )\n );\n\n // Create and link the long headline text asset.\n operations.AddRange(\n CreateAndLinkTextAsset(\n assetGroupResourceName,\n resourceNameGenerator.Next(),\n \"Travel the World\",\n AssetFieldType.LongHeadline\n )\n );\n\n // Create and link the image assets.\n\n // Create and link the Marketing Image Asset.\n operations.AddRange(\n CreateAndLinkImageAsset(\n assetGroupResourceName,\n resourceNameGenerator.Next(),\n \"https://gaagl.page.link/Eit5\",\n AssetFieldType.MarketingImage,\n \"Marketing Image\",\n config\n )\n );\n\n // Create and link the Square Marketing Image Asset.\n operations.AddRange(\n CreateAndLinkImageAsset(\n assetGroupResourceName,\n resourceNameGenerator.Next(),\n \"https://gaagl.page.link/bjYi\",\n AssetFieldType.SquareMarketingImage,\n \"Square Marketing Image\",\n config\n )\n );\n\n return operations;\n}\nAddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createAssetGroupOperations(\n int $customerId,\n array $headlineAssetResourceNames,\n array $descriptionAssetResourceNames,\n bool $brandGuidelinesEnabled\n): array {\n $operations = [];\n // Creates a new mutate operation that creates an asset group operation.\n $operations[] = new MutateOperation([\n 'asset_group_operation' => new AssetGroupOperation([\n 'create' => new AssetGroup([\n 'resource_name' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'name' => 'Performance Max asset group #' . Helper::getPrintableDatetime(),\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'final_urls' => ['http://www.example.com'],\n 'final_mobile_urls' => ['http://www.example.com'],\n 'status' => AssetGroupStatus::PAUSED\n ])\n ])\n ]);\n\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // - the resource name of the AssetGroup\n // - the resource name of the Asset\n // - the field_type of the Asset in this AssetGroup\n //\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n // Links the previously created multiple text assets.\n\n // Links the headline assets.\n foreach ($headlineAssetResourceNames as $resourceName) {\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $resourceName,\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::HEADLINE\n ])\n ])\n ]);\n }\n // Links the description assets.\n foreach ($descriptionAssetResourceNames as $resourceName) {\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $resourceName,\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::DESCRIPTION\n ])\n ])\n ]);\n }\n\n // Creates and links the long headline text asset.\n $operations = array_merge($operations, self::createAndLinkTextAsset(\n $customerId,\n 'Travel the World',\n AssetFieldType::LONG_HEADLINE\n ));\n // Creates and links the business name text asset.\n $operations = array_merge($operations, self::createAndLinkBrandAssets(\n $customerId,\n $brandGuidelinesEnabled,\n 'Interplanetary Cruises',\n 'https://gaagl.page.link/bjYi',\n 'Marketing Logo'\n ));\n\n // Creates and links the image assets.\n\n // Creates and links the Marketing Image Asset.\n $operations = array_merge($operations, self::createAndLinkImageAsset(\n $customerId,\n 'https://gaagl.page.link/Eit5',\n AssetFieldType::MARKETING_IMAGE,\n 'Marketing Image'\n ));\n // Creates and links the Square Marketing Image Asset.\n $operations = array_merge($operations, self::createAndLinkImageAsset(\n $customerId,\n 'https://gaagl.page.link/bjYi',\n AssetFieldType::SQUARE_MARKETING_IMAGE,\n 'Square Marketing Image'\n ));\n\n return $operations;\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\ndef create_asset_group_operation(\n client: GoogleAdsClient,\n customer_id: str,\n headline_asset_resource_names: List[str],\n description_asset_resource_names: List[str],\n brand_guidelines_enabled: bool,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create a new asset_group.\n\n A temporary ID will be assigned to this asset group so that it can\n be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n headline_asset_resource_names: a list of headline resource names.\n description_asset_resource_names: a list of description resource names.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n\n Returns:\n MutateOperations that create a new asset group and related assets.\n \"\"\"\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n operations: List[MutateOperation] = []\n\n # Create the AssetGroup\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group: AssetGroup = mutate_operation.asset_group_operation.create\n asset_group.name = f\"Performance Max asset group #{uuid4()}\"\n asset_group.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n asset_group.final_urls.append(\"http://www.example.com\")\n asset_group.final_mobile_urls.append(\"http://www.example.com\")\n asset_group.status = client.enums.AssetGroupStatusEnum.PAUSED\n asset_group.resource_name = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n operations.append(mutate_operation)\n\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # the resource name of the AssetGroup\n # the resource name of the Asset\n # the field_type of the Asset in this AssetGroup.\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n for resource_name in headline_asset_resource_names:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_asset: AssetGroupAsset = (\n mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.HEADLINE\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = resource_name\n operations.append(mutate_operation)\n\n # Link the description assets.\n for resource_name in description_asset_resource_names:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_asset: AssetGroupAsset = (\n mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = (\n client.enums.AssetFieldTypeEnum.DESCRIPTION\n )\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = resource_name\n operations.append(mutate_operation)\n\n # Create and link the long headline text asset.\n mutate_operations: List[MutateOperation] = create_and_link_text_asset(\n client,\n customer_id,\n \"Travel the World\",\n client.enums.AssetFieldTypeEnum.LONG_HEADLINE,\n )\n operations.extend(mutate_operations)\n\n # Create and link the business name and logo asset.\n mutate_operations: List[MutateOperation] = create_and_link_brand_assets(\n client,\n customer_id,\n brand_guidelines_enabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\",\n )\n operations.extend(mutate_operations)\n\n # Create and link the image assets.\n\n # Create and link the Marketing Image Asset.\n mutate_operations: List[MutateOperation] = create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/Eit5\",\n client.enums.AssetFieldTypeEnum.MARKETING_IMAGE,\n \"Marketing Image\",\n )\n operations.extend(mutate_operations)\n\n # Create and link the Square Marketing Image Asset.\n mutate_operations: List[MutateOperation] = create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/bjYi\",\n client.enums.AssetFieldTypeEnum.SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\",\n )\n operations.extend(mutate_operations)\n return operationsadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates a list of MutateOperations that create a new asset_group.\n#\n# A temporary ID will be assigned to this asset group so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_asset_group_operation(\n client,\n customer_id,\n headline_asset_resource_names,\n description_asset_resource_names,\n brand_guidelines_enabled)\n operations = []\n\n # Create the AssetGroup\n operations << client.operation.mutate do |m|\n m.asset_group_operation = client.operation.create_resource.asset_group do |ag|\n ag.name = \"Performance Max asset group #{SecureRandom.uuid}\"\n ag.campaign = client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n ag.final_urls << \"http://www.example.com\"\n ag.final_mobile_urls << \"http://www.example.com\"\n ag.status = :PAUSED\n ag.resource_name = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n end\n end\n\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n #\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # the resource name of the AssetGroup\n # the resource name of the Asset\n # the field_type of the Asset in this AssetGroup.\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n headline_asset_resource_names.each do |resource_name|\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource\n .asset_group_asset do |aga|\n aga.field_type = :HEADLINE\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n aga.asset = resource_name\n end\n end\n end\n\n # Link the description assets.\n description_asset_resource_names.each do |resource_name|\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource\n .asset_group_asset do |aga|\n aga.field_type = :DESCRIPTION\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n aga.asset = resource_name\n end\n end\n end\n\n # Create and link the long headline text asset.\n operations += create_and_link_text_asset(\n client,\n customer_id,\n \"Travel the World\",\n :LONG_HEADLINE)\n\n # Create and link the business name and logo asset.\n operations += create_and_link_brand_assets(\n client,\n customer_id,\n brand_guidelines_enabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\")\n\n # Create and link the image assets.\n\n # Create and link the Marketing Image Asset.\n operations += create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/Eit5\",\n :MARKETING_IMAGE,\n \"Marketing Image\")\n\n # Create and link the Square Marketing Image Asset.\n operations += create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/bjYi\",\n :SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\")\n\n operations\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_asset_group_operations {\n my (\n $customer_id,\n $headline_asset_resource_names,\n $description_asset_resource_names,\n $brand_guidelines_enabled\n ) = @_;\n\n my $operations = [];\n # Create a mutate operation that creates an asset group operation.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupService::AssetGroupOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::AssetGroup->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n name => \"Performance Max asset group #\" . uniqid(),\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n finalUrls => [\"http://www.example.com\"],\n finalMobileUrls => [\"http://www.example.com\"],\n status =>\n Google::Ads::GoogleAds::V25::Enums::AssetGroupStatusEnum::PAUSED\n })})});\n\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets.\n\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # - the resource name of the AssetGroup\n # - the resource name of the Asset\n # - the fieldType of the Asset in this AssetGroup\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n foreach my $resource_name (@$headline_asset_resource_names) {\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $resource_name,\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => HEADLINE\n })})});\n }\n\n # Link the description assets.\n foreach my $resource_name (@$description_asset_resource_names) {\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $resource_name,\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => DESCRIPTION\n })})});\n }\n\n # Create and link the long headline text asset.\n push @$operations,\n @{create_and_link_text_asset($customer_id, \"Travel the World\",\n LONG_HEADLINE)};\n\n # Create and link the business name and logo asset.\n push @$operations,\n @{\n create_and_link_brand_assets(\n $customer_id, $brand_guidelines_enabled,\n \"Interplanetary Cruises\", \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\"\n )};\n\n # Create and link the image assets.\n\n # Create and link the marketing image asset.\n push @$operations,\n @{\n create_and_link_image_asset(\n $customer_id, \"https://gaagl.page.link/Eit5\",\n MARKETING_IMAGE, \"Marketing Image\"\n )};\n\n # Create and link the square marketing image asset.\n push @$operations,\n @{\n create_and_link_image_asset(\n $customer_id, \"https://gaagl.page.link/bjYi\",\n SQUARE_MARKETING_IMAGE, \"Square Marketing Image\"\n )};\n\n return $operations;\n}add_performance_max_campaign.pl\n```\n\nExample:\n```text\nAssetGroupSignal audienceSignal =\n AssetGroupSignal.newBuilder()\n .setAssetGroup(assetGroupResourceName)\n .setAudience(\n AudienceInfo.newBuilder()\n .setAudience(ResourceNames.audience(customerId, audienceId)))\n .build();\n\nmutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupSignalOperation(\n AssetGroupSignalOperation.newBuilder().setCreate(audienceSignal))\n .build());AddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\noperations.Add(\n new MutateOperation()\n {\n AssetGroupSignalOperation = new AssetGroupSignalOperation()\n {\n Create = new AssetGroupSignal()\n {\n AssetGroup = assetGroupResourceName,\n Audience = new AudienceInfo()\n {\n Audience = ResourceNames.Audience(customerId, audienceId.Value)\n }\n }\n }\n }\n);AddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createAssetGroupSignalOperations(\n int $customerId,\n string $assetGroupResourceName,\n ?int $audienceId\n): array {\n $operations = [];\n if (is_null($audienceId)) {\n return $operations;\n }\n\n $operations[] = new MutateOperation([\n 'asset_group_signal_operation' => new AssetGroupSignalOperation([\n // To learn more about Audience Signals, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals.\n 'create' => new AssetGroupSignal([\n 'asset_group' => $assetGroupResourceName,\n 'audience' => new AudienceInfo([\n 'audience' => ResourceNames::forAudience($customerId, $audienceId)\n ])\n ])\n ])\n ]);\n\n return $operations;\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\nmutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\noperation: AssetGroupSignal = (\n mutate_operation.asset_group_signal_operation.create\n)\noperation.asset_group = asset_group_resource_name\noperation.audience.audience = googleads_service.audience_path(\n customer_id, audience_id\n)\noperations.append(mutate_operation)add_performance_max_campaign.py\n```\n\nExample:\n```text\n# Create a list of MutateOperations that create AssetGroupSignals.\ndef create_asset_group_signal_operations(client, customer_id, audience_id)\n operations = []\n return operations if audience_id.nil?\n\n operations << client.operation.mutate do |m|\n m.asset_group_signal_operation = client.operation.create_resource.\n asset_group_signal do |ags|\n ags.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID,\n )\n ags.audience = client.resource.audience_info do |ai|\n ai.audience = client.path.audience(customer_id, audience_id)\n end\n end\n end\n\n operations\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_asset_group_signal_operations {\n my ($customer_id, $audience_id) = @_;\n\n my $operations = [];\n return $operations if not defined $audience_id;\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupSignalOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupSignalService::AssetGroupSignalOperation\n ->new({\n # To learn more about Audience Signals, see:\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupSignal->new({\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n audience =>\n Google::Ads::GoogleAds::V25::Common::AudienceInfo->new({\n audience =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::audience(\n $customer_id, $audience_id\n )})})})});\n return $operations;\n}add_performance_max_campaign.pl\n```\n\nExample:\n```text\nAssetGroupSignal searchThemeSignal =\n AssetGroupSignal.newBuilder()\n .setAssetGroup(assetGroupResourceName)\n .setSearchTheme(SearchThemeInfo.newBuilder().setText(\"travel\").build())\n .build();\n\nmutateOperations.add(\n MutateOperation.newBuilder()\n .setAssetGroupSignalOperation(\n AssetGroupSignalOperation.newBuilder().setCreate(searchThemeSignal))\n .build());AddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\nThis example is not yet available in C#; you can take a look at the other languages.\n```\n\nExample:\n```text\nThis example is not yet available in PHP; you can take a look at the other languages.\n```\n\nExample:\n```text\nmutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\noperation: AssetGroupSignal = (\n mutate_operation.asset_group_signal_operation.create\n)\noperation.asset_group = asset_group_resource_name\noperation.search_theme.text = \"travel\"\noperations.append(mutate_operation)add_performance_max_campaign.py\n```\n\nExample:\n```text\nThis example is not yet available in Ruby; you can take a look at the other languages.\n```\n\nExample:\n```text\nThis example is not yet available in Perl; you can take a look at the other languages.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.507Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":48,"totalLines":2833,"estimatedTokens":27699}}196{"id":"doc-campaign_level_performance_google_ads_api_google-29626635","source":"documentation","title":"Campaign level performance | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/campaign-reporting","text":"Example:\n```text\nSELECT\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.cost_micros\nFROM campaign\nWHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'\n AND segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n performance_max_placement_view.display_name,\n performance_max_placement_view.placement,\n performance_max_placement_view.placement_type,\n performance_max_placement_view.target_url,\n segments.ad_network_type,\n metrics.impressions,\n campaign.id\nFROM performance_max_placement_view\nWHERE\n campaign.id = CAMPAIGN_ID\n AND segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n segments.ad_network_type,\n segments.ad_using_product_data,\n segments.ad_using_video,\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n metrics.cost_micros\nFROM campaign\nWHERE\n campaign.advertising_channel_type = 'PERFORMANCE_MAX'\n AND segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.feed_types\nFROM campaign\nWHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n campaign.pmax_campaign_settings.local_services_enabled\nFROM campaign\nWHERE campaign.advertising_channel_type = 'PERFORMANCE_MAX'\n AND campaign.pmax_campaign_settings.local_services_enabled = true\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.509Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":66,"estimatedTokens":342}}197{"id":"doc-get_started_with_ai_max_for_search_campaigns_goo-c3b8b710","source":"documentation","title":"Get started with AI Max for Search campaigns | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/ai-max-for-search-campaigns/getting-started","text":"Example:\n```text\n{\n \"ad_group\": \"customers/CUSTOMER_ID/adGroups/AD_GROUP_ID\",\n \"webpage\": {\n \"criterion_name\": \"Ad group level inclusion criterion for widgets and top sellers\",\n \"conditions\": [\n {\n \"operand\": \"URL\",\n \"argument\": \"/product/widget-a\"\n },\n {\n \"operand\": \"CUSTOM_LABEL\",\n \"argument\": \"TopSellers\"\n }\n ]\n },\n \"negative\": false\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.510Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":22,"estimatedTokens":105}}198{"id":"doc-controls_and_inventory_filtering_for_vertical_ad-31d9f8d6","source":"documentation","title":"Controls and inventory filtering for vertical ads | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/campaigns/ai-max-for-search-campaigns/vertical-ads-controls","text":"Example:\n```text\n// Assuming 'adGroup' is an existing AdGroup object.\nAdGroup adGroupToUpdate = AdGroup.newBuilder()\n .setResourceName(adGroup.getResourceName()) // Example: \"customers/{id}/adGroups/{id}\"\n .setVerticalAdsFormatSetting(\n VerticalAdsFormatSetting.newBuilder()\n .setDisableTextAds(true)\n .setEnableBookingLinks(false)\n .setEnableVerticalPromotionAds(true)\n )\n .build();\n\nAdGroupOperation operation = AdGroupOperation.newBuilder()\n .setUpdate(adGroupToUpdate)\n .setUpdateMask(FieldMasks.allSetFieldsOf(adGroupToUpdate))\n .build();\n\n// Submit the operation using AdGroupService...\n```\n\nExample:\n```text\n# 1. Create the SharedSet\nshared_set_operation = client.get_type(\"SharedSetOperation\")\nshared_set = shared_set_operation.create\nshared_set.name = \"Boston/SF Premium Hotels\"\nshared_set.type_ = client.enums.SharedSetTypeEnum.VERTICAL_ADS_ITEM_GROUP_RULE_LIST\nshared_set.vertical_ads_item_vertical_type = client.enums.VerticalAdsItemVerticalTypeEnum.HOTELS\n# Submit SharedSetOperation...\n\n# 2. Add Criteria (Rules) to the SharedSet\nshared_criteria_operations = []\n\n# Rule A: Include Boston and SF\nincluded_city_ids = [1006543, 1014221] # Geo Target Constant IDs\nfor city_id in included_city_ids:\n op = client.get_type(\"SharedCriterionOperation\")\n criterion = op.create\n criterion.shared_set = shared_set_resource_name\n criterion.vertical_ads_item_group_rule.city_criterion_id = city_id\n shared_criteria_operations.append(op)\n\n# Rule B: Exclude 1 and 2 Star Hotels\nexcluded_stars = [1, 2]\nfor star_rating in excluded_stars:\n op = client.get_type(\"SharedCriterionOperation\")\n criterion = op.create\n criterion.shared_set = shared_set_resource_name\n criterion.vertical_ads_item_group_rule.hotel_class = star_rating\n criterion.negative = True # Mark as exclusion\n shared_criteria_operations.append(op)\n\n# Submit SharedCriterionOperations...\n\n# 3. Link to AdGroup\nagc_operation = client.get_type(\"AdGroupCriterionOperation\")\nagc = agc_operation.create\nagc.ad_group = ad_group_resource_name\nagc.status = client.enums.AdGroupCriterionStatusEnum.ENABLED\nagc.vertical_ads_item_group_rule_list.shared_set = shared_set_resource_name\n\n# Submit AdGroupCriterionOperation...\n```\n\nExample:\n```text\nSELECT\n segments.vertical_ads_listing_city,\n metrics.clicks,\n metrics.all_conversions_value,\n metrics.impressions\nFROM\n ad_group\nWHERE\n segments.date DURING LAST_30_DAYS\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.511Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":79,"estimatedTokens":621}}199{"id":"doc-create_a_budget_google_ads_api_google_for_develo-ad2c3720","source":"documentation","title":"Create a budget | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/smart-campaigns/create-budget","text":"Example:\n```text\nprivate MutateOperation createCampaignBudgetOperation(long customerId, long dailyBudgetMicros) {\n MutateOperation.Builder builder = MutateOperation.newBuilder();\n builder\n .getCampaignBudgetOperationBuilder()\n .getCreateBuilder()\n .setName(\"Smart campaign budget \" + CodeSampleHelper.getShortPrintableDateTime())\n .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)\n // A budget used for Smart campaigns must have the type SMART_CAMPAIGN.\n .setType(BudgetType.SMART_CAMPAIGN)\n // The suggested budget amount from the SmartCampaignSuggestService is for a _daily_ budget.\n // We don't need to specify that here, because the budget period already defaults to DAILY.\n .setAmountMicros(dailyBudgetMicros)\n // Sets a temporary ID in the budget's resource name so it can be referenced by the campaign\n // in later steps.\n .setResourceName(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID));\n return builder.build();\n}AddSmartCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a MutateOperation that creates a new CampaignBudget.\n/// A temporary ID will be assigned to this campaign budget so that it can be referenced by\n/// other objects being created in the same Mutate request.\n/// </summary>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <param name=\"suggestedBudgetAmount\">A daily amount budget in micros.</param>\n/// <returns>A MutateOperation that creates a CampaignBudget</returns>\nprivate MutateOperation CreateCampaignBudgetOperation(long customerId,\n long suggestedBudgetAmount)\n{\n return new MutateOperation\n {\n CampaignBudgetOperation = new CampaignBudgetOperation\n {\n Create = new CampaignBudget\n {\n Name = $\"Smart campaign budget #{ExampleUtilities.GetRandomString()}\",\n // A budget used for Smart campaigns must have the type SMART_CAMPAIGN.\n Type = BudgetType.SmartCampaign,\n // The suggested budget amount from the SmartCampaignSuggestService is a\n // daily budget. We don't need to specify that here, because the budget\n // period already defaults to DAILY.\n AmountMicros = suggestedBudgetAmount,\n // Set a temporary ID in the budget's resource name so it can be referenced\n // by the campaign in later steps.\n ResourceName = ResourceNames.CampaignBudget(\n customerId, BUDGET_TEMPORARY_ID)\n }\n }\n };\n}AddSmartCampaign.cs\n```\n\nExample:\n```text\nprivate static function createCampaignBudgetOperation(\n int $customerId,\n int $suggestedBudgetAmount\n): MutateOperation {\n // Creates the campaign budget object.\n $campaignBudget = new CampaignBudget([\n 'name' => \"Smart campaign budget #\" . Helper::getPrintableDatetime(),\n // A budget used for Smart campaigns must have the type SMART_CAMPAIGN.\n 'type' => BudgetType::SMART_CAMPAIGN,\n // The suggested budget amount from the SmartCampaignSuggestService is a daily budget.\n // We don't need to specify that here, because the budget period already defaults to\n // DAILY.\n 'amount_micros' => $suggestedBudgetAmount,\n // Sets a temporary ID in the budget's resource name so it can be referenced by the\n // campaign in later steps.\n 'resource_name' =>\n ResourceNames::forCampaignBudget($customerId, self::BUDGET_TEMPORARY_ID)\n ]);\n\n // Creates the MutateOperation that creates the campaign budget.\n return new MutateOperation([\n 'campaign_budget_operation' => new CampaignBudgetOperation([\n 'create' => $campaignBudget\n ])\n ]);\n}AddSmartCampaign.php\n```\n\nExample:\n```text\ndef create_campaign_budget_operation(\n client: GoogleAdsClient, customer_id: str, suggested_budget_amount: int\n) -> MutateOperation:\n \"\"\"Creates a MutateOperation that creates a new CampaignBudget.\n\n A temporary ID will be assigned to this campaign budget so that it can be\n referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n suggested_budget_amount: a numeric daily budget amount in micros.\n\n Returns:\n a MutateOperation that creates a CampaignBudget.\n \"\"\"\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n campaign_budget_operation: CampaignBudgetOperation = (\n mutate_operation.campaign_budget_operation\n )\n campaign_budget: CampaignBudget = campaign_budget_operation.create\n campaign_budget.name = f\"Smart campaign budget #{uuid4()}\"\n # A budget used for Smart campaigns must have the type SMART_CAMPAIGN.\n # Note that the field name \"type_\" is an implementation detail in Python,\n # the field's actual name is \"type\".\n campaign_budget.type_ = client.enums.BudgetTypeEnum.SMART_CAMPAIGN\n # The suggested budget amount from the SmartCampaignSuggestService is\n # a daily budget. We don't need to specify that here, because the budget\n # period already defaults to DAILY.\n campaign_budget.amount_micros = suggested_budget_amount\n # Set a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n campaign_budget.resource_name = client.get_service(\n \"CampaignBudgetService\"\n ).campaign_budget_path(customer_id, _BUDGET_TEMPORARY_ID)\n\n return mutate_operationadd_smart_campaign.py\n```\n\nExample:\n```text\n# Creates a mutate_operation that creates a new campaign_budget.\n# A temporary ID will be assigned to this campaign budget so that it can be\n# referenced by other objects being created in the same mutate request.\ndef create_campaign_budget_operation(\n client,\n customer_id,\n suggested_budget_amount)\n mutate_operation = client.operation.mutate do |m|\n m.campaign_budget_operation = client.operation.create_resource.campaign_budget do |cb|\n cb.name = \"Smart campaign budget ##{(Time.new.to_f * 1000).to_i}\"\n # A budget used for Smart campaigns must have the type SMART_CAMPAIGN.\n cb.type = :SMART_CAMPAIGN\n # The suggested budget amount from the smart_campaign_suggest_service is\n # a daily budget. We don't need to specify that here, because the budget\n # period already defaults to DAILY.\n cb.amount_micros = suggested_budget_amount\n # Sets a temporary ID in the budget's resource name so it can be referenced\n # by the campaign in later steps.\n cb.resource_name = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)\n end\n end\n\n mutate_operation\nendadd_smart_campaign.rb\n```\n\nExample:\n```text\n# Creates a MutateOperation that creates a new CampaignBudget.\n# A temporary ID will be assigned to this campaign budget so that it can be\n# referenced by other objects being created in the same Mutate request.\nsub _create_campaign_budget_operation {\n my ($customer_id, $suggested_budget_amount) = @_;\n\n return\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n campaignBudgetOperation =>\n Google::Ads::GoogleAds::V25::Services::CampaignBudgetService::CampaignBudgetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::CampaignBudget->new({\n name => \"Smart campaign budget #\" . uniqid(),\n # A budget used for Smart campaigns must have the type SMART_CAMPAIGN.\n type =>\n Google::Ads::GoogleAds::V25::Enums::BudgetTypeEnum::SMART_CAMPAIGN,\n # The suggested budget amount from the SmartCampaignSuggestService is\n # a daily budget. We don't need to specify that here, because the\n # budget period already defaults to DAILY.\n amountMicros => $suggested_budget_amount,\n # Set a temporary ID in the budget's resource name so it can be\n # referenced by the campaign in later steps.\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, BUDGET_TEMPORARY_ID\n )})})});\n}add_smart_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.512Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":190,"estimatedTokens":2078}}200{"id":"doc-reporting_google_ads_api_google_for_developers-5db37b25","source":"documentation","title":"Reporting | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/smart-campaigns/reporting","text":"Example:\n```text\nSELECT\n campaign.id,\n campaign.name,\n segments.date,\n metrics.impressions,\n metrics.clicks,\n smart_campaign_search_term_view.search_term\nFROM smart_campaign_search_term_view\nWHERE segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n segments.date,\n metrics.impressions,\n metrics.cost_micros,\n smart_campaign_search_term_view.search_term\nFROM smart_campaign_search_term_view\nORDER BY metrics.impressions DESC\nLIMIT 10\n```\n\nExample:\n```text\nSELECT\n campaign_criterion.type,\n campaign_criterion.status,\n campaign_criterion.criterion_id,\n campaign_criterion.keyword_theme.keyword_theme_constant\nFROM campaign_criterion\nWHERE campaign_criterion.type = KEYWORD_THEME\n```\n\nExample:\n```text\nSELECT\n keyword_theme_constant.resource_name,\n keyword_theme_constant.display_name,\n keyword_theme_constant.country_code\nFROM keyword_theme_constant\nWHERE keyword_theme_constant.resource_name = 'keywordThemeConstants/40804~0'\n```\n\nExample:\n```text\nSELECT\n metrics.clicks,\n metrics.cost_micros,\n metrics.impressions,\n metrics.conversions,\n metrics.all_conversions\nFROM campaign\n```\n\nExample:\n```text\nSELECT\n metrics.clicks,\n metrics.cost_micros\nFROM smart_campaign_search_term_view\n```\n\nExample:\n```text\nSELECT\n metrics.all_conversions_from_click_to_call,\n metrics.all_conversions_from_directions,\n metrics.all_conversions_from_menu,\n metrics.all_conversions_from_order,\n metrics.all_conversions_from_other_engagement,\n metrics.all_conversions_from_store_visit,\n metrics.all_conversions_from_store_website\nFROM campaign\n```\n\nExample:\n```text\nSELECT\n segments.hour,\n metrics.phone_calls\nFROM campaign\nWHERE segments.hour BETWEEN 12 and 17\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.513Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":90,"estimatedTokens":433}}201{"id":"doc-performance_max_asset_groups_google_ads_api_goog-86fae3dd","source":"documentation","title":"Performance Max Asset Groups | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/performance-max/asset-groups","text":"Example:\n```text\n/** Creates a list of MutateOperations that create a new AssetGroup. */\nprivate List<MutateOperation> createAssetGroupOperations(\n long customerId,\n String assetGroupResourceName,\n List<String> headlineAssetResourceNames,\n List<String> descriptionAssetResourceNames,\n boolean brandGuidelinesEnabled)\n throws IOException {\n List<MutateOperation> mutateOperations = new ArrayList<>();\n String campaignResourceName =\n ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID);\n // Creates the AssetGroup.\n AssetGroup assetGroup =\n AssetGroup.newBuilder()\n .setName(\"Performance Max asset group #\" + getPrintableDateTime())\n .setCampaign(campaignResourceName)\n .addFinalUrls(\"http://www.example.com\")\n .addFinalMobileUrls(\"http://www.example.com\")\n .setStatus(AssetGroupStatus.PAUSED)\n .setResourceName(assetGroupResourceName)\n .build();\n AssetGroupOperation assetGroupOperation =\n AssetGroupOperation.newBuilder().setCreate(assetGroup).build();\n mutateOperations.add(\n MutateOperation.newBuilder().setAssetGroupOperation(assetGroupOperation).build());\n\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // the resource name of the AssetGroup\n // the resource name of the Asset\n // the field_type of the Asset in this AssetGroup.\n\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n // Links the previously created multiple text assets.\n\n // Links the headline assets.\n for (String resourceName : headlineAssetResourceNames) {\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.HEADLINE, resourceName, assetGroupResourceName));\n }\n\n // Links the description assets.\n for (String resourceName : descriptionAssetResourceNames) {\n mutateOperations.add(\n createAssetGroupAssetMutateOperation(\n AssetFieldType.DESCRIPTION, resourceName, assetGroupResourceName));\n }\n\n // Creates and links the long headline text asset.\n List<MutateOperation> createAndLinkTextAssetOperations =\n createAndLinkTextAsset(customerId, \"Travel the World\", AssetFieldType.LONG_HEADLINE);\n mutateOperations.addAll(createAndLinkTextAssetOperations);\n\n // Creates and links the business name and logo assets.\n List<MutateOperation> createAndLinkBrandAssets =\n createAndLinkBrandAssets(\n customerId,\n brandGuidelinesEnabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\");\n mutateOperations.addAll(createAndLinkBrandAssets);\n\n // Creates and links the image assets.\n\n // Creates and links the Marketing Image Asset.\n createAndLinkTextAssetOperations =\n createAndLinkImageAsset(\n customerId,\n \"https://gaagl.page.link/Eit5\",\n AssetFieldType.MARKETING_IMAGE,\n \"Marketing Image\");\n mutateOperations.addAll(createAndLinkTextAssetOperations);\n\n // Creates and links the Square Marketing Image Asset.\n createAndLinkTextAssetOperations =\n createAndLinkImageAsset(\n customerId,\n \"https://gaagl.page.link/bjYi\",\n AssetFieldType.SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\");\n mutateOperations.addAll(createAndLinkTextAssetOperations);\n\n return mutateOperations;\n}\nAddPerformanceMaxCampaign.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates a list of MutateOperations that create a new asset_group.\n/// </summary>\n/// <param name=\"campaignResourceName\">The campaign resource name.</param>\n/// <param name=\"assetGroupResourceName\">The asset group resource name.</param>\n/// <param name=\"headlineAssetResourceNames\">The headline asset resource names.</param>\n/// <param name=\"descriptionAssetResourceNames\">The description asset resource\n/// names.</param>\n/// <param name=\"resourceNameGenerator\">A generator for unique temporary ID's.</param>\n/// <param name=\"config\">The Google Ads config.</param>\n/// <param name=\"brandGuidelinesEnabled\">Whether or not to enable brand guidelines.</param>\n/// <returns>A list of MutateOperations that create the new asset group.</returns>\nprivate List<MutateOperation> CreateAssetGroupOperations(\n string campaignResourceName,\n string assetGroupResourceName,\n List<string> headlineAssetResourceNames,\n List<string> descriptionAssetResourceNames,\n AssetTemporaryResourceNameGenerator resourceNameGenerator,\n GoogleAdsConfig config,\n bool brandGuidelinesEnabled)\n{\n List<MutateOperation> operations = new List<MutateOperation>();\n\n // Create the AssetGroup\n operations.Add(\n new MutateOperation()\n {\n AssetGroupOperation = new AssetGroupOperation()\n {\n Create = new AssetGroup()\n {\n Name = \"Performance Max asset group #\" +\n ExampleUtilities.GetRandomString(),\n Campaign = campaignResourceName,\n FinalUrls = { \"http://www.example.com\" },\n FinalMobileUrls = { \"http://www.example.com\" },\n Status = AssetGroupStatus.Paused,\n ResourceName = assetGroupResourceName\n }\n }\n }\n );\n\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // the resource name of the AssetGroup\n // the resource name of the Asset\n // the field_type of the Asset in this AssetGroup.\n //\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n // Link the previously created multiple text assets.\n\n // Link the headline assets.\n foreach (string resourceName in headlineAssetResourceNames)\n {\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.Headline,\n AssetGroup = assetGroupResourceName,\n Asset = resourceName\n }\n }\n }\n );\n }\n\n // Link the description assets.\n foreach (string resourceName in descriptionAssetResourceNames)\n {\n operations.Add(\n new MutateOperation()\n {\n AssetGroupAssetOperation = new AssetGroupAssetOperation()\n {\n Create = new AssetGroupAsset()\n {\n FieldType = AssetFieldType.Description,\n AssetGroup = assetGroupResourceName,\n Asset = resourceName\n }\n }\n }\n );\n }\n\n // Create and link the brand assets.\n operations.AddRange(\n CreateAndLinkBrandAssets(\n assetGroupResourceName,\n campaignResourceName,\n resourceNameGenerator,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\",\n config,\n brandGuidelinesEnabled\n )\n );\n\n // Create and link the long headline text asset.\n operations.AddRange(\n CreateAndLinkTextAsset(\n assetGroupResourceName,\n resourceNameGenerator.Next(),\n \"Travel the World\",\n AssetFieldType.LongHeadline\n )\n );\n\n // Create and link the image assets.\n\n // Create and link the Marketing Image Asset.\n operations.AddRange(\n CreateAndLinkImageAsset(\n assetGroupResourceName,\n resourceNameGenerator.Next(),\n \"https://gaagl.page.link/Eit5\",\n AssetFieldType.MarketingImage,\n \"Marketing Image\",\n config\n )\n );\n\n // Create and link the Square Marketing Image Asset.\n operations.AddRange(\n CreateAndLinkImageAsset(\n assetGroupResourceName,\n resourceNameGenerator.Next(),\n \"https://gaagl.page.link/bjYi\",\n AssetFieldType.SquareMarketingImage,\n \"Square Marketing Image\",\n config\n )\n );\n\n return operations;\n}\nAddPerformanceMaxCampaign.cs\n```\n\nExample:\n```text\nprivate static function createAssetGroupOperations(\n int $customerId,\n array $headlineAssetResourceNames,\n array $descriptionAssetResourceNames,\n bool $brandGuidelinesEnabled\n): array {\n $operations = [];\n // Creates a new mutate operation that creates an asset group operation.\n $operations[] = new MutateOperation([\n 'asset_group_operation' => new AssetGroupOperation([\n 'create' => new AssetGroup([\n 'resource_name' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'name' => 'Performance Max asset group #' . Helper::getPrintableDatetime(),\n 'campaign' => ResourceNames::forCampaign(\n $customerId,\n self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n 'final_urls' => ['http://www.example.com'],\n 'final_mobile_urls' => ['http://www.example.com'],\n 'status' => AssetGroupStatus::PAUSED\n ])\n ])\n ]);\n\n // For the list of required assets for a Performance Max campaign, see\n // https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n // and providing:\n // - the resource name of the AssetGroup\n // - the resource name of the Asset\n // - the field_type of the Asset in this AssetGroup\n //\n // To learn more about AssetGroups, see\n // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n // Links the previously created multiple text assets.\n\n // Links the headline assets.\n foreach ($headlineAssetResourceNames as $resourceName) {\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $resourceName,\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::HEADLINE\n ])\n ])\n ]);\n }\n // Links the description assets.\n foreach ($descriptionAssetResourceNames as $resourceName) {\n $operations[] = new MutateOperation([\n 'asset_group_asset_operation' => new AssetGroupAssetOperation([\n 'create' => new AssetGroupAsset([\n 'asset' => $resourceName,\n 'asset_group' => ResourceNames::forAssetGroup(\n $customerId,\n self::ASSET_GROUP_TEMPORARY_ID\n ),\n 'field_type' => AssetFieldType::DESCRIPTION\n ])\n ])\n ]);\n }\n\n // Creates and links the long headline text asset.\n $operations = array_merge($operations, self::createAndLinkTextAsset(\n $customerId,\n 'Travel the World',\n AssetFieldType::LONG_HEADLINE\n ));\n // Creates and links the business name text asset.\n $operations = array_merge($operations, self::createAndLinkBrandAssets(\n $customerId,\n $brandGuidelinesEnabled,\n 'Interplanetary Cruises',\n 'https://gaagl.page.link/bjYi',\n 'Marketing Logo'\n ));\n\n // Creates and links the image assets.\n\n // Creates and links the Marketing Image Asset.\n $operations = array_merge($operations, self::createAndLinkImageAsset(\n $customerId,\n 'https://gaagl.page.link/Eit5',\n AssetFieldType::MARKETING_IMAGE,\n 'Marketing Image'\n ));\n // Creates and links the Square Marketing Image Asset.\n $operations = array_merge($operations, self::createAndLinkImageAsset(\n $customerId,\n 'https://gaagl.page.link/bjYi',\n AssetFieldType::SQUARE_MARKETING_IMAGE,\n 'Square Marketing Image'\n ));\n\n return $operations;\n}AddPerformanceMaxCampaign.php\n```\n\nExample:\n```text\ndef create_asset_group_operation(\n client: GoogleAdsClient,\n customer_id: str,\n headline_asset_resource_names: List[str],\n description_asset_resource_names: List[str],\n brand_guidelines_enabled: bool,\n) -> List[MutateOperation]:\n \"\"\"Creates a list of MutateOperations that create a new asset_group.\n\n A temporary ID will be assigned to this asset group so that it can\n be referenced by other objects being created in the same Mutate request.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n headline_asset_resource_names: a list of headline resource names.\n description_asset_resource_names: a list of description resource names.\n brand_guidelines_enabled: a boolean value indicating if the campaign is\n enabled for brand guidelines.\n\n Returns:\n MutateOperations that create a new asset group and related assets.\n \"\"\"\n asset_group_service: AssetGroupServiceClient = client.get_service(\n \"AssetGroupService\"\n )\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n operations: List[MutateOperation] = []\n\n # Create the AssetGroup\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group: AssetGroup = mutate_operation.asset_group_operation.create\n asset_group.name = f\"Performance Max asset group #{uuid4()}\"\n asset_group.campaign = campaign_service.campaign_path(\n customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n )\n asset_group.final_urls.append(\"http://www.example.com\")\n asset_group.final_mobile_urls.append(\"http://www.example.com\")\n asset_group.status = client.enums.AssetGroupStatusEnum.PAUSED\n asset_group.resource_name = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n operations.append(mutate_operation)\n\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # the resource name of the AssetGroup\n # the resource name of the Asset\n # the field_type of the Asset in this AssetGroup.\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n for resource_name in headline_asset_resource_names:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_asset: AssetGroupAsset = (\n mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.HEADLINE\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = resource_name\n operations.append(mutate_operation)\n\n # Link the description assets.\n for resource_name in description_asset_resource_names:\n mutate_operation: MutateOperation = client.get_type(\"MutateOperation\")\n asset_group_asset: AssetGroupAsset = (\n mutate_operation.asset_group_asset_operation.create\n )\n asset_group_asset.field_type = (\n client.enums.AssetFieldTypeEnum.DESCRIPTION\n )\n asset_group_asset.asset_group = asset_group_service.asset_group_path(\n customer_id,\n _ASSET_GROUP_TEMPORARY_ID,\n )\n asset_group_asset.asset = resource_name\n operations.append(mutate_operation)\n\n # Create and link the long headline text asset.\n mutate_operations: List[MutateOperation] = create_and_link_text_asset(\n client,\n customer_id,\n \"Travel the World\",\n client.enums.AssetFieldTypeEnum.LONG_HEADLINE,\n )\n operations.extend(mutate_operations)\n\n # Create and link the business name and logo asset.\n mutate_operations: List[MutateOperation] = create_and_link_brand_assets(\n client,\n customer_id,\n brand_guidelines_enabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\",\n )\n operations.extend(mutate_operations)\n\n # Create and link the image assets.\n\n # Create and link the Marketing Image Asset.\n mutate_operations: List[MutateOperation] = create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/Eit5\",\n client.enums.AssetFieldTypeEnum.MARKETING_IMAGE,\n \"Marketing Image\",\n )\n operations.extend(mutate_operations)\n\n # Create and link the Square Marketing Image Asset.\n mutate_operations: List[MutateOperation] = create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/bjYi\",\n client.enums.AssetFieldTypeEnum.SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\",\n )\n operations.extend(mutate_operations)\n return operationsadd_performance_max_campaign.py\n```\n\nExample:\n```text\n# Creates a list of MutateOperations that create a new asset_group.\n#\n# A temporary ID will be assigned to this asset group so that it can\n# be referenced by other objects being created in the same Mutate request.\ndef create_asset_group_operation(\n client,\n customer_id,\n headline_asset_resource_names,\n description_asset_resource_names,\n brand_guidelines_enabled)\n operations = []\n\n # Create the AssetGroup\n operations << client.operation.mutate do |m|\n m.asset_group_operation = client.operation.create_resource.asset_group do |ag|\n ag.name = \"Performance Max asset group #{SecureRandom.uuid}\"\n ag.campaign = client.path.campaign(\n customer_id,\n PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)\n ag.final_urls << \"http://www.example.com\"\n ag.final_mobile_urls << \"http://www.example.com\"\n ag.status = :PAUSED\n ag.resource_name = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n end\n end\n\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets\n #\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # the resource name of the AssetGroup\n # the resource name of the Asset\n # the field_type of the Asset in this AssetGroup.\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n headline_asset_resource_names.each do |resource_name|\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource\n .asset_group_asset do |aga|\n aga.field_type = :HEADLINE\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n aga.asset = resource_name\n end\n end\n end\n\n # Link the description assets.\n description_asset_resource_names.each do |resource_name|\n operations << client.operation.mutate do |m|\n m.asset_group_asset_operation = client.operation.create_resource\n .asset_group_asset do |aga|\n aga.field_type = :DESCRIPTION\n aga.asset_group = client.path.asset_group(\n customer_id,\n ASSET_GROUP_TEMPORARY_ID)\n aga.asset = resource_name\n end\n end\n end\n\n # Create and link the long headline text asset.\n operations += create_and_link_text_asset(\n client,\n customer_id,\n \"Travel the World\",\n :LONG_HEADLINE)\n\n # Create and link the business name and logo asset.\n operations += create_and_link_brand_assets(\n client,\n customer_id,\n brand_guidelines_enabled,\n \"Interplanetary Cruises\",\n \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\")\n\n # Create and link the image assets.\n\n # Create and link the Marketing Image Asset.\n operations += create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/Eit5\",\n :MARKETING_IMAGE,\n \"Marketing Image\")\n\n # Create and link the Square Marketing Image Asset.\n operations += create_and_link_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/bjYi\",\n :SQUARE_MARKETING_IMAGE,\n \"Square Marketing Image\")\n\n operations\nendadd_performance_max_campaign.rb\n```\n\nExample:\n```text\nsub create_asset_group_operations {\n my (\n $customer_id,\n $headline_asset_resource_names,\n $description_asset_resource_names,\n $brand_guidelines_enabled\n ) = @_;\n\n my $operations = [];\n # Create a mutate operation that creates an asset group operation.\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation->\n new({\n assetGroupOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupService::AssetGroupOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::AssetGroup->new({\n resourceName =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n name => \"Performance Max asset group #\" . uniqid(),\n campaign =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID\n ),\n finalUrls => [\"http://www.example.com\"],\n finalMobileUrls => [\"http://www.example.com\"],\n status =>\n Google::Ads::GoogleAds::V25::Enums::AssetGroupStatusEnum::PAUSED\n })})});\n\n # For the list of required assets for a Performance Max campaign, see\n # https://developers.google.com/google-ads/api/docs/performance-max/assets.\n\n # An AssetGroup is linked to an Asset by creating a new AssetGroupAsset\n # and providing:\n # - the resource name of the AssetGroup\n # - the resource name of the Asset\n # - the fieldType of the Asset in this AssetGroup\n #\n # To learn more about AssetGroups, see\n # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.\n\n # Link the previously created multiple text assets.\n\n # Link the headline assets.\n foreach my $resource_name (@$headline_asset_resource_names) {\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $resource_name,\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => HEADLINE\n })})});\n }\n\n # Link the description assets.\n foreach my $resource_name (@$description_asset_resource_names) {\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::MutateOperation\n ->new({\n assetGroupAssetOperation =>\n Google::Ads::GoogleAds::V25::Services::AssetGroupAssetService::AssetGroupAssetOperation\n ->new({\n create =>\n Google::Ads::GoogleAds::V25::Resources::AssetGroupAsset->new({\n asset => $resource_name,\n assetGroup =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::asset_group(\n $customer_id, ASSET_GROUP_TEMPORARY_ID\n ),\n fieldType => DESCRIPTION\n })})});\n }\n\n # Create and link the long headline text asset.\n push @$operations,\n @{create_and_link_text_asset($customer_id, \"Travel the World\",\n LONG_HEADLINE)};\n\n # Create and link the business name and logo asset.\n push @$operations,\n @{\n create_and_link_brand_assets(\n $customer_id, $brand_guidelines_enabled,\n \"Interplanetary Cruises\", \"https://gaagl.page.link/bjYi\",\n \"Marketing Logo\"\n )};\n\n # Create and link the image assets.\n\n # Create and link the marketing image asset.\n push @$operations,\n @{\n create_and_link_image_asset(\n $customer_id, \"https://gaagl.page.link/Eit5\",\n MARKETING_IMAGE, \"Marketing Image\"\n )};\n\n # Create and link the square marketing image asset.\n push @$operations,\n @{\n create_and_link_image_asset(\n $customer_id, \"https://gaagl.page.link/bjYi\",\n SQUARE_MARKETING_IMAGE, \"Square Marketing Image\"\n )};\n\n return $operations;\n}add_performance_max_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.515Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":729,"estimatedTokens":6467}}202{"id":"doc-implement_an_oauth_2_0_server_cloud_to_cloud_goo-1b120d68","source":"documentation","title":"Implement an OAuth 2.0 server | Cloud-to-cloud | Google Home Developers","url":"https://developers.google.com/assistant/smarthome/develop/implement-oauth","text":"Example:\n```text\nGET https://myservice.example.com/auth?client_id=GOOGLE_CLIENT_ID&redirect_uri=REDIRECT_URI&state=STATE_STRING&scope=REQUESTED_SCOPES&response_type=code\n```\n\nExample:\n```text\nhttps://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID\n https://oauth-redirect-sandbox.googleusercontent.com/r/YOUR_PROJECT_ID\n```\n\nExample:\n```text\nhttps://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID?code=AUTHORIZATION_CODE&state=STATE_STRING\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.example.com\nContent-Type: application/x-www-form-urlencoded\n\nclient_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET&grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=REDIRECT_URI\n```\n\nExample:\n```text\n{\n\"token_type\": \"Bearer\",\n\"access_token\": \"ACCESS_TOKEN\",\n\"refresh_token\": \"REFRESH_TOKEN\",\n\"expires_in\": SECONDS_TO_EXPIRATION\n}\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.example.com\nContent-Type: application/x-www-form-urlencoded\n\nclient_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET&grant_type=refresh_token&refresh_token=REFRESH_TOKEN\n```\n\nExample:\n```text\n{\n\"token_type\": \"Bearer\",\n\"access_token\": \"ACCESS_TOKEN\",\n\"expires_in\": SECONDS_TO_EXPIRATION\n}\n```\n\nExample:\n```text\nGET /userinfo HTTP/1.1\nHost: myservice.example.com\nAuthorization: Bearer ACCESS_TOKEN\n```\n\nExample:\n```text\nHTTP/1.1 401 Unauthorized\nWWW-Authenticate: error=\"invalid_token\",\nerror_description=\"The Access Token expired\"\n```\n\nExample:\n```text\n{\n\"sub\": \"USER_UUID\",\n\"email\": \"EMAIL_ADDRESS\",\n\"given_name\": \"FIRST_NAME\",\n\"family_name\": \"LAST_NAME\",\n\"name\": \"FULL_NAME\",\n\"picture\": \"PROFILE_PICTURE\",\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.517Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":80,"estimatedTokens":414}}203{"id":"doc-prerequisites_google_ads_api_google_for_develope-61446bd6","source":"documentation","title":"Prerequisites | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/dynamic-remarketing/prerequisites","text":"Example:\n```text\ngtag('event', 'view_item', {\n value: 29.99,\n items: [\n {\n item_id: '34592212'\n }\n ]\n});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.519Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":34}}204{"id":"doc-create_a_user_list_google_ads_api_google_for_dev-0f727d1b","source":"documentation","title":"Create a user list | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audience-segments/scenario/create-user-list","text":"Example:\n```text\nFlexibleRuleUserListInfo flexibleRuleUserListInfo =\n FlexibleRuleUserListInfo.newBuilder()\n .setInclusiveRuleOperator(UserListFlexibleRuleOperator.AND)\n .addInclusiveOperands(\n FlexibleRuleOperandInfo.newBuilder()\n .setRule(\n // The default rule_type for a UserListRuleInfo object is OR of ANDs\n // (disjunctive normal form). That is, rule items will be ANDed together\n // within rule item groups and the groups themselves will be ORed together.\n UserListRuleInfo.newBuilder()\n .addRuleItemGroups(checkoutDateRuleGroup)\n .addRuleItemGroups(checkoutAndCartSizeRuleGroup))\n // Optional: includes a lookback window for this rule, in days.\n .setLookbackWindowDays(7L))\n .build();SetUpAdvancedRemarketing.java\n```\n\nExample:\n```text\nFlexibleRuleUserListInfo flexibleRuleUserListInfo = new FlexibleRuleUserListInfo();\nFlexibleRuleOperandInfo flexibleRuleOperandInfo = new FlexibleRuleOperandInfo() {\n Rule = new UserListRuleInfo()\n};\nflexibleRuleOperandInfo.Rule.RuleItemGroups.Add(checkoutAndCartSizeRuleGroup);\nflexibleRuleOperandInfo.Rule.RuleItemGroups.Add(checkoutDateRuleGroup);\nflexibleRuleUserListInfo.InclusiveOperands.Add(flexibleRuleOperandInfo);SetUpAdvancedRemarketing.cs\n```\n\nExample:\n```text\n$flexibleRuleUserListInfo = new FlexibleRuleUserListInfo([\n 'inclusive_rule_operator' => UserListFlexibleRuleOperator::PBAND,\n 'inclusive_operands' => [\n new FlexibleRuleOperandInfo([\n 'rule' => new UserListRuleInfo([\n // The default rule_type for a UserListRuleInfo object is OR of ANDs\n // (disjunctive normal form). That is, rule items will be ANDed together\n // within rule item groups and the groups themselves will be ORed together.\n 'rule_item_groups' => [\n $checkoutAndCartSizeRuleGroup,\n $checkoutDateRuleGroup\n ]\n ]),\n // Optionally add a lookback window for this rule, in days.\n 'lookback_window_days' => 7\n ])\n ],\n 'exclusive_operands' => []\n]);SetUpAdvancedRemarketing.php\n```\n\nExample:\n```text\n# Create a FlexibleRuleUserListInfo object, or a flexible rule\n# representation of visitors with one or multiple actions.\n# FlexibleRuleUserListInfo wraps UserListRuleInfo in a\n# FlexibleRuleOperandInfo object that represents which user lists to\n# include or exclude.\nflexible_rule_user_list_info: FlexibleRuleUserListInfo = (\n rule_based_user_list_info.flexible_rule_user_list\n)\nflexible_rule_user_list_info.inclusive_rule_operator = (\n client.enums.UserListFlexibleRuleOperatorEnum.AND\n)\n# The default rule_type for a UserListRuleInfo object is OR of\n# ANDs (disjunctive normal form). That is, rule items will be\n# ANDed together within rule item groups and the groups\n# themselves will be ORed together.\nrule_operand: FlexibleRuleOperandInfo = client.get_type(\n \"FlexibleRuleOperandInfo\"\n)\nrule_operand.rule.rule_item_groups.extend(\n [\n checkout_and_cart_size_rule_group,\n checkout_date_rule_group,\n ]\n)\nrule_operand.lookback_window_days = 7\nflexible_rule_user_list_info.inclusive_operands.append(rule_operand)set_up_advanced_remarketing.py\n```\n\nExample:\n```text\nr.flexible_rule_user_list = client.resource.flexible_rule_user_list_info do |frul|\n frul.inclusive_rule_operator = :AND\n frul.inclusive_operands << client.resource.flexible_rule_operand_info do |froi|\n froi.rule = client.resource.user_list_rule_info do |info|\n info.rule_item_groups += [checkout_date_rule_group, checkout_and_cart_size_rule_group]\n end\n # Optionally include a lookback window for this rule, in days.\n froi.lookback_window_days = 7\n end\nendset_up_advanced_remarketing.rb\n```\n\nExample:\n```text\nmy $flexible_rule_user_list_info =\n Google::Ads::GoogleAds::V25::Common::FlexibleRuleUserListInfo->new({\n inclusiveRuleOperator => AND,\n inclusiveOperands => [\n Google::Ads::GoogleAds::V25::Common::FlexibleRuleOperandInfo->new({\n rule => Google::Ads::GoogleAds::V25::Common::UserListRuleInfo->new({\n # The default rule_type for a UserListRuleInfo object is OR of\n # ANDs (disjunctive normal form). That is, rule items will be\n # ANDed together within rule item groups and the groups\n # themselves will be ORed together.\n ruleItemGroups => [\n $checkout_date_rule_group, $checkout_and_cart_size_rule_group\n ]}\n ),\n # Optionally include a lookback window for this rule, in days.\n lookback_window_days => 7\n })\n ],\n exclusiveOperands => []});set_up_advanced_remarketing.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.520Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":121,"estimatedTokens":1221}}205{"id":"doc-lookalike_audience_segments_google_ads_api_googl-b6d5c4e3","source":"documentation","title":"Lookalike audience segments | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audience-segments/lookalike-audiences","text":"Example:\n```text\nSELECT\n ad_group_criterion.criterion_id,\n metrics.conversions,\n metrics.cost_per_conversion\nFROM ad_group_audience_view\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.521Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":40}}206{"id":"doc-custom_audiences_google_ads_api_google_for_devel-ee7f3c37","source":"documentation","title":"Custom Audiences | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audience-segments/custom-audiences","text":"Example:\n```text\n// Copyright 2020 Google LLC\n//\n// 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// http://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.\n\npackage com.google.ads.googleads.examples.remarketing;\n\nimport static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.enums.CustomAudienceMemberTypeEnum.CustomAudienceMemberType;\nimport com.google.ads.googleads.v25.enums.CustomAudienceStatusEnum.CustomAudienceStatus;\nimport com.google.ads.googleads.v25.enums.CustomAudienceTypeEnum.CustomAudienceType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.resources.CustomAudience;\nimport com.google.ads.googleads.v25.resources.CustomAudienceMember;\nimport com.google.ads.googleads.v25.services.CustomAudienceOperation;\nimport com.google.ads.googleads.v25.services.CustomAudienceServiceClient;\nimport com.google.ads.googleads.v25.services.MutateCustomAudiencesResponse;\nimport com.google.common.collect.ImmutableList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\n\n/**\n * Illustrates adding a custom audience. Custom audiences help you reach your ideal audience by\n * entering relevant keywords, URLs and apps. For more information about custom audiences, see:\n * https://support.google.com/google-ads/answer/9805516.\n */\npublic class AddCustomAudience {\n\n private static class AddCustomAudienceParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n }\n\n public static void main(String[] args) {\n AddCustomAudienceParams params = new AddCustomAudienceParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new AddCustomAudience().runExample(googleAdsClient, params.customerId);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /** Runs the example. */\n private void runExample(GoogleAdsClient googleAdsClient, long customerId) {\n // Creates a CustomAudience object to represent the new audience.\n CustomAudience customAudience =\n CustomAudience.newBuilder()\n .setName(\"Example CustomAudience #\" + getPrintableDateTime())\n .setDescription(\"Custom audiences who have searched specific terms on Google Search\")\n // Matches customers by what they searched on Google Search.\n // Note: \"INTEREST\" OR \"PURCHASE_INTENT\" is not allowed for the type field\n // of newly created custom audience. Use \"AUTO\" instead of these 2 options\n // when creating a new custom audience.\n .setType(CustomAudienceType.SEARCH)\n .setStatus(CustomAudienceStatus.ENABLED)\n\n // Lists the members that this custom audience is composed of. Customers that meet any\n // of the membership conditions will be reached.\n\n // Adds Keywords or keyword phrases, which describe the customers' interests or search\n // terms.\n .addMembers(createCustomAudienceMember(CustomAudienceMemberType.KEYWORD, \"mars cruise\"))\n .addMembers(\n createCustomAudienceMember(CustomAudienceMemberType.KEYWORD, \"jupiter cruise\"))\n\n // Adds website URLs that your customers might visit.\n .addMembers(\n createCustomAudienceMember(\n CustomAudienceMemberType.URL, \"http://www.example.com/locations/mars\"))\n .addMembers(\n createCustomAudienceMember(\n CustomAudienceMemberType.URL, \"http://www.example.com/locations/jupiter\"))\n\n // Adds package names of Android apps which customers might install.\n .addMembers(\n createCustomAudienceMember(\n CustomAudienceMemberType.APP, \"com.google.android.apps.adwords\"))\n .build();\n\n // Creates an operation to add the CustomAudience.\n CustomAudienceOperation operation =\n CustomAudienceOperation.newBuilder().setCreate(customAudience).build();\n\n // Creates an API client and send the mutate request.\n try (CustomAudienceServiceClient serviceClient =\n googleAdsClient.getLatestVersion().createCustomAudienceServiceClient()) {\n // Issues the mutate request.\n MutateCustomAudiencesResponse response =\n serviceClient.mutateCustomAudiences(\n String.valueOf(customerId), ImmutableList.of(operation));\n\n // Prints some information about the result.\n System.out.printf(\n \"New custom audience added with resource name: '%s'.\\n\",\n response.getResults(0).getResourceName());\n }\n }\n\n /**\n * Constructs a {@link CustomAudienceMember} from a {@link CustomAudienceMemberType} and value for\n * the member type.\n */\n private static CustomAudienceMember createCustomAudienceMember(\n CustomAudienceMemberType memberType, String value) {\n CustomAudienceMember.Builder builder =\n CustomAudienceMember.newBuilder().setMemberType(memberType);\n if (memberType == CustomAudienceMemberType.KEYWORD) {\n builder.setKeyword(value);\n } else if (memberType == CustomAudienceMemberType.URL) {\n builder.setUrl(value);\n } else if (memberType == CustomAudienceMemberType.APP) {\n builder.setApp(value);\n }\n return builder.build();\n }\n}\nAddCustomAudience.java\n```\n\nExample:\n```text\n// Copyright 2021 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing static Google.Ads.GoogleAds.V25.Enums.CustomAudienceMemberTypeEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.CustomAudienceStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.CustomAudienceTypeEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This example illustrates adding a custom audience. Custom audiences help you reach your\n /// ideal audience by entering relevant keywords, URLs and apps. For more information about\n /// custom audiences, see:\n /// https://support.google.com/google-ads/answer/9805516.\n /// </summary>\n public class AddCustomAudience : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"AddCustomAudience\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID for which the conversion action is added.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID for which the conversion action is added.\")]\n public long CustomerId { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n AddCustomAudience codeExample = new AddCustomAudience();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId);\n }\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This example illustrates adding a custom audience. Custom audiences help you reach \" +\n \"your ideal audience by entering relevant keywords, URLs and apps. For more \" +\n \"information about custom audiences, see:\" +\n \"https://support.google.com/google-ads/answer/9805516.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the conversion action is\n /// added.</param>\n public void Run(GoogleAdsClient client, long customerId)\n {\n // Get the CustomAudienceService client.\n CustomAudienceServiceClient customAudienceServiceClient =\n client.GetService(Services.V25.CustomAudienceService);\n\n // Create a custom audience.\n CustomAudience customAudience = new CustomAudience\n {\n Name = $\"Example CustomAudience #{ExampleUtilities.GetRandomString()}\",\n Description = \"Custom audiences who have searched specific terms on Google Search\",\n // Match customers by what they searched on Google Search.\n // Note: \"INTEREST\" OR \"PURCHASE_INTENT\" is not allowed for the type field of newly\n // created custom audience. Use \"AUTO\" instead of these 2 options when creating a\n // new custom audience.\n Type = CustomAudienceType.Search,\n Status = CustomAudienceStatus.Enabled,\n };\n\n // Add custom audience members to the custom audience. Customers that meet any of the\n // membership conditions will be reached.\n // Keywords or keyword phrases, which describe the customers' interests or search terms.\n customAudience.Members.Add(CreateCustomAudienceMember(CustomAudienceMemberType.Keyword,\n \"mars cruise\"));\n customAudience.Members.Add(CreateCustomAudienceMember(CustomAudienceMemberType.Keyword,\n \"jupiter cruise\"));\n // Website URLs that your customers might visit.\n customAudience.Members.Add(CreateCustomAudienceMember(CustomAudienceMemberType.Url,\n \"http://www.example.com/locations/mars\"));\n customAudience.Members.Add(CreateCustomAudienceMember(CustomAudienceMemberType.Url,\n \"http://www.example.com/locations/jupiter\"));\n // Package names of Android apps which customers might install.\n customAudience.Members.Add(CreateCustomAudienceMember(CustomAudienceMemberType.App,\n \"com.google.android.apps.adwords\"));\n\n // Create a custom audience operation.\n CustomAudienceOperation customAudienceOperation = new CustomAudienceOperation\n {\n Create = customAudience\n };\n\n try\n {\n // Add the custom audience and display the results.\n MutateCustomAudiencesResponse customAudiencesResponse = customAudienceServiceClient\n .MutateCustomAudiences(customerId.ToString(), new[] { customAudienceOperation });\n\n Console.WriteLine(\"New custom audience added with resource name: \" +\n $\"'{customAudiencesResponse.Results.First().ResourceName}'.\");\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Creates a custom audience member.\n /// </summary>\n /// <param name=\"memberType\">The intended type of the new audience member.</param>\n /// <param name=\"value\">The custom value to assign to the new audience member.</param>\n /// <returns></returns>\n public CustomAudienceMember CreateCustomAudienceMember(CustomAudienceMemberType memberType,\n string value)\n {\n CustomAudienceMember customAudienceMember = new CustomAudienceMember\n {\n MemberType = memberType\n };\n\n switch (memberType)\n {\n case CustomAudienceMemberType.Keyword:\n customAudienceMember.Keyword = value;\n break;\n\n case CustomAudienceMemberType.Url:\n customAudienceMember.Url = value;\n break;\n\n case CustomAudienceMemberType.App:\n customAudienceMember.App = value;\n break;\n }\n\n return customAudienceMember;\n }\n }\n}\nAddCustomAudience.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2021 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\Remarketing;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\CustomAudienceMemberTypeEnum\\CustomAudienceMemberType;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\CustomAudienceStatusEnum\\CustomAudienceStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\CustomAudienceTypeEnum\\CustomAudienceType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CustomAudience;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\CustomAudienceMember;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CustomAudienceOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateCustomAudiencesRequest;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * Illustrates adding a custom audience. Custom audiences help you reach your ideal audience by\n * entering relevant keywords, URLs and apps. For more information about custom audiences, see:\n * https://support.google.com/google-ads/answer/9805516.\n */\nclass AddCustomAudience\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())\n ->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n */\n public static function runExample(GoogleAdsClient $googleAdsClient, int $customerId)\n {\n // Creates a CustomAudience object to represent the new audience.\n $customAudience = new CustomAudience([\n 'name' => 'Example CustomAudience #' . Helper::getPrintableDatetime(),\n 'description' => 'Custom audiences who have searched specific terms on Google Search',\n // Matches customers by what they searched on Google Search.\n // Note: \"INTEREST\" OR \"PURCHASE_INTENT\" is not allowed for the type field\n // of newly created custom audience. Use \"AUTO\" instead of these 2 options\n // when creating a new custom audience.\n 'type' => CustomAudienceType::SEARCH,\n 'status' => CustomAudienceStatus::ENABLED,\n // Lists the members that this custom audience is composed of. Customers that meet any\n // of the membership conditions will be reached.\n 'members' => [\n // Adds Keywords or keyword phrases, which describe the customers' interests or\n // search terms.\n self::createCustomAudienceMember(CustomAudienceMemberType::KEYWORD, \"mars cruise\"),\n self::createCustomAudienceMember(\n CustomAudienceMemberType::KEYWORD,\n \"jupiter cruise\"\n ),\n // Adds website URLs that your customers might visit.\n self::createCustomAudienceMember(\n CustomAudienceMemberType::URL,\n \"http://www.example.com/locations/mars\"\n ),\n self::createCustomAudienceMember(\n CustomAudienceMemberType::URL,\n \"http://www.example.com/locations/jupiter\"\n ),\n // Adds package names of Android apps which customers might install.\n self::createCustomAudienceMember(\n CustomAudienceMemberType::APP,\n \"com.google.android.apps.adwords\"\n )\n ]\n ]);\n\n // Creates the operation.\n $operation = new CustomAudienceOperation();\n $operation->setCreate($customAudience);\n\n // Issues a mutate request to add the custom audience and prints some information.\n $customAudienceServiceClient = $googleAdsClient->getCustomAudienceServiceClient();\n $response = $customAudienceServiceClient->mutateCustomAudiences(\n MutateCustomAudiencesRequest::build($customerId, [$operation])\n );\n printf(\n \"Created custom audience with resource name '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n }\n\n /**\n * Constructs a custom audience member object for a given customer audience member type and\n * value.\n *\n * @param int $memberType the custom audience member type\n * @param string $value the custom audience member value\n * @return CustomAudienceMember the newly constructed customer audience member object\n */\n private static function createCustomAudienceMember(\n int $memberType,\n string $value\n ): CustomAudienceMember {\n $customerAudienceMember = new CustomAudienceMember(['member_type' => $memberType]);\n if ($memberType == CustomAudienceMemberType::KEYWORD) {\n $customerAudienceMember->setKeyword($value);\n } elseif ($memberType == CustomAudienceMemberType::URL) {\n $customerAudienceMember->setUrl($value);\n } elseif ($memberType == CustomAudienceMemberType::APP) {\n $customerAudienceMember->setApp($value);\n }\n return $customerAudienceMember;\n }\n}\n\nAddCustomAudience::main();\nAddCustomAudience.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2020 Google LLC\n#\n# 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.\n\"\"\"This example illustrates adding a custom audience.\n\nCustom audiences help you reach your ideal audience by entering relevant\nkeywords, URLs, and apps. For more information about custom audiences, see:\nhttps://support.google.com/google-ads/answer/9805516\n\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom uuid import uuid4\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.enums.types.custom_audience_member_type import (\n CustomAudienceMemberTypeEnum,\n)\nfrom google.ads.googleads.v24.resources.types.custom_audience import (\n CustomAudience,\n CustomAudienceMember,\n)\nfrom google.ads.googleads.v24.services.types.custom_audience_service import (\n CustomAudienceOperation,\n MutateCustomAudiencesResponse,\n)\nfrom google.ads.googleads.v24.services.services.custom_audience_service import (\n CustomAudienceServiceClient,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef main(client: GoogleAdsClient, customer_id: str) -> None:\n \"\"\"The main method that creates all necessary entities for the example.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n \"\"\"\n custom_audience_service: CustomAudienceServiceClient = client.get_service(\n \"CustomAudienceService\"\n )\n\n # Create a custom audience operation.\n custom_audience_operation: CustomAudienceOperation = client.get_type(\n \"CustomAudienceOperation\"\n )\n\n # Create a custom audience\n custom_audience: CustomAudience = custom_audience_operation.create\n custom_audience.name = f\"Example CustomAudience #{uuid4()}\"\n custom_audience.description = (\n \"Custom audiences who have searched specific terms on Google Search.\"\n )\n # Match customers by what they searched on Google Search. Note: \"INTEREST\"\n # or \"PURCHASE_INTENT\" is not allowed for the type field of a newly\n # created custom audience. Use \"AUTO\" instead of these two options when\n # creating a new custom audience.\n custom_audience.type_ = client.enums.CustomAudienceTypeEnum.SEARCH\n custom_audience.status = client.enums.CustomAudienceStatusEnum.ENABLED\n # List of members that this custom audience is composed of. Customers that\n # meet any of the membership conditions will be reached.\n member_type_enum: CustomAudienceMemberTypeEnum = (\n client.enums.CustomAudienceMemberTypeEnum\n )\n\n member1: CustomAudienceMember = create_custom_audience_member(\n client, member_type_enum.KEYWORD, \"mars cruise\"\n )\n\n member2: CustomAudienceMember = create_custom_audience_member(\n client, member_type_enum.KEYWORD, \"jupiter cruise\"\n )\n\n member3: CustomAudienceMember = create_custom_audience_member(\n client, member_type_enum.URL, \"http://www.example.com/locations/mars\"\n )\n\n member4: CustomAudienceMember = create_custom_audience_member(\n client, member_type_enum.URL, \"http://www.example.com/locations/jupiter\"\n )\n\n member5: CustomAudienceMember = create_custom_audience_member(\n client, member_type_enum.APP, \"com.google.android.apps.adwords\"\n )\n\n custom_audience.members.extend(\n [member1, member2, member3, member4, member5]\n )\n\n # Add the custom audience.\n custom_audience_response: MutateCustomAudiencesResponse = (\n custom_audience_service.mutate_custom_audiences(\n customer_id=customer_id, operations=[custom_audience_operation]\n )\n )\n\n print(\n \"New custom audience added with resource name: \"\n f\"'{custom_audience_response.results[0].resource_name}'\"\n )\n\n\ndef create_custom_audience_member(\n client: GoogleAdsClient,\n member_type: CustomAudienceMemberTypeEnum,\n value: str,\n) -> CustomAudienceMember:\n \"\"\"Creates a custom audience member for a given member type and value.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n member_type: the custom audience member type.\n value: the custom audience member value.\n\n Returns:\n A newly created CustomAudienceMember.\n \"\"\"\n member: CustomAudienceMember = client.get_type(\"CustomAudienceMember\")\n member.member_type = member_type\n\n member_type_enum: CustomAudienceMemberTypeEnum = (\n client.enums.CustomAudienceMemberTypeEnum\n )\n\n if member_type == member_type_enum.KEYWORD:\n member.keyword = value\n elif member_type == member_type_enum.URL:\n member.url = value\n elif member_type == member_type_enum.APP:\n member.app = value\n else:\n raise ValueError(\n \"The member type must be a MemberTypeEnum value of KEYWORD, URL, or APP\"\n )\n\n return member\n\n\nif __name__ == \"__main__\":\n parser: argparse.ArgumentParser = argparse.ArgumentParser(\n description=\"Adds a custom audience for a specified customer.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n args: argparse.Namespace = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(googleads_client, args.customer_id)\n except GoogleAdsException as ex:\n print(\n f'Request with ID \"{ex.request_id}\" failed with status '\n f'\"{ex.error.code().name}\" and includes the following errors:'\n )\n for error in ex.failure.errors:\n print(f'\\tError with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nadd_custom_audience.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2020 Google LLC\n#\n# 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.\n#\n# This example uses Customer Match to create a new user list (a.k.a. audience)\n# and adds users to it.\n#\n# This example illustrates adding a custom audience. Custom audiences help you\n# reach your ideal audience by entering relevant keywords, URLs and apps.\n# For more information about custom audiences, see:\n# https://support.google.com/google-ads/answer/9805516.\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\nrequire 'date'\nrequire 'digest'\n\ndef add_custom_audience(customer_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Creates a custom audience operation.\n operation = client.operation.create_resource.custom_audience do |ca|\n ca.name = \"Example Custom Audience ##{(Time.new.to_f * 1000).to_i}\"\n ca.description = \"Custom audiences who have searched specific terms on Google Search\"\n # Match customers by what they searched on Google Search.\n # Note: \"INTEREST\" OR \"PURCHASE_INTENT\" is not allowed for the type field\n # of newly created custom audience. Use \"AUTO\" instead of these 2 options\n # when creating a new custom audience.\n ca.type = :SEARCH\n ca.status = :ENABLED\n # List of members that this custom audience is composed of. Customers that\n # meet any of the membership conditions will be reached.\n ca.members += [\n # Keywords or keyword phrases, which describe the customers' interests\n # or search terms.\n create_custom_audience_member(client, :KEYWORD, \"Mars Cruise\"),\n create_custom_audience_member(client, :KEYWORD, \"Jupiter Cruise\"),\n # Website URLs that your customers might visit.\n create_custom_audience_member(client, :URL, \"http://www.example.com/locations/mars\"),\n create_custom_audience_member(client, :URL, \"http://www.example.com/locations/jupiter\"),\n # Package names of Android apps which customers might install.\n create_custom_audience_member(client, :APP, \"com.google.android.apps.adwords\"),\n ]\n end\n\n # Issues a mutate request to add the custom audience.\n response = client.service.custom_audience.mutate_custom_audiences(\n customer_id: customer_id,\n operations: [operation],\n )\n puts \"New custom audience added with resource name: \" \\\n \"'#{response.results.first.resource_name}'.\"\nend\n\n# Creates a custom audience member.\ndef create_custom_audience_member(client, member_type, member_value)\n client.resource.custom_audience_member do |m|\n m.member_type = member_type\n case member_type\n when :KEYWORD\n m.keyword = member_value\n when :URL\n m.url = member_value\n when :APP\n m.app = member_value\n else\n raise \"Invalid audience member type.\"\n end\n end\nend\n\nif __FILE__ == $0\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n add_custom_audience(options.fetch(:customer_id).tr(\"-\", \"\"))\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\n\nadd_custom_audience.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2020, Google LLC\n#\n# 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# http://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.\n#\n# This example illustrates adding a custom audience. Custom audiences help you\n# reach your ideal audience by entering relevant keywords, URLs and apps. For more\n# information about custom audiences, see:\n# https://support.google.com/google-ads/answer/9805516.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::CustomAudience;\nuse Google::Ads::GoogleAds::V25::Resources::CustomAudienceMember;\nuse Google::Ads::GoogleAds::V25::Enums::CustomAudienceTypeEnum qw(SEARCH);\nuse Google::Ads::GoogleAds::V25::Enums::CustomAudienceStatusEnum qw(ENABLED);\nuse Google::Ads::GoogleAds::V25::Enums::CustomAudienceMemberTypeEnum\n qw(KEYWORD URL APP);\nuse\n Google::Ads::GoogleAds::V25::Services::CustomAudienceService::CustomAudienceOperation;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\n\nsub add_custom_audience {\n my ($api_client, $customer_id) = @_;\n\n # Create a custom audience.\n my $custom_audience =\n Google::Ads::GoogleAds::V25::Resources::CustomAudience->new({\n name => \"Example CustomAudience #\" . uniqid(),\n description =>\n \"Custom audiences who have searched specific terms on Google Search\",\n # Match customers by what they searched on Google Search.\n # Note: \"INTEREST\" OR \"PURCHASE_INTENT\" is not allowed for the type field\n # of newly created custom audience. Use \"AUTO\" instead of these 2 options\n # when creating a new custom audience.\n type => SEARCH,\n status => ENABLED,\n # List of members that this custom audience is composed of. Customers that\n # meet any of the membership conditions will be reached.\n members => [\n # Keywords or keyword phrases, which describe the customers' interests\n # or search terms.\n create_custom_audience_member(KEYWORD, \"mars cruise\"),\n create_custom_audience_member(KEYWORD, \"jupiter cruise\"),\n # Website URLs that your customers might visit.\n create_custom_audience_member(\n URL, \"http://www.example.com/locations/mars\"\n ),\n create_custom_audience_member(\n URL, \"http://www.example.com/locations/jupiter\"\n ),\n # Package names of Android apps which customers might install.\n create_custom_audience_member(APP, \"com.google.android.apps.adwords\"),\n ]});\n\n # Create a custom audience operation.\n my $custom_audience_operation =\n Google::Ads::GoogleAds::V25::Services::CustomAudienceService::CustomAudienceOperation\n ->new({create => $custom_audience});\n\n # Add the custom audience.\n my $custom_audiences_response = $api_client->CustomAudienceService()->mutate({\n customerId => $customer_id,\n operations => [$custom_audience_operation]});\n\n printf \"New custom audience added with resource name: '%s'.\\n\",\n $custom_audiences_response->{results}[0]{resourceName};\n\n return 1;\n}\n\n# Creates a custom audience member.\nsub create_custom_audience_member {\n my ($member_type, $value) = @_;\n my $custom_audience_member =\n Google::Ads::GoogleAds::V25::Resources::CustomAudienceMember->new({\n memberType => $member_type\n });\n\n if ($member_type eq KEYWORD) {\n $custom_audience_member->{keyword} = $value;\n } elsif ($member_type eq URL) {\n $custom_audience_member->{url} = $value;\n } elsif ($member_type eq APP) {\n $custom_audience_member->{app} = $value;\n }\n\n return $custom_audience_member;\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\"customer_id=s\" => \\$customer_id);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id);\n\n# Call the example.\nadd_custom_audience($api_client, $customer_id =~ s/-//gr);\n\n=pod\n\n=head1 NAME\n\nadd_custom_audience\n\n=head1 DESCRIPTION\n\nThis example illustrates adding a custom audience. Custom audiences help you\nreach your ideal audience by entering relevant keywords, URLs and apps. For more\ninformation about custom audiences, see:\nhttps://support.google.com/google-ads/answer/9805516.\n\n=head1 SYNOPSIS\n\nadd_custom_audience.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n\n=cut\nadd_custom_audience.pl\n```\n\nExample:\n```text\nSELECT\n ad_group_criterion.criterion_id,\n metrics.conversions,\n metrics.cost_per_conversion\nFROM ad_group_audience_view\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.521Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":1044,"estimatedTokens":10114}}207{"id":"doc-creating_the_rule_item_groups_google_ads_api_goo-0e2b2648","source":"documentation","title":"Creating the Rule Item Groups | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audience-segments/scenario/rule-item-groups","text":"Example:\n```text\nUserListRuleItemInfo checkoutRule =\n UserListRuleItemInfo.newBuilder()\n // The rule variable name must match a corresponding key name fired from a pixel.\n // To learn more about setting up remarketing tags, visit\n // https://support.google.com/google-ads/answer/2476688.\n // To learn more about remarketing events and parameters, visit\n // https://support.google.com/google-ads/answer/7305793.\n .setName(\"ecomm_pagetype\")\n .setStringRuleItem(\n UserListStringRuleItemInfo.newBuilder()\n .setOperator(UserListStringRuleItemOperator.EQUALS)\n .setValue(\"checkout\")\n .build())\n .build();SetUpAdvancedRemarketing.java\n```\n\nExample:\n```text\nUserListRuleItemInfo checkoutRule = new UserListRuleItemInfo\n{\n // The rule variable name must match a corresponding key name fired from a pixel.\n // To learn more about setting up remarketing tags, visit\n // https://support.google.com/google-ads/answer/2476688.\n // To learn more about remarketing events and parameters, visit\n // https://support.google.com/google-ads/answer/7305793.\n Name = \"ecomm_pagetype\",\n StringRuleItem = new UserListStringRuleItemInfo\n {\n Operator = UserListStringRuleItemOperator.Equals,\n Value = \"checkout\"\n }\n};SetUpAdvancedRemarketing.cs\n```\n\nExample:\n```text\n$checkoutRule = new UserListRuleItemInfo([\n // The rule variable name must match a corresponding key name fired from a pixel.\n // To learn more about setting up remarketing tags, visit\n // https://support.google.com/google-ads/answer/2476688.\n // To learn more about remarketing events and parameters, visit\n // https://support.google.com/google-ads/answer/7305793.\n 'name' => 'ecomm_pagetype',\n 'string_rule_item' => new UserListStringRuleItemInfo([\n 'operator' => UserListStringRuleItemOperator::EQUALS,\n 'value' => 'checkout'\n ])\n]);SetUpAdvancedRemarketing.php\n```\n\nExample:\n```text\ncheckout_rule: UserListRuleItemInfo = client.get_type(\n \"UserListRuleItemInfo\"\n)\n\n# The rule variable name must match a corresponding key name fired from a\n# pixel. To learn more about setting up remarketing tags, visit:\n# https://support.google.com/google-ads/answer/2476688.\n#\n# To learn more about remarketing events and parameters, visit:\n# https://support.google.com/google-ads/answer/7305793.\ncheckout_rule.name = \"ecomm_pagetype\"\ncheckout_string_rule_item: UserListStringRuleItemInfo = (\n checkout_rule.string_rule_item\n)\ncheckout_string_rule_item.operator = (\n client.enums.UserListStringRuleItemOperatorEnum.EQUALS\n)\ncheckout_string_rule_item.value = \"checkout\"set_up_advanced_remarketing.py\n```\n\nExample:\n```text\ncheckout_rule = client.resource.user_list_rule_item_info do |rule|\n # The rule variable name must match a corresponding key name fired\n # from a pixel.\n # To learn more about setting up remarketing tags, visit\n # https://support.google.com/google-ads/answer/2476688.\n # To learn more about remarketing events and parameters, visit\n # https://support.google.com/google-ads/answer/7305793.\n rule.name = \"ecomm_pagetype\"\n rule.string_rule_item = client.resource.user_list_string_rule_item_info do |sr|\n sr.operator = :EQUALS\n sr.value = \"checkout\"\n end\nendset_up_advanced_remarketing.rb\n```\n\nExample:\n```text\nmy $checkout_rule =\n Google::Ads::GoogleAds::V25::Common::UserListRuleItemInfo->new({\n # The rule variable name must match a corresponding key name fired from a\n # pixel. To learn more about setting up remarketing tags, visit\n # https://support.google.com/google-ads/answer/2476688.\n # To learn more about remarketing events and parameters, visit\n # https://support.google.com/google-ads/answer/7305793.\n name => \"ecomm_pagetype\",\n stringRuleItem =>\n Google::Ads::GoogleAds::V25::Common::UserListStringRuleItemInfo->new({\n operator => EQUALS,\n value => \"checkout\"\n })});set_up_advanced_remarketing.pl\n```\n\nExample:\n```text\nUserListRuleItemInfo cartSizeRule =\n UserListRuleItemInfo.newBuilder()\n // The rule variable name must match a corresponding key name fired from a pixel.\n .setName(\"cart_size\")\n .setNumberRuleItem(\n UserListNumberRuleItemInfo.newBuilder()\n .setOperator(UserListNumberRuleItemOperator.GREATER_THAN)\n .setValue(1.0)\n .build())\n .build();SetUpAdvancedRemarketing.java\n```\n\nExample:\n```text\nUserListRuleItemInfo cartSizeRule = new UserListRuleItemInfo\n{\n // The rule variable name must match a corresponding key name fired from a pixel.\n Name = \"cart_size\",\n NumberRuleItem = new UserListNumberRuleItemInfo\n {\n Operator = UserListNumberRuleItemOperator.GreaterThan,\n Value = 1.0\n }\n};SetUpAdvancedRemarketing.cs\n```\n\nExample:\n```text\n$cartSizeRule = new UserListRuleItemInfo([\n // The rule variable name must match a corresponding key name fired from a pixel.\n 'name' => 'cart_size',\n 'number_rule_item' => new UserListNumberRuleItemInfo([\n 'operator' => UserListNumberRuleItemOperator::GREATER_THAN,\n 'value' => 1.0\n ])\n]);SetUpAdvancedRemarketing.php\n```\n\nExample:\n```text\ncart_size_rule: UserListRuleItemInfo = client.get_type(\n \"UserListRuleItemInfo\"\n)\n# The rule variable name must match a corresponding key name fired from a\n# pixel.\ncart_size_rule.name = \"cart_size\"\ncart_size_number_rule_item: UserListNumberRuleItemInfo = (\n cart_size_rule.number_rule_item\n)\ncart_size_number_rule_item.operator = (\n client.enums.UserListNumberRuleItemOperatorEnum.GREATER_THAN\n)\ncart_size_number_rule_item.value = 1.0set_up_advanced_remarketing.py\n```\n\nExample:\n```text\ncart_size_rule = client.resource.user_list_rule_item_info do |rule|\n # The rule variable name must match a corresponding key name fired\n # from a pixel.\n rule.name = \"cart_size\"\n rule.number_rule_item = client.resource.user_list_number_rule_item_info do |nr|\n nr.operator = :GREATER_THAN\n nr.value = 1.0\n end\nendset_up_advanced_remarketing.rb\n```\n\nExample:\n```text\nmy $cart_size_rule =\n Google::Ads::GoogleAds::V25::Common::UserListRuleItemInfo->new({\n # The rule variable name must match a corresponding key name fired from a\n # pixel.\n name => \"cart_size\",\n numberRuleItem =>\n Google::Ads::GoogleAds::V25::Common::UserListNumberRuleItemInfo->new({\n # Available UserListNumberRuleItemOperators can be found at\n # https://developers.google.com/google-ads/api/reference/rpc/latest/UserListNumberRuleItemOperatorEnum.UserListNumberRuleItemOperator\n operator => GREATER_THAN,\n value => 1.0\n })});set_up_advanced_remarketing.pl\n```\n\nExample:\n```text\nUserListRuleItemGroupInfo checkoutAndCartSizeRuleGroup =\n UserListRuleItemGroupInfo.newBuilder()\n .addAllRuleItems(ImmutableList.of(checkoutRule, cartSizeRule))\n .build();SetUpAdvancedRemarketing.java\n```\n\nExample:\n```text\nUserListRuleItemGroupInfo checkoutAndCartSizeRuleGroup =\n new UserListRuleItemGroupInfo();\ncheckoutAndCartSizeRuleGroup.RuleItems.Add(checkoutRule);\ncheckoutAndCartSizeRuleGroup.RuleItems.Add(cartSizeRule);SetUpAdvancedRemarketing.cs\n```\n\nExample:\n```text\n$checkoutAndCartSizeRuleGroup = new UserListRuleItemGroupInfo([\n 'rule_items' => [$checkoutRule, $cartSizeRule]\n]);SetUpAdvancedRemarketing.php\n```\n\nExample:\n```text\ncheckout_and_cart_size_rule_group.rule_items.extend(\n [\n checkout_rule,\n cart_size_rule,\n ]\n)set_up_advanced_remarketing.py\n```\n\nExample:\n```text\ncheckout_and_cart_size_rule_group = client.resource.user_list_rule_item_group_info do |g|\n g.rule_items += [checkout_rule, cart_size_rule]\n end\n\n # Create the RuleItem for checkout start date.\n # The tags and keys used below must have been in place in the past for the\n # date range specified in the rules.\n start_date_rule = client.resource.user_list_rule_item_info do |rule|\n # The rule variable name must match a corresponding key name fired\n # from a pixel.\n rule.name = \"checkoutdate\"\n rule.date_rule_item = client.resource.user_list_date_rule_item_info do |dr|\n dr.operator = :AFTER\n dr.value = \"20191031\"\n end\n end\n\n # Create the RuleItem for checkout end date.\n end_date_rule = client.resource.user_list_rule_item_info do |rule|\n # The rule variable name must match a corresponding key name fired\n # from a pixel.\n rule.name = \"checkoutdate\"\n rule.date_rule_item = client.resource.user_list_date_rule_item_info do |dr|\n dr.operator = :BEFORE\n dr.value = \"20200101\"\n end\n end\n\n # Creates a rule group targeting users who checked out between\n # November and December by using the start and end date rules.\n # Combining the two rule items into a user_list_rule_item_group_info\n # object causes Google Ads to AND their rules together.\n # To instead OR the rules together, each rule should be placed in its\n # own rule item group.\n checkout_date_rule_group = client.resource.user_list_rule_item_group_info do |g|\n g.rule_items += [start_date_rule, end_date_rule]\n end\n\n # Creates the user list operation.\n operation = client.operation.create_resource.user_list do |ul|\n ul.name = \"My expression rule user list ##{(Time.new.to_f * 1000).to_i}\"\n ul.description = \"Users who checked out in November or December OR visited \" \\\n \"the checkout page with more than one item in their cart\"\n ul.membership_status = :OPEN\n ul.membership_life_span = 90\n ul.rule_based_user_list = client.resource.rule_based_user_list_info do |r|\n # Optional: To include past users in the user list, set the\n # prepopulation_status to REQUESTED.\n r.prepopulation_status = :REQUESTED\n # Create a flexible_rule_user_list object, or a flexible rule representation\n # of visitors with one or multiple actions. FlexibleRuleUserListInfo wraps\n # UserListRuleInfo in a FlexibleRuleOperandInfo object that represents which\n # user lists to include or exclude.\n r.flexible_rule_user_list = client.resource.flexible_rule_user_list_info do |frul|\n frul.inclusive_rule_operator = :AND\n frul.inclusive_operands << client.resource.flexible_rule_operand_info do |froi|\n froi.rule = client.resource.user_list_rule_info do |info|\n info.rule_item_groups += [checkout_date_rule_group, checkout_and_cart_size_rule_group]\n end\n # Optionally include a lookback window for this rule, in days.\n froi.lookback_window_days = 7\n end\n end\n end\n end\n\n # Issues a muate request to create the user list.\n response = client.service.user_list.mutate_user_lists(\n customer_id: customer_id,\n operations: [operation],\n )\n puts \"Created user list with resource name '#{response.results.first.resource_name}'\"\nend\n\nif __FILE__ == $0\n options = {}\n\n # Running the example with -h will print the command line usage.\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n set_up_advanced_remarketing(options.fetch(:customer_id).tr(\"-\", \"\"))\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\nset_up_advanced_remarketing.rb\n```\n\nExample:\n```text\nmy $checkout_and_cart_size_rule_group =\n Google::Ads::GoogleAds::V25::Common::UserListRuleItemGroupInfo->new(\n {ruleItems => [$checkout_rule, $cart_size_rule]});set_up_advanced_remarketing.pl\n```\n\nExample:\n```text\nUserListRuleItemInfo startDateRule =\n UserListRuleItemInfo.newBuilder()\n // The rule variable name must match a corresponding key name fired from a pixel.\n .setName(\"checkoutdate\")\n .setDateRuleItem(\n UserListDateRuleItemInfo.newBuilder()\n // Available UserListDateRuleItemOperators can be found at\n // https://developers.google.com/google-ads/api/reference/rpc/latest/UserListDateRuleItemOperatorEnum.UserListDateRuleItemOperator\n .setOperator(UserListDateRuleItemOperator.AFTER)\n .setValue(\"20191031\")\n .build())\n .build();SetUpAdvancedRemarketing.java\n```\n\nExample:\n```text\nUserListRuleItemInfo startDateRule = new UserListRuleItemInfo\n{\n // The rule variable name must match a corresponding key name fired from a pixel.\n Name = \"checkoutdate\",\n DateRuleItem = new UserListDateRuleItemInfo\n {\n // Available UserListDateRuleItemOperators can be found at\n // https://developers.google.com/google-ads/api/reference/rpc/latest/UserListDateRuleItemOperatorEnum.UserListDateRuleItemOperator\n Operator = UserListDateRuleItemOperator.After,\n Value = \"20191031\"\n }\n};SetUpAdvancedRemarketing.cs\n```\n\nExample:\n```text\n$startDateRule = new UserListRuleItemInfo([\n // The rule variable name must match a corresponding key name fired from a pixel.\n 'name' => 'checkoutdate',\n 'date_rule_item' => new UserListDateRuleItemInfo([\n // Available UserListDateRuleItemOperators can be found at\n // https://developers.google.com/google-ads/api/reference/rpc/latest/UserListDateRuleItemOperatorEnum.UserListDateRuleItemOperator\n 'operator' => UserListDateRuleItemOperator::AFTER,\n 'value' => '20191031'\n ])\n]);SetUpAdvancedRemarketing.php\n```\n\nExample:\n```text\nstart_date_rule: UserListRuleItemInfo = client.get_type(\n \"UserListRuleItemInfo\"\n)\nstart_date_rule.name = \"checkoutdate\"\nstart_date_rule_item: UserListDateRuleItemInfo = (\n start_date_rule.date_rule_item\n)\n# Available UserListDateRuleItemOperators can be found at:\n# https://developers.google.com/google-ads/api/reference/rpc/latest/UserListDateRuleItemOperatorEnum.UserListDateRuleItemOperator\nuser_list_data_rule_item_operator_enum: UserListDateRuleItemOperatorEnum = (\n client.enums.UserListDateRuleItemOperatorEnum\n)\nstart_date_rule_item.operator = user_list_data_rule_item_operator_enum.AFTER\nstart_date_rule_item.value = \"20191031\"set_up_advanced_remarketing.py\n```\n\nExample:\n```text\nstart_date_rule = client.resource.user_list_rule_item_info do |rule|\n # The rule variable name must match a corresponding key name fired\n # from a pixel.\n rule.name = \"checkoutdate\"\n rule.date_rule_item = client.resource.user_list_date_rule_item_info do |dr|\n dr.operator = :AFTER\n dr.value = \"20191031\"\n end\nendset_up_advanced_remarketing.rb\n```\n\nExample:\n```text\nmy $start_date_rule =\n Google::Ads::GoogleAds::V25::Common::UserListRuleItemInfo->new({\n # The rule variable name must match a corresponding key name fired from a\n # pixel.\n name => \"checkoutdate\",\n dateRuleItem =>\n Google::Ads::GoogleAds::V25::Common::UserListDateRuleItemInfo->new({\n # Available UserListDateRuleItemOperators can be found at\n # https://developers.google.com/google-ads/api/reference/rpc/latest/UserListDateRuleItemOperatorEnum.UserListDateRuleItemOperator\n operator => AFTER,\n value => \"20191031\"\n })});set_up_advanced_remarketing.pl\n```\n\nExample:\n```text\nUserListRuleItemInfo endDateRule =\n UserListRuleItemInfo.newBuilder()\n // The rule variable name must match a corresponding key name fired from a pixel.\n .setName(\"checkoutdate\")\n .setDateRuleItem(\n UserListDateRuleItemInfo.newBuilder()\n .setOperator(UserListDateRuleItemOperator.BEFORE)\n .setValue(\"20200101\")\n .build())\n .build();SetUpAdvancedRemarketing.java\n```\n\nExample:\n```text\nUserListRuleItemInfo endDateRule = new UserListRuleItemInfo\n{\n // The rule variable name must match a corresponding key name fired from a pixel.\n Name = \"checkoutdate\",\n DateRuleItem = new UserListDateRuleItemInfo\n {\n Operator = UserListDateRuleItemOperator.Before,\n Value = \"20200101\"\n }\n};SetUpAdvancedRemarketing.cs\n```\n\nExample:\n```text\n$endDateRule = new UserListRuleItemInfo([\n // The rule variable name must match a corresponding key name fired from a pixel.\n 'name' => 'checkoutdate',\n 'date_rule_item' => new UserListDateRuleItemInfo([\n 'operator' => UserListDateRuleItemOperator::BEFORE,\n 'value' => '20200101'\n ])\n]);SetUpAdvancedRemarketing.php\n```\n\nExample:\n```text\nend_date_rule: UserListRuleItemInfo = client.get_type(\n \"UserListRuleItemInfo\"\n)\nend_date_rule.name = \"checkoutdate\"\nend_date_rule_item: UserListDateRuleItemInfo = end_date_rule.date_rule_item\nend_date_rule_item.operator = user_list_data_rule_item_operator_enum.BEFORE\nend_date_rule_item.value = \"20200101\"set_up_advanced_remarketing.py\n```\n\nExample:\n```text\nend_date_rule = client.resource.user_list_rule_item_info do |rule|\n # The rule variable name must match a corresponding key name fired\n # from a pixel.\n rule.name = \"checkoutdate\"\n rule.date_rule_item = client.resource.user_list_date_rule_item_info do |dr|\n dr.operator = :BEFORE\n dr.value = \"20200101\"\n end\nendset_up_advanced_remarketing.rb\n```\n\nExample:\n```text\nmy $end_date_rule =\n Google::Ads::GoogleAds::V25::Common::UserListRuleItemInfo->new({\n # The rule variable name must match a corresponding key name fired from a\n # pixel.\n name => \"checkoutdate\",\n dateRuleItem =>\n Google::Ads::GoogleAds::V25::Common::UserListDateRuleItemInfo->new({\n operator => BEFORE,\n value => \"20200101\"\n })});set_up_advanced_remarketing.pl\n```\n\nExample:\n```text\nUserListRuleItemGroupInfo checkoutDateRuleGroup =\n UserListRuleItemGroupInfo.newBuilder()\n .addAllRuleItems(ImmutableList.of(startDateRule, endDateRule))\n .build();SetUpAdvancedRemarketing.java\n```\n\nExample:\n```text\nUserListRuleItemGroupInfo checkoutDateRuleGroup = new UserListRuleItemGroupInfo();\ncheckoutDateRuleGroup.RuleItems.Add(startDateRule);\ncheckoutDateRuleGroup.RuleItems.Add(endDateRule);SetUpAdvancedRemarketing.cs\n```\n\nExample:\n```text\n$checkoutDateRuleGroup = new UserListRuleItemGroupInfo([\n 'rule_items' => [$startDateRule, $endDateRule]\n]);SetUpAdvancedRemarketing.php\n```\n\nExample:\n```text\ncheckout_date_rule_group.rule_items.extend(\n [\n start_date_rule,\n end_date_rule,\n ]\n)set_up_advanced_remarketing.py\n```\n\nExample:\n```text\ncheckout_date_rule_group = client.resource.user_list_rule_item_group_info do |g|\n g.rule_items += [start_date_rule, end_date_rule]\nendset_up_advanced_remarketing.rb\n```\n\nExample:\n```text\nmy $checkout_date_rule_group =\n Google::Ads::GoogleAds::V25::Common::UserListRuleItemGroupInfo->new(\n {ruleItems => [$start_date_rule, $end_date_rule]});set_up_advanced_remarketing.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.523Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":36,"totalLines":572,"estimatedTokens":4852}}208{"id":"doc-usage_flow_google_ads_api_google_for_developers-fbaa5966","source":"documentation","title":"Usage flow | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/batch-processing/flow","text":"Example:\n```text\nprivate String createBatchJob(BatchJobServiceClient batchJobServiceClient, long customerId) {\n BatchJobOperation operation =\n BatchJobOperation.newBuilder().setCreate(BatchJob.newBuilder().build()).build();\n String batchJobResourceName =\n batchJobServiceClient\n .mutateBatchJob(Long.toString(customerId), operation)\n .getResult()\n .getResourceName();\n System.out.printf(\"Created a mutate job with resource name: '%s'.%n\", batchJobResourceName);\n\n return batchJobResourceName;\n}AddCompleteCampaignsUsingBatchJob.java\n```\n\nExample:\n```text\nprivate static string CreateBatchJob(BatchJobServiceClient batchJobService,\n long customerId)\n{\n BatchJobOperation operation = new BatchJobOperation()\n {\n Create = new BatchJob()\n {\n }\n };\n string batchJobResourceName =\n batchJobService.MutateBatchJob(customerId.ToString(), operation)\n .Result.ResourceName;\n Console.WriteLine($\"Created a batch job with resource name: \" +\n $\"'{batchJobResourceName}'.\");\n\n return batchJobResourceName;\n}AddCompleteCampaignsUsingBatchJob.cs\n```\n\nExample:\n```text\nprivate static function createBatchJob(\n BatchJobServiceClient $batchJobServiceClient,\n int $customerId\n): string {\n // Creates a batch job operation to create a new batch job.\n $batchJobOperation = new BatchJobOperation();\n $batchJobOperation->setCreate(new BatchJob());\n\n // Issues a request to the API and get the batch job's resource name.\n $batchJobResourceName = $batchJobServiceClient->mutateBatchJob(\n MutateBatchJobRequest::build($customerId, $batchJobOperation)\n )->getResult()->getResourceName();\n printf(\n \"Created a batch job with resource name: '%s'.%s\",\n $batchJobResourceName,\n PHP_EOL\n );\n return $batchJobResourceName;\n}AddCompleteCampaignsUsingBatchJob.php\n```\n\nExample:\n```text\ndef create_batch_job(\n batch_job_service: BatchJobServiceClient,\n customer_id: str,\n batch_job_operation: BatchJobOperation,\n) -> str:\n \"\"\"Creates a batch job for the specified customer ID.\n\n Args:\n batch_job_service: an instance of the BatchJobService message class.\n customer_id: a str of a customer ID.\n batch_job_operation: a BatchJobOperation instance set to \"create\"\n\n Returns: a str of a resource name for a batch job.\n \"\"\"\n try:\n response: MutateBatchJobResponse = batch_job_service.mutate_batch_job(\n customer_id=customer_id, operation=batch_job_operation\n )\n resource_name: str = response.result.resource_name\n print(f'Created a batch job with resource name \"{resource_name}\"')\n return resource_name\n except GoogleAdsException as exception:\n handle_googleads_exception(exception)\n # This line will likely not be reached due to sys.exit(1) in handle_googleads_exception\n # but to satisfy the type checker, we add a return statement.\n return \"\" # Or raise an exceptionadd_complete_campaigns_using_batch_job.py\n```\n\nExample:\n```text\ndef create_batch_job(client, batch_job_service, customer_id)\n # Creates a batch job operation to create a new batch job.\n operation = client.operation.create_resource.batch_job\n\n # Issues a request to the API and get the batch job's resource name.\n response = batch_job_service.mutate_batch_job(\n customer_id: customer_id,\n operation: operation\n )\n\n batch_job_resource_name = response.result.resource_name\n puts \"Created a batch job with resource name: '#{batch_job_resource_name}'\"\n\n batch_job_resource_name\nendadd_complete_campaigns_using_batch_job.rb\n```\n\nExample:\n```text\nsub create_batch_job {\n my ($batch_job_service, $customer_id) = @_;\n\n # Create a batch job operation.\n my $batch_job_operation =\n Google::Ads::GoogleAds::V25::Services::BatchJobService::BatchJobOperation->\n new({create => Google::Ads::GoogleAds::V25::Resources::BatchJob->new({})});\n\n my $batch_job_resource_name = $batch_job_service->mutate({\n customerId => $customer_id,\n operation => $batch_job_operation\n })->{result}{resourceName};\n\n printf\n \"Created a batch job with resource name: '%s'.\\n\",\n $batch_job_resource_name;\n\n return $batch_job_resource_name;\n}add_complete_campaigns_using_batch_job.pl\n```\n\nExample:\n```text\n# Creates a batch job.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n\ncurl -f --request POST \\\n\"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/batchJobs:mutate\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data @- <<EOF\n{\n \"operation\": {\n \"create\": {}\n }\n}\nEOFadd_complete_campaigns_using_batch_job.sh\n```\n\nExample:\n```text\nprivate void addAllBatchJobOperations(\n BatchJobServiceClient batchJobServiceClient, long customerId, String batchJobResourceName) {\n AddBatchJobOperationsResponse response =\n batchJobServiceClient.addBatchJobOperations(\n AddBatchJobOperationsRequest.newBuilder()\n .setResourceName(batchJobResourceName)\n .addAllMutateOperations(buildAllOperations(customerId))\n .build());\n System.out.printf(\n \"%d mutate operations have been added so far.%n\", response.getTotalOperations());\n\n // You can use this next sequence token for calling addBatchJobOperations() next time.\n System.out.printf(\n \"Next sequence token for adding next operations is '%s'.%n\",\n response.getNextSequenceToken());\n}AddCompleteCampaignsUsingBatchJob.java\n```\n\nExample:\n```text\nprivate static void AddAllBatchJobOperations(BatchJobServiceClient batchJobService,\n long customerId, string batchJobResourceName)\n{\n AddBatchJobOperationsResponse response =\n batchJobService.AddBatchJobOperations(\n new AddBatchJobOperationsRequest()\n {\n ResourceName = batchJobResourceName,\n MutateOperations = { BuildAllOperations(customerId) }\n });\n Console.WriteLine($\"{response.TotalOperations} mutate operations have been added\" +\n $\" so far.\");\n\n // You can use this next sequence token for calling AddBatchJobOperations() next time.\n Console.WriteLine($\"Next sequence token for adding next operations is \" +\n $\"'{response.NextSequenceToken}'.\");\n}AddCompleteCampaignsUsingBatchJob.cs\n```\n\nExample:\n```text\nprivate static function addAllBatchJobOperations(\n BatchJobServiceClient $batchJobServiceClient,\n int $customerId,\n string $batchJobResourceName\n): void {\n $response = $batchJobServiceClient->addBatchJobOperations(\n AddBatchJobOperationsRequest::build(\n $batchJobResourceName,\n '',\n self::buildAllOperations($customerId)\n )\n );\n printf(\n \"%d mutate operations have been added so far.%s\",\n $response->getTotalOperations(),\n PHP_EOL\n );\n // You can use this next sequence token for calling addBatchJobOperations() next time.\n printf(\n \"Next sequence token for adding next operations is '%s'.%s\",\n $response->getNextSequenceToken(),\n PHP_EOL\n );\n}AddCompleteCampaignsUsingBatchJob.php\n```\n\nExample:\n```text\ndef add_all_batch_job_operations(\n batch_job_service: BatchJobServiceClient,\n operations: List[MutateOperation],\n resource_name: str,\n) -> None:\n \"\"\"Adds all mutate operations to the batch job.\n\n As this is the first time for this batch job, we pass null as a sequence\n token. The response will contain the next sequence token that we can use\n to upload more operations in the future.\n\n Args:\n batch_job_service: an instance of the BatchJobService message class.\n operations: a list of a mutate operations.\n resource_name: a str of a resource name for a batch job.\n \"\"\"\n try:\n response: AddBatchJobOperationsResponse = (\n batch_job_service.add_batch_job_operations(\n resource_name=resource_name,\n sequence_token=None, # type: ignore\n mutate_operations=operations,\n )\n )\n\n print(\n f\"{response.total_operations} mutate operations have been \"\n \"added so far.\"\n )\n\n # You can use this next sequence token for calling\n # add_batch_job_operations() next time.\n print(\n \"Next sequence token for adding next operations is \"\n f\"{response.next_sequence_token}\"\n )\n except GoogleAdsException as exception:\n handle_googleads_exception(exception)add_complete_campaigns_using_batch_job.py\n```\n\nExample:\n```text\ndef add_all_batch_job_operations(\n client,\n batch_job_service,\n customer_id,\n batch_job_resource_name)\n response = batch_job_service.add_batch_job_operations(\n resource_name: batch_job_resource_name,\n mutate_operations: build_all_operations(client, customer_id),\n )\n puts \"#{response.total_operations} mutate operations have been added so far.\"\n\n # You can use this next sequence token for calling\n # add_all_batch_job_operations() next time\n puts \"Next sequence token for adding next operations is \" \\\n \"'#{response.next_sequence_token}'\"\nendadd_complete_campaigns_using_batch_job.rb\n```\n\nExample:\n```text\nsub add_all_batch_job_operations {\n my ($batch_job_service, $customer_id, $batch_job_resource_name) = @_;\n\n my $add_batch_job_operations_response = $batch_job_service->add_operations({\n resourceName => $batch_job_resource_name,\n sequenceToken => undef,\n mutateOperations => build_all_operations($customer_id)});\n\n printf\n \"%d batch operations have been added so far.\\n\",\n $add_batch_job_operations_response->{totalOperations};\n\n # You can use this next sequence token for calling add_operations() next time.\n printf\n \"Next sequence token for adding next operations is '%s'.\\n\",\n $add_batch_job_operations_response->{nextSequenceToken};\n}add_complete_campaigns_using_batch_job.pl\n```\n\nExample:\n```text\n# Adds operations to a batch job.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n# BATCH_JOB_RESOURCE_NAME:\n# The resource name of the batch job to which the operations should be added\n# as returned by the previous step.\n\ncurl -f --request POST \\\n\"https://googleads.googleapis.com/v${API_VERSION}/${BATCH_JOB_RESOURCE_NAME}:addOperations\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data @- <<EOF\n{\n \"mutateOperations\": [\n {\n \"campaignBudgetOperation\": {\n \"create\": {\n \"resourceName\": \"customers/${CUSTOMER_ID}/campaignBudgets/-1\",\n \"name\": \"batch job budget #${RANDOM}\",\n \"deliveryMethod\": \"STANDARD\",\n \"amountMicros\": 5000000\n }\n }\n },\n {\n \"campaignOperation\": {\n \"create\": {\n \"advertisingChannelType\": \"SEARCH\",\n \"status\": \"PAUSED\",\n \"name\": \"batch job campaign #${RANDOM}\",\n \"campaignBudget\": \"customers/${CUSTOMER_ID}/campaignBudgets/-1\",\n \"resourceName\": \"customers/${CUSTOMER_ID}/campaigns/-2\",\n \"manualCpc\": {\n }\n }\n },\n }\n ]\n}\nEOFadd_complete_campaigns_using_batch_job.sh\n```\n\nExample:\n```text\nprivate OperationFuture runBatchJob(\n BatchJobServiceClient batchJobServiceClient, String batchJobResourceName) {\n OperationFuture operationResponse =\n batchJobServiceClient.runBatchJobAsync(batchJobResourceName);\n\n // BEWARE! The above call returns an OperationFuture. The execution of that future depends on\n // the thread pool which is owned by batchJobServiceClient. If you use this future, you *must*\n // keep the service client in scope too.\n // See https://developers.google.com/google-ads/api/docs/client-libs/java/lro for more detail.\n\n System.out.printf(\n \"Mutate job with resource name '%s' has been executed.%n\", batchJobResourceName);\n\n return operationResponse;\n}AddCompleteCampaignsUsingBatchJob.java\n```\n\nExample:\n```text\nprivate Operation<Empty, BatchJobMetadata> RunBatchJob(\n BatchJobServiceClient batchJobService, string batchJobResourceName)\n{\n Operation<Empty, BatchJobMetadata> operationResponse =\n batchJobService.RunBatchJob(batchJobResourceName);\n Console.WriteLine($\"Batch job with resource name '{batchJobResourceName}' has been \" +\n $\"executed.\");\n\n return operationResponse;\n}AddCompleteCampaignsUsingBatchJob.cs\n```\n\nExample:\n```text\nprivate static function runBatchJob(\n BatchJobServiceClient $batchJobServiceClient,\n string $batchJobResourceName\n): OperationResponse {\n $operationResponse =\n $batchJobServiceClient->runBatchJob(RunBatchJobRequest::build($batchJobResourceName));\n printf(\n \"Batch job with resource name '%s' has been executed.%s\",\n $batchJobResourceName,\n PHP_EOL\n );\n return $operationResponse;\n}AddCompleteCampaignsUsingBatchJob.php\n```\n\nExample:\n```text\ndef run_batch_job(\n batch_job_service: BatchJobServiceClient, resource_name: str\n) -> Operation:\n \"\"\"Runs the batch job for executing all uploaded mutate operations.\n\n Args:\n batch_job_service: an instance of the BatchJobService message class.\n resource_name: a str of a resource name for a batch job.\n\n Returns: a google.api_core.operation.Operation instance.\n \"\"\"\n try:\n response: Operation = batch_job_service.run_batch_job(\n resource_name=resource_name\n )\n print(\n f'Batch job with resource name \"{resource_name}\" has been '\n \"executed.\"\n )\n return response\n except GoogleAdsException as exception:\n handle_googleads_exception(exception)\n # This line will likely not be reached due to sys.exit(1) in handle_googleads_exception\n # but to satisfy the type checker, we add a return statement.\n # In a real application, you might want to return a dummy Operation or raise an error.\n return Operation(\n op_type_name=\"type.googleapis.com/google.protobuf.Empty\",\n complete=True,\n done_callbacks=[],\n metadata_type=None,\n result_type=None,\n ) # type: ignoreadd_complete_campaigns_using_batch_job.py\n```\n\nExample:\n```text\ndef run_batch_job(batch_job_service, batch_job_resource_name)\n operation_response = batch_job_service.run_batch_job(\n resource_name: batch_job_resource_name,\n )\n puts \"Batch job with resource name '#{batch_job_resource_name}' \" \\\n \"has been executed.\"\n operation_response\nendadd_complete_campaigns_using_batch_job.rb\n```\n\nExample:\n```text\nsub run_batch_job {\n my ($batch_job_service, $batch_job_resource_name) = @_;\n\n my $batch_job_lro =\n $batch_job_service->run({resourceName => $batch_job_resource_name});\n\n printf\n \"Batch job with resource name '%s' has been executed.\\n\",\n $batch_job_resource_name;\n\n return $batch_job_lro;\n}add_complete_campaigns_using_batch_job.pl\n```\n\nExample:\n```text\n# Runs a batch job.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n# BATCH_JOB_RESOURCE_NAME:\n# The resource name of the batch job to run as returned by the previous step.\n\ncurl -f --request POST \\\n\"https://googleads.googleapis.com/v19/${BATCH_JOB_RESOURCE_NAME}:run\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data @- <<EOF\n{}\nEOF\nadd_complete_campaigns_using_batch_job.sh\n```\n\nExample:\n```text\nprivate void pollBatchJob(OperationFuture operationResponse) {\n try {\n operationResponse.get(MAX_TOTAL_POLL_INTERVAL_SECONDS, TimeUnit.SECONDS);\n } catch (InterruptedException | ExecutionException | TimeoutException e) {\n System.err.printf(\"Failed polling the mutate job. Exception: %s%n\", e);\n System.exit(1);\n }\n}AddCompleteCampaignsUsingBatchJob.java\n```\n\nExample:\n```text\nprivate static void PollBatchJob(Operation<Empty, BatchJobMetadata> operationResponse)\n{\n PollSettings pollSettings = new PollSettings(\n Expiration.FromTimeout(TimeSpan.FromSeconds(MAX_TOTAL_POLL_INTERVAL_SECONDS)),\n TimeSpan.FromSeconds(1));\n operationResponse.PollUntilCompleted(pollSettings);\n}AddCompleteCampaignsUsingBatchJob.cs\n```\n\nExample:\n```text\nprivate static function pollBatchJob(OperationResponse $operationResponse): void\n{\n $operationResponse->pollUntilComplete([\n 'initialPollDelayMillis' => self::POLL_FREQUENCY_SECONDS * 1000,\n 'totalPollTimeoutMillis' => self::MAX_TOTAL_POLL_INTERVAL_SECONDS * 1000\n ]);\n}AddCompleteCampaignsUsingBatchJob.php\n```\n\nExample:\n```text\ndef poll_batch_job(\n operations_response: Operation, event: asyncio.Event\n) -> None:\n \"\"\"Polls the server until the batch job execution finishes.\n\n Sets the initial poll delay time and the total time to wait before time-out.\n\n Args:\n operations_response: a google.api_core.operation.Operation instance.\n event: an instance of asyncio.Event to invoke once the operations have\n completed, alerting the awaiting calling code that it can proceed.\n \"\"\"\n loop: asyncio.AbstractEventLoop = asyncio.get_event_loop()\n\n def done_callback(future: Coroutine[Any, Any, Any]) -> None:\n # The operations_response object will call callbacks from a daemon\n # thread so we must use a threadsafe method of setting the event here\n # otherwise it will not trigger the awaiting code.\n loop.call_soon_threadsafe(event.set)\n\n # operations_response represents a Long-Running Operation or LRO. The class\n # provides an interface for polling the API to check when the operation is\n # complete. Below we use the asynchronous interface, but there's also a\n # synchronous interface that uses the Operation.result method.\n # See: https://googleapis.dev/python/google-api-core/latest/operation.html\n operations_response.add_done_callback(done_callback) # type: ignoreadd_complete_campaigns_using_batch_job.py\n```\n\nExample:\n```text\ndef poll_batch_job(operation_response)\n operation_response.wait_until_done!\nendadd_complete_campaigns_using_batch_job.rb\n```\n\nExample:\n```text\nsub poll_batch_job {\n my ($operation_service, $batch_job_lro) = @_;\n\n $operation_service->poll_until_done({\n name => $batch_job_lro->{name},\n pollFrequencySeconds => POLL_FREQUENCY_SECONDS,\n pollTimeoutSeconds => POLL_TIMEOUT_SECONDS\n });\n}add_complete_campaigns_using_batch_job.pl\n```\n\nExample:\n```text\n# Gets the status of a batch job.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n# BATCH_JOB_OPERATION_NAME:\n# The operation name of the running batch job as returned by the previous\n# step.\n\ncurl -f --request GET \\\n\"https://googleads.googleapis.com/v${API_VERSION}/${BATCH_JOB_OPERATION_NAME}\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\nadd_complete_campaigns_using_batch_job.sh\n```\n\nExample:\n```text\nprivate void fetchAndPrintResults(\n BatchJobServiceClient batchJobServiceClient, String batchJobResourceName) {\n System.out.printf(\n \"Mutate job with resource name '%s' has finished. Now, printing its results...%n\",\n batchJobResourceName);\n // Gets all the results from running mutate job and prints their information.\n ListBatchJobResultsPagedResponse batchJobResults =\n batchJobServiceClient.listBatchJobResults(\n ListBatchJobResultsRequest.newBuilder()\n .setResourceName(batchJobResourceName)\n .setPageSize(PAGE_SIZE)\n .build());\n for (BatchJobResult batchJobResult : batchJobResults.iterateAll()) {\n System.out.printf(\n \"Mutate job #%d has a status '%s' and response of type '%s'.%n\",\n batchJobResult.getOperationIndex(),\n batchJobResult.getStatus().getMessage().isEmpty()\n ? \"N/A\"\n : batchJobResult.getStatus().getMessage(),\n batchJobResult\n .getMutateOperationResponse()\n .getResponseCase()\n .equals(ResponseCase.RESPONSE_NOT_SET)\n ? \"N/A\"\n : batchJobResult.getMutateOperationResponse().getResponseCase());\n }\n}AddCompleteCampaignsUsingBatchJob.java\n```\n\nExample:\n```text\nprivate static void FetchAndPrintResults(BatchJobServiceClient batchJobService,\n string batchJobResourceName)\n{\n Console.WriteLine($\"batch job with resource name '{batchJobResourceName}' has \" +\n $\"finished. Now, printing its results...\");\n\n ListBatchJobResultsRequest request = new ListBatchJobResultsRequest()\n {\n ResourceName = batchJobResourceName,\n PageSize = PAGE_SIZE,\n };\n ListBatchJobResultsResponse resp = new ListBatchJobResultsResponse();\n // Gets all the results from running batch job and prints their information.\n foreach (BatchJobResult batchJobResult in\n batchJobService.ListBatchJobResults(request))\n {\n if (!batchJobResult.IsFailed)\n {\n Console.WriteLine($\"batch job result #{batchJobResult.OperationIndex} is \" +\n $\"successful and response is of type \" +\n $\"'{batchJobResult.MutateOperationResponse.ResponseCase}'.\");\n }\n else\n {\n Console.WriteLine($\"batch job result #{batchJobResult.OperationIndex} \" +\n $\"failed with error message {batchJobResult.Status.Message}.\");\n\n foreach (GoogleAdsError error in batchJobResult.Failure.Errors)\n {\n Console.WriteLine($\"Error found: {error}.\");\n }\n }\n }\n}AddCompleteCampaignsUsingBatchJob.cs\n```\n\nExample:\n```text\nprivate static function fetchAndPrintResults(\n BatchJobServiceClient $batchJobServiceClient,\n string $batchJobResourceName\n): void {\n printf(\n \"Batch job with resource name '%s' has finished. Now, printing its results...%s\",\n $batchJobResourceName,\n PHP_EOL\n );\n // Gets all the results from running batch job and print their information.\n $batchJobResults = $batchJobServiceClient->listBatchJobResults(\n ListBatchJobResultsRequest::build($batchJobResourceName)->setPageSize(self::PAGE_SIZE)\n );\n foreach ($batchJobResults->iterateAllElements() as $batchJobResult) {\n /** @var BatchJobResult $batchJobResult */\n printf(\n \"Batch job #%d has a status '%s' and response of type '%s'.%s\",\n $batchJobResult->getOperationIndex(),\n $batchJobResult->getStatus()\n ? $batchJobResult->getStatus()->getMessage() : 'N/A',\n $batchJobResult->getMutateOperationResponse()\n ? $batchJobResult->getMutateOperationResponse()->getResponse()\n : 'N/A',\n PHP_EOL\n );\n }\n}AddCompleteCampaignsUsingBatchJob.php\n```\n\nExample:\n```text\ndef fetch_and_print_results(\n client: GoogleAdsClient,\n batch_job_service: BatchJobServiceClient,\n resource_name: str,\n) -> None:\n \"\"\"Prints all the results from running the batch job.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n batch_job_service: an instance of the BatchJobService message class.\n resource_name: a str of a resource name for a batch job.\n \"\"\"\n print(\n f'Batch job with resource name \"{resource_name}\" has finished. '\n \"Now, printing its results...\"\n )\n\n list_results_request: ListBatchJobResultsRequest = client.get_type(\n \"ListBatchJobResultsRequest\"\n )\n list_results_request.resource_name = resource_name\n list_results_request.page_size = 1000\n # Gets all the results from running batch job and prints their information.\n batch_job_results: ListBatchJobResultsResponse = (\n batch_job_service.list_batch_job_results(request=list_results_request)\n )\n\n for batch_job_result in batch_job_results:\n status: str = batch_job_result.status.message\n status = status if status else \"N/A\"\n result: Any = batch_job_result.mutate_operation_response\n result = result or \"N/A\"\n print(\n f\"Batch job #{batch_job_result.operation_index} \"\n f'has a status \"{status}\" and response type \"{result}\"'\n )add_complete_campaigns_using_batch_job.py\n```\n\nExample:\n```text\ndef fetch_and_print_results(batch_job_service, batch_job_resource_name)\n puts \"Batch job with resource name '#{batch_job_resource_name}' has \" \\\n \"finished. Now, printing its results...\" \\\n\n # Gets all the results from running batch job and print their information.\n batch_job_results = batch_job_service.list_batch_job_results(\n resource_name: batch_job_resource_name,\n page_size: PAGE_SIZE,\n )\n batch_job_results.each do |result|\n puts \"Batch job ##{result.operation_index} has a status \" \\\n \"#{result.status ? result.status.message : 'N/A'} and response of type \" \\\n \"#{result.mutate_operation_response ? result.mutate_operation_response.response : 'N/A'}\"\n end\nendadd_complete_campaigns_using_batch_job.rb\n```\n\nExample:\n```text\nsub fetch_and_print_results {\n my ($batch_job_service, $batch_job_resource_name) = @_;\n\n printf \"Batch job with resource name '%s' has finished. \" .\n \"Now, printing its results...\\n\", $batch_job_resource_name;\n\n # Get all the results from running batch job and print their information.\n my $list_batch_job_results_response = $batch_job_service->list_results({\n resourceName => $batch_job_resource_name,\n pageSize => PAGE_SIZE\n });\n\n foreach my $batch_job_result (@{$list_batch_job_results_response->{results}})\n {\n printf\n \"Batch job #%d has a status '%s' and response of type '%s'.\\n\",\n $batch_job_result->{operationIndex},\n $batch_job_result->{status} ? $batch_job_result->{status}{message}\n : \"N/A\",\n $batch_job_result->{mutateOperationResponse}\n ? [keys %{$batch_job_result->{mutateOperationResponse}}]->[0]\n : \"N/A\";\n }\n}add_complete_campaigns_using_batch_job.pl\n```\n\nExample:\n```text\n# Gets the results of a batch job.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n# BATCH_JOB_RESOURCE_NAME:\n# The operation name of the running batch job as returned by the previous\n# step.\ncurl -f --request GET \\\n\"https://googleads.googleapis.com/v${API_VERSION}/${BATCH_JOB_RESOURCE_NAME}:listResults?pageSize=1000\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\"\nadd_complete_campaigns_using_batch_job.sh\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.528Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":35,"totalLines":827,"estimatedTokens":6937}}209{"id":"doc-manage_customer_lists_google_ads_api_google_for_-62946492","source":"documentation","title":"Manage customer lists | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audience-segments/customer-match/manage","text":"Example:\n```text\nprivate void addUsersToCustomerMatchUserList(\n GoogleAdsClient googleAdsClient,\n long customerId,\n boolean runJob,\n String userListResourceName,\n Long offlineUserDataJobId,\n ConsentStatus adPersonalizationConsent,\n ConsentStatus adUserDataConsent)\n throws UnsupportedEncodingException {\n try (OfflineUserDataJobServiceClient offlineUserDataJobServiceClient =\n googleAdsClient.getLatestVersion().createOfflineUserDataJobServiceClient()) {\n String offlineUserDataJobResourceName;\n if (offlineUserDataJobId == null) {\n // Creates a new offline user data job.\n OfflineUserDataJob.Builder offlineUserDataJobBuilder =\n OfflineUserDataJob.newBuilder()\n .setType(OfflineUserDataJobType.CUSTOMER_MATCH_USER_LIST)\n .setCustomerMatchUserListMetadata(\n CustomerMatchUserListMetadata.newBuilder().setUserList(userListResourceName));\n // Adds consent information to the job if specified.\n if (adPersonalizationConsent != null || adUserDataConsent != null) {\n Consent.Builder consentBuilder = Consent.newBuilder();\n if (adPersonalizationConsent != null) {\n consentBuilder.setAdPersonalization(adPersonalizationConsent);\n }\n if (adUserDataConsent != null) {\n consentBuilder.setAdUserData(adUserDataConsent);\n }\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy for details.\n offlineUserDataJobBuilder\n .getCustomerMatchUserListMetadataBuilder()\n .setConsent(consentBuilder);\n }\n\n // Issues a request to create the offline user data job.\n CreateOfflineUserDataJobResponse createOfflineUserDataJobResponse =\n offlineUserDataJobServiceClient.createOfflineUserDataJob(\n Long.toString(customerId), offlineUserDataJobBuilder.build());\n offlineUserDataJobResourceName = createOfflineUserDataJobResponse.getResourceName();\n System.out.printf(\n \"Created an offline user data job with resource name: %s.%n\",\n offlineUserDataJobResourceName);\n } else {\n // Reuses the specified offline user data job.\n offlineUserDataJobResourceName =\n ResourceNames.offlineUserDataJob(customerId, offlineUserDataJobId);\n }\n\n // Issues a request to add the operations to the offline user data job. This example\n // only adds a few operations, so it only sends one AddOfflineUserDataJobOperations request.\n // If your application is adding a large number of operations, split the operations into\n // batches and send multiple AddOfflineUserDataJobOperations requests for the SAME job. See\n // https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations\n // and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data\n // for more information on the per-request limits.\n List<OfflineUserDataJobOperation> userDataJobOperations = buildOfflineUserDataJobOperations();\n AddOfflineUserDataJobOperationsResponse response =\n offlineUserDataJobServiceClient.addOfflineUserDataJobOperations(\n AddOfflineUserDataJobOperationsRequest.newBuilder()\n .setResourceName(offlineUserDataJobResourceName)\n .setEnablePartialFailure(true)\n .addAllOperations(userDataJobOperations)\n .build());\n\n // Prints the status message if any partial failure error is returned.\n // NOTE: The details of each partial failure error are not printed here, you can refer to\n // the example HandlePartialFailure.java to learn more.\n if (response.hasPartialFailureError()) {\n GoogleAdsFailure googleAdsFailure =\n ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError());\n System.out.printf(\n \"Encountered %d partial failure errors while adding %d operations to the offline user \"\n + \"data job: '%s'. Only the successfully added operations will be executed when \"\n + \"the job runs.%n\",\n googleAdsFailure.getErrorsCount(),\n userDataJobOperations.size(),\n response.getPartialFailureError().getMessage());\n } else {\n System.out.printf(\n \"Successfully added %d operations to the offline user data job.%n\",\n userDataJobOperations.size());\n }\n\n if (!runJob) {\n System.out.printf(\n \"Not running offline user data job '%s', as requested.%n\",\n offlineUserDataJobResourceName);\n return;\n }\n\n // Issues an asynchronous request to run the offline user data job for executing\n // all added operations.\n offlineUserDataJobServiceClient.runOfflineUserDataJobAsync(offlineUserDataJobResourceName);\n\n // BEWARE! The above call returns an OperationFuture. The execution of that future depends on\n // the thread pool which is owned by offlineUserDataJobServiceClient. If you use this future,\n // you *must* keep the service client in scope too.\n // See https://developers.google.com/google-ads/api/docs/client-libs/java/lro for more detail.\n\n // Offline user data jobs may take 6 hours or more to complete, so instead of waiting for the\n // job to complete, retrieves and displays the job status once. If the job is completed\n // successfully, prints information about the user list. Otherwise, prints the query to use\n // to check the job again later.\n checkJobStatus(googleAdsClient, customerId, offlineUserDataJobResourceName);\n }\n}\nAddCustomerMatchUserList.java\n```\n\nExample:\n```text\nprivate static string AddUsersToCustomerMatchUserList(GoogleAdsClient client,\n long customerId, string userListResourceName, bool runJob,\n long? offlineUserDataJobId, ConsentStatus? adPersonalizationConsent,\n ConsentStatus? adUserDataConsent)\n{\n // Get the OfflineUserDataJobService.\n OfflineUserDataJobServiceClient service = client.GetService(\n Services.V25.OfflineUserDataJobService);\n\n string offlineUserDataJobResourceName;\n if (offlineUserDataJobId == null)\n {\n // Creates a new offline user data job.\n OfflineUserDataJob offlineUserDataJob = new OfflineUserDataJob()\n {\n Type = OfflineUserDataJobType.CustomerMatchUserList,\n CustomerMatchUserListMetadata = new CustomerMatchUserListMetadata()\n {\n UserList = userListResourceName,\n }\n };\n\n if (adUserDataConsent != null || adPersonalizationConsent != null)\n {\n // Specifies whether user consent was obtained for the data you are uploading.\n // See https://www.google.com/about/company/user-consent-policy\n // for details.\n offlineUserDataJob.CustomerMatchUserListMetadata.Consent = new Consent();\n\n if (adPersonalizationConsent != null)\n {\n offlineUserDataJob.CustomerMatchUserListMetadata.Consent.AdPersonalization =\n (ConsentStatus)adPersonalizationConsent;\n }\n\n if (adUserDataConsent != null)\n {\n offlineUserDataJob.CustomerMatchUserListMetadata.Consent.AdUserData =\n (ConsentStatus)adUserDataConsent;\n }\n }\n\n // Issues a request to create the offline user data job.\n CreateOfflineUserDataJobResponse response1 = service.CreateOfflineUserDataJob(\n customerId.ToString(), offlineUserDataJob);\n offlineUserDataJobResourceName = response1.ResourceName;\n Console.WriteLine($\"Created an offline user data job with resource name: \" +\n $\"'{offlineUserDataJobResourceName}'.\");\n } else {\n // Reuses the specified offline user data job.\n offlineUserDataJobResourceName =\n ResourceNames.OfflineUserDataJob(customerId, offlineUserDataJobId.Value);\n }\n\n AddOfflineUserDataJobOperationsRequest request =\n new AddOfflineUserDataJobOperationsRequest()\n {\n ResourceName = offlineUserDataJobResourceName,\n Operations = { BuildOfflineUserDataJobOperations() },\n EnablePartialFailure = true,\n };\n // Issues a request to add the operations to the offline user data job. This example\n // only adds a few operations, so it only sends one AddOfflineUserDataJobOperations\n // request.\n // If your application is adding a large number of operations, split the operations into\n // batches and send multiple AddOfflineUserDataJobOperations requests for the SAME job.\n // See https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations\n // and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data\n // for more information on the per-request limits.\n AddOfflineUserDataJobOperationsResponse response2 =\n service.AddOfflineUserDataJobOperations(request);\n\n // Prints the status message if any partial failure error is returned.\n // Note: The details of each partial failure error are not printed here,\n // you can refer to the example HandlePartialFailure.cs to learn more.\n if (response2.PartialFailureError != null)\n {\n // Extracts the partial failure from the response status.\n GoogleAdsFailure partialFailure = response2.PartialFailure;\n Console.WriteLine($\"{partialFailure.Errors.Count} partial failure error(s) \" +\n $\"occurred\");\n }\n Console.WriteLine(\"The operations are added to the offline user data job.\");\n\n if (!runJob)\n {\n Console.WriteLine($\"Not running offline user data job \" +\n \"'{offlineUserDataJobResourceName}', as requested.\");\n return offlineUserDataJobResourceName;\n }\n\n // Issues an asynchronous request to run the offline user data job for executing\n // all added operations.\n Operation<Empty, OfflineUserDataJobMetadata> operationResponse =\n service.RunOfflineUserDataJob(offlineUserDataJobResourceName);\n\n Console.WriteLine(\"Asynchronous request to execute the added operations started.\");\n\n // Since offline user data jobs may take 24 hours or more to complete, it may not be\n // practical to do operationResponse.PollUntilCompleted() to wait for the results.\n // Instead, we save the offlineUserDataJobResourceName and use GoogleAdsService.Search\n // to check for the job status periodically.\n // In case you wish to follow the PollUntilCompleted or PollOnce approach, make sure\n // you keep both operationResponse and service variables alive until the polling\n // completes.\n\n return offlineUserDataJobResourceName;\n}AddCustomerMatchUserList.cs\n```\n\nExample:\n```text\nprivate static function addUsersToCustomerMatchUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n bool $runJob,\n ?string $userListResourceName,\n ?int $offlineUserDataJobId,\n ?int $adPersonalizationConsent,\n ?int $adUserDataConsent\n) {\n $offlineUserDataJobServiceClient =\n $googleAdsClient->getOfflineUserDataJobServiceClient();\n\n if (is_null($offlineUserDataJobId)) {\n // Creates a new offline user data job.\n $offlineUserDataJob = new OfflineUserDataJob([\n 'type' => OfflineUserDataJobType::CUSTOMER_MATCH_USER_LIST,\n 'customer_match_user_list_metadata' => new CustomerMatchUserListMetadata([\n 'user_list' => $userListResourceName\n ])\n ]);\n // Adds consent information to the job if specified.\n if (!empty($adPersonalizationConsent) || !empty($adUserDataConsent)) {\n $consent = new Consent();\n if (!empty($adPersonalizationConsent)) {\n $consent->setAdPersonalization($adPersonalizationConsent);\n }\n if (!empty($adUserDataConsent)) {\n $consent->setAdUserData($adUserDataConsent);\n }\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy for details.\n $offlineUserDataJob->getCustomerMatchUserListMetadata()->setConsent($consent);\n }\n\n // Issues a request to create the offline user data job.\n /** @var CreateOfflineUserDataJobResponse $createOfflineUserDataJobResponse */\n $createOfflineUserDataJobResponse =\n $offlineUserDataJobServiceClient->createOfflineUserDataJob(\n CreateOfflineUserDataJobRequest::build($customerId, $offlineUserDataJob)\n );\n $offlineUserDataJobResourceName = $createOfflineUserDataJobResponse->getResourceName();\n printf(\n \"Created an offline user data job with resource name: '%s'.%s\",\n $offlineUserDataJobResourceName,\n PHP_EOL\n );\n } else {\n // Reuses the specified offline user data job.\n $offlineUserDataJobResourceName =\n ResourceNames::forOfflineUserDataJob($customerId, $offlineUserDataJobId);\n }\n\n // Issues a request to add the operations to the offline user data job. This example\n // only adds a few operations, so it only sends one AddOfflineUserDataJobOperations request.\n // If your application is adding a large number of operations, split the operations into\n // batches and send multiple AddOfflineUserDataJobOperations requests for the SAME job. See\n // https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations\n // and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data\n // for more information on the per-request limits.\n /** @var AddOfflineUserDataJobOperationsResponse $operationResponse */\n $response = $offlineUserDataJobServiceClient->addOfflineUserDataJobOperations(\n AddOfflineUserDataJobOperationsRequest::build(\n $offlineUserDataJobResourceName,\n self::buildOfflineUserDataJobOperations()\n )->setEnablePartialFailure(true)\n );\n\n // Prints the status message if any partial failure error is returned.\n // Note: The details of each partial failure error are not printed here, you can refer to\n // the example HandlePartialFailure.php to learn more.\n if ($response->hasPartialFailureError()) {\n // Extracts the partial failure from the response status.\n $partialFailure = GoogleAdsFailures::fromAny(\n $response->getPartialFailureError()->getDetails()->getIterator()->current()\n );\n printf(\n \"%d partial failure error(s) occurred: %s.%s\",\n count($partialFailure->getErrors()),\n $response->getPartialFailureError()->getMessage(),\n PHP_EOL\n );\n } else {\n print 'The operations are added to the offline user data job.' . PHP_EOL;\n }\n\n if ($runJob === false) {\n printf(\n \"Not running offline user data job '%s', as requested.%s\",\n $offlineUserDataJobResourceName,\n PHP_EOL\n );\n return;\n }\n\n // Issues an asynchronous request to run the offline user data job for executing all added\n // operations. The result is OperationResponse. Visit the OperationResponse.php file for\n // more details.\n $offlineUserDataJobServiceClient->runOfflineUserDataJob(\n RunOfflineUserDataJobRequest::build($offlineUserDataJobResourceName)\n );\n\n // Offline user data jobs may take 6 hours or more to complete, so instead of waiting\n // for the job to complete, retrieves and displays the job status once. If the job is\n // completed successfully, prints information about the user list. Otherwise, prints the\n // query to use to check the job again later.\n self::checkJobStatus($googleAdsClient, $customerId, $offlineUserDataJobResourceName);\n}AddCustomerMatchUserList.php\n```\n\nExample:\n```text\ndef add_users_to_customer_match_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n user_list_resource_name: str,\n run_job: bool,\n offline_user_data_job_id: Optional[str],\n ad_user_data_consent: Optional[str],\n ad_personalization_consent: Optional[str],\n) -> None:\n \"\"\"Uses Customer Match to create and add users to a new user list.\n\n Args:\n client: The Google Ads client.\n customer_id: The ID for the customer that owns the user list.\n user_list_resource_name: The resource name of the user list to which to\n add users.\n run_job: If true, runs the OfflineUserDataJob after adding operations.\n Otherwise, only adds operations to the job.\n offline_user_data_job_id: ID of an existing OfflineUserDataJob in the\n PENDING state. If None, a new job is created.\n ad_user_data_consent: The consent status for ad user data for all\n members in the job.\n ad_personalization_consent: The personalization consent status for ad\n user data for all members in the job.\n \"\"\"\n # Creates the OfflineUserDataJobService client.\n offline_user_data_job_service_client: OfflineUserDataJobServiceClient = (\n client.get_service(\"OfflineUserDataJobService\")\n )\n offline_user_data_job_resource_name: str\n\n if offline_user_data_job_id:\n # Reuses the specified offline user data job.\n offline_user_data_job_resource_name = (\n offline_user_data_job_service_client.offline_user_data_job_path(\n customer_id, offline_user_data_job_id\n )\n )\n else:\n # Creates a new offline user data job.\n offline_user_data_job: OfflineUserDataJob = client.get_type(\n \"OfflineUserDataJob\"\n )\n offline_user_data_job.type_ = (\n client.enums.OfflineUserDataJobTypeEnum.CUSTOMER_MATCH_USER_LIST\n )\n offline_user_data_job.customer_match_user_list_metadata.user_list = (\n user_list_resource_name\n )\n\n # Specifies whether user consent was obtained for the data you are\n # uploading. For more details, see:\n # https://www.google.com/about/company/user-consent-policy\n if ad_user_data_consent:\n offline_user_data_job.customer_match_user_list_metadata.consent.ad_user_data = client.enums.ConsentStatusEnum[\n ad_user_data_consent\n ]\n if ad_personalization_consent:\n offline_user_data_job.customer_match_user_list_metadata.consent.ad_personalization = client.enums.ConsentStatusEnum[\n ad_personalization_consent\n ]\n\n # Issues a request to create an offline user data job.\n create_offline_user_data_job_response: (\n CreateOfflineUserDataJobResponse\n ) = offline_user_data_job_service_client.create_offline_user_data_job(\n customer_id=customer_id, job=offline_user_data_job\n )\n offline_user_data_job_resource_name = (\n create_offline_user_data_job_response.resource_name\n )\n print(\n \"Created an offline user data job with resource name: \"\n f\"'{offline_user_data_job_resource_name}'.\"\n )\n\n # Issues a request to add the operations to the offline user data job.\n\n # Best Practice: This example only adds a few operations, so it only sends\n # one AddOfflineUserDataJobOperations request. If your application is adding\n # a large number of operations, split the operations into batches and send\n # multiple AddOfflineUserDataJobOperations requests for the SAME job. See\n # https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations\n # and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data\n # for more information on the per-request limits.\n request: AddOfflineUserDataJobOperationsRequest = client.get_type(\n \"AddOfflineUserDataJobOperationsRequest\"\n )\n request.resource_name = offline_user_data_job_resource_name\n request.operations = build_offline_user_data_job_operations(client)\n request.enable_partial_failure = True\n\n # Issues a request to add the operations to the offline user data job.\n response: AddOfflineUserDataJobOperationsResponse = (\n offline_user_data_job_service_client.add_offline_user_data_job_operations(\n request=request\n )\n )\n\n # Prints the status message if any partial failure error is returned.\n # Note: the details of each partial failure error are not printed here.\n # Refer to the error_handling/handle_partial_failure.py example to learn\n # more.\n # Extracts the partial failure from the response status.\n partial_failure: Union[status_pb2.Status, None] = getattr(\n response, \"partial_failure_error\", None\n )\n if getattr(partial_failure, \"code\", None) != 0:\n error_details: Iterable[Any, None] = getattr(\n partial_failure, \"details\", []\n )\n for error_detail in error_details:\n failure_message: GoogleAdsFailure = client.get_type(\n \"GoogleAdsFailure\"\n )\n # Retrieve the class definition of the GoogleAdsFailure instance\n # in order to use the \"deserialize\" class method to parse the\n # error_detail string into a protobuf message object.\n failure_object: GoogleAdsFailure = type(\n failure_message\n ).deserialize(error_detail.value)\n errors: Iterable[GoogleAdsError] = failure_object.errors\n\n for error in errors:\n print(\n \"A partial failure at index \"\n f\"{error.location.field_path_elements[0].index} occurred.\\n\"\n f\"Error message: {error.message}\\n\"\n f\"Error code: {error.error_code}\"\n )\n\n print(\"The operations are added to the offline user data job.\")\n\n if not run_job:\n print(\n \"Not running offline user data job \"\n f\"'{offline_user_data_job_resource_name}', as requested.\"\n )\n return\n\n # Issues a request to run the offline user data job for executing all\n # added operations.\n offline_user_data_job_service_client.run_offline_user_data_job(\n resource_name=offline_user_data_job_resource_name\n )\n\n # Retrieves and displays the job status.\n check_job_status(client, customer_id, offline_user_data_job_resource_name)add_customer_match_user_list.py\n```\n\nExample:\n```text\ndef add_users_to_customer_match_user_list(client, customer_id, run_job, user_list, job_id, ad_user_data_consent, ad_personalization_consent)\n offline_user_data_service = client.service.offline_user_data_job\n\n job_name = if job_id.nil?\n # Creates the offline user data job.\n offline_user_data_job = client.resource.offline_user_data_job do |job|\n job.type = :CUSTOMER_MATCH_USER_LIST\n job.customer_match_user_list_metadata =\n client.resource.customer_match_user_list_metadata do |m|\n m.user_list = user_list\n\n if !ad_user_data_consent.nil? || !ad_personalization_consent.nil?\n m.consent = client.resource.consent do |c|\n # Specifies whether user consent was obtained for the data you are\n # uploading. For more details, see:\n # https://www.google.com/about/company/user-consent-policy\n unless ad_user_data_consent.nil?\n c.ad_user_data = ad_user_data_consent\n end\n unless ad_personalization_consent.nil?\n c.ad_personalization = ad_personalization_consent\n end\n end\n end\n end\n end\n\n # Issues a request to create the offline user data job.\n response = offline_user_data_service.create_offline_user_data_job(\n customer_id: customer_id,\n job: offline_user_data_job,\n )\n offline_user_data_job_resource_name = response.resource_name\n puts \"Created an offline user data job with resource name: \" \\\n \"#{offline_user_data_job_resource_name}\"\n\n offline_user_data_job_resource_name\n else\n client.path.offline_user_data_job(customer_id, job_id)\n end\n\n # Issues a request to add the operations to the offline user data job. This\n # example only adds a few operations, so it only sends one\n # AddOfflineUserDataJobOperations request. If your application is adding a\n # large number of operations, split the operations into batches and send\n # multiple AddOfflineUserDataJobOperations requests for the SAME job. See\n # https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations\n # and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data\n # for more information on the per-request limits.\n response = offline_user_data_service.add_offline_user_data_job_operations(\n resource_name: offline_user_data_job_resource_name,\n enable_partial_failure: true,\n operations: build_offline_user_data_job_operations(client),\n )\n\n # Prints errors if any partial failure error is returned.\n if response.partial_failure_error\n failures = client.decode_partial_failure_error(response.partial_failure_error)\n failures.each do |failure|\n failure.errors.each do |error|\n human_readable_error_path = error\n .location\n .field_path_elements\n .map { |location_info|\n if location_info.index\n \"#{location_info.field_name}[#{location_info.index}]\"\n else\n \"#{location_info.field_name}\"\n end\n }.join(\" > \")\n\n errmsg = \"error occured while adding operations \" \\\n \"#{human_readable_error_path}\" \\\n \" with value: #{error.trigger.string_value}\" \\\n \" because #{error.message.downcase}\"\n puts errmsg\n end\n end\n end\n puts \"The operations are added to the offline user data job.\"\n\n unless run_job\n puts \"Not running offline user data job #{job_name}, as requested.\"\n return\n end\n\n # Issues an asynchronous request to run the offline user data job\n # for executing all added operations.\n response = offline_user_data_service.run_offline_user_data_job(\n resource_name: offline_user_data_job_resource_name\n )\n puts \"Asynchronous request to execute the added operations started.\"\n puts \"Waiting until operation completes.\"\n\n # Offline user data jobs may take 6 hours or more to complete, so instead of\n # waiting for the job to complete, retrieves and displays the job status\n # once. If the job is completed successfully, prints information about the\n # user list. Otherwise, prints the query to use to check the job again later.\n check_job_status(\n client,\n customer_id,\n offline_user_data_job_resource_name,\n )\nendadd_customer_match_user_list.rb\n```\n\nExample:\n```text\nsub add_users_to_customer_match_user_list {\n my ($api_client, $customer_id, $run_job, $user_list_resource_name,\n $offline_user_data_job_id, $ad_personalization_consent,\n $ad_user_data_consent)\n = @_;\n\n my $offline_user_data_job_service = $api_client->OfflineUserDataJobService();\n\n my $offline_user_data_job_resource_name = undef;\n if (!defined $offline_user_data_job_id) {\n # Create a new offline user data job.\n my $offline_user_data_job =\n Google::Ads::GoogleAds::V25::Resources::OfflineUserDataJob->new({\n type => CUSTOMER_MATCH_USER_LIST,\n customerMatchUserListMetadata =>\n Google::Ads::GoogleAds::V25::Common::CustomerMatchUserListMetadata->\n new({\n userList => $user_list_resource_name\n })});\n\n # Add consent information to the job if specified.\n if ($ad_personalization_consent or $ad_user_data_consent) {\n my $consent = Google::Ads::GoogleAds::V25::Common::Consent->new({});\n if ($ad_personalization_consent) {\n $consent->{adPersonalization} = $ad_personalization_consent;\n }\n if ($ad_user_data_consent) {\n $consent->{adUserData} = $ad_user_data_consent;\n }\n # Specify whether user consent was obtained for the data you are uploading.\n # See https://www.google.com/about/company/user-consent-policy for details.\n $offline_user_data_job->{customerMatchUserListMetadata}{consent} =\n $consent;\n }\n\n # Issue a request to create the offline user data job.\n my $create_offline_user_data_job_response =\n $offline_user_data_job_service->create({\n customerId => $customer_id,\n job => $offline_user_data_job\n });\n $offline_user_data_job_resource_name =\n $create_offline_user_data_job_response->{resourceName};\n printf\n \"Created an offline user data job with resource name: '%s'.\\n\",\n $offline_user_data_job_resource_name;\n } else {\n # Reuse the specified offline user data job.\n $offline_user_data_job_resource_name =\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::offline_user_data_job(\n $customer_id, $offline_user_data_job_id);\n }\n\n # Issue a request to add the operations to the offline user data job.\n # This example only adds a few operations, so it only sends one AddOfflineUserDataJobOperations\n # request. If your application is adding a large number of operations, split\n # the operations into batches and send multiple AddOfflineUserDataJobOperations\n # requests for the SAME job. See\n # https://developers.google.com/google-ads/api/docs/remarketing/audience-types/customer-match#customer_match_considerations\n # and https://developers.google.com/google-ads/api/docs/best-practices/quotas#user_data\n # for more information on the per-request limits.\n my $user_data_job_operations = build_offline_user_data_job_operations();\n my $response = $offline_user_data_job_service->add_operations(\n {\n resourceName => $offline_user_data_job_resource_name,\n enablePartialFailure => \"true\",\n operations => $user_data_job_operations\n });\n\n # Print the status message if any partial failure error is returned.\n # Note: The details of each partial failure error are not printed here, you can\n # refer to the example handle_partial_failure.pl to learn more.\n if ($response->{partialFailureError}) {\n # Extract the partial failure from the response status.\n my $partial_failure = $response->{partialFailureError}{details}[0];\n printf \"Encountered %d partial failure errors while adding %d operations \" .\n \"to the offline user data job: '%s'. Only the successfully added \" .\n \"operations will be executed when the job runs.\\n\",\n scalar @{$partial_failure->{errors}}, scalar @$user_data_job_operations,\n $response->{partialFailureError}{message};\n } else {\n printf \"Successfully added %d operations to the offline user data job.\\n\",\n scalar @$user_data_job_operations;\n }\n\n if (!defined $run_job) {\n print\n\"Not running offline user data job $offline_user_data_job_resource_name, as requested.\\n\";\n return;\n }\n\n # Issue an asynchronous request to run the offline user data job for executing\n # all added operations.\n my $operation_response = $offline_user_data_job_service->run({\n resourceName => $offline_user_data_job_resource_name\n });\n\n # Offline user data jobs may take 6 hours or more to complete, so instead of waiting\n # for the job to complete, this example retrieves and displays the job status once.\n # If the job is completed successfully, it prints information about the user list.\n # Otherwise, it prints, the query to use to check the job status again later.\n check_job_status($api_client, $customer_id,\n $offline_user_data_job_resource_name);\n}add_customer_match_user_list.pl\n```\n\nExample:\n```text\n// Creates a request to add user data operations to the user list based on email addresses.\nString userListResourceName = ResourceNames.userList(customerId, userListId);\nUploadUserDataRequest.Builder uploadUserDataRequest =\n UploadUserDataRequest.newBuilder()\n .setCustomerId(String.valueOf(customerId))\n .setCustomerMatchUserListMetadata(\n CustomerMatchUserListMetadata.newBuilder()\n .setUserList(StringValue.of(userListResourceName))\n .build());\n```\n\nExample:\n```text\nImmutableList<String> EMAILS =\n ImmutableList.of(\"client1@example.com\", \"client2@example.com\", \" Client3@example.com \");\n\n// Hash normalized email addresses based on SHA-256 hashing algorithm.\nList<UserDataOperation> userDataOperations = new ArrayList<>(EMAILS.size());\nfor (String email : EMAILS) {\n UserDataOperation userDataOperation =\n UserDataOperation.newBuilder()\n .setCreate(\n UserData.newBuilder()\n .addUserIdentifiers(\n UserIdentifier.newBuilder()\n .setHashedEmail(StringValue.of(toSHA256String(email)))\n .build())\n .build())\n .build();\n userDataOperations.add(userDataOperation);\n}\nuploadUserDataRequest.addAllOperations(userDataOperations);\n```\n\nExample:\n```text\nString firstName = \"Alex\";\nString lastName = \"Quinn\";\nString countryCode = \"US\";\nString postalCode = \"94045\";\n\nUserIdentifier userIdentifierWithAddress =\n UserIdentifier.newBuilder()\n .setAddressInfo(\n OfflineUserAddressInfo.newBuilder()\n // First and last name must be normalized and hashed.\n .setHashedFirstName(\n StringValue.of(toSHA256String(firstName)))\n .setHashedLastName(StringValue.of(toSHA256String(lastName)))\n // Country code and zip code are sent in plaintext.\n .setCountryCode(StringValue.of(countryCode))\n .setPostalCode(StringValue.of(postalCode))\n .build())\n .build();\n\nUserDataOperation userDataOperation =\n UserDataOperation.newBuilder()\n .setCreate(\n UserData.newBuilder()\n .addUserIdentifiers(userIdentifierWithAddress)\n .build())\n .build();\nuploadUserDataRequest.addOperations(userDataOperation);\n```\n\nExample:\n```text\n// Creates the user data service client.\ntry (UserDataServiceClient userDataServiceClient =\n googleAdsClient.getLatestVersion().createUserDataServiceClient()) {\n // Add operations to the user list based on the user data type.\n UploadUserDataResponse response =\n userDataServiceClient.uploadUserData(uploadUserDataRequest.build());\n\n // Displays the results.\n // Reminder: it may take several hours for the list to be populated with members.\n System.out.printf(\n \"Received %d operations at %s\",\n response.getReceivedOperationsCount().getValue(),\n response.getUploadDateTime().getValue());\n}\n```\n\nExample:\n```text\nSELECT\n ad_group_criterion.criterion_id,\n metrics.conversions,\n metrics.cost_per_conversion\nFROM ad_group_audience_view\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.531Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":798,"estimatedTokens":8751}}210{"id":"doc-dynamic_remarketing_with_assets_google_ads_api_g-ad147a7c","source":"documentation","title":"Dynamic remarketing with assets | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/dynamic-remarketing/asset-based","text":"Example:\n```text\n// Creates a DynamicEducationAsset.\n// See https://support.google.com/google-ads/answer/6053288?#zippy=%2Ceducation for a\n// detailed explanation of the field format.\nDynamicEducationAsset educationAsset =\n DynamicEducationAsset.newBuilder()\n // Defines meta-information about the school and program.\n .setSchoolName(\"The University of Unknown\")\n .setAddress(\"Building 1, New York, 12345, USA\")\n .setProgramName(\"BSc. Computer Science\")\n .setSubject(\"Computer Science\")\n .setProgramDescription(\"Slinging code for fun and profit!\")\n // Sets up the program ID which is the ID that should be specified in the tracking\n // pixel.\n .setProgramId(\"bsc-cs-uofu\")\n // Sets up the location ID which may additionally be specified in the tracking pixel.\n .setLocationId(\"nyc\")\n .setImageUrl(\"https://gaagl.page.link/Eit5\")\n .setAndroidAppLink(\"android-app://com.example.android/http/example.com/gizmos?1234\")\n .setIosAppLink(\"exampleApp://content/page\")\n .setIosAppStoreId(123L)\n .build();\nAsset asset =\n Asset.newBuilder()\n .setDynamicEducationAsset(educationAsset)\n .addFinalUrls(\"https://www.example.com\")\n .build();\n// Creates an operation to add the asset.\nAssetOperation operation = AssetOperation.newBuilder().setCreate(asset).build();\n// Connects to the API.\ntry (AssetServiceClient client =\n googleAdsClient.getLatestVersion().createAssetServiceClient()) {\n // Sends the mutate request.\n MutateAssetsResponse response =\n client.mutateAssets(String.valueOf(params.customerId), ImmutableList.of(operation));\n // Prints some information about the response.\n String resourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Created a dynamic education asset with resource name %s.%n\", resourceName);\n return resourceName;\n}AddDynamicRemarketingAsset.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates an Asset to use in dynamic remarketing.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <returns>The resource name of the newly created asset.</returns>\nprivate string CreateAsset(GoogleAdsClient client, long customerId)\n{\n AssetServiceClient assetService = client.GetService(Services.V25.AssetService);\n\n // Creates a DynamicEducationAsset.\n // See https://support.google.com/google-ads/answer/6053288?#zippy=%2Ceducation for a\n // detailed explanation of the field format.\n DynamicEducationAsset educationAsset = new DynamicEducationAsset()\n {\n // Defines meta-information about the school and program.\n SchoolName = \"The University of Unknown\",\n Address = \"Building 1, New York, 12345, USA\",\n ProgramName = \"BSc. Computer Science\",\n Subject = \"Computer Science\",\n ProgramDescription = \"Slinging code for fun and profit!\",\n // Sets up the program ID which is the ID that should be specified in\n // the tracking pixel.\n ProgramId = \"bsc-cs-uofu\",\n // Sets up the location ID which may additionally be specified in the\n // tracking pixel.\n LocationId = \"nyc\",\n ImageUrl = \"https://gaagl.page.link/Eit5\",\n AndroidAppLink = \"android-app://com.example.android/http/example.com/gizmos?1234\",\n IosAppLink = \"exampleApp://content/page\",\n IosAppStoreId = 123L\n };\n Asset asset = new Asset()\n {\n DynamicEducationAsset = educationAsset,\n // The final_urls list must not be empty\n FinalUrls = { \"https://www.example.com\" }\n };\n\n // Creates an operation to add the asset.\n AssetOperation operation = new AssetOperation()\n {\n Create = asset\n };\n\n // Sends the mutate request.\n MutateAssetsResponse response =\n assetService.MutateAssets(customerId.ToString(), new[] { operation });\n // Prints some information about the response.\n string resourceName = response.Results[0].ResourceName;\n Console.Write($\"Created a dynamic education asset with resource name {resourceName}.\");\n return resourceName;\n}AddDynamicRemarketingAsset.cs\n```\n\nExample:\n```text\n// Creates a dynamic education asset.\n// See https://support.google.com/google-ads/answer/6053288?#zippy=%2Ceducation for a\n// detailed explanation of the field format.\n$dynamicEducationAsset = new DynamicEducationAsset([\n // Defines meta-information about the school and program.\n 'school_name' => 'The University of Unknown',\n 'address' => 'Building 1, New York, 12345, USA',\n 'program_name' => 'BSc. Computer Science',\n 'subject' => 'Computer Science',\n 'program_description' => 'Slinging code for fun and profit!',\n // Sets up the program ID which is the ID that should be specified in the tracking\n // pixel.\n 'program_id' => 'bsc-cs-uofu',\n // Sets up the location ID which may additionally be specified in the tracking pixel.\n 'location_id' => 'nyc',\n 'image_url' => 'https://gaagl.page.link/Eit5',\n 'android_app_link' => 'android-app://com.example.android/http/example.com/gizmos?1234',\n 'ios_app_link' => 'exampleApp://content/page',\n 'ios_app_store_id' => 123\n]);\n\n// Wraps the dynamic education asset in an asset.\n$asset = new Asset([\n 'dynamic_education_asset' => $dynamicEducationAsset,\n 'final_urls' => ['https://www.example.com']\n]);\n\n// Creates an asset operation.\n$assetOperation = new AssetOperation();\n$assetOperation->setCreate($asset);\n\n// Issues a mutate request to add the asset and prints its information.\n$assetServiceClient = $googleAdsClient->getAssetServiceClient();\n$response = $assetServiceClient->mutateAssets(\n MutateAssetsRequest::build($customerId, [$assetOperation])\n);\n$assetResourceName = $response->getResults()[0]->getResourceName();\nprintf(\n \"Created a dynamic education asset with resource name: '%s'.%s\",\n $assetResourceName,\n PHP_EOL\n);\n\nreturn $assetResourceName;AddDynamicRemarketingAsset.php\n```\n\nExample:\n```text\ndef create_asset(client: GoogleAdsClient, customer_id: str) -> str:\n \"\"\"Creates a DynamicEducationAsset.\n\n See https://support.google.com/google-ads/answer/6053288?#zippy=%2Ceducation\n for a detailed explanation of the field format.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n The resource name for an asset.\n \"\"\"\n # Creates an operation to add the asset.\n operation: AssetOperation = client.get_type(\"AssetOperation\")\n asset: Asset = operation.create\n # The final_urls list must not be empty\n asset.final_urls.append(\"https://www.example.com\")\n education_asset: DynamicEducationAsset = asset.dynamic_education_asset\n # Defines meta-information about the school and program.\n education_asset.school_name = \"The University of Unknown\"\n education_asset.address = \"Building 1, New York, 12345, USA\"\n education_asset.program_name = \"BSc. Computer Science\"\n education_asset.subject = \"Computer Science\"\n education_asset.program_description = \"Slinging code for fun and profit!\"\n # Sets up the program ID which is the ID that should be specified in the\n # tracking pixel.\n education_asset.program_id = \"bsc-cs-uofu\"\n # Sets up the location ID which may additionally be specified in the\n # tracking pixel.\n education_asset.location_id = \"nyc\"\n education_asset.image_url = \"https://gaagl.page.link/Eit5\"\n education_asset.android_app_link = (\n \"android-app://com.example.android/http/example.com/gizmos?1234\"\n )\n education_asset.ios_app_link = \"exampleApp://content/page\"\n education_asset.ios_app_store_id = 123\n\n asset_service: AssetServiceClient = client.get_service(\"AssetService\")\n response: MutateAssetsResponse = asset_service.mutate_assets(\n customer_id=customer_id, operations=[operation]\n )\n resource_name: str = response.results[0].resource_name\n print(\n f\"Created a dynamic education asset with resource name '{resource_name}'\"\n )\n\n return resource_nameadd_dynamic_remarketing_asset.py\n```\n\nExample:\n```text\ndef create_asset(client, customer_id)\n # Creates a DynamicEducationAsset.\n # See https://support.google.com/google-ads/answer/6053288?#zippy=%2Ceducation for a\n # detailed explanation of the field format.\n\n # Creates an operation to add the asset.\n operation = client.operation.create_resource.asset do |asset|\n asset.final_urls << 'https://www.example.com'\n asset.dynamic_education_asset = client.resource.dynamic_education_asset do |dea|\n # Defines meta-information about the school and program.\n dea.school_name = 'The University of Unknown'\n dea.address = 'Building 1, New York, 12345, USA'\n dea.program_name = 'BSc. Computer Science'\n dea.subject = 'Computer Science'\n dea.program_description = 'Slinging code for fun and profit!'\n # Sets up the program ID which is the ID that should be specified in the\n # tracking pixel.\n dea.program_id = 'bsc-cs-uofu'\n # Sets up the location ID which may additionally be specified in the\n # tracking pixel.\n dea.location_id = 'nyc'\n dea.image_url = 'https://gaagl.page.link/Eit5'\n dea.android_app_link = 'android-app://com.example.android/http/example.com/gizmos?1234'\n dea.ios_app_link = 'exampleApp://content/page'\n dea.ios_app_store_id = 123\n end\n end\n\n # Sends the mutate request.\n response = client.service.asset.mutate_assets(\n customer_id: customer_id,\n operations: [operation],\n )\n resource_name = response.results.first.resource_name\n puts \"Created a dynamic education asset with resource name '#{resource_name}'\"\n\n resource_name\nendadd_dynamic_remarketing_asset.rb\n```\n\nExample:\n```text\n# Create a DynamicEducationAsset.\n# See https://support.google.com/google-ads/answer/6053288?#zippy=%2Ceducation\n# for a detailed explanation of the field format.\nmy $education_asset =\n Google::Ads::GoogleAds::V25::Common::DynamicEducationAsset->new({\n # Define meta-information about the school and program.\n schoolName => \"The University of Unknown\",\n address => \"Building 1, New York, 12345, USA\",\n programName => \"BSc. Computer Science\",\n subject => \"Computer Science\",\n programDescription => \"Slinging code for fun and profit!\",\n # Set up the program ID which is the ID that should be specified in the\n # tracking pixel.\n programId => \"bsc-cs-uofu\",\n # Set up the location ID which may additionally be specified in the tracking pixel.\n locationId => \"nyc\",\n imageUrl => \"https://gaagl.page.link/Eit5\",\n androidAppLink =>\n \"android-app://com.example.android/http/example.com/gizmos?1234\",\n iosAppLink => \"exampleApp://content/page\",\n iosAppStoreId => 123\n });\nmy $asset = Google::Ads::GoogleAds::V25::Resources::Asset->new({\n dynamicEducationAsset => $education_asset,\n finalUrls => [\"https://www.example.com\"]});\n\n# Create an operation to add the asset.\nmy $operation =\n Google::Ads::GoogleAds::V25::Services::AssetService::AssetOperation->new({\n create => $asset\n });\n\n# Send the mutate request.\nmy $response = $api_client->AssetService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n\n# Print some information about the response.\nmy $resource_name = $response->{results}[0]{resourceName};\nprintf \"Created a dynamic education asset with resource name '%s'.\\n\",\n $resource_name;\nreturn $resource_name;add_dynamic_remarketing_asset.pl\n```\n\nExample:\n```text\n// Creates an AssetSet which will be used to link the dynamic remarketing assets to a campaign.\nAssetSet assetSet =\n AssetSet.newBuilder()\n .setName(\"My dynamic remarketing assets \" + CodeSampleHelper.getPrintableDateTime())\n .setType(AssetSetType.DYNAMIC_EDUCATION)\n .build();\n// Creates an operation to add the link.\nAssetSetOperation operation = AssetSetOperation.newBuilder().setCreate(assetSet).build();\ntry (AssetSetServiceClient serviceClient =\n googleAdsClient.getLatestVersion().createAssetSetServiceClient()) {\n // Sends the mutate request.\n MutateAssetSetsResponse response =\n serviceClient.mutateAssetSets(\n String.valueOf(params.customerId), ImmutableList.of(operation));\n // Prints some information about the response.\n String resourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Created asset set with resource name %s.%n\", resourceName);\n return resourceName;\n}AddDynamicRemarketingAsset.java\n```\n\nExample:\n```text\n/// <summary>\n/// Creates the asset set.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <returns>The resource name of the asset set.</returns>\nprivate string CreateAssetSet(GoogleAdsClient client, long customerId)\n{\n AssetSetServiceClient assetSetService = client.GetService(\n Services.V25.AssetSetService);\n\n // Creates an AssetSet which will be used to link the dynamic remarketing assets\n // to a campaign.\n AssetSet assetSet = new AssetSet()\n {\n Name = \"My dynamic remarketing assets \" + ExampleUtilities.GetRandomString(),\n Type = AssetSetType.DynamicEducation\n };\n\n // Creates an operation to add the link.\n AssetSetOperation operation = new AssetSetOperation()\n {\n Create = assetSet\n };\n // Sends the mutate request.\n MutateAssetSetsResponse response = assetSetService.MutateAssetSets(\n customerId.ToString(), new[] { operation });\n // Prints some information about the response.\n string resourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Created asset set with resource name {resourceName}.\");\n return resourceName;\n}AddDynamicRemarketingAsset.cs\n```\n\nExample:\n```text\n// Creates an asset set which will be used to link the dynamic remarketing assets to a\n// campaign.\n$assetSet = new AssetSet([\n 'name' => 'My dynamic remarketing assets ' . Helper::getPrintableDatetime(),\n 'type' => AssetSetType::DYNAMIC_EDUCATION\n]);\n\n// Creates an asset set operation.\n$assetSetOperation = new AssetSetOperation();\n$assetSetOperation->setCreate($assetSet);\n\n// Issues a mutate request to add the asset set and prints its information.\n$assetSetServiceClient = $googleAdsClient->getAssetSetServiceClient();\n$response = $assetSetServiceClient->mutateAssetSets(\n MutateAssetSetsRequest::build($customerId, [$assetSetOperation])\n);\n$assetSetResourceName = $response->getResults()[0]->getResourceName();\nprintf(\n \"Created an asset set with resource name: '%s'.%s\",\n $assetSetResourceName,\n PHP_EOL\n);\n\nreturn $assetSetResourceName;AddDynamicRemarketingAsset.php\n```\n\nExample:\n```text\ndef create_asset_set(client: GoogleAdsClient, customer_id: str) -> str:\n \"\"\"Creates an AssetSet.\n\n The AssetSet will be used to link the dynamic remarketing assets to a\n campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n The resource name for an asset set.\n \"\"\"\n # Creates an operation to create the asset set.\n operation: AssetSetOperation = client.get_type(\"AssetSetOperation\")\n asset_set: AssetSet = operation.create\n asset_set.name = f\"My dynamic remarketing assets {datetime.now()}\"\n asset_set.type_ = client.enums.AssetSetTypeEnum.DYNAMIC_EDUCATION\n\n asset_set_service: AssetSetServiceClient = client.get_service(\n \"AssetSetService\"\n )\n response: MutateAssetSetsResponse = asset_set_service.mutate_asset_sets(\n customer_id=customer_id, operations=[operation]\n )\n resource_name: str = response.results[0].resource_name\n print(f\"Created asset set with resource name '{resource_name}'\")\n\n return resource_nameadd_dynamic_remarketing_asset.py\n```\n\nExample:\n```text\ndef create_asset_set(client, customer_id)\n # Creates an AssetSet which will be used to link the dynamic remarketing assets to a campaign.\n\n # Creates an operation to add the asset set.\n operation = client.operation.create_resource.asset_set do |asset_set|\n asset_set.name = \"My dynamic remarketing assets #{Time.now}\"\n asset_set.type = :DYNAMIC_EDUCATION\n end\n\n # Sends the mutate request.\n response = client.service.asset_set.mutate_asset_sets(\n customer_id: customer_id,\n operations: [operation],\n )\n resource_name = response.results.first.resource_name\n puts \"Created asset set with resource name '#{resource_name}'\"\n\n resource_name\nendadd_dynamic_remarketing_asset.rb\n```\n\nExample:\n```text\n# Create an AssetSet which will be used to link the dynamic remarketing assets\n# to a campaign.\nmy $asset_set = Google::Ads::GoogleAds::V25::Resources::AssetSet->new({\n name => \"My dynamic remarketing assets #\" . uniqid(),\n type => DYNAMIC_EDUCATION\n});\n\n# Create an operation to add the AssetSet.\nmy $operation =\n Google::Ads::GoogleAds::V25::Services::AssetSetService::AssetSetOperation->\n new({\n create => $asset_set\n });\n\n# Send the mutate request.\nmy $response = $api_client->AssetSetService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n\n# Print some information about the response.\nmy $resource_name = $response->{results}[0]{resourceName};\nprintf \"Created asset set with resource name '%s'.\\n\", $resource_name;\nreturn $resource_name;add_dynamic_remarketing_asset.pl\n```\n\nExample:\n```text\nAssetSetAsset assetSetAsset =\n AssetSetAsset.newBuilder()\n .setAsset(assetResourceName)\n .setAssetSet(assetSetResourceName)\n .build();\n// Creates an operation to add the link.\nAssetSetAssetOperation operation =\n AssetSetAssetOperation.newBuilder().setCreate(assetSetAsset).build();\ntry (AssetSetAssetServiceClient client =\n googleAdsClient.getLatestVersion().createAssetSetAssetServiceClient()) {\n // Sends the mutate request.\n // Note this is the point that the API will enforce uniqueness of the\n // DynamicEducationAsset.product_id field. You can have any number of assets with the same\n // product_id, however, only one Asset is allowed per AssetSet with the same product ID.\n MutateAssetSetAssetsResponse response =\n client.mutateAssetSetAssets(\n String.valueOf(params.customerId), ImmutableList.of(operation));\n // Prints some information about the response.\n String resourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Created AssetSetAsset link with resource name %s.%n\", resourceName);\n}AddDynamicRemarketingAsset.java\n```\n\nExample:\n```text\n/// <summary>\n/// Adds an Asset to an AssetSet by creating an AssetSetAsset link.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <param name=\"assetResourceName\">Name of the asset resource.</param>\n/// <param name=\"assetSetResourceName\">Name of the asset set resource.</param>\nprivate void AddAssetsToAssetSet(GoogleAdsClient client, long customerId,\n string assetResourceName, string assetSetResourceName)\n{\n AssetSetAssetServiceClient assetSetAssetService = client.GetService(\n Services.V25.AssetSetAssetService);\n\n AssetSetAsset assetSetAsset = new AssetSetAsset()\n {\n Asset = assetResourceName,\n AssetSet = assetSetResourceName\n };\n\n // Creates an operation to add the link.\n AssetSetAssetOperation operation = new AssetSetAssetOperation()\n {\n Create = assetSetAsset\n };\n // Sends the mutate request.\n // Note this is the point that the API will enforce uniqueness of the\n // DynamicEducationAsset.program_id field. You can have any number of\n // assets with the same program_id, however, only one Asset is allowed\n // per AssetSet with the same program ID.\n MutateAssetSetAssetsResponse response =\n assetSetAssetService.MutateAssetSetAssets(\n customerId.ToString(), new[] { operation });\n // Prints some information about the response.\n string resourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Created AssetSetAsset link with resource name {resourceName}.\");\n}AddDynamicRemarketingAsset.cs\n```\n\nExample:\n```text\n// Creates an asset set asset.\n$assetSetAsset = new AssetSetAsset([\n 'asset' => $assetResourceName,\n 'asset_set' => $assetSetResourceName\n]);\n\n// Creates an asset set asset operation.\n$assetSetAssetOperation = new AssetSetAssetOperation();\n$assetSetAssetOperation->setCreate($assetSetAsset);\n\n// Issues a mutate request to add the asset set asset and prints its information.\n// Note this is the point that the API will enforce uniqueness of the\n// DynamicEducationAsset::program_id field. You can have any number of assets with the same\n// program_id, however, only one asset is allowed per asset set with the same product ID.\n$assetSetAssetServiceClient = $googleAdsClient->getAssetSetAssetServiceClient();\n$response = $assetSetAssetServiceClient->mutateAssetSetAssets(\n MutateAssetSetAssetsRequest::build($customerId, [$assetSetAssetOperation])\n);\nprintf(\n \"Created asset set asset link with resource name: '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n);AddDynamicRemarketingAsset.php\n```\n\nExample:\n```text\ndef add_assets_to_asset_set(\n client: GoogleAdsClient,\n asset_resource_name: str,\n asset_set_resource_name: str,\n customer_id: str,\n) -> None:\n \"\"\"Adds an Asset to an AssetSet by creating an AssetSetAsset link.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n asset_set_resource_name; the resource name for an asset set.\n asset_resource_name; the resource name for an asset.\n customer_id: a client customer ID.\n \"\"\"\n # Creates an operation to add the asset set asset.\n operation: AssetSetAssetOperation = client.get_type(\n \"AssetSetAssetOperation\"\n )\n asset_set_asset: AssetSetAsset = operation.create\n asset_set_asset.asset = asset_resource_name\n asset_set_asset.asset_set = asset_set_resource_name\n\n asset_set_asset_service: AssetSetAssetServiceClient = client.get_service(\n \"AssetSetAssetService\"\n )\n # Note this is the point that the API will enforce uniqueness of the\n # DynamicEducationAsset.program_id field. You can have any number of assets\n # with the same program ID, however, only one asset is allowed per asset set\n # with the same program ID.\n response: MutateAssetSetAssetsResponse = (\n asset_set_asset_service.mutate_asset_set_assets(\n customer_id=customer_id, operations=[operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n print(f\"Created asset set asset link with resource name '{resource_name}'\")add_dynamic_remarketing_asset.py\n```\n\nExample:\n```text\ndef add_assets_to_asset_set(client, asset_resource_name, asset_set_resource_name, customer_id)\n # Creates an operation to add the asset set asset.\n operation = client.operation.create_resource.asset_set_asset do |asa|\n asa.asset = asset_resource_name\n asa.asset_set = asset_set_resource_name\n end\n\n # Sends the mutate request.\n #\n # Note this is the point that the API will enforce uniqueness of the\n # DynamicEducationAsset.program_id field. You can have any number of assets\n # with the same program ID, however, only one asset is allowed per asset set\n # with the same program ID.\n response = client.service.asset_set_asset.mutate_asset_set_assets(\n customer_id: customer_id,\n operations: [operation],\n )\n resource_name = response.results.first.resource_name\n puts \"Created asset set asset link with resource name '#{resource_name}'\"\nendadd_dynamic_remarketing_asset.rb\n```\n\nExample:\n```text\nmy $asset_set_asset =\n Google::Ads::GoogleAds::V25::Resources::AssetSetAsset->new({\n asset => $asset_resource_name,\n assetSet => $asset_set_resource_name\n });\n\n# Create an operation to add the link.\nmy $operation =\n Google::Ads::GoogleAds::V25::Services::AssetSetAssetService::AssetSetAssetOperation\n ->new({\n create => $asset_set_asset\n });\n\n# Send the mutate request.\n# Note this is the point that the API will enforce uniqueness of the\n# DynamicEducationAsset.programId field. You can have any number of assets\n# with the same programId, however, only one Asset is allowed per AssetSet\n# with the same program ID.\nmy $response = $api_client->AssetSetAssetService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n\n# Print some information about the response.\nmy $resource_name = $response->{results}[0]{resourceName};\nprintf \"Created AssetSetAsset link with resource name '%s'.\\n\",\n $resource_name;add_dynamic_remarketing_asset.pl\n```\n\nExample:\n```text\n// Creates a CampaignAssetSet representing the link between an AssetSet and a Campaign.\nCampaignAssetSet campaignAssetSet =\n CampaignAssetSet.newBuilder()\n .setCampaign(ResourceNames.campaign(params.customerId, params.campaignId))\n .setAssetSet(assetSetResourceName)\n .build();\n// Creates an operation to add the CampaignAssetSet.\nCampaignAssetSetOperation operation =\n CampaignAssetSetOperation.newBuilder().setCreate(campaignAssetSet).build();\n// Creates an API connection.\ntry (CampaignAssetSetServiceClient client =\n googleAdsClient.getLatestVersion().createCampaignAssetSetServiceClient()) {\n // Issues the mutate request.\n MutateCampaignAssetSetsResponse response =\n client.mutateCampaignAssetSets(\n String.valueOf(params.customerId), ImmutableList.of(operation));\n String resourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Created a CampaignAssetSet with resource name %s.%n\", resourceName);\n}AddDynamicRemarketingAsset.java\n```\n\nExample:\n```text\n/// <summary>\n/// Links an AssetSet to Campaign by creating a CampaignAssetSet.\n/// </summary>\n/// <param name=\"client\">The Google Ads client.</param>\n/// <param name=\"customerId\">The Google Ads customer ID.</param>\n/// <param name=\"campaignId\">ID of the campaign to which the asset is linked. Specify a\n/// campaign type which supports dynamic remarketing, such as Display.</param>\n/// <param name=\"assetSetResourceName\">Name of the asset set resource.</param>\nprivate void LinkAssetSetToCampaign(GoogleAdsClient client, long customerId,\n long campaignId, string assetSetResourceName)\n{\n CampaignAssetSetServiceClient campaignAssetSetService = client.GetService(\n Services.V25.CampaignAssetSetService);\n\n // Creates a CampaignAssetSet representing the link between an AssetSet and a Campaign.\n CampaignAssetSet campaignAssetSet = new CampaignAssetSet()\n {\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n AssetSet = assetSetResourceName\n };\n\n // Creates an operation to add the CampaignAssetSet.\n CampaignAssetSetOperation operation = new CampaignAssetSetOperation()\n {\n Create = campaignAssetSet\n };\n\n // Issues the mutate request.\n MutateCampaignAssetSetsResponse response =\n campaignAssetSetService.MutateCampaignAssetSets(\n customerId.ToString(), new[] { operation });\n string resourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"Created a CampaignAssetSet with resource name {resourceName}.\");\n}AddDynamicRemarketingAsset.cs\n```\n\nExample:\n```text\n// Creates a campaign asset set representing the link between an asset set and a campaign.\n$campaignAssetSet = new CampaignAssetSet([\n 'asset_set' => $assetSetResourceName,\n 'campaign' => ResourceNames::forCampaign($customerId, $campaignId)\n]);\n\n// Creates a campaign asset set operation.\n$campaignAssetSetOperation = new CampaignAssetSetOperation();\n$campaignAssetSetOperation->setCreate($campaignAssetSet);\n\n// Issues a mutate request to add the campaign asset set and prints its information.\n$campaignAssetSetServiceClient = $googleAdsClient->getCampaignAssetSetServiceClient();\n$response = $campaignAssetSetServiceClient->mutateCampaignAssetSets(\n MutateCampaignAssetSetsRequest::build($customerId, [$campaignAssetSetOperation])\n);\nprintf(\n \"Created a campaign asset set with resource name: '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n);AddDynamicRemarketingAsset.php\n```\n\nExample:\n```text\ndef link_asset_set_to_campaign(\n client: GoogleAdsClient,\n asset_set_resource_name: str,\n customer_id: str,\n campaign_id: str,\n) -> None:\n \"\"\"Creates a CampaignAssetSet.\n\n The CampaignAssetSet represents the link between an AssetSet and a Campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n asset_set_resource_name; the resource name for an asset set.\n customer_id: a client customer ID.\n campaign_id: the ID for a campaign of a type that supports dynamic\n remarketing, such as Display.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n # Creates an operation to add the campaign asset set.\n operation: CampaignAssetSetOperation = client.get_type(\n \"CampaignAssetSetOperation\"\n )\n campaign_asset_set: CampaignAssetSet = operation.create\n campaign_asset_set.campaign = googleads_service.campaign_path(\n customer_id, campaign_id\n )\n campaign_asset_set.asset_set = asset_set_resource_name\n\n campaign_asset_set_service: CampaignAssetSetServiceClient = (\n client.get_service(\"CampaignAssetSetService\")\n )\n response: MutateCampaignAssetSetsResponse = (\n campaign_asset_set_service.mutate_campaign_asset_sets(\n customer_id=customer_id, operations=[operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n print(f\"Created a campaign asset set with resource name '{resource_name}'\")add_dynamic_remarketing_asset.py\n```\n\nExample:\n```text\ndef link_asset_set_to_campaign(client, asset_set_resource_name, customer_id, campaign_id)\n # Creates a CampaignAssetSet representing the link between an AssetSet and a Campaign.\n\n # Creates an operation to add the campaign asset set.\n operation = client.operation.create_resource.campaign_asset_set do |cas|\n cas.campaign = client.path.campaign(customer_id, campaign_id)\n cas.asset_set = asset_set_resource_name\n end\n\n # Issues the mutate request.\n response = client.service.campaign_asset_set.mutate_campaign_asset_sets(\n customer_id: customer_id,\n operations: [operation],\n )\n resource_name = response.results.first.resource_name\n puts \"Created a campaign asset set with resource name '#{resource_name}'\"\nendadd_dynamic_remarketing_asset.rb\n```\n\nExample:\n```text\n# Create a CampaignAssetSet representing the link between an AssetSet and a Campaign.\nmy $campaign_asset_set =\n Google::Ads::GoogleAds::V25::Resources::CampaignAssetSet->new({\n campaign => Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $campaign_id\n ),\n assetSet => $asset_set_resource_name\n });\n\n# Create an operation to add the CampaignAssetSet.\nmy $operation =\n Google::Ads::GoogleAds::V25::Services::CampaignAssetSetService::CampaignAssetSetOperation\n ->new({\n create => $campaign_asset_set\n });\n\n# Issue the mutate request.\nmy $response = $api_client->CampaignAssetSetService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n\n# Print some information about the response.\nmy $resource_name = $response->{results}[0]{resourceName};\nprintf \"Created a CampaignAssetSet with resource name '%s'.\\n\",\n $resource_name;add_dynamic_remarketing_asset.pl\n```\n\nExample:\n```text\nprivate void attachUserList(\n GoogleAdsClient googleAdsClient,\n long customerId,\n String adGroupResourceName,\n long userListId) {\n String userListResourceName = ResourceNames.userList(customerId, userListId);\n // Creates the ad group criterion that targets the user list.\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n .setAdGroup(adGroupResourceName)\n .setUserList(UserListInfo.newBuilder().setUserList(userListResourceName).build())\n .build();\n\n // Creates the ad group criterion operation.\n AdGroupCriterionOperation operation =\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterion).build();\n\n // Creates the ad group criterion service client.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Adds the ad group criterion.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n System.out.printf(\n \"Created ad group criterion with resource name '%s'.%n\",\n response.getResults(0).getResourceName());\n }\n}AddMerchantCenterDynamicRemarketingCampaign.java\n```\n\nExample:\n```text\nprivate void AttachUserList(GoogleAdsClient client, long customerId,\n string adGroupResourceName, long userListId)\n{\n // Creates the ad group criterion service client.\n AdGroupCriterionServiceClient adGroupCriterionServiceClient = client.GetService\n (Services.V25.AdGroupCriterionService);\n\n string userListResourceName = ResourceNames.UserList(customerId, userListId);\n\n // Creates the ad group criterion that targets the user list.\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion()\n {\n AdGroup = adGroupResourceName,\n UserList = new UserListInfo()\n {\n UserList = userListResourceName\n }\n };\n\n // Creates the ad group criterion operation.\n AdGroupCriterionOperation operation = new AdGroupCriterionOperation()\n {\n Create = adGroupCriterion\n };\n\n // Adds the ad group criterion.\n MutateAdGroupCriteriaResponse response = adGroupCriterionServiceClient\n .MutateAdGroupCriteria(customerId.ToString(), new[] { operation });\n Console.WriteLine(\"Created ad group criterion with resource name \" +\n $\"'{response.Results.First().ResourceName}'.\");\n}AddMerchantCenterDynamicRemarketingCampaign.cs\n```\n\nExample:\n```text\nprivate static function attachUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $adGroupResourceName,\n int $userListId\n) {\n // Creates the ad group criterion that targets the user list.\n $adGroupCriterion = new AdGroupCriterion([\n 'ad_group' => $adGroupResourceName,\n 'user_list' => new UserListInfo([\n 'user_list' => ResourceNames::forUserList($customerId, $userListId)\n ])\n ]);\n\n // Creates an ad group criterion operation.\n $adGroupCriterionOperation = new AdGroupCriterionOperation();\n $adGroupCriterionOperation->setCreate($adGroupCriterion);\n\n // Issues a mutate request to add the ad group criterion.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$adGroupCriterionOperation])\n );\n\n /** @var AdGroupCriterion $addedAdGroupCriterion */\n $addedAdGroupCriterion = $response->getResults()[0];\n printf(\n \"Created ad group criterion with resource name '%s'.%s\",\n $addedAdGroupCriterion->getResourceName(),\n PHP_EOL\n );\n}AddMerchantCenterDynamicRemarketingCampaign.php\n```\n\nExample:\n```text\ndef attach_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_resource_name: str,\n user_list_id: int,\n) -> None:\n \"\"\"Targets a user list with an ad group.\n\n Args:\n client: An initialized GoogleAds client.\n customer_id: The Google Ads customer ID.\n ad_group_resource_name: The resource name of the target ad group.\n user_list_id: The ID of the user list to target for remarketing.\n \"\"\"\n # Get the AdGroupCriterionService client.\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n\n # Create an ad group criterion operation and set the ad group criterion\n # values.\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n ad_group_criterion: AdGroupCriterion = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = ad_group_resource_name\n ad_group_criterion.user_list.user_list = client.get_service(\n \"UserListService\"\n ).user_list_path(customer_id, str(user_list_id))\n\n # Issue a mutate request to add the ad group criterion.\n ad_group_criterion_response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n )\n print(\n \"Created ad group criterion with resource name \"\n f\"'{ad_group_criterion_response.results[0].resource_name}'.\"\n )add_merchant_center_dynamic_remarketing_campaign.py\n```\n\nExample:\n```text\ndef attach_user_list(client, customer_id, ad_group_resource_name, user_list_id)\n user_list_resource_name = client.path.user_list(customer_id, user_list_id)\n\n # Creates the ad group criterion that targets the user list.\n ad_group_criterion = client.resource.ad_group_criterion do |agc|\n agc.ad_group = ad_group_resource_name\n agc.user_list = client.resource.user_list_info do |ul|\n ul.user_list = user_list_resource_name\n end\n end\n\n # Creates the ad group criterion operation.\n op = client.operation.create_resource.ad_group_criterion(ad_group_criterion)\n\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [op]\n )\n\n puts \"Created ad group criterion: #{response.results.first.resource_name}\"\nendadd_merchant_center_dynamic_remarketing_campaign.rb\n```\n\nExample:\n```text\nsub attach_user_list {\n my ($api_client, $customer_id, $ad_group_resource_name, $user_list_id) = @_;\n\n # Create the ad group criterion that targets the user list.\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n adGroup => $ad_group_resource_name,\n userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({\n userList =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::user_list(\n $customer_id, $user_list_id\n )})});\n\n # Create an ad group criterion operation.\n my $ad_group_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({create => $ad_group_criterion});\n\n # Issue a mutate request to add the ad group criterion.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n printf \"Created ad group criterion with resource name '%s'.\\n\",\n $ad_group_criteria_response->{results}[0]{resourceName};\n}add_merchant_center_dynamic_remarketing_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.535Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":30,"totalLines":1028,"estimatedTokens":9775}}211{"id":"doc-example_creating_a_dynamic_remarketing_campaign_-82af9086","source":"documentation","title":"Example: Creating a dynamic remarketing campaign with Google Merchant Center | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/dynamic-remarketing/merchant-center-example","text":"Example:\n```text\nprivate String createCampaign(\n GoogleAdsClient googleAdsClient,\n long customerId,\n long merchantCenterAccountId,\n long campaignBudgetId) {\n String budgetResourceName = ResourceNames.campaignBudget(customerId, campaignBudgetId);\n\n // Creates the campaign.\n Campaign campaign =\n Campaign.newBuilder()\n .setName(\"Shopping campaign #\" + getPrintableDateTime())\n // Dynamic remarketing campaigns are only available on the Google Display Network.\n .setAdvertisingChannelType(AdvertisingChannelType.DISPLAY)\n .setStatus(CampaignStatus.PAUSED)\n .setCampaignBudget(budgetResourceName)\n .setManualCpc(ManualCpc.newBuilder().build())\n // The settings for the shopping campaign.\n // This connects the campaign to the merchant center account.\n .setShoppingSetting(\n ShoppingSetting.newBuilder()\n .setCampaignPriority(0)\n .setMerchantId(merchantCenterAccountId)\n .setEnableLocal(true)\n .build())\n // Declares whether this campaign serves political ads targeting the EU.\n .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)\n .build();\n\n // Creates the campaign operation.\n CampaignOperation operation = CampaignOperation.newBuilder().setCreate(campaign).build();\n\n // Creates the campaign service client.\n try (CampaignServiceClient campaignServiceClient =\n googleAdsClient.getLatestVersion().createCampaignServiceClient()) {\n // Adds the campaign.\n MutateCampaignsResponse response =\n campaignServiceClient.mutateCampaigns(\n Long.toString(customerId), ImmutableList.of(operation));\n String campaignResourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Created campaign with resource name '%s'.%n\", campaignResourceName);\n return campaignResourceName;\n }\n}\nAddMerchantCenterDynamicRemarketingCampaign.java\n```\n\nExample:\n```text\nprivate string CreateCampaign(GoogleAdsClient client, long customerId,\n long merchantCenterAccountId, long campaignBudgetId)\n{\n // Creates the Campaign Service client.\n CampaignServiceClient campaignServiceClient =\n client.GetService(Services.V25.CampaignService);\n\n string budgetResourceName = ResourceNames.CampaignBudget(customerId, campaignBudgetId);\n\n // Creates the campaign.\n Campaign campaign = new Campaign()\n {\n Name = \"Shopping campaign #\" + ExampleUtilities.GetRandomString(),\n // Dynamic remarketing campaigns are only available on the Google Display Network.\n AdvertisingChannelType = AdvertisingChannelType.Display,\n Status = CampaignStatus.Paused,\n CampaignBudget = budgetResourceName,\n ManualCpc = new ManualCpc(),\n // The settings for the shopping campaign.\n // This connects the campaign to the Merchant Center account.\n ShoppingSetting = new Campaign.Types.ShoppingSetting()\n {\n CampaignPriority = 0,\n MerchantId = merchantCenterAccountId,\n EnableLocal = true\n },\n\n // Declare whether or not this campaign contains political ads targeting the EU.\n ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,\n };\n\n // Creates the campaign operation.\n CampaignOperation operation = new CampaignOperation()\n {\n Create = campaign\n };\n\n // Adds the campaign.\n MutateCampaignsResponse response = campaignServiceClient.MutateCampaigns(customerId\n .ToString(), new[] { operation });\n string campaignResourceName = response.Results.First().ResourceName;\n Console.WriteLine($\"Created campaign with resource name '{campaignResourceName}'.\");\n return campaignResourceName;\n}AddMerchantCenterDynamicRemarketingCampaign.cs\n```\n\nExample:\n```text\nprivate static function createCampaign(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $merchantCenterAccountId,\n int $campaignBudgetId\n): string {\n // Configures the settings for the shopping campaign.\n $shoppingSettings = new ShoppingSetting([\n 'campaign_priority' => 0,\n 'merchant_id' => $merchantCenterAccountId,\n 'enable_local' => true\n ]);\n\n // Creates the campaign.\n $campaign = new Campaign([\n 'name' => 'Shopping campaign #' . Helper::getPrintableDatetime(),\n // Dynamic remarketing campaigns are only available on the Google Display Network.\n 'advertising_channel_type' => AdvertisingChannelType::DISPLAY,\n 'status' => CampaignStatus::PAUSED,\n 'campaign_budget' => ResourceNames::forCampaignBudget($customerId, $campaignBudgetId),\n 'manual_cpc' => new ManualCpc(),\n // Declare whether or not this campaign serves political ads targeting the EU.\n 'contains_eu_political_advertising' =>\n EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n // This connects the campaign to the merchant center account.\n 'shopping_setting' => $shoppingSettings\n ]);\n\n // Creates a campaign operation.\n $campaignOperation = new CampaignOperation();\n $campaignOperation->setCreate($campaign);\n\n // Issues a mutate request to add the campaign.\n $campaignServiceClient = $googleAdsClient->getCampaignServiceClient();\n $response = $campaignServiceClient->mutateCampaigns(\n MutateCampaignsRequest::build($customerId, [$campaignOperation])\n );\n\n /** @var Campaign $addedCampaign */\n $addedCampaign = $response->getResults()[0];\n $addedCampaignResourceName = $addedCampaign->getResourceName();\n printf(\"Created campaign with resource name '%s'.%s\", $addedCampaignResourceName, PHP_EOL);\n\n return $addedCampaignResourceName;\n}AddMerchantCenterDynamicRemarketingCampaign.php\n```\n\nExample:\n```text\ndef create_campaign(\n client: GoogleAdsClient,\n customer_id: str,\n merchant_center_account_id: int,\n campaign_budget_id: int,\n) -> str:\n \"\"\"Creates a campaign linked to a Merchant Center product feed.\n\n Args:\n client: An initialized GoogleAds client.\n customer_id: The Google Ads customer ID.\n merchant_center_account_id: The target Merchant Center account ID.\n campaign_budget_id: The ID of the campaign budget to utilize.\n Returns:\n The string resource name of the newly created campaign.\n \"\"\"\n # Gets the CampaignService client.\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n # Creates a campaign operation and configures the new campaign.\n campaign_operation: CampaignOperation = client.get_type(\"CampaignOperation\")\n campaign: Campaign = campaign_operation.create\n campaign.name = f\"Shopping campaign #{uuid4()}\"\n # Configures the settings for the shopping campaign.\n campaign.shopping_setting.campaign_priority = 0\n # This connects the campaign to the Merchant Center account.\n campaign.shopping_setting.merchant_id = merchant_center_account_id\n campaign.shopping_setting.enable_local = True\n # Dynamic remarketing campaigns are only available on the Google Display\n # Network.\n campaign.advertising_channel_type = (\n client.enums.AdvertisingChannelTypeEnum.DISPLAY\n )\n campaign.status = client.enums.CampaignStatusEnum.PAUSED\n campaign.campaign_budget = client.get_service(\n \"CampaignBudgetService\"\n ).campaign_budget_path(customer_id, str(campaign_budget_id))\n client.copy_from(campaign.manual_cpc, client.get_type(\"ManualCpc\"))\n\n # Declare whether or not this campaign serves political ads targeting the\n # EU. Valid values are:\n # CONTAINS_EU_POLITICAL_ADVERTISING\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n campaign.contains_eu_political_advertising = (\n client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n )\n\n # Issues a mutate request to add the campaign.\n campaign_response: MutateCampaignsResponse = (\n campaign_service.mutate_campaigns(\n customer_id=customer_id, operations=[campaign_operation]\n )\n )\n campaign_resource_name: str = campaign_response.results[0].resource_name\n print(f\"Created campaign with resource name '{campaign_resource_name}'.\")\n\n return campaign_resource_nameadd_merchant_center_dynamic_remarketing_campaign.py\n```\n\nExample:\n```text\ndef create_campaign(client, customer_id, merchant_center_id, campaign_budget_id)\n operation = client.operation.create_resource.campaign do |c|\n c.name = \"Shopping campaign ##{(Time.new.to_f * 1000).to_i}\"\n\n # Dynamic remarketing campaigns are only available on the Google Display\n # Network.\n c.advertising_channel_type = :DISPLAY\n c.status = :PAUSED\n c.campaign_budget = client.path.campaign_budget(customer_id,\n campaign_budget_id)\n c.manual_cpc = client.resource.manual_cpc\n\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n\n # The settings for the shopping campaign.\n # This connects the campaign to the merchant center account.\n c.shopping_setting = client.resource.shopping_setting do |ss|\n ss.campaign_priority = 0\n ss.merchant_id = merchant_center_id.to_i\n ss.enable_local = true\n end\n end\n\n response = client.service.campaign.mutate_campaigns(\n customer_id: customer_id,\n operations: [operation]\n )\n\n puts \"Created campaign: #{response.results.first.resource_name}\"\n response.results.first.resource_name\nendadd_merchant_center_dynamic_remarketing_campaign.rb\n```\n\nExample:\n```text\nsub create_campaign {\n my ($api_client, $customer_id, $merchant_center_account_id,\n $campaign_budget_id)\n = @_;\n\n # Configure the settings for the shopping campaign.\n my $shopping_settings =\n Google::Ads::GoogleAds::V25::Resources::ShoppingSetting->new({\n campaignPriority => 0,\n merchantId => $merchant_center_account_id,\n enableLocal => \"true\"\n });\n\n # Create the campaign.\n my $campaign = Google::Ads::GoogleAds::V25::Resources::Campaign->new({\n name => \"Shopping campaign #\" . uniqid(),\n # Dynamic remarketing campaigns are only available on the Google Display Network.\n advertisingChannelType => DISPLAY,\n status => Google::Ads::GoogleAds::V25::Enums::CampaignStatusEnum::PAUSED,\n campaignBudget =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign_budget(\n $customer_id, $campaign_budget_id\n ),\n manualCpc => Google::Ads::GoogleAds::V25::Common::ManualCpc->new(),\n # This connects the campaign to the Merchant Center account.\n shoppingSetting => $shopping_settings,\n # Declare whether or not this campaign serves political ads targeting the EU.\n # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and\n # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.\n containsEuPoliticalAdvertising =>\n DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING\n });\n\n # Create a campaign operation.\n my $campaign_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignService::CampaignOperation->\n new({create => $campaign});\n\n # Issue a mutate request to add the campaign.\n my $campaigns_response = $api_client->CampaignService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_operation]});\n\n my $campaign_resource_name = $campaigns_response->{results}[0]{resourceName};\n printf \"Created campaign with resource name '%s'.\\n\", $campaign_resource_name;\n\n return $campaign_resource_name;\n}add_merchant_center_dynamic_remarketing_campaign.pl\n```\n\nExample:\n```text\nprivate String createAdGroup(\n GoogleAdsClient googleAdsClient, long customerId, String campaignResourceName) {\n // Creates the ad group.\n AdGroup adGroup =\n AdGroup.newBuilder()\n .setName(\"Dynamic remarketing ad group\")\n .setCampaign(campaignResourceName)\n .setStatus(AdGroupStatus.ENABLED)\n .build();\n\n // Creates the ad group operation.\n AdGroupOperation operation = AdGroupOperation.newBuilder().setCreate(adGroup).build();\n\n // Creates the ad group service client.\n try (AdGroupServiceClient adGroupServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupServiceClient()) {\n // Adds the ad group.\n MutateAdGroupsResponse response =\n adGroupServiceClient.mutateAdGroups(\n Long.toString(customerId), ImmutableList.of(operation));\n String adGroupResourceName = response.getResults(0).getResourceName();\n System.out.printf(\"Created ad group with resource name '%s'.%n\", adGroupResourceName);\n return adGroupResourceName;\n }\n}\nAddMerchantCenterDynamicRemarketingCampaign.java\n```\n\nExample:\n```text\nprivate string CreateAdGroup(GoogleAdsClient client, long customerId,\n string campaignResourceName)\n{\n // Creates the ad group service client.\n AdGroupServiceClient adGroupServiceClient =\n client.GetService(Services.V25.AdGroupService);\n\n // Creates the ad group.\n AdGroup adGroup = new AdGroup()\n {\n Name = \"Dynamic remarketing ad group\",\n Campaign = campaignResourceName,\n Status = AdGroupStatus.Enabled\n };\n\n // Creates the ad group operation.\n AdGroupOperation operation = new AdGroupOperation()\n {\n Create = adGroup\n };\n\n // Adds the ad group.\n MutateAdGroupsResponse response = adGroupServiceClient.MutateAdGroups(\n customerId.ToString(), new[] { operation });\n\n string adGroupResourceName = response.Results.First().ResourceName;\n Console.WriteLine($\"Created ad group with resource name '{adGroupResourceName}'.\");\n return adGroupResourceName;\n}AddMerchantCenterDynamicRemarketingCampaign.cs\n```\n\nExample:\n```text\nprivate static function createAdGroup(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $campaignResourceName\n): string {\n // Creates the ad group.\n $adGroup = new AdGroup([\n 'name' => 'Dynamic remarketing ad group',\n 'campaign' => $campaignResourceName,\n 'status' => AdGroupStatus::ENABLED\n ]);\n\n // Creates an ad group operation.\n $adGroupOperation = new AdGroupOperation();\n $adGroupOperation->setCreate($adGroup);\n\n // Issues a mutate request to add the ad group.\n $adGroupServiceClient = $googleAdsClient->getAdGroupServiceClient();\n $response = $adGroupServiceClient->mutateAdGroups(\n MutateAdGroupsRequest::build($customerId, [$adGroupOperation])\n );\n\n /** @var AdGroup $addedAdGroup */\n $addedAdGroup = $response->getResults()[0];\n $addedAdGroupResourceName = $addedAdGroup->getResourceName();\n printf(\"Created ad group with resource name '%s'.%s\", $addedAdGroupResourceName, PHP_EOL);\n\n return $addedAdGroupResourceName;\n}AddMerchantCenterDynamicRemarketingCampaign.php\n```\n\nExample:\n```text\ndef create_ad_group(\n client: GoogleAdsClient, customer_id: str, campaign_resource_name: str\n) -> str:\n \"\"\"Creates an ad group for the remarketing campaign.\n\n Args:\n client: An initialized GoogleAds client.\n customer_id: The Google Ads customer ID.\n campaign_resource_name: The resource name of the target campaign.\n Returns:\n The string resource name of the newly created ad group.\n \"\"\"\n # Gets the AdGroupService.\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n\n # Creates an ad group operation and configures the new ad group.\n ad_group_operation: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group: AdGroup = ad_group_operation.create\n ad_group.name = \"Dynamic remarketing ad group\"\n ad_group.campaign = campaign_resource_name\n ad_group.status = client.enums.AdGroupStatusEnum.ENABLED\n\n # Issues a mutate request to add the ad group.\n ad_group_response: MutateAdGroupsResponse = (\n ad_group_service.mutate_ad_groups(\n customer_id=customer_id, operations=[ad_group_operation]\n )\n )\n ad_group_resource_name: str = ad_group_response.results[0].resource_name\n\n return ad_group_resource_nameadd_merchant_center_dynamic_remarketing_campaign.py\n```\n\nExample:\n```text\ndef create_ad_group(client, customer_id, campaign_resource_name)\n # Creates the ad group.\n ad_group = client.resource.ad_group do |ag|\n ag.name = \"Dynamic remarketing ad group #{(Time.now.to_f * 1000).to_i}\"\n ag.campaign = campaign_resource_name\n ag.status = :ENABLED\n end\n\n # Creates the ad group operation.\n operation = client.operation.create_resource.ad_group(ad_group)\n response = client.service.ad_group.mutate_ad_groups(\n customer_id: customer_id,\n operations: [operation]\n )\n\n puts \"Created ad group: #{response.results.first.resource_name}\"\n response.results.first.resource_name\nendadd_merchant_center_dynamic_remarketing_campaign.rb\n```\n\nExample:\n```text\nsub create_ad_group {\n my ($api_client, $customer_id, $campaign_resource_name) = @_;\n\n # Create the ad group.\n my $ad_group = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Dynamic remarketing ad group\",\n campaign => $campaign_resource_name,\n status => Google::Ads::GoogleAds::V25::Enums::AdGroupStatusEnum::ENABLED\n });\n\n # Create an ad group operation.\n my $ad_group_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group});\n\n # Issue a mutate request to add the ad group.\n my $ad_groups_response = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_operation]});\n\n my $ad_group_resource_name = $ad_groups_response->{results}[0]{resourceName};\n printf \"Created ad group with resource name '%s'.\\n\", $ad_group_resource_name;\n\n return $ad_group_resource_name;\n}add_merchant_center_dynamic_remarketing_campaign.pl\n```\n\nExample:\n```text\nprivate void createAd(\n GoogleAdsClient googleAdsClient, long customerId, String adGroupResourceName)\n throws IOException {\n String marketingImageUrl = \"https://gaagl.page.link/Eit5\";\n String marketingImageName = \"Marketing Image\";\n String marketingImageResourceName =\n uploadAsset(googleAdsClient, customerId, marketingImageUrl, marketingImageName);\n String squareMarketingImageName = \"Square Marketing Image\";\n String squareMarketingImageUrl = \"https://gaagl.page.link/bjYi\";\n String squareMarketingImageResourceName =\n uploadAsset(googleAdsClient, customerId, squareMarketingImageUrl, squareMarketingImageName);\n\n // Creates the responsive display ad info object.\n ResponsiveDisplayAdInfo responsiveDisplayAdInfo =\n ResponsiveDisplayAdInfo.newBuilder()\n .addMarketingImages(\n AdImageAsset.newBuilder().setAsset(marketingImageResourceName).build())\n .addSquareMarketingImages(\n AdImageAsset.newBuilder().setAsset(squareMarketingImageResourceName).build())\n .addHeadlines(AdTextAsset.newBuilder().setText(\"Travel\").build())\n .setLongHeadline(AdTextAsset.newBuilder().setText(\"Travel the World\").build())\n .addDescriptions(AdTextAsset.newBuilder().setText(\"Take to the air!\").build())\n .setBusinessName(\"Interplanetary Cruises\")\n // Optional: Call to action text.\n // Valid texts: https://support.google.com/adwords/answer/7005917\n .setCallToActionText(\"Apply Now\")\n // Optional: Sets the ad colors.\n .setMainColor(\"#0000ff\")\n .setAccentColor(\"#ffff00\")\n // Optional: Sets to false to strictly render the ad using the colors.\n .setAllowFlexibleColor(false)\n // Optional: Sets the format setting that the ad will be served in.\n .setFormatSetting(DisplayAdFormatSetting.NON_NATIVE)\n // Optional: Creates a logo image and sets it to the ad.\n /*\n .addLogoImages(\n AdImageAsset.newBuilder()\n .setAsset(StringValue.of(\"INSERT_LOGO_IMAGE_RESOURCE_NAME_HERE\"))\n .build())\n */\n // Optional: Creates a square logo image and sets it to the ad.\n /*\n .addSquareLogoImages(\n AdImageAsset.newBuilder()\n .setAsset(StringValue.of(\"INSERT_SQUARE_LOGO_IMAGE_RESOURCE_NAME_HERE\"))\n .build())\n */\n .build();\n\n // Creates the ad.\n Ad ad =\n Ad.newBuilder()\n .setResponsiveDisplayAd(responsiveDisplayAdInfo)\n .addFinalUrls(\"http://www.example.com/\")\n .build();\n\n // Creates the ad group ad.\n AdGroupAd adGroupAd = AdGroupAd.newBuilder().setAdGroup(adGroupResourceName).setAd(ad).build();\n\n // Creates the ad group ad operation.\n AdGroupAdOperation operation = AdGroupAdOperation.newBuilder().setCreate(adGroupAd).build();\n\n // Creates the ad group ad service client.\n try (AdGroupAdServiceClient adGroupAdServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupAdServiceClient()) {\n // Adds the ad group ad.\n MutateAdGroupAdsResponse response =\n adGroupAdServiceClient.mutateAdGroupAds(\n Long.toString(customerId), ImmutableList.of(operation));\n System.out.printf(\n \"Created ad group ad with resource name '%s'.%n\",\n response.getResults(0).getResourceName());\n }\n}\nAddMerchantCenterDynamicRemarketingCampaign.java\n```\n\nExample:\n```text\nprivate void CreateAd(GoogleAdsClient client, long customerId, string adGroupResourceName)\n{\n // Creates the ad group ad service client.\n AdGroupAdServiceClient adGroupAdServiceClient =\n client.GetService(Services.V25.AdGroupAdService);\n\n string marketingImageUrl = \"https://gaagl.page.link/Eit5\";\n string marketingImageName = \"Marketing Image\";\n string marketingImageResourceName =\n UploadAsset(client, customerId, marketingImageUrl, marketingImageName);\n string squareMarketingImageName = \"Square Marketing Image\";\n string squareMarketingImageUrl = \"https://gaagl.page.link/bjYi\";\n string squareMarketingImageResourceName =\n UploadAsset(client, customerId, squareMarketingImageUrl, squareMarketingImageName);\n\n // Creates the responsive display ad info object.\n ResponsiveDisplayAdInfo responsiveDisplayAdInfo = new ResponsiveDisplayAdInfo()\n {\n MarketingImages =\n {\n new AdImageAsset()\n {\n Asset = marketingImageResourceName\n }\n },\n SquareMarketingImages =\n {\n new AdImageAsset()\n {\n Asset = squareMarketingImageResourceName\n }\n },\n Headlines =\n {\n new AdTextAsset()\n {\n Text = \"Travel\"\n }\n },\n LongHeadline = new AdTextAsset()\n {\n Text = \"Travel the World\"\n },\n Descriptions =\n {\n new AdTextAsset()\n {\n Text = \"Take to the air!\"\n }\n },\n BusinessName = \"Interplanetary Cruises\",\n // Optional: Call to action text.\n // Valid texts: https://support.google.com/adwords/answer/7005917\n CallToActionText = \"Apply Now\",\n // Optional: Sets the ad colors.\n MainColor = \"#0000ff\",\n AccentColor = \"#ffff00\",\n // Optional: Sets to false to strictly render the ad using the colors.\n AllowFlexibleColor = false,\n // Optional: Sets the format setting that the ad will be served in.\n FormatSetting = DisplayAdFormatSetting.NonNative,\n // Optional: Creates a logo image and sets it to the ad.\n /*\n LogoImages = { new AdImageAsset()\n {\n Asset = \"INSERT_LOGO_IMAGE_RESOURCE_NAME_HERE\"\n }}\n */\n // Optional: Creates a square logo image and sets it to the ad.\n /*\n SquareLogoImages = { new AdImageAsset()\n {\n Asset = \"INSERT_SQUARE_LOGO_IMAGE_RESOURCE_NAME_HERE\"\n }}\n */\n };\n\n // Creates the ad.\n Ad ad = new Ad()\n {\n ResponsiveDisplayAd = responsiveDisplayAdInfo,\n FinalUrls = { \"http://www.example.com/\" }\n };\n\n // Creates the ad group ad.\n AdGroupAd adGroupAd = new AdGroupAd()\n {\n AdGroup = adGroupResourceName,\n Ad = ad\n };\n\n // Creates the ad group ad operation.\n AdGroupAdOperation operation = new AdGroupAdOperation()\n {\n Create = adGroupAd\n };\n\n // Adds the ad group ad.\n MutateAdGroupAdsResponse response = adGroupAdServiceClient.MutateAdGroupAds\n (customerId.ToString(), new[] { operation });\n Console.WriteLine(\"Created ad group ad with resource name \" +\n $\"'{response.Results.First().ResourceName}'.\");\n}AddMerchantCenterDynamicRemarketingCampaign.cs\n```\n\nExample:\n```text\nprivate static function createAd(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $adGroupResourceName\n) {\n $marketingImageResourceName = self::uploadAsset(\n $googleAdsClient,\n $customerId,\n 'https://gaagl.page.link/Eit5',\n 'Marketing Image'\n );\n $squareMarketingImageResourceName = self::uploadAsset(\n $googleAdsClient,\n $customerId,\n 'https://gaagl.page.link/bjYi',\n 'Square Marketing Image'\n );\n\n // Creates the responsive display ad info object.\n $responsiveDisplayAdInfo = new ResponsiveDisplayAdInfo([\n 'marketing_images' => [new AdImageAsset(['asset' => $marketingImageResourceName])],\n 'square_marketing_images' => [new AdImageAsset([\n 'asset' => $squareMarketingImageResourceName\n ])],\n 'headlines' => [new AdTextAsset(['text' => 'Travel'])],\n 'long_headline' => new AdTextAsset(['text' => 'Travel the World']),\n 'descriptions' => [new AdTextAsset(['text' => 'Take to the air!'])],\n 'business_name' => 'Interplanetary Cruises',\n // Optional: Call to action text.\n // Valid texts: https://support.google.com/google-ads/answer/7005917\n 'call_to_action_text' => 'Apply Now',\n // Optional: Sets the ad colors.\n 'main_color' => '#0000ff',\n 'accent_color' => '#ffff00',\n // Optional: Sets to false to strictly render the ad using the colors.\n 'allow_flexible_color' => false,\n // Optional: Sets the format setting that the ad will be served in.\n 'format_setting' => DisplayAdFormatSetting::NON_NATIVE\n // Optional: Creates a logo image and sets it to the ad.\n // 'logo_images' => [new AdImageAsset([\n // 'asset' => 'INSERT_LOGO_IMAGE_RESOURCE_NAME_HERE'\n // ])],\n // Optional: Creates a square logo image and sets it to the ad.\n // 'square_logo_images' => [new AdImageAsset([\n // 'asset' => 'INSERT_SQUARE_LOGO_IMAGE_RESOURCE_NAME_HERE'\n // ])]\n ]);\n\n // Creates a new ad group ad.\n $adGroupAd = new AdGroupAd([\n 'ad' => new Ad([\n 'responsive_display_ad' => $responsiveDisplayAdInfo,\n 'final_urls' => ['http://www.example.com/']\n ]),\n 'ad_group' => $adGroupResourceName\n ]);\n\n // Creates an ad group ad operation.\n $adGroupAdOperation = new AdGroupAdOperation();\n $adGroupAdOperation->setCreate($adGroupAd);\n\n // Issues a mutate request to add the ad group ad.\n $adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();\n $response = $adGroupAdServiceClient->mutateAdGroupAds(\n MutateAdGroupAdsRequest::build($customerId, [$adGroupAdOperation])\n );\n\n /** @var AdGroupAd $addedAdGroupAd */\n $addedAdGroupAd = $response->getResults()[0];\n printf(\n \"Created ad group ad with resource name '%s'.%s\",\n $addedAdGroupAd->getResourceName(),\n PHP_EOL\n );\n}AddMerchantCenterDynamicRemarketingCampaign.php\n```\n\nExample:\n```text\ndef create_ad(\n client: GoogleAdsClient, customer_id: str, ad_group_resource_name: str\n) -> None:\n \"\"\"Creates the responsive display ad.\n\n Args:\n client: An initialized GoogleAds client.\n customer_id: The Google Ads customer ID.\n ad_group_resource_name: The resource name of the target ad group.\n \"\"\"\n # Get the AdGroupAdService client.\n ad_group_ad_service: AdGroupAdServiceClient = client.get_service(\n \"AdGroupAdService\"\n )\n\n # Upload image assets for the ad.\n marketing_image_resource_name: str = upload_image_asset(\n client, customer_id, \"https://gaagl.page.link/Eit5\", \"Marketing Image\"\n )\n square_marketing_image_resource_name: str = upload_image_asset(\n client,\n customer_id,\n \"https://gaagl.page.link/bjYi\",\n \"Square Marketing Image\",\n )\n\n # Create the relevant asset objects for the ad.\n marketing_image: AdImageAsset = client.get_type(\"AdImageAsset\")\n marketing_image.asset = marketing_image_resource_name\n square_marketing_image: AdImageAsset = client.get_type(\"AdImageAsset\")\n square_marketing_image.asset = square_marketing_image_resource_name\n headline: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline.text = \"Travel\"\n description: AdTextAsset = client.get_type(\"AdTextAsset\")\n description.text = \"Take to the air!\"\n\n # Create an ad group ad operation and set the ad group ad values.\n ad_group_ad_operation: AdGroupAdOperation = client.get_type(\n \"AdGroupAdOperation\"\n )\n ad_group_ad: AdGroupAd = ad_group_ad_operation.create\n ad_group_ad.ad_group = ad_group_resource_name\n ad_group_ad.ad.final_urls.append(\"http://www.example.com/\")\n\n # Configure the responsive display ad info object.\n responsive_display_ad_info: ResponsiveDisplayAdInfo = (\n ad_group_ad.ad.responsive_display_ad\n )\n responsive_display_ad_info.marketing_images.append(marketing_image)\n responsive_display_ad_info.square_marketing_images.append(\n square_marketing_image\n )\n responsive_display_ad_info.headlines.append(headline)\n responsive_display_ad_info.long_headline.text = \"Travel the World\"\n responsive_display_ad_info.descriptions.append(description)\n responsive_display_ad_info.business_name = \"Interplanetary Cruises\"\n # Optional: Call to action text.\n # Valid texts: https://support.google.com/google-ads/answer/7005917\n responsive_display_ad_info.call_to_action_text = \"Apply Now\"\n # Optional: Set the ad colors.\n responsive_display_ad_info.main_color = \"#0000ff\"\n responsive_display_ad_info.accent_color = \"#ffff00\"\n # Optional: Set to false to strictly render the ad using the colors.\n responsive_display_ad_info.allow_flexible_color = False\n # Optional: Set the format setting that the ad will be served in.\n responsive_display_ad_info.format_setting = (\n client.enums.DisplayAdFormatSettingEnum.NON_NATIVE\n )\n # Optional: Create a logo image and set it to the ad.\n # logo_image = client.get_type(\"AdImageAsset\")\n # logo_image.asset = \"INSERT_LOGO_IMAGE_RESOURCE_NAME_HERE\"\n # responsive_display_ad_info.logo_images.append(logo_image)\n # Optional: Create a square logo image and set it to the ad.\n # square_logo_image = client.get_type(\"AdImageAsset\")\n # square_logo_image.asset = \"INSERT_SQUARE_LOGO_IMAGE_RESOURCE_NAME_HERE\"\n # responsive_display_ad_info.square_logo_images.append(square_logo_image)\n\n # Issue a mutate request to add the ad group ad.\n ad_group_ad_response: MutateAdGroupAdsResponse = (\n ad_group_ad_service.mutate_ad_group_ads(\n customer_id=customer_id, operations=[ad_group_ad_operation]\n )\n )\n print(\n \"Created ad group ad with resource name \"\n f\"'{ad_group_ad_response.results[0].resource_name}'.\"\n )add_merchant_center_dynamic_remarketing_campaign.py\n```\n\nExample:\n```text\ndef create_ad(client, customer_id, ad_group_resource_name)\n marketing_image_url = \"https://gaagl.page.link/Eit5\"\n square_marketing_image_url = \"https://gaagl.page.link/bjYi\"\n marketing_image_asset_resource_name = upload_asset(\n client, customer_id, marketing_image_url, \"Marketing Image\"\n )\n square_marketing_image_asset_resource_name = upload_asset(\n client, customer_id, square_marketing_image_url, \"Square Marketing Image\"\n )\n\n # Creates an ad group ad operation.\n operation = client.operation.create_resource.ad_group_ad do |aga|\n aga.ad_group = ad_group_resource_name\n aga.status = :PAUSED\n aga.ad = client.resource.ad do |a|\n a.final_urls << \"https://www.example.com\"\n\n # Creates the responsive display ad info object.\n a.responsive_display_ad = client.resource.responsive_display_ad_info do |rda|\n rda.headlines << client.resource.ad_text_asset do |ata|\n ata.text = \"Travel\"\n end\n rda.long_headline = client.resource.ad_text_asset do |ata|\n ata.text = \"Travel the World\"\n end\n rda.descriptions << client.resource.ad_text_asset do |ata|\n ata.text = \"Take to the air!\"\n end\n rda.business_name = \"Interplanetary Cruises\"\n rda.marketing_images << client.resource.ad_image_asset do |aia|\n aia.asset = marketing_image_asset_resource_name\n end\n rda.square_marketing_images << client.resource.ad_image_asset do |aia|\n aia.asset = square_marketing_image_asset_resource_name\n end\n # Optional: Call to action text.\n # Valid texts: https://support.google.com/google-ads/answer/7005917\n rda.call_to_action_text = \"Apply Now\"\n # Optional: Sets the ad colors.\n rda.main_color = \"#0000ff\"\n rda.accent_color = \"#ffff00\"\n # Optional: Sets to false to strictly render the ad using the colors.\n rda.allow_flexible_color = false\n # Optional: Sets the format setting that the ad will be served in.\n rda.format_setting = :NON_NATIVE\n # Optional: Creates a logo image and sets it to the ad.\n # rda.logo_images << client.resource.ad_image_asset do |aia|\n # aia.asset = \"INSERT_LOGO_IMAGE_RESOURCE_NAME_HERE\"\n # end\n # Optional: Creates a square logo image and sets it to the ad.\n # rda.square_logo_images << client.resource.ad_image_asset do |aia|\n # aia.asset = \"INSERT_SQUARE_LOGO_IMAGE_RESOURCE_NAME_HERE\"\n # end\n end\n end\n end\n\n # Issues a mutate request to add the ad group ad.\n response = client.service.ad_group_ad.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [operation]\n )\n\n # Prints out some information about the newly created ad.\n resource_name = response.results.first.resource_name\n puts \"Created ad group ad: #{resource_name}\"\n\n resource_name\nendadd_merchant_center_dynamic_remarketing_campaign.rb\n```\n\nExample:\n```text\nsub create_ad {\n my ($api_client, $customer_id, $ad_group_resource_name) = @_;\n\n my $marketing_image_resource_name = upload_asset(\n $api_client, $customer_id,\n \"https://gaagl.page.link/Eit5\",\n \"Marketing Image\"\n );\n\n my $square_marketing_image_resource_name = upload_asset(\n $api_client, $customer_id,\n \"https://gaagl.page.link/bjYi\",\n \"Square Marketing Image\"\n );\n\n # Create the responsive display ad info object.\n my $responsive_display_ad_info =\n Google::Ads::GoogleAds::V25::Common::ResponsiveDisplayAdInfo->new({\n marketingImages => [\n Google::Ads::GoogleAds::V25::Common::AdImageAsset->new({\n asset => $marketing_image_resource_name\n })\n ],\n squareMarketingImages => [\n Google::Ads::GoogleAds::V25::Common::AdImageAsset->new({\n asset => $square_marketing_image_resource_name\n })\n ],\n headlines => [\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Travel\"\n })\n ],\n longHeadline => Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Travel the World\"\n }\n ),\n descriptions => [\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Take to the air!\"\n })\n ],\n businessName => \"Interplanetary Cruises\",\n # Optional: Call to action text.\n # Valid texts: https://support.google.com/google-ads/answer/7005917\n callToActionText => \"Apply Now\",\n # Optional: Set the ad colors.\n mainColor => \"#0000ff\",\n accentColor => \"#ffff00\",\n # Optional: Set to false to strictly render the ad using the colors.\n allowFlexibleColor => \"false\",\n # Optional: Set the format setting that the ad will be served in.\n formatSetting => NON_NATIVE,\n # Optional: Create a logo image and set it to the ad.\n # logoImages => [\n # Google::Ads::GoogleAds::V25::Common::AdImageAsset->new({\n # asset => \"INSERT_LOGO_IMAGE_RESOURCE_NAME_HERE\"\n # })\n # ],\n # Optional: Create a square logo image and set it to the ad.\n # squareLogoImages => [\n # Google::Ads::GoogleAds::V25::Common::AdImageAsset->new({\n # asset => \"INSERT_SQUARE_LOGO_IMAGE_RESOURCE_NAME_HERE\"\n # })\n # ]\n });\n\n # Create an ad group ad.\n my $ad_group_ad = Google::Ads::GoogleAds::V25::Resources::AdGroupAd->new({\n adGroup => $ad_group_resource_name,\n ad => Google::Ads::GoogleAds::V25::Resources::Ad->new({\n responsiveDisplayAd => $responsive_display_ad_info,\n finalUrls => [\"http://www.example.com/\"]})});\n\n # Create an ad group ad operation.\n my $ad_group_ad_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation\n ->new({create => $ad_group_ad});\n\n # Issue a mutate request to add the ad group ad.\n my $ad_group_ads_response = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]});\n\n printf \"Created ad group ad with resource name '%s'.\\n\",\n $ad_group_ads_response->{results}[0]{resourceName};\n}add_merchant_center_dynamic_remarketing_campaign.pl\n```\n\nExample:\n```text\nprivate void attachUserList(\n GoogleAdsClient googleAdsClient,\n long customerId,\n String adGroupResourceName,\n long userListId) {\n String userListResourceName = ResourceNames.userList(customerId, userListId);\n // Creates the ad group criterion that targets the user list.\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n .setAdGroup(adGroupResourceName)\n .setUserList(UserListInfo.newBuilder().setUserList(userListResourceName).build())\n .build();\n\n // Creates the ad group criterion operation.\n AdGroupCriterionOperation operation =\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterion).build();\n\n // Creates the ad group criterion service client.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Adds the ad group criterion.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n System.out.printf(\n \"Created ad group criterion with resource name '%s'.%n\",\n response.getResults(0).getResourceName());\n }\n}AddMerchantCenterDynamicRemarketingCampaign.java\n```\n\nExample:\n```text\nprivate void AttachUserList(GoogleAdsClient client, long customerId,\n string adGroupResourceName, long userListId)\n{\n // Creates the ad group criterion service client.\n AdGroupCriterionServiceClient adGroupCriterionServiceClient = client.GetService\n (Services.V25.AdGroupCriterionService);\n\n string userListResourceName = ResourceNames.UserList(customerId, userListId);\n\n // Creates the ad group criterion that targets the user list.\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion()\n {\n AdGroup = adGroupResourceName,\n UserList = new UserListInfo()\n {\n UserList = userListResourceName\n }\n };\n\n // Creates the ad group criterion operation.\n AdGroupCriterionOperation operation = new AdGroupCriterionOperation()\n {\n Create = adGroupCriterion\n };\n\n // Adds the ad group criterion.\n MutateAdGroupCriteriaResponse response = adGroupCriterionServiceClient\n .MutateAdGroupCriteria(customerId.ToString(), new[] { operation });\n Console.WriteLine(\"Created ad group criterion with resource name \" +\n $\"'{response.Results.First().ResourceName}'.\");\n}AddMerchantCenterDynamicRemarketingCampaign.cs\n```\n\nExample:\n```text\nprivate static function attachUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $adGroupResourceName,\n int $userListId\n) {\n // Creates the ad group criterion that targets the user list.\n $adGroupCriterion = new AdGroupCriterion([\n 'ad_group' => $adGroupResourceName,\n 'user_list' => new UserListInfo([\n 'user_list' => ResourceNames::forUserList($customerId, $userListId)\n ])\n ]);\n\n // Creates an ad group criterion operation.\n $adGroupCriterionOperation = new AdGroupCriterionOperation();\n $adGroupCriterionOperation->setCreate($adGroupCriterion);\n\n // Issues a mutate request to add the ad group criterion.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$adGroupCriterionOperation])\n );\n\n /** @var AdGroupCriterion $addedAdGroupCriterion */\n $addedAdGroupCriterion = $response->getResults()[0];\n printf(\n \"Created ad group criterion with resource name '%s'.%s\",\n $addedAdGroupCriterion->getResourceName(),\n PHP_EOL\n );\n}AddMerchantCenterDynamicRemarketingCampaign.php\n```\n\nExample:\n```text\ndef attach_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_resource_name: str,\n user_list_id: int,\n) -> None:\n \"\"\"Targets a user list with an ad group.\n\n Args:\n client: An initialized GoogleAds client.\n customer_id: The Google Ads customer ID.\n ad_group_resource_name: The resource name of the target ad group.\n user_list_id: The ID of the user list to target for remarketing.\n \"\"\"\n # Get the AdGroupCriterionService client.\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n\n # Create an ad group criterion operation and set the ad group criterion\n # values.\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n ad_group_criterion: AdGroupCriterion = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = ad_group_resource_name\n ad_group_criterion.user_list.user_list = client.get_service(\n \"UserListService\"\n ).user_list_path(customer_id, str(user_list_id))\n\n # Issue a mutate request to add the ad group criterion.\n ad_group_criterion_response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n )\n print(\n \"Created ad group criterion with resource name \"\n f\"'{ad_group_criterion_response.results[0].resource_name}'.\"\n )add_merchant_center_dynamic_remarketing_campaign.py\n```\n\nExample:\n```text\ndef attach_user_list(client, customer_id, ad_group_resource_name, user_list_id)\n user_list_resource_name = client.path.user_list(customer_id, user_list_id)\n\n # Creates the ad group criterion that targets the user list.\n ad_group_criterion = client.resource.ad_group_criterion do |agc|\n agc.ad_group = ad_group_resource_name\n agc.user_list = client.resource.user_list_info do |ul|\n ul.user_list = user_list_resource_name\n end\n end\n\n # Creates the ad group criterion operation.\n op = client.operation.create_resource.ad_group_criterion(ad_group_criterion)\n\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [op]\n )\n\n puts \"Created ad group criterion: #{response.results.first.resource_name}\"\nendadd_merchant_center_dynamic_remarketing_campaign.rb\n```\n\nExample:\n```text\nsub attach_user_list {\n my ($api_client, $customer_id, $ad_group_resource_name, $user_list_id) = @_;\n\n # Create the ad group criterion that targets the user list.\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n adGroup => $ad_group_resource_name,\n userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({\n userList =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::user_list(\n $customer_id, $user_list_id\n )})});\n\n # Create an ad group criterion operation.\n my $ad_group_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({create => $ad_group_criterion});\n\n # Issue a mutate request to add the ad group criterion.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n printf \"Created ad group criterion with resource name '%s'.\\n\",\n $ad_group_criteria_response->{results}[0]{resourceName};\n}add_merchant_center_dynamic_remarketing_campaign.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.538Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":24,"totalLines":1207,"estimatedTokens":11435}}212{"id":"doc-multiple_user_lists_google_ads_api_google_for_de-c0772364","source":"documentation","title":"Multiple User Lists | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audience-segments/multiple-user-lists","text":"Example:\n```text\nprivate void runExample(\n GoogleAdsClient googleAdsClient, long customerId, List<Long> userListIds) {\n // Adds each of the provided list IDs to a list of rule operands specifying which lists the\n // operator should target.\n List<LogicalUserListOperandInfo> logicalUserListOperandInfoList = new ArrayList<>();\n for (long userListId : userListIds) {\n String userListResourceName = ResourceNames.userList(customerId, userListId);\n logicalUserListOperandInfoList.add(\n LogicalUserListOperandInfo.newBuilder().setUserList(userListResourceName).build());\n }\n\n // Creates the UserListLogicalRuleInfo specifying that a user should be added to the new list if\n // they are present in any of the provided lists.\n UserListLogicalRuleInfo userListLogicalRuleInfo =\n UserListLogicalRuleInfo.newBuilder()\n // Using ANY means that a user should be added to the combined list if they are present\n // on any of the lists targeted in the LogicalUserListOperandInfo. Use ALL to add users\n // present on all of the provided lists or NONE to add users that aren't present on any\n // of the targeted lists.\n .setOperator(UserListLogicalRuleOperator.ANY)\n .addAllRuleOperands(logicalUserListOperandInfoList)\n .build();\n\n // Creates the new combination user list.\n UserList userList =\n UserList.newBuilder()\n .setName(\"My combination list of other user lists #\" + getPrintableDateTime())\n .setLogicalUserList(\n LogicalUserListInfo.newBuilder().addRules(userListLogicalRuleInfo).build())\n .build();\n\n // Creates the operation.\n UserListOperation operation = UserListOperation.newBuilder().setCreate(userList).build();\n\n // Creates the service client.\n try (UserListServiceClient userListServiceClient =\n googleAdsClient.getLatestVersion().createUserListServiceClient()) {\n // Adds the user list.\n MutateUserListsResponse response =\n userListServiceClient.mutateUserLists(\n Long.toString(customerId), ImmutableList.of(operation));\n // Prints the response.\n System.out.printf(\n \"Created combination user list with resource name, '%s'.%n\",\n response.getResults(0).getResourceName());\n }\n}AddLogicalUserList.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long[] userListIds)\n{\n // Gets the UserListService client.\n UserListServiceClient userListServiceClient =\n client.GetService(Services.V25.UserListService);\n\n // Adds each of the provided list IDs to a list of rule operands specifying which lists\n // the operator should target.\n List<LogicalUserListOperandInfo> logicalUserListOperandInfoList =\n userListIds.Select(userListId => new LogicalUserListOperandInfo\n { UserList = ResourceNames.UserList(customerId, userListId) }).ToList();\n\n // Creates the UserListLogicalRuleInfo specifying that a user should be added to the new\n // list if they are present in any of the provided lists.\n UserListLogicalRuleInfo userListLogicalRuleInfo = new UserListLogicalRuleInfo\n {\n // Using ANY means that a user should be added to the combined list if they are\n // present on any of the lists targeted in the LogicalUserListOperandInfo. Use ALL\n // to add users present on all of the provided lists or NONE to add users that\n // aren't present on any of the targeted lists.\n Operator = UserListLogicalRuleOperatorEnum.Types.UserListLogicalRuleOperator.Any,\n };\n userListLogicalRuleInfo.RuleOperands.Add(logicalUserListOperandInfoList);\n\n LogicalUserListInfo logicalUserListInfo = new LogicalUserListInfo();\n logicalUserListInfo.Rules.Add(userListLogicalRuleInfo);\n\n // Creates the new combination user list.\n UserList userList = new UserList\n {\n Name = \"My combination list of other user lists \" +\n $\"#{ExampleUtilities.GetRandomString()}\",\n LogicalUserList = logicalUserListInfo\n };\n\n // Creates the operation.\n UserListOperation operation = new UserListOperation\n {\n Create = userList\n };\n\n try\n {\n // Sends the request to add the user list and prints the response.\n MutateUserListsResponse response = userListServiceClient.MutateUserLists\n (customerId.ToString(), new[] { operation });\n Console.WriteLine(\"Created combination user list with resource name: \" +\n $\"{response.Results.First().ResourceName}\");\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}AddLogicalUserList.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n array $userListIds\n) {\n // Adds each of the provided list IDs to a list of rule operands specifying which lists the\n // operator should target.\n $logicalUserListOperandInfoList = [];\n foreach ($userListIds as $userListId) {\n $logicalUserListOperandInfoList[] = new LogicalUserListOperandInfo([\n 'user_list' => ResourceNames::forUserList($customerId, $userListId)\n ]);\n }\n\n // Creates the UserListLogicalRuleInfo specifying that a user should be added to the new\n // list if they are present in any of the provided lists.\n $userListLogicalRuleInfo = new UserListLogicalRuleInfo([\n // Using ANY means that a user should be added to the combined list if they are present\n // on any of the lists targeted in the LogicalUserListOperandInfo. Use ALL to add users\n // present on all of the provided lists or NONE to add users that aren't present on any\n // of the targeted lists.\n 'operator' => UserListLogicalRuleOperator::ANY,\n 'rule_operands' => $logicalUserListOperandInfoList\n ]);\n\n // Creates the new combination user list.\n $userList = new UserList([\n 'name' => 'My combination list of other user lists #' . Helper::getPrintableDatetime(),\n 'logical_user_list' => new LogicalUserListInfo([\n 'rules' => [$userListLogicalRuleInfo]\n ])\n ]);\n\n // Creates the operation.\n $operation = new UserListOperation();\n $operation->setCreate($userList);\n\n // Issues a mutate request to add the user list and prints some information.\n $userListServiceClient = $googleAdsClient->getUserListServiceClient();\n $response = $userListServiceClient->mutateUserLists(\n MutateUserListsRequest::build($customerId, [$operation])\n );\n printf(\n \"Created combination user list with resource name '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n}AddLogicalUserList.php\n```\n\nExample:\n```text\ndef main(\n client: GoogleAdsClient, customer_id: str, user_list_ids: List[str]\n) -> None:\n \"\"\"Creates a combination user list.\n\n Args:\n client: The Google Ads client.\n customer_id: The customer ID for which to add the user list.\n user_list_ids: A list of user list IDs to logically combine.\n \"\"\"\n # Get the UserListService client.\n user_list_service: UserListServiceClient = client.get_service(\n \"UserListService\"\n )\n\n # Add each of the provided list IDs to a list of rule operands specifying\n # which lists the operator should target.\n logical_user_list_operand_info_list: List[LogicalUserListOperandInfo] = []\n for user_list_id in user_list_ids:\n logical_user_list_operand_info: LogicalUserListOperandInfo = (\n client.get_type(\"LogicalUserListOperandInfo\")\n )\n logical_user_list_operand_info.user_list = (\n user_list_service.user_list_path(customer_id, user_list_id)\n )\n logical_user_list_operand_info_list.append(\n logical_user_list_operand_info\n )\n\n # Create a UserListOperation and populate the UserList.\n user_list_operation: UserListOperation = client.get_type(\n \"UserListOperation\"\n )\n user_list: UserList = user_list_operation.create\n user_list.name = f\"My combination list of other user lists #{uuid4()}\"\n # Create a UserListLogicalRuleInfo specifying that a user should be added to\n # the new list if they are present in any of the provided lists.\n user_list_logical_rule_info: UserListLogicalRuleInfo = client.get_type(\n \"UserListLogicalRuleInfo\"\n )\n # Using ANY means that a user should be added to the combined list if they\n # are present on any of the lists targeted in the\n # LogicalUserListOperandInfo. Use ALL to add users present on all of the\n # provided lists or NONE to add users that aren't present on any of the\n # targeted lists.\n user_list_logical_rule_info.operator = (\n client.enums.UserListLogicalRuleOperatorEnum.ANY\n )\n user_list_logical_rule_info.rule_operands.extend(\n logical_user_list_operand_info_list\n )\n user_list.logical_user_list.rules.append(user_list_logical_rule_info)\n\n # Issue a mutate request to add the user list, then print the results.\n response: MutateUserListsResponse = user_list_service.mutate_user_lists(\n customer_id=customer_id, operations=[user_list_operation]\n )\n print(\n \"Created logical user list with resource name \"\n f\"'{response.results[0].resource_name}.'\"\n )add_logical_user_list.py\n```\n\nExample:\n```text\ndef add_logical_user_list(customer_id, user_list_ids)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Creates the UserListLogicalRuleInfo specifying that a user should be added\n # to the new list if they are present in any of the provided lists.\n user_list_logical_rule_info = client.resource.user_list_logical_rule_info do |info|\n # Using ANY means that a user should be added to the combined list if they\n # are present on any of the lists targeted in the logical_user_list_operand_info.\n # Use ALL to add users present on all of the provided lists or NONE to add\n # users that aren't present on any of the targeted lists.\n info.operator = :ANY\n user_list_ids.each do |list_id|\n info.rule_operands << client.resource.logical_user_list_operand_info do |op|\n op.user_list = client.path.user_list(customer_id, list_id)\n end\n end\n end\n\n # Creates the new combination user list operation.\n operation = client.operation.create_resource.user_list do |ul|\n ul.name = \"My combination list of other user lists #{(Time.new.to_f * 1000).to_i}\"\n ul.logical_user_list = client.resource.logical_user_list_info do |info|\n info.rules << user_list_logical_rule_info\n end\n end\n\n # Issues a mutate request to add the user list and prints some information.\n response = client.service.user_list.mutate_user_lists(\n customer_id: customer_id,\n operations: [operation],\n )\n puts \"Created combination user list with resource name \"\\\n \"'#{response.results.first.resource_name}'\"\nendadd_logical_user_list.rb\n```\n\nExample:\n```text\nsub add_logical_user_list {\n my ($api_client, $customer_id, $user_list_ids) = @_;\n\n # Add each of the provided list IDs to a list of rule operands specifying which\n # lists the operator should target.\n my $logical_user_list_operand_info_list = [];\n foreach my $user_list_id (@$user_list_ids) {\n push @$logical_user_list_operand_info_list,\n Google::Ads::GoogleAds::V25::Common::LogicalUserListOperandInfo->new({\n userList =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::user_list(\n $customer_id, $user_list_id\n )});\n }\n\n # Create the UserListLogicalRuleInfo specifying that a user should be added to\n # the new list if they are present in any of the provided lists.\n my $user_list_logical_rule_info =\n Google::Ads::GoogleAds::V25::Common::UserListLogicalRuleInfo->new({\n # Using ANY means that a user should be added to the combined list if they\n # are present on any of the lists targeted in the LogicalUserListOperandInfo.\n # Use ALL to add users present on all of the provided lists or NONE to add\n # users that aren't present on any of the targeted lists.\n operator => ANY,\n ruleOperands => $logical_user_list_operand_info_list\n });\n\n # Create the new combination user list.\n my $user_list = Google::Ads::GoogleAds::V25::Resources::UserList->new({\n name => \"My combination list of other user lists #\" . uniqid(),\n logicalUserList =>\n Google::Ads::GoogleAds::V25::Common::LogicalUserListInfo->new({\n rules => [$user_list_logical_rule_info]})});\n\n # Create the operation.\n my $user_list_operation =\n Google::Ads::GoogleAds::V25::Services::UserListService::UserListOperation->\n new({\n create => $user_list\n });\n\n # Issue a mutate request to add the user list and print some information.\n my $user_lists_response = $api_client->UserListService()->mutate({\n customerId => $customer_id,\n operations => [$user_list_operation]});\n printf \"Created combination user list with resource name '%s'.\\n\",\n $user_lists_response->{results}[0]{resourceName};\n\n return 1;\n}add_logical_user_list.pl\n```\n\nExample:\n```text\nSELECT\n user_list.name,\n user_list.membership_status,\n user_list.membership_life_span\nFROM user_list\nWHERE\n user_list.resource_name = 'USER_LIST_RESOURCE_NAME'\n```\n\nExample:\n```text\nprivate String targetAdsInAdGroupToUserList(\n GoogleAdsClient googleAdsClient, long customerId, long adGroupId, String userList) {\n // Creates the ad group criterion targeting members of the user list.\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))\n .setUserList(UserListInfo.newBuilder().setUserList(userList).build())\n .build();\n\n // Creates the operation.\n AdGroupCriterionOperation operation =\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterion).build();\n\n // Creates the ad group criterion service.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Adds the ad group criterion.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n // Gets and prints the results.\n String adGroupCriterionResourceName = response.getResults(0).getResourceName();\n System.out.printf(\n \"Successfully created ad group criterion with resource name '%s' \"\n + \"targeting user list with resource name '%s' with ad group with ID %d.%n\",\n adGroupCriterionResourceName, userList, adGroupId);\n return adGroupCriterionResourceName;\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate string TargetAdsInAdGroupToUserList(\n GoogleAdsClient client, long customerId, long adGroupId, string userListResourceName)\n{\n // Get the AdGroupCriterionService client.\n AdGroupCriterionServiceClient adGroupCriterionServiceClient = client.GetService\n (Services.V25.AdGroupCriterionService);\n\n // Create the ad group criterion targeting members of the user list.\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion\n {\n AdGroup = ResourceNames.AdGroup(customerId, adGroupId),\n UserList = new UserListInfo\n {\n UserList = userListResourceName\n }\n };\n\n // Create the operation.\n AdGroupCriterionOperation adGroupCriterionOperation = new AdGroupCriterionOperation\n {\n Create = adGroupCriterion\n };\n\n // Add the ad group criterion, then print and return the new criterion's resource name.\n MutateAdGroupCriteriaResponse mutateAdGroupCriteriaResponse =\n adGroupCriterionServiceClient.MutateAdGroupCriteria(customerId.ToString(),\n new[] { adGroupCriterionOperation });\n\n string adGroupCriterionResourceName =\n mutateAdGroupCriteriaResponse.Results.First().ResourceName;\n Console.WriteLine(\"Successfully created ad group criterion with resource name \" +\n $\"'{adGroupCriterionResourceName}' targeting user list with resource name \" +\n $\"'{userListResourceName}' with ad group with ID {adGroupId}.\");\n return adGroupCriterionResourceName;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function targetAdsInAdGroupToUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n string $userListResourceName\n): string {\n // Creates the ad group criterion targeting members of the user list.\n $adGroupCriterion = new AdGroupCriterion([\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'user_list' => new UserListInfo(['user_list' => $userListResourceName])\n ]);\n\n // Creates the operation.\n $operation = new AdGroupCriterionOperation();\n $operation->setCreate($adGroupCriterion);\n\n // Issues a mutate request to add an ad group criterion.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n /** @var MutateAdGroupCriteriaResponse $adGroupCriterionResponse */\n $adGroupCriterionResponse = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$operation])\n );\n\n $adGroupCriterionResourceName =\n $adGroupCriterionResponse->getResults()[0]->getResourceName();\n printf(\n \"Successfully created ad group criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with ad group with ID %d.%s\",\n $adGroupCriterionResourceName,\n $userListResourceName,\n $adGroupId,\n PHP_EOL\n );\n\n return $adGroupCriterionResourceName;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef target_ads_in_ad_group_to_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n user_list_resource_name: str,\n) -> str:\n \"\"\"Creates an ad group criterion that targets a user list with an ad group.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID used to create an ad group\n criterion.\n ad_group_id: a str ID for an ad group used to create an ad group\n criterion that targets members of a user list.\n user_list_resource_name: a str resource name for a user list.\n\n Returns:\n a str resource name for an ad group criterion.\n \"\"\"\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n # Creates the ad group criterion targeting members of the user list.\n ad_group_criterion: AdGroupCriterion = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = client.get_service(\n \"AdGroupService\"\n ).ad_group_path(customer_id, ad_group_id)\n ad_group_criterion.user_list.user_list = user_list_resource_name\n\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n print(\n \"Successfully created ad group criterion with resource name: \"\n f\"'{resource_name}' targeting user list with resource name: \"\n f\"'{user_list_resource_name}' and with ad group with ID \"\n f\"{ad_group_id}.\"\n )\n return resource_nameset_up_remarketing.py\n```\n\nExample:\n```text\ndef target_ads_in_ad_group_to_user_list(\n client,\n customer_id,\n ad_group_id,\n user_list\n)\n # Creates the ad group criterion targeting members of the user list.\n operation = client.operation.create_resource.ad_group_criterion do |agc|\n agc.ad_group = client.path.ad_group(customer_id, ad_group_id)\n agc.user_list = client.resource.user_list_info do |info|\n info.user_list = user_list\n end\n end\n\n # Issues a mutate request to create the ad group criterion.\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [operation],\n )\n ad_group_criterion_resource_name = response.results.first.resource_name\n puts \"Successfully created ad group criterion with resource name \" \\\n \"'#{ad_group_criterion_resource_name}' targeting user list with resource name \" \\\n \"'#{user_list}' with ad group with ID #{ad_group_id}\"\n\n ad_group_criterion_resource_name\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub target_ads_in_ad_group_to_user_list {\n my ($api_client, $customer_id, $ad_group_id, $user_list_resource_name) = @_;\n\n # Create the ad group criterion targeting members of the user list.\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({\n userList => $user_list_resource_name\n })});\n\n # Create the operation.\n my $ad_group_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion\n });\n\n # Add the ad group criterion, then print and return the new criterion's resource name.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n my $ad_group_criterion_resource_name =\n $ad_group_criteria_response->{results}[0]{resourceName};\n printf \"Successfully created ad group criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with ad group with ID %d.\\n\",\n $ad_group_criterion_resource_name, $user_list_resource_name, $ad_group_id;\n\n return $ad_group_criterion_resource_name;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate List<String> getUserListAdGroupCriterion(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n List<String> userListCriteria = new ArrayList<>();\n // Creates the Google Ads service client.\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Creates a request that will retrieve all of the ad group criteria under a campaign.\n SearchGoogleAdsRequest request =\n SearchGoogleAdsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setQuery(\n \"SELECT ad_group_criterion.criterion_id\"\n + \" FROM ad_group_criterion\"\n + \" WHERE campaign.id = \"\n + campaignId\n + \" AND ad_group_criterion.type = 'USER_LIST'\")\n .build();\n // Issues the search request.\n SearchPagedResponse searchPagedResponse = googleAdsServiceClient.search(request);\n // Iterates over all rows in all pages. Prints the results and adds the ad group criteria\n // resource names to the list.\n for (GoogleAdsRow googleAdsRow : searchPagedResponse.iterateAll()) {\n String adGroupCriterionResourceName = googleAdsRow.getAdGroupCriterion().getResourceName();\n System.out.printf(\n \"Ad group criterion with resource name '%s' was found.%n\",\n adGroupCriterionResourceName);\n userListCriteria.add(adGroupCriterionResourceName);\n }\n }\n return userListCriteria;\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate List<string> GetUserListAdGroupCriteria(\n GoogleAdsClient client, long customerId, long campaignId)\n{\n // Get the GoogleAdsService client.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n List<string> userListCriteriaResourceNames = new List<string>();\n\n // Create a query that will retrieve all of the ad group criteria under a campaign.\n string query = $@\"\n SELECT ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE\n campaign.id = {campaignId}\n AND ad_group_criterion.type = 'USER_LIST'\";\n\n // Issue the search request.\n googleAdsServiceClient.SearchStream(customerId.ToString(), query,\n delegate (SearchGoogleAdsStreamResponse resp)\n {\n // Display the results and add the resource names to the list.\n foreach (GoogleAdsRow googleAdsRow in resp.Results)\n {\n string adGroupCriterionResourceName =\n googleAdsRow.AdGroupCriterion.ResourceName;\n Console.WriteLine(\"Ad group criterion with resource name \" +\n $\"{adGroupCriterionResourceName} was found.\");\n userListCriteriaResourceNames.Add(adGroupCriterionResourceName);\n }\n });\n\n return userListCriteriaResourceNames;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function getUserListAdGroupCriteria(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n): array {\n // Creates a query that retrieves all of the ad group criteria under a campaign.\n $query = sprintf(\n \"SELECT ad_group_criterion.criterion_id \" .\n \"FROM ad_group_criterion \" .\n \"WHERE campaign.id = %d \" .\n \"AND ad_group_criterion.type = 'USER_LIST'\",\n $campaignId\n );\n\n // Creates the Google Ads service client.\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n\n // Issues the search request.\n $response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n $userListCriteria = [];\n // Iterates over all rows in all pages. Prints the user list criteria and adds the ad group\n // criteria resource names to the list.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $adGroupCriterionResourceName = $googleAdsRow->getAdGroupCriterion()->getResourceName();\n\n printf(\n \"Ad group criterion with resource name '%s' was found.%s\",\n $adGroupCriterionResourceName,\n PHP_EOL\n );\n\n $userListCriteria[] = $adGroupCriterionResourceName;\n }\n\n return $userListCriteria;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef get_user_list_ad_group_criteria(\n client: GoogleAdsClient, customer_id: str, campaign_id: str\n) -> List[str]:\n \"\"\"Finds all of user list ad group criteria under a campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID.\n campaign_id: a str campaign ID.\n\n Returns:\n a list of ad group criterion resource names.\n \"\"\"\n # Creates a query that retrieves all of the ad group criteria under a\n # campaign.\n query: str = f\"\"\"\n SELECT\n ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE campaign.id = {campaign_id}\n AND ad_group_criterion.type = USER_LIST\"\"\"\n\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n search_request: SearchGoogleAdsRequest = client.get_type(\n \"SearchGoogleAdsRequest\"\n )\n search_request.customer_id = customer_id\n search_request.query = query\n response: SearchGoogleAdsResponse = googleads_service.search(\n request=search_request\n )\n\n # Iterates over all rows in all pages. Prints the user list criteria and\n # adds the ad group criteria resource names to the list.\n user_list_criteria: List[str] = []\n row: GoogleAdsRow\n for row in response:\n resource_name: str = row.ad_group_criterion.resource_name\n print(\n \"Ad group criterion with resource name '{resource_name}' was \"\n \"found.\"\n )\n user_list_criteria.append(resource_name)\n\n return user_list_criteriaset_up_remarketing.py\n```\n\nExample:\n```text\ndef get_user_list_ad_group_criterion(\n client,\n customer_id,\n campaign_id\n)\n user_list_criteria = []\n\n # Creates a query that will retrieve all of the ad group criteria \n # under a campaign.\n query = <<~QUERY\n SELECT ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE campaign.id = #{campaign_id}\n AND ad_group_criterion.type = 'USER_LIST'\n QUERY\n\n # Issues the search request.\n response = client.service.google_ads.search(\n customer_id: customer_id,\n query: query,\n )\n\n # Iterates over all rows in all pages. Prints the results and adds the ad\n # group criteria resource names to the list.\n response.each do |row|\n ad_group_criterion_resource_name = row.ad_group_criterion.resource_name\n puts \"Ad group criterion with resource name \" \\\n \"'#{ad_group_criterion_resource_name}' was found\"\n user_list_criteria << ad_group_criterion_resource_name\n end\n\n user_list_criteria\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub get_user_list_ad_group_criteria {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n my $user_list_criterion_resource_names = [];\n\n # Create a search stream request that will retrieve all of the user list ad\n # group criteria under a campaign.\n my $search_stream_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsStreamRequest\n ->new({\n customerId => $customer_id,\n query => sprintf(\n \"SELECT ad_group_criterion.criterion_id \" .\n \"FROM ad_group_criterion \" .\n \"WHERE campaign.id = %d AND ad_group_criterion.type = 'USER_LIST'\",\n $campaign_id\n )});\n\n my $search_stream_handler =\n Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({\n service => $api_client->GoogleAdsService(),\n request => $search_stream_request\n });\n\n # Issue a search request and process the stream response.\n $search_stream_handler->process_contents(\n sub {\n # Display the results and add the resource names to the list.\n my $google_ads_row = shift;\n\n my $ad_group_criterion_resource_name =\n $google_ads_row->{adGroupCriterion}{resourceName};\n printf \"Ad group criterion with resource name '%s' was found.\\n\",\n $ad_group_criterion_resource_name;\n push(@$user_list_criterion_resource_names,\n $ad_group_criterion_resource_name);\n });\n\n return $user_list_criterion_resource_names;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate void removeExistingListCriteriaFromAdGroup(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n // Retrieves all of the ad group criteria under a campaign.\n List<String> adGroupCriteria =\n getUserListAdGroupCriterion(googleAdsClient, customerId, campaignId);\n\n List<AdGroupCriterionOperation> operations = new ArrayList<>();\n\n // Creates a list of remove operations.\n for (String adGroupCriterion : adGroupCriteria) {\n operations.add(AdGroupCriterionOperation.newBuilder().setRemove(adGroupCriterion).build());\n }\n\n // Creates the ad group criterion service.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Removes the ad group criterion.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), operations);\n // Gets and prints the results.\n System.out.printf(\"Removed %d ad group criteria.%n\", response.getResultsCount());\n for (MutateAdGroupCriterionResult result : response.getResultsList()) {\n System.out.printf(\n \"Successfully removed ad group criterion with resource name '%s'.%n\",\n result.getResourceName());\n }\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate void RemoveExistingListCriteriaFromAdGroup(GoogleAdsClient client, long customerId,\n long campaignId)\n{\n // Get the AdGroupCriterionService client.\n AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n client.GetService(Services.V25.AdGroupCriterionService);\n\n // Retrieve all of the ad group criteria under a campaign.\n List<string> adGroupCriteria =\n GetUserListAdGroupCriteria(client, customerId, campaignId);\n\n // Create a list of remove operations.\n List<AdGroupCriterionOperation> operations = adGroupCriteria.Select(adGroupCriterion =>\n new AdGroupCriterionOperation { Remove = adGroupCriterion }).ToList();\n\n // Remove the ad group criteria and print the resource names of the removed criteria.\n MutateAdGroupCriteriaResponse mutateAdGroupCriteriaResponse =\n adGroupCriterionServiceClient.MutateAdGroupCriteria(customerId.ToString(),\n operations);\n\n Console.WriteLine($\"Removed {mutateAdGroupCriteriaResponse.Results.Count} ad group \" +\n \"criteria.\");\n foreach (MutateAdGroupCriterionResult result in mutateAdGroupCriteriaResponse.Results)\n {\n Console.WriteLine(\"Successfully removed ad group criterion with resource name \" +\n $\"'{result.ResourceName}'.\");\n }\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function removeExistingListCriteriaFromAdGroup(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n) {\n // Retrieves all of the ad group criteria under a campaign.\n $allAdGroupCriteria = self::getUserListAdGroupCriteria(\n $googleAdsClient,\n $customerId,\n $campaignId\n );\n\n $removeOperations = [];\n // Creates a list of remove operations.\n foreach ($allAdGroupCriteria as $adGroupCriterionResourceName) {\n $operation = new AdGroupCriterionOperation();\n $operation->setRemove($adGroupCriterionResourceName);\n $removeOperations[] = $operation;\n }\n\n // Issues a mutate request to remove the ad group criteria.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n /** @var MutateAdGroupCriteriaResponse $adGroupCriteriaResponse */\n $adGroupCriteriaResponse = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, $removeOperations)\n );\n\n foreach ($adGroupCriteriaResponse->getResults() as $adGroupCriteriaResult) {\n printf(\n \"Successfully removed ad group criterion with resource name '%s'.%s\",\n $adGroupCriteriaResult->getResourceName(),\n PHP_EOL\n );\n }\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef remove_existing_criteria_from_ad_group(\n client: GoogleAdsClient, customer_id: str, campaign_id: str\n) -> None:\n \"\"\"Removes all ad group criteria targeting a user list under a campaign.\n\n This is a necessary step before targeting a user list at the campaign level.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID.\n campaign_id: a str ID for a campaign that will have all ad group\n criteria that targets user lists removed.\n \"\"\"\n # Retrieves all of the ad group criteria under a campaign.\n all_ad_group_criteria: List[str] = get_user_list_ad_group_criteria(\n client, customer_id, campaign_id\n )\n\n # Creates a list of remove operations.\n remove_operations: List[AdGroupCriterionOperation] = []\n for ad_group_criterion_resource_name in all_ad_group_criteria:\n remove_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n remove_operation.remove = ad_group_criterion_resource_name\n remove_operations.append(remove_operation)\n\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=remove_operations\n )\n )\n print(\n \"Successfully removed ad group criterion with resource name: \"\n f\"'{response.results[0].resource_name}'\"\n )set_up_remarketing.py\n```\n\nExample:\n```text\ndef remove_existing_list_criteria_from_ad_group(\n client,\n customer_id,\n campaign_id\n)\n # Retrieves all of the ad group criteria under a campaign.\n ad_group_criteria = get_user_list_ad_group_criterion(\n client, customer_id, campaign_id)\n\n # Creates a list of remove operations.\n operations = []\n ad_group_criteria.each do |agc|\n operations << client.operation.remove_resource.ad_group_criterion(agc)\n end\n\n # Issues a mutate request to remove all ad group criteria.\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: operations,\n )\n puts \"Removed #{response.results.size} ad group criteria.\"\n response.results.each do |result|\n puts \"Successfully removed ad group criterion with resource name \" \\\n \"'#{result.resource_name}'\"\n end\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub remove_existing_list_criteria_from_ad_group {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n # Retrieve all of the ad group criteria under a campaign.\n my $ad_group_criteria =\n get_user_list_ad_group_criteria($api_client, $customer_id, $campaign_id);\n\n # Create a list of remove operations.\n my $operations = [];\n foreach my $ad_group_criterion (@$ad_group_criteria) {\n push(\n @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n remove => $ad_group_criterion\n }));\n }\n\n # Remove the ad group criteria and print the resource names of the removed criteria.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => $operations\n });\n\n printf \"Removed %d ad group criteria.\\n\",\n scalar @{$ad_group_criteria_response->{results}};\n foreach my $result (@{$ad_group_criteria_response->{results}}) {\n printf \"Successfully removed ad group criterion with resource name '%s'.\\n\",\n $result->{resourceName};\n }\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate String targetAdsInCampaignToUserList(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId, String userList) {\n // Creates the campaign criterion.\n CampaignCriterion campaignCriterion =\n CampaignCriterion.newBuilder()\n .setCampaign(ResourceNames.campaign(customerId, campaignId))\n .setUserList(UserListInfo.newBuilder().setUserList(userList).build())\n .build();\n\n // Creates the operation.\n CampaignCriterionOperation operation =\n CampaignCriterionOperation.newBuilder().setCreate(campaignCriterion).build();\n\n // Creates the campaign criterion service client.\n try (CampaignCriterionServiceClient campaignCriterionServiceClient =\n googleAdsClient.getLatestVersion().createCampaignCriterionServiceClient()) {\n // Adds the campaign criterion.\n MutateCampaignCriteriaResponse response =\n campaignCriterionServiceClient.mutateCampaignCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n // Gets and prints the campaign criterion resource name.\n String campaignCriterionResourceName = response.getResults(0).getResourceName();\n System.out.printf(\n \"Successfully created campaign criterion with resource name '%s' \"\n + \"targeting user list with resource name '%s' with campaign with ID %d.%n\",\n campaignCriterionResourceName, userList, campaignId);\n return campaignCriterionResourceName;\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate string TargetAdsInCampaignToUserList(\n GoogleAdsClient client, long customerId, long campaignId, string userListResourceName)\n{\n // Get the CampaignCriterionService client.\n CampaignCriterionServiceClient campaignCriterionServiceClient =\n client.GetService(Services.V25.CampaignCriterionService);\n\n // Create the campaign criterion.\n CampaignCriterion campaignCriterion = new CampaignCriterion\n {\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n UserList = new UserListInfo\n {\n UserList = userListResourceName\n }\n };\n\n // Create the operation.\n CampaignCriterionOperation campaignCriterionOperation = new CampaignCriterionOperation\n {\n Create = campaignCriterion\n };\n\n // Add the campaign criterion and print the resulting criterion's resource name.\n MutateCampaignCriteriaResponse mutateCampaignCriteriaResponse =\n campaignCriterionServiceClient.MutateCampaignCriteria(customerId.ToString(),\n new[] { campaignCriterionOperation });\n\n string campaignCriterionResourceName =\n mutateCampaignCriteriaResponse.Results.First().ResourceName;\n Console.WriteLine(\"Successfully created campaign criterion with resource name \" +\n $\"'{campaignCriterionResourceName}' targeting user list with resource name \" +\n $\"'{userListResourceName}' with campaign with ID {campaignId}.\");\n\n return campaignCriterionResourceName;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function targetAdsInCampaignToUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId,\n string $userListResourceName\n): string {\n // Creates the campaign criterion.\n $campaignCriterion = new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign($customerId, $campaignId),\n 'user_list' => new UserListInfo(['user_list' => $userListResourceName])\n ]);\n\n // Creates the operation.\n $operation = new CampaignCriterionOperation();\n $operation->setCreate($campaignCriterion);\n\n // Issues a mutate request to create a campaign criterion.\n $campaignCriterionServiceClient = $googleAdsClient->getCampaignCriterionServiceClient();\n /** @var MutateCampaignCriteriaResponse $campaignCriteriaResponse */\n $campaignCriteriaResponse = $campaignCriterionServiceClient->mutateCampaignCriteria(\n MutateCampaignCriteriaRequest::build($customerId, [$operation])\n );\n\n $campaignCriterionResourceName =\n $campaignCriteriaResponse->getResults()[0]->getResourceName();\n printf(\n \"Successfully created campaign criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with campaign with ID %d.%s\",\n $campaignCriterionResourceName,\n $userListResourceName,\n $campaignId,\n PHP_EOL\n );\n\n return $campaignCriterionResourceName;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef target_ads_in_campaign_to_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n campaign_id: str,\n user_list_resource_name: str,\n) -> str:\n \"\"\"Creates a campaign criterion that targets a user list with a campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID used to create an campaign\n criterion.\n campaign_id: a str ID for a campaign used to create a campaign\n criterion that targets members of a user list.\n user_list_resource_name: a str resource name for a user list.\n\n Returns:\n a str resource name for a campaign criterion.\n \"\"\"\n campaign_criterion_operation: CampaignCriterionOperation = client.get_type(\n \"CampaignCriterionOperation\"\n )\n campaign_criterion: CampaignCriterion = campaign_criterion_operation.create\n campaign_criterion.campaign = client.get_service(\n \"CampaignService\"\n ).campaign_path(customer_id, campaign_id)\n campaign_criterion.user_list.user_list = user_list_resource_name\n\n campaign_criterion_service: CampaignCriterionServiceClient = (\n client.get_service(\"CampaignCriterionService\")\n )\n response: MutateCampaignCriteriaResponse = (\n campaign_criterion_service.mutate_campaign_criteria(\n customer_id=customer_id, operations=[campaign_criterion_operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n print(\n \"Successfully created campaign criterion with resource name \"\n f\"'{resource_name}' targeting user list with resource name \"\n f\"'{user_list_resource_name}' with campaign with ID {campaign_id}\"\n )\n return resource_nameset_up_remarketing.py\n```\n\nExample:\n```text\ndef target_ads_in_campaign_to_user_list(\n client,\n customer_id,\n campaign_id,\n user_list\n)\n # Creates the campaign criterion targeting members of the user list.\n operation = client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(customer_id, campaign_id)\n cc.user_list = client.resource.user_list_info do |info|\n info.user_list = user_list\n end\n end\n\n # Issues a mutate request to create the campaign criterion.\n response = client.service.campaign_criterion.mutate_campaign_criteria(\n customer_id: customer_id,\n operations: [operation],\n )\n campaign_criterion_resource_name = response.results.first.resource_name\n puts \"Successfully created campaign criterion with resource name \" \\\n \"'#{campaign_criterion_resource_name}' targeting user list with resource name \" \\\n \"'#{user_list}' with campaign with ID #{campaign_id}\"\n\n campaign_criterion_resource_name\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub target_ads_in_campaign_to_user_list {\n my ($api_client, $customer_id, $campaign_id, $user_list_resource_name) = @_;\n\n # Create the campaign criterion.\n my $campaign_criterion =\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign => Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $campaign_id\n ),\n userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({\n userList => $user_list_resource_name\n })});\n\n # Create the operation.\n my $campaign_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create => $campaign_criterion\n });\n\n # Add the campaign criterion and print the resulting criterion's resource name.\n my $campaign_criteria_response =\n $api_client->CampaignCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_criterion_operation]});\n\n my $campaign_criterion_resource_name =\n $campaign_criteria_response->{results}[0]{resourceName};\n printf \"Successfully created campaign criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with campaign with ID %d.\\n\",\n $campaign_criterion_resource_name, $user_list_resource_name, $campaign_id;\n\n return $campaign_criterion_resource_name;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nSELECT\n ad_group_criterion.criterion_id,\n metrics.conversions,\n metrics.cost_per_conversion\nFROM ad_group_audience_view\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.541Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":32,"totalLines":1267,"estimatedTokens":11810}}213{"id":"doc-asset_optimization_experiments_google_ads_api_go-8214712c","source":"documentation","title":"Asset optimization experiments | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/experiments/asset-optimization","text":"Example:\n```text\nThis example is not yet available in Java; you can take a look at the other languages.\n```\n\nExample:\n```text\nThis example is not yet available in C#; you can take a look at the other languages.\n```\n\nExample:\n```text\nThis example is not yet available in PHP; you can take a look at the other languages.\n```\n\nExample:\n```text\n# 1. Create Assets with temporary resource names.\n# We create a text asset and an image asset to showcase different types.\nasset_operation_1 = create_text_asset_operation(\n client,\n customer_id,\n ASSET_1_TEMP_ID,\n \"Fly to Mars!\",\n)\nasset_operation_2 = create_image_asset_operation(\n client,\n customer_id,\n ASSET_2_TEMP_ID,\n \"https://gaagl.page.link/Eit5\",\n \"Mars Landscape View\",\n)\n\n# 2. Create an Experiment with a temporary resource name.\nexperiment_operation = client.get_type(\"MutateOperation\")\nexperiment = experiment_operation.experiment_operation.create\nexperiment.resource_name = googleads_service.experiment_path(\n customer_id, EXPERIMENT_TEMP_ID\n)\nexperiment.name = f\"Interstellar Asset Experiment #{uuid4()}\"\nexperiment.type_ = client.enums.ExperimentTypeEnum.OPTIMIZE_ASSETS\n# Set the optimize assets experiment subtype to COMPARE_ASSETS.\nexperiment.optimize_assets_experiment.optimize_assets_experiment_subtype = (\n client.enums.OptimizeAssetsExperimentSubtypeEnum.COMPARE_ASSETS\n)\n\n# 3. Create two ExperimentArm resources.\ntreatment_assets = [\n (ASSET_1_TEMP_ID, client.enums.AssetFieldTypeEnum.HEADLINE),\n (ASSET_2_TEMP_ID, client.enums.AssetFieldTypeEnum.MARKETING_IMAGE),\n]\narm_operations = create_arms_operations(\n client,\n customer_id,\n EXPERIMENT_TEMP_ID,\n campaign_resource_name,\n asset_group_id,\n treatment_assets,\n)\n\n# 4. Create AssetGroupAssets linking the assets to the asset group.\nasset_group_asset_operation_1 = create_asset_group_asset_operation(\n client,\n customer_id,\n asset_group_id,\n ASSET_1_TEMP_ID,\n client.enums.AssetFieldTypeEnum.HEADLINE,\n)\nasset_group_asset_operation_2 = create_asset_group_asset_operation(\n client,\n customer_id,\n asset_group_id,\n ASSET_2_TEMP_ID,\n client.enums.AssetFieldTypeEnum.MARKETING_IMAGE,\n)\n\n# Send all operations in a single Mutate request.\n# The operations must be in this specific order.\nmutate_operations = [\n asset_operation_1,\n asset_operation_2,\n experiment_operation,\n *arm_operations,\n asset_group_asset_operation_1,\n asset_group_asset_operation_2,\n]\n\nresponse = googleads_service.mutate(\n customer_id=customer_id,\n mutate_operations=mutate_operations,\n)create_asset_optimization_experiment.py\n```\n\nExample:\n```text\nThis example is not yet available in Ruby; you can take a look at the other languages.\n```\n\nExample:\n```text\nThis example is not yet available in Perl; you can take a look at the other languages.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.542Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":104,"estimatedTokens":714}}214{"id":"doc-get_started_with_customer_match_google_ads_api_g-7c95693a","source":"documentation","title":"Get started with Customer Match | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audience-segments/customer-match/get-started","text":"Example:\n```text\nprivate String createCustomerMatchUserList(GoogleAdsClient googleAdsClient, long customerId) {\n // Creates the new user list.\n UserList userList =\n UserList.newBuilder()\n .setName(\"Customer Match list #\" + getPrintableDateTime())\n .setDescription(\"A list of customers that originated from email addresses\")\n // Membership life span must be between 0 and 540 days inclusive. See:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/UserList#membership_life_span\n // Sets the membership life span to 30 days.\n .setMembershipLifeSpan(30)\n // Sets the upload key type to indicate the type of identifier that will be used to\n // add users to the list. This field is immutable and required for a CREATE operation.\n .setCrmBasedUserList(\n CrmBasedUserListInfo.newBuilder()\n .setUploadKeyType(CustomerMatchUploadKeyType.CONTACT_INFO))\n .build();\n\n // Creates the operation.\n UserListOperation operation = UserListOperation.newBuilder().setCreate(userList).build();\n\n // Creates the service client.\n try (UserListServiceClient userListServiceClient =\n googleAdsClient.getLatestVersion().createUserListServiceClient()) {\n // Adds the user list.\n MutateUserListsResponse response =\n userListServiceClient.mutateUserLists(\n Long.toString(customerId), ImmutableList.of(operation));\n // Prints the response.\n System.out.printf(\n \"Created Customer Match user list with resource name: %s.%n\",\n response.getResults(0).getResourceName());\n return response.getResults(0).getResourceName();\n }\n}\nAddCustomerMatchUserList.java\n```\n\nExample:\n```text\nprivate string CreateCustomerMatchUserList(GoogleAdsClient client, long customerId)\n{\n // Get the UserListService.\n UserListServiceClient service = client.GetService(Services.V25.UserListService);\n\n // Creates the user list.\n UserList userList = new UserList()\n {\n Name = $\"Customer Match list# {ExampleUtilities.GetShortRandomString()}\",\n Description = \"A list of customers that originated from email and physical\" +\n \" addresses\",\n // Membership life span must be between 0 and 540 days inclusive. See:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/UserList#membership_life_span\n // Sets the membership life span to 30 days.\n MembershipLifeSpan = 30,\n CrmBasedUserList = new CrmBasedUserListInfo()\n {\n UploadKeyType = CustomerMatchUploadKeyType.ContactInfo\n }\n };\n // Creates the user list operation.\n UserListOperation operation = new UserListOperation()\n {\n Create = userList\n };\n\n // Issues a mutate request to add the user list and prints some information.\n MutateUserListsResponse response = service.MutateUserLists(\n customerId.ToString(), new[] { operation });\n string userListResourceName = response.Results[0].ResourceName;\n Console.WriteLine($\"User list with resource name '{userListResourceName}' \" +\n $\"was created.\");\n return userListResourceName;\n}AddCustomerMatchUserList.cs\n```\n\nExample:\n```text\nprivate static function createCustomerMatchUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n): string {\n // Creates the user list.\n $userList = new UserList([\n 'name' => 'Customer Match list #' . Helper::getPrintableDatetime(),\n 'description' => 'A list of customers that originated from email '\n . 'and physical addresses',\n // Membership life span must be between 0 and 540 days inclusive. See:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/UserList#membership_life_span\n // Sets the membership life span to 30 days.\n 'membership_life_span' => 30,\n 'crm_based_user_list' => new CrmBasedUserListInfo([\n // Sets the upload key type to indicate the type of identifier that will be used to\n // add users to the list. This field is immutable and required for a CREATE\n // operation.\n 'upload_key_type' => CustomerMatchUploadKeyType::CONTACT_INFO\n ])\n ]);\n\n // Creates the user list operation.\n $operation = new UserListOperation();\n $operation->setCreate($userList);\n\n // Issues a mutate request to add the user list and prints some information.\n $userListServiceClient = $googleAdsClient->getUserListServiceClient();\n $response = $userListServiceClient->mutateUserLists(\n MutateUserListsRequest::build($customerId, [$operation])\n );\n $userListResourceName = $response->getResults()[0]->getResourceName();\n printf(\"User list with resource name '%s' was created.%s\", $userListResourceName, PHP_EOL);\n\n return $userListResourceName;\n}AddCustomerMatchUserList.php\n```\n\nExample:\n```text\ndef create_customer_match_user_list(\n client: GoogleAdsClient, customer_id: str\n) -> str:\n \"\"\"Creates a Customer Match user list.\n\n Args:\n client: The Google Ads client.\n customer_id: The ID for the customer that owns the user list.\n\n Returns:\n The string resource name of the newly created user list.\n \"\"\"\n # Creates the UserListService client.\n user_list_service_client: UserListServiceClient = client.get_service(\n \"UserListService\"\n )\n\n # Creates the user list operation.\n user_list_operation: UserListOperation = client.get_type(\n \"UserListOperation\"\n )\n\n # Creates the new user list.\n user_list: UserList = user_list_operation.create\n user_list.name = f\"Customer Match list #{uuid.uuid4()}\"\n user_list.description = (\n \"A list of customers that originated from email and physical addresses\"\n )\n # Sets the upload key type to indicate the type of identifier that is used\n # to add users to the list. This field is immutable and required for a\n # CREATE operation.\n user_list.crm_based_user_list.upload_key_type = (\n client.enums.CustomerMatchUploadKeyTypeEnum.CONTACT_INFO\n )\n # Membership life span must be between 0 and 540 days inclusive. See:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/UserList#membership_life_span\n # Sets the membership life span to 30 days.\n user_list.membership_life_span = 30\n\n response: MutateUserListsResponse = (\n user_list_service_client.mutate_user_lists(\n customer_id=customer_id, operations=[user_list_operation]\n )\n )\n user_list_resource_name: str = response.results[0].resource_name\n print(\n f\"User list with resource name '{user_list_resource_name}' was created.\"\n )\n\n return user_list_resource_nameadd_customer_match_user_list.py\n```\n\nExample:\n```text\ndef create_customer_match_user_list(client, customer_id)\n # Creates the user list.\n operation = client.operation.create_resource.user_list do |ul|\n ul.name = \"Customer Match List #{(Time.new.to_f * 1000).to_i}\"\n ul.description = \"A list of customers that originated from email and \" \\\n \"physical addresses\"\n # Membership life span must be between 0 and 540 days inclusive. See:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/UserList#membership_life_span\n # Sets the membership life span to 30 days.\n ul.membership_life_span = 30\n ul.crm_based_user_list = client.resource.crm_based_user_list_info do |crm|\n crm.upload_key_type = :CONTACT_INFO\n end\n end\n\n # Issues a mutate request to add the user list and prints some information.\n response = client.service.user_list.mutate_user_lists(\n customer_id: customer_id,\n operations: [operation],\n )\n\n # Prints out some information about the newly created user list.\n resource_name = response.results.first.resource_name\n puts \"User list with resource name #{resource_name} was created.\"\n\n resource_name\nendadd_customer_match_user_list.rb\n```\n\nExample:\n```text\nsub create_customer_match_user_list {\n my ($api_client, $customer_id) = @_;\n\n # Create the user list.\n my $user_list = Google::Ads::GoogleAds::V25::Resources::UserList->new({\n name => \"Customer Match list #\" . uniqid(),\n description =>\n \"A list of customers that originated from email and physical addresses\",\n # Membership life span must be between 0 and 540 days inclusive. See:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/UserList#membership_life_span\n # Set the membership life span to 30 days.\n membershipLifeSpan => 30,\n # Set the upload key type to indicate the type of identifier that will be\n # used to add users to the list. This field is immutable and required for\n # a CREATE operation.\n crmBasedUserList =>\n Google::Ads::GoogleAds::V25::Common::CrmBasedUserListInfo->new({\n uploadKeyType => CONTACT_INFO\n })});\n\n # Create the user list operation.\n my $user_list_operation =\n Google::Ads::GoogleAds::V25::Services::UserListService::UserListOperation->\n new({\n create => $user_list\n });\n\n # Issue a mutate request to add the user list and print some information.\n my $user_lists_response = $api_client->UserListService()->mutate({\n customerId => $customer_id,\n operations => [$user_list_operation]});\n my $user_list_resource_name =\n $user_lists_response->{results}[0]{resourceName};\n printf \"User list with resource name '%s' was created.\\n\",\n $user_list_resource_name;\n\n return $user_list_resource_name;\n}add_customer_match_user_list.pl\n```\n\nExample:\n```text\n// Creates a raw input list of unhashed user information, where each element of the list\n// represents a single user and is a map containing a separate entry for the keys \"email\",\n// \"phone\", \"firstName\", \"lastName\", \"countryCode\", and \"postalCode\". In your application, this\n// data might come from a file or a database.\nList<Map<String, String>> rawRecords = new ArrayList<>();\n// The first user data has an email address and a phone number.\nMap<String, String> rawRecord1 =\n ImmutableMap.<String, String>builder()\n .put(\"email\", \"dana@example.com\")\n // Phone number to be converted to E.164 format, with a leading '+' as required. This\n // includes whitespace that will be removed later.\n .put(\"phone\", \"+1 800 5550101\")\n .build();\n// The second user data has an email address, a mailing address, and a phone number.\nMap<String, String> rawRecord2 =\n ImmutableMap.<String, String>builder()\n // Email address that includes a period (.) before the domain.\n .put(\"email\", \"alex.2@example.com\")\n // Address that includes all four required elements: first name, last name, country\n // code, and postal code.\n .put(\"firstName\", \"Alex\")\n .put(\"lastName\", \"Quinn\")\n .put(\"countryCode\", \"US\")\n .put(\"postalCode\", \"94045\")\n // Phone number to be converted to E.164 format, with a leading '+' as required.\n .put(\"phone\", \"+1 800 5550102\")\n .build();\n// The third user data only has an email address.\nMap<String, String> rawRecord3 =\n ImmutableMap.<String, String>builder().put(\"email\", \"charlie@example.com\").build();\n// Adds the raw records to the raw input list.\nrawRecords.add(rawRecord1);\nrawRecords.add(rawRecord2);\nrawRecords.add(rawRecord3);\n\n// Iterates over the raw input list and creates a UserData object for each record.\nList<UserData> userDataList = new ArrayList<>();\nfor (Map<String, String> rawRecord : rawRecords) {\n // Creates a builder for the UserData object that represents a member of the user list.\n UserData.Builder userDataBuilder = UserData.newBuilder();\n // Checks if the record has email, phone, or address information, and adds a SEPARATE\n // UserIdentifier object for each one found. For example, a record with an email address and a\n // phone number will result in a UserData with two UserIdentifiers.\n\n // IMPORTANT: Since the identifier attribute of UserIdentifier\n // (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is a\n // oneof\n // (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only ONE of\n // hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId, or addressInfo. Setting more\n // than one of these attributes on the same UserIdentifier will clear all the other members\n // of the oneof. For example, the following code is INCORRECT and will result in a\n // UserIdentifier with ONLY a hashedPhoneNumber.\n //\n // UserIdentifier incorrectlyPopulatedUserIdentifier =\n // UserIdentifier.newBuilder()\n // .setHashedEmail(\"...\")\n // .setHashedPhoneNumber(\"...\")\n // .build();\n //\n // The separate 'if' statements below demonstrate the correct approach for creating a UserData\n // for a member with multiple UserIdentifiers.\n\n // Checks if the record has an email address, and if so, adds a UserIdentifier for it.\n if (rawRecord.containsKey(\"email\")) {\n UserIdentifier hashedEmailIdentifier =\n UserIdentifier.newBuilder()\n .setHashedEmail(normalizeAndHash(sha256Digest, rawRecord.get(\"email\"), true))\n .build();\n // Adds the hashed email identifier to the UserData object's list.\n userDataBuilder.addUserIdentifiers(hashedEmailIdentifier);\n }\n\n // Checks if the record has a phone number, and if so, adds a UserIdentifier for it.\n if (rawRecord.containsKey(\"phone\")) {\n UserIdentifier hashedPhoneNumberIdentifier =\n UserIdentifier.newBuilder()\n .setHashedPhoneNumber(normalizeAndHash(sha256Digest, rawRecord.get(\"phone\"), true))\n .build();\n // Adds the hashed phone number identifier to the UserData object's list.\n userDataBuilder.addUserIdentifiers(hashedPhoneNumberIdentifier);\n }\n\n // Checks if the record has all the required mailing address elements, and if so, adds a\n // UserIdentifier for the mailing address.\n if (rawRecord.containsKey(\"firstName\")) {\n // Checks if the record contains all the other required elements of a mailing address.\n Set<String> missingAddressKeys = new HashSet<>();\n for (String addressKey : new String[] {\"lastName\", \"countryCode\", \"postalCode\"}) {\n if (!rawRecord.containsKey(addressKey)) {\n missingAddressKeys.add(addressKey);\n }\n }\n\n if (!missingAddressKeys.isEmpty()) {\n System.out.printf(\n \"Skipping addition of mailing address information because the following required keys\"\n + \" are missing: %s%n\",\n missingAddressKeys);\n } else {\n // Creates an OfflineUserAddressInfo object that contains all the required elements of a\n // mailing address.\n OfflineUserAddressInfo addressInfo =\n OfflineUserAddressInfo.newBuilder()\n .setHashedFirstName(\n normalizeAndHash(sha256Digest, rawRecord.get(\"firstName\"), false))\n .setHashedLastName(\n normalizeAndHash(sha256Digest, rawRecord.get(\"lastName\"), false))\n .setCountryCode(rawRecord.get(\"countryCode\"))\n .setPostalCode(rawRecord.get(\"postalCode\"))\n .build();\n UserIdentifier addressIdentifier =\n UserIdentifier.newBuilder().setAddressInfo(addressInfo).build();\n // Adds the address identifier to the UserData object's list.\n userDataBuilder.addUserIdentifiers(addressIdentifier);\n }\n }\n\n if (!userDataBuilder.getUserIdentifiersList().isEmpty()) {\n // Builds the UserData and adds it to the list.\n userDataList.add(userDataBuilder.build());\n }\n}\n\n// Creates the operations to add users.\nList<OfflineUserDataJobOperation> operations = new ArrayList<>();\nfor (UserData userData : userDataList) {\n operations.add(OfflineUserDataJobOperation.newBuilder().setCreate(userData).build());\n}AddCustomerMatchUserList.java\n```\n\nExample:\n```text\n// Creates a raw input list of unhashed user information, where each element of the list\n// represents a single user and is a map containing a separate entry for the keys\n// \"email\", \"phone\", \"firstName\", \"lastName\", \"countryCode\", and \"postalCode\".\n// In your application, this data might come from a file or a database.\nList<Dictionary<string, string>> rawRecords = new List<Dictionary<string, string>>();\n\n// The first user data has an email address and a phone number.\nDictionary<string, string> rawRecord1 = new Dictionary<string, string>();\nrawRecord1.Add(\"email\", \"dana@example.com\");\n// Phone number to be converted to E.164 format, with a leading '+' as required.\n// This includes whitespace that will be removed later.\nrawRecord1.Add(\"phone\", \"+1 800 5550101\");\n\n// The second user data has an email address, a mailing address, and a phone number.\nDictionary<string, string> rawRecord2 = new Dictionary<string, string>();\n// Email address that includes a period (.) before the Gmail domain.\nrawRecord2.Add(\"email\", \"alex.2@example.com\");\n// Address that includes all four required elements: first name, last name, country\n// code, and postal code.\nrawRecord2.Add(\"firstName\", \"Alex\");\nrawRecord2.Add(\"lastName\", \"Quinn\");\nrawRecord2.Add(\"countryCode\", \"US\");\nrawRecord2.Add(\"postalCode\", \"94045\");\n// Phone number to be converted to E.164 format, with a leading '+' as required.\n// This includes whitespace that will be removed later.\nrawRecord2.Add(\"phone\", \"+1 800 5550102\");\n\n// The third user data only has an email address.\nDictionary<string, string> rawRecord3 = new Dictionary<string, string>();\nrawRecord3.Add(\"email\", \"charlie@example.com\");\n\n// Adds the raw records to the raw input list.\nrawRecords.Add(rawRecord1);\nrawRecords.Add(rawRecord2);\nrawRecords.Add(rawRecord3);\n\n// Iterates over the raw input list and creates a UserData object for each record.\nList<UserData> userDataList = new List<UserData>();\nforeach (Dictionary<string, string> rawRecord in rawRecords) {\n // Creates a UserData object that represents a member of the user list.\n UserData userData = new UserData();\n // Checks if the record has email, phone, or address information, and adds a\n // SEPARATE UserIdentifier object for each one found.\n // For example, a record with an email address and a phone number will result in a\n // UserData with two UserIdentifiers.\n\n // IMPORTANT: Since the identifier attribute of UserIdentifier\n // (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)\n // is a oneof\n // (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set\n // only ONE of hashedEmail, hashedPhoneNumber, mobileId, thirdPartyUserId,\n // or addressInfo.\n // Setting more than one of these attributes on the same UserIdentifier will clear\n // all the other members of the oneof.\n // For example, the following code is INCORRECT and will result in a UserIdentifier\n // with ONLY a hashedPhoneNumber.\n //\n // UserIdentifier incorrectlyPopulatedUserIdentifier = new UserIdentifier()\n // {\n // HashedEmail = \"...\",\n // HashedPhoneNumber = \"...\"\n // };\n //\n // The separate 'if' statements below demonstrate the correct approach for creating\n // a UserData for a member with multiple UserIdentifiers.\n\n // Checks if the record has an email address, and if so, adds a UserIdentifier\n // for it.\n if (rawRecord.ContainsKey(\"email\")) {\n UserIdentifier hashedEmailIdentifier = new UserIdentifier()\n {\n HashedEmail = NormalizeAndHash(rawRecord[\"email\"], true)\n };\n\n userData.UserIdentifiers.Add(hashedEmailIdentifier);\n }\n\n // Checks if the record has a phone number, and if so, adds a UserIdentifier for it.\n if (rawRecord.ContainsKey(\"phone\")) {\n UserIdentifier hashedPhoneNumberIdentifier = new UserIdentifier()\n {\n HashedPhoneNumber = NormalizeAndHash(rawRecord[\"phone\"], true)\n };\n\n // Adds the hashed phone number identifier to the UserData object's list.\n userData.UserIdentifiers.Add(hashedPhoneNumberIdentifier);\n }\n\n // Checks if the record has all the required mailing address elements, and if so,\n // adds a UserIdentifier for the mailing address.\n if (rawRecord.ContainsKey(\"firstName\")) {\n // Checks if the record contains all the other required elements of a mailing\n // address.\n HashSet<string> missingAddressKeys = new HashSet<string>();\n foreach (string addressKey in new string[] {\"lastName\", \"countryCode\",\n \"postalCode\"}) {\n if (!rawRecord.ContainsKey(addressKey)) {\n missingAddressKeys.Add(addressKey);\n }\n }\n\n if (!missingAddressKeys.Any()) {\n Console.WriteLine(\n $\"Skipping addition of mailing address information because the following \" +\n \"required keys are missing: {missingAddressKeys}\");\n } else {\n // Creates an OfflineUserAddressInfo object that contains all the required\n // elements of a mailing address.\n OfflineUserAddressInfo addressInfo = new OfflineUserAddressInfo()\n {\n HashedFirstName = NormalizeAndHash(rawRecord[\"firstName\"]),\n HashedLastName = NormalizeAndHash(rawRecord[\"lastName\"]),\n CountryCode = rawRecord[\"countryCode\"],\n PostalCode = rawRecord[\"postalCode\"]\n };\n\n UserIdentifier addressIdentifier = new UserIdentifier()\n {\n AddressInfo = addressInfo\n };\n\n // Adds the address identifier to the UserData object's list.\n userData.UserIdentifiers.Add(addressIdentifier);\n }\n }\n\n if (userData.UserIdentifiers.Any())\n {\n userDataList.Add(userData);\n }\n}\n\n// Creates the operations to add the users.\nList<OfflineUserDataJobOperation> operations = new List<OfflineUserDataJobOperation>();\nforeach(UserData userData in userDataList)\n{\n operations.Add(new OfflineUserDataJobOperation()\n {\n Create = userData\n });\n}AddCustomerMatchUserList.cs\n```\n\nExample:\n```text\n// Creates a raw input list of unhashed user information, where each element of the list\n// represents a single user and is a map containing a separate entry for the keys 'email',\n// 'phone', 'firstName', 'lastName', 'countryCode', and 'postalCode'. In your application,\n// this data might come from a file or a database.\n$rawRecords = [];\n// The first user data has an email address and a phone number.\n$rawRecord1 = [\n // The first user data has an email address and a phone number.\n 'email' => 'dana@example.com',\n // Phone number to be converted to E.164 format, with a leading '+' as required. This\n // includes whitespace that will be removed later.\n 'phone' => '+1 800 5550101'\n];\n$rawRecords[] = $rawRecord1;\n\n// The second user data has an email address, a mailing address, and a phone number.\n$rawRecord2 = [\n // Email address that includes a period (.) before the Gmail domain.\n 'email' => 'alex.2@example.com',\n // Address that includes all four required elements: first name, last name, country\n // code, and postal code.\n 'firstName' => 'Alex',\n 'lastName' => 'Quinn',\n 'countryCode' => 'US',\n 'postalCode' => '94045',\n // Phone number to be converted to E.164 format, with a leading '+' as required.\n 'phone' => '+1 800 5550102',\n];\n$rawRecords[] = $rawRecord2;\n\n// The third user data only has an email address.\n$rawRecord3 = ['email' => 'charlie@example.com'];\n$rawRecords[] = $rawRecord3;\n\n// Iterates over the raw input list and creates a UserData object for each record.\n$userDataList = [];\nforeach ($rawRecords as $rawRecord) {\n // Checks if the record has email, phone, or address information, and adds a SEPARATE\n // UserIdentifier object for each one found. For example, a record with an email address\n // and a phone number will result in a UserData with two UserIdentifiers.\n\n // IMPORTANT: Since the identifier attribute of UserIdentifier\n // (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier) is\n // a oneof\n // (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set only\n // ONE of 'hashed_email, 'hashed_phone_number', 'mobile_id', 'third_party_user_id', or\n // 'address_info'.\n // Setting more than one of these attributes on the same UserIdentifier will clear all\n // the other members of the oneof. For example, the following code is INCORRECT and will\n // result in a UserIdentifier with ONLY a 'hashed_phone_number'.\n //\n // $incorrectlyPopulatedUserIdentifier = new UserIdentifier();\n // $incorrectlyPopulatedUserIdentifier->setHashedEmail('...');\n // $incorrectlyPopulatedUserIdentifier->setHashedPhoneNumber('...');\n //\n // The separate 'if' statements below demonstrate the correct approach for creating a\n // UserData for a member with multiple UserIdentifiers.\n\n $userIdentifiers = [];\n // Checks if the record has an email address, and if so, adds a UserIdentifier for it.\n if (array_key_exists('email', $rawRecord)) {\n $hashedEmailIdentifier = new UserIdentifier([\n 'hashed_email' => self::normalizeAndHash($rawRecord['email'], true)\n ]);\n // Adds the hashed email identifier to the user identifiers list.\n $userIdentifiers[] = $hashedEmailIdentifier;\n }\n\n // Checks if the record has a phone number, and if so, adds a UserIdentifier for it.\n if (array_key_exists('phone', $rawRecord)) {\n $hashedPhoneNumberIdentifier = new UserIdentifier([\n 'hashed_phone_number' => self::normalizeAndHash($rawRecord['phone'], true)\n ]);\n // Adds the hashed email identifier to the user identifiers list.\n $userIdentifiers[] = $hashedPhoneNumberIdentifier;\n }\n\n // Checks if the record has all the required mailing address elements, and if so, adds a\n // UserIdentifier for the mailing address.\n if (array_key_exists('firstName', $rawRecord)) {\n // Checks if the record contains all the other required elements of a mailing\n // address.\n $missingAddressKeys = [];\n foreach (['lastName', 'countryCode', 'postalCode'] as $addressKey) {\n if (!array_key_exists($addressKey, $rawRecord)) {\n $missingAddressKeys[] = $addressKey;\n }\n }\n if (!empty($missingAddressKeys)) {\n printf(\n \"Skipping addition of mailing address information because the \"\n . \"following required keys are missing: %s%s\",\n json_encode($missingAddressKeys),\n PHP_EOL\n );\n } else {\n // Creates an OfflineUserAddressInfo object that contains all the required\n // elements of a mailing address.\n $addressIdentifier = new UserIdentifier([\n 'address_info' => new OfflineUserAddressInfo([\n 'hashed_first_name' => self::normalizeAndHash(\n $rawRecord['firstName'],\n false\n ),\n 'hashed_last_name' => self::normalizeAndHash(\n $rawRecord['lastName'],\n false\n ),\n 'country_code' => $rawRecord['countryCode'],\n 'postal_code' => $rawRecord['postalCode']\n ])\n ]);\n // Adds the address identifier to the user identifiers list.\n $userIdentifiers[] = $addressIdentifier;\n }\n }\n if (!empty($userIdentifiers)) {\n // Builds the UserData and adds it to the list.\n $userDataList[] = new UserData(['user_identifiers' => $userIdentifiers]);\n }\n}\n\n// Creates the operations to add users.\n$operations = array_map(\n function (UserData $userData) {\n return new OfflineUserDataJobOperation(['create' => $userData]);\n },\n $userDataList\n);AddCustomerMatchUserList.php\n```\n\nExample:\n```text\ndef build_offline_user_data_job_operations(\n client: GoogleAdsClient,\n) -> List[OfflineUserDataJobOperation]:\n \"\"\"Creates a raw input list of unhashed user information.\n\n Each element of the list represents a single user and is a dict containing a\n separate entry for the keys \"email\", \"phone\", \"first_name\", \"last_name\",\n \"country_code\", and \"postal_code\". In your application, this data might come\n from a file or a database.\n\n Args:\n client: The Google Ads client.\n\n Returns:\n A list containing the operations.\n \"\"\"\n # The first user data has an email address and a phone number.\n raw_record_1: Dict[str, str] = {\n \"email\": \"dana@example.com\",\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required. This includes whitespace that will be removed later.\n \"phone\": \"+1 800 5550101\",\n }\n\n # The second user data has an email address, a mailing address, and a phone\n # number.\n raw_record_2: Dict[str, str] = {\n # Email address that includes a period (.) before the email domain.\n \"email\": \"alex.2@example.com\",\n # Address that includes all four required elements: first name, last\n # name, country code, and postal code.\n \"first_name\": \"Alex\",\n \"last_name\": \"Quinn\",\n \"country_code\": \"US\",\n \"postal_code\": \"94045\",\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required.\n \"phone\": \"+1 800 5550102\",\n }\n\n # The third user data only has an email address.\n raw_record_3: Dict[str, str] = {\"email\": \"charlie@example.com\"}\n\n # Adds the raw records to a raw input list.\n raw_records: List[Dict[str, str]] = [\n raw_record_1,\n raw_record_2,\n raw_record_3,\n ]\n\n operations: List[OfflineUserDataJobOperation] = []\n # Iterates over the raw input list and creates a UserData object for each\n # record.\n for record in raw_records:\n # Creates a UserData object that represents a member of the user list.\n user_data: UserData = client.get_type(\"UserData\")\n\n # Checks if the record has email, phone, or address information, and\n # adds a SEPARATE UserIdentifier object for each one found. For example,\n # a record with an email address and a phone number will result in a\n # UserData with two UserIdentifiers.\n\n # IMPORTANT: Since the identifier attribute of UserIdentifier\n # (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)\n # is a oneof\n # (https://protobuf.dev/programming-guides/proto3/#oneof-features), you\n # must set only ONE of hashed_email, hashed_phone_number, mobile_id,\n # third_party_user_id, or address-info. Setting more than one of these\n # attributes on the same UserIdentifier will clear all the other members\n # of the oneof. For example, the following code is INCORRECT and will\n # result in a UserIdentifier with ONLY a hashed_phone_number:\n\n # incorrect_user_identifier = client.get_type(\"UserIdentifier\")\n # incorrect_user_identifier.hashed_email = \"...\"\n # incorrect_user_identifier.hashed_phone_number = \"...\"\n\n # The separate 'if' statements below demonstrate the correct approach\n # for creating a UserData object for a member with multiple\n # UserIdentifiers.\n\n # Checks if the record has an email address, and if so, adds a\n # UserIdentifier for it.\n if \"email\" in record:\n user_identifier: UserIdentifier = client.get_type(\"UserIdentifier\")\n user_identifier.hashed_email = normalize_and_hash(\n record[\"email\"], True\n )\n # Adds the hashed email identifier to the UserData object's list.\n user_data.user_identifiers.append(user_identifier)\n\n # Checks if the record has a phone number, and if so, adds a\n # UserIdentifier for it.\n if \"phone\" in record:\n user_identifier: UserIdentifier = client.get_type(\"UserIdentifier\")\n user_identifier.hashed_phone_number = normalize_and_hash(\n record[\"phone\"], True\n )\n # Adds the hashed phone number identifier to the UserData object's\n # list.\n user_data.user_identifiers.append(user_identifier)\n\n # Checks if the record has all the required mailing address elements,\n # and if so, adds a UserIdentifier for the mailing address.\n if \"first_name\" in record:\n required_keys = (\"last_name\", \"country_code\", \"postal_code\")\n # Checks if the record contains all the other required elements of\n # a mailing address.\n if not all(key in record for key in required_keys):\n # Determines which required elements are missing from the\n # record.\n missing_keys = record.keys() - required_keys\n print(\n \"Skipping addition of mailing address information \"\n \"because the following required keys are missing: \"\n f\"{missing_keys}\"\n )\n else:\n user_identifier: UserIdentifier = client.get_type(\n \"UserIdentifier\"\n )\n address_info: AddressInfo = user_identifier.address_info\n address_info.hashed_first_name = normalize_and_hash(\n record[\"first_name\"], False\n )\n address_info.hashed_last_name = normalize_and_hash(\n record[\"last_name\"], False\n )\n address_info.country_code = record[\"country_code\"]\n address_info.postal_code = record[\"postal_code\"]\n user_data.user_identifiers.append(user_identifier)\n\n # If the user_identifiers repeated field is not empty, create a new\n # OfflineUserDataJobOperation and add the UserData to it.\n if user_data.user_identifiers:\n operation: OfflineUserDataJobOperation = client.get_type(\n \"OfflineUserDataJobOperation\"\n )\n operation.create = user_data\n operations.append(operation)add_customer_match_user_list.py\n```\n\nExample:\n```text\n# Create a list of unhashed user data records that we will format in the\n# following steps to prepare for the API.\nraw_records = [\n # The first user data has an email address and a phone number.\n {\n email: 'dana@example.com',\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required. This includes whitespace that will be removed later.\n phone: '+1 800 5550100',\n },\n # The second user data has an email address, a phone number, and an address.\n {\n # Email address that includes a period (.) before the Gmail domain.\n email: 'alex.2@example.com',\n # Address that includes all four required elements: first name, last\n # name, country code, and postal code.\n first_name: 'Alex',\n last_name: 'Quinn',\n country_code: 'US',\n postal_code: '94045',\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required.\n phone: '+1 800 5550102',\n },\n # The third user data only has an email address.\n {\n email: 'charlie@example.com',\n },\n]\n\n# Create a UserData for each entry in the raw records.\nuser_data_list = raw_records.map do |record|\n client.resource.user_data do |data|\n if record[:email]\n data.user_identifiers << client.resource.user_identifier do |ui|\n ui.hashed_email = normalize_and_hash(record[:email], true)\n end\n end\n if record[:phone]\n data.user_identifiers << client.resource.user_identifier do |ui|\n ui.hashed_phone_number = normalize_and_hash(record[:phone], true)\n end\n end\n if record[:first_name]\n # Check that we have all the required information.\n missing_keys = [:last_name, :country_code, :postal_code].reject {|key|\n record[key].nil?\n }\n if missing_keys.empty?\n # If nothing is missing, add the address.\n data.user_identifiers << client.resource.user_identifier do |ui|\n ui.address_identifier = client.resource.offline_user_address_info do |address|\n address.hashed_first_name = normalize_and_hash(record[:first_name])\n address.hashed_last_name = normalize_and_hash(record[:last_name])\n address.country_code = record[:country_code]\n address.postal_code = record[:postal_code]\n end\n end\n else\n # If some data is missing, skip this entry.\n puts \"Skipping addition of mailing information because the following keys are missing:\" \\\n \"#{missing_keys}\"\n end\n end\n end\nend\n\noperations = user_data_list.map do |user_data|\n client.operation.create_resource.offline_user_data_job(user_data)\nendadd_customer_match_user_list.rb\n```\n\nExample:\n```text\n# The first user data has an email address and a phone number.\n my $raw_record_1 = {\n email => 'dana@example.com',\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required. This includes whitespace that will be removed later.\n phone => '+1 800 5550101',\n };\n\n # The second user data has an email address, a mailing address, and a phone\n # number.\n my $raw_record_2 = {\n # Email address that includes a period (.) before the Gmail domain.\n email => 'alex.2@example.com',\n # Address that includes all four required elements: first name, last\n # name, country code, and postal code.\n firstName => 'Alex',\n lastName => 'Quinn',\n countryCode => 'US',\n postalCode => '94045',\n # Phone number to be converted to E.164 format, with a leading '+' as\n # required.\n phone => '+1 800 5550102',\n };\n\n # The third user data only has an email address.\n my $raw_record_3 = {email => 'charlie@example.com',};\n\n my $raw_records = [$raw_record_1, $raw_record_2, $raw_record_3];\n\n my $operations = [];\n foreach my $record (@$raw_records) {\n # Check if the record has email, phone, or address information, and adds a\n # SEPARATE UserIdentifier object for each one found. For example, a record\n # with an email address and a phone number will result in a UserData with two\n # UserIdentifiers.\n #\n # IMPORTANT: Since the identifier attribute of UserIdentifier\n # (https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier)\n # is a oneof\n # (https://protobuf.dev/programming-guides/proto3/#oneof-features), you must set\n # only ONE of hashed_email, hashed_phone_number, mobile_id, third_party_user_id,\n # or address-info. Setting more than one of these attributes on the same UserIdentifier\n # will clear all the other members of the oneof. For example, the following code is\n # INCORRECT and will result in a UserIdentifier with ONLY a hashed_phone_number:\n #\n # my $incorrect_user_identifier = Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n # hashedEmail => '...',\n # hashedPhoneNumber => '...',\n # });\n #\n # The separate 'if' statements below demonstrate the correct approach for creating a\n # UserData object for a member with multiple UserIdentifiers.\n\n my $user_identifiers = [];\n\n # Check if the record has an email address, and if so, add a UserIdentifier for it.\n if (defined $record->{email}) {\n # Add the hashed email identifier to the list of UserIdentifiers.\n push(\n @$user_identifiers,\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n hashedEmail => normalize_and_hash($record->{email}, 1)}));\n }\n\n # Check if the record has a phone number, and if so, add a UserIdentifier for it.\n if (defined $record->{phone}) {\n # Add the hashed phone number identifier to the list of UserIdentifiers.\n push(\n @$user_identifiers,\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n hashedPhoneNumber => normalize_and_hash($record->{phone}, 1)}));\n }\n\n # Check if the record has all the required mailing address elements, and if so, add\n # a UserIdentifier for the mailing address.\n if (defined $record->{firstName}) {\n my $required_keys = [\"lastName\", \"countryCode\", \"postalCode\"];\n my $missing_keys = [];\n\n foreach my $key (@$required_keys) {\n if (!defined $record->{$key}) {\n push(@$missing_keys, $key);\n }\n }\n\n if (@$missing_keys) {\n print\n\"Skipping addition of mailing address information because the following\"\n . \"keys are missing: \"\n . join(\",\", @$missing_keys);\n } else {\n push(\n @$user_identifiers,\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n addressInfo =>\n Google::Ads::GoogleAds::V25::Common::OfflineUserAddressInfo->\n new({\n # First and last name must be normalized and hashed.\n hashedFirstName => normalize_and_hash($record->{firstName}),\n hashedLastName => normalize_and_hash($record->{lastName}),\n # Country code and zip code are sent in plain text.\n countryCode => $record->{countryCode},\n postalCode => $record->{postalCode},\n })}));\n }\n }\n\n # If the user_identifiers array is not empty, create a new\n # OfflineUserDataJobOperation and add the UserData to it.\n if (@$user_identifiers) {\n my $user_data = Google::Ads::GoogleAds::V25::Common::UserData->new({\n userIdentifiers => [$user_identifiers]});\n push(\n @$operations,\n Google::Ads::GoogleAds::V25::Services::OfflineUserDataJobService::OfflineUserDataJobOperation\n ->new({\n create => $user_data\n }));\n }\n }add_customer_match_user_list.pl\n```\n\nExample:\n```text\nprivate void checkJobStatus(\n GoogleAdsClient googleAdsClient, long customerId, String offlineUserDataJobResourceName) {\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n String query =\n String.format(\n \"SELECT offline_user_data_job.resource_name, \"\n + \"offline_user_data_job.id, \"\n + \"offline_user_data_job.status, \"\n + \"offline_user_data_job.type, \"\n + \"offline_user_data_job.failure_reason, \"\n + \"offline_user_data_job.customer_match_user_list_metadata.user_list \"\n + \"FROM offline_user_data_job \"\n + \"WHERE offline_user_data_job.resource_name = '%s'\",\n offlineUserDataJobResourceName);\n // Issues the query and gets the GoogleAdsRow containing the job from the response.\n GoogleAdsRow googleAdsRow =\n googleAdsServiceClient\n .search(Long.toString(customerId), query)\n .iterateAll()\n .iterator()\n .next();\n OfflineUserDataJob offlineUserDataJob = googleAdsRow.getOfflineUserDataJob();\n System.out.printf(\n \"Offline user data job ID %d with type '%s' has status: %s%n\",\n offlineUserDataJob.getId(), offlineUserDataJob.getType(), offlineUserDataJob.getStatus());\n OfflineUserDataJobStatus jobStatus = offlineUserDataJob.getStatus();\n if (OfflineUserDataJobStatus.SUCCESS == jobStatus) {\n // Prints information about the user list.\n printCustomerMatchUserListInfo(\n googleAdsClient,\n customerId,\n offlineUserDataJob.getCustomerMatchUserListMetadata().getUserList());\n } else if (OfflineUserDataJobStatus.FAILED == jobStatus) {\n System.out.printf(\" Failure reason: %s%n\", offlineUserDataJob.getFailureReason());\n } else if (OfflineUserDataJobStatus.PENDING == jobStatus\n || OfflineUserDataJobStatus.RUNNING == jobStatus) {\n System.out.println();\n System.out.printf(\n \"To check the status of the job periodically, use the following GAQL query with\"\n + \" GoogleAdsService.search:%n%s%n\",\n query);\n }\n }\n}\nAddCustomerMatchUserList.java\n```\n\nExample:\n```text\nprivate static void CheckJobStatusAndPrintResults(GoogleAdsClient client, long customerId,\n string offlineUserDataJobResourceName)\n{\n // Get the GoogleAdsService.\n GoogleAdsServiceClient service = client.GetService(Services.V25.GoogleAdsService);\n\n string query = \"SELECT offline_user_data_job.resource_name, \" +\n \"offline_user_data_job.id, offline_user_data_job.status, \" +\n \"offline_user_data_job.type, offline_user_data_job.failure_reason, \" +\n \"offline_user_data_job.customer_match_user_list_metadata.user_list \" +\n \"FROM offline_user_data_job WHERE \" +\n $\"offline_user_data_job.resource_name = '{offlineUserDataJobResourceName}'\";\n\n // Issues the query and gets the GoogleAdsRow containing the job from the response.\n GoogleAdsRow googleAdsRow = service.Search(customerId.ToString(), query).First();\n\n OfflineUserDataJob offlineUserDataJob = googleAdsRow.OfflineUserDataJob;\n Console.WriteLine($\"Offline user data job ID {offlineUserDataJob.Id} with type \" +\n $\"'{offlineUserDataJob.Type}' has status: {offlineUserDataJob.Status}\");\n\n switch (offlineUserDataJob.Status)\n {\n case OfflineUserDataJobStatus.Success:\n // Prints information about the user list.\n PrintCustomerMatchUserListInfo(client, customerId,\n offlineUserDataJob.CustomerMatchUserListMetadata.UserList);\n break;\n\n case OfflineUserDataJobStatus.Failed:\n Console.WriteLine($\" Failure reason: {offlineUserDataJob.FailureReason}\");\n break;\n\n case OfflineUserDataJobStatus.Pending:\n case OfflineUserDataJobStatus.Running:\n Console.WriteLine(\"To check the status of the job periodically, use the \" +\n $\"following GAQL query with GoogleAdsService.search:\\n\\n{query}\");\n break;\n }\n}AddCustomerMatchUserList.cs\n```\n\nExample:\n```text\nprivate static function checkJobStatus(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $offlineUserDataJobResourceName\n) {\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n\n // Creates a query that retrieves the offline user data job.\n $query = \"SELECT offline_user_data_job.resource_name, \"\n . \"offline_user_data_job.id, \"\n . \"offline_user_data_job.status, \"\n . \"offline_user_data_job.type, \"\n . \"offline_user_data_job.failure_reason, \"\n . \"offline_user_data_job.customer_match_user_list_metadata.user_list \"\n . \"FROM offline_user_data_job \"\n . \"WHERE offline_user_data_job.resource_name = '$offlineUserDataJobResourceName'\";\n\n // Issues a search request to get the GoogleAdsRow containing the job from the response.\n /** @var GoogleAdsRow $googleAdsRow */\n $googleAdsRow =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query))\n ->getIterator()\n ->current();\n $offlineUserDataJob = $googleAdsRow->getOfflineUserDataJob();\n\n // Prints out some information about the offline user data job.\n $offlineUserDataJobStatus = $offlineUserDataJob->getStatus();\n printf(\n \"Offline user data job ID %d with type '%s' has status: %s.%s\",\n $offlineUserDataJob->getId(),\n OfflineUserDataJobType::name($offlineUserDataJob->getType()),\n OfflineUserDataJobStatus::name($offlineUserDataJobStatus),\n PHP_EOL\n );\n\n if ($offlineUserDataJobStatus === OfflineUserDataJobStatus::SUCCESS) {\n // Prints information about the user list.\n self::printCustomerMatchUserListInfo(\n $googleAdsClient,\n $customerId,\n $offlineUserDataJob->getCustomerMatchUserListMetadata()->getUserList()\n );\n } elseif ($offlineUserDataJobStatus === OfflineUserDataJobStatus::FAILED) {\n printf(\" Failure reason: %s.%s\", $offlineUserDataJob->getFailureReason(), PHP_EOL);\n } elseif (\n $offlineUserDataJobStatus === OfflineUserDataJobStatus::PENDING\n || $offlineUserDataJobStatus === OfflineUserDataJobStatus::RUNNING\n ) {\n printf(\n '%1$sTo check the status of the job periodically, use the following GAQL query with'\n . ' GoogleAdsService.search:%1$s%2$s%1$s',\n PHP_EOL,\n $query\n );\n }\n}AddCustomerMatchUserList.php\n```\n\nExample:\n```text\ndef check_job_status(\n client: GoogleAdsClient,\n customer_id: str,\n offline_user_data_job_resource_name: str,\n) -> None:\n \"\"\"Retrieves, checks, and prints the status of the offline user data job.\n\n If the job is completed successfully, information about the user list is\n printed. Otherwise, a GAQL query will be printed, which can be used to\n check the job status at a later date.\n\n Offline user data jobs may take 6 hours or more to complete, so checking the\n status periodically, instead of waiting, can be more efficient.\n\n Args:\n client: The Google Ads client.\n customer_id: The ID for the customer that owns the user list.\n offline_user_data_job_resource_name: The resource name of the offline\n user data job to get the status of.\n \"\"\"\n query: str = f\"\"\"\n SELECT\n offline_user_data_job.resource_name,\n offline_user_data_job.id,\n offline_user_data_job.status,\n offline_user_data_job.type,\n offline_user_data_job.failure_reason,\n offline_user_data_job.customer_match_user_list_metadata.user_list\n FROM offline_user_data_job\n WHERE offline_user_data_job.resource_name =\n '{offline_user_data_job_resource_name}'\n LIMIT 1\"\"\"\n\n # Issues a search request using streaming.\n google_ads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n results: SearchGoogleAdsStreamResponse = google_ads_service.search(\n customer_id=customer_id, query=query\n )\n offline_user_data_job_result: OfflineUserDataJob = next(\n iter(results)\n ).offline_user_data_job\n status_name: str = offline_user_data_job_result.status.name\n user_list_resource_name: str = (\n offline_user_data_job_result.customer_match_user_list_metadata.user_list\n )\n\n print(\n f\"Offline user data job ID '{offline_user_data_job_result.id}' with type \"\n f\"'{offline_user_data_job_result.type_.name}' has status: {status_name}\"\n )\n\n if status_name == \"SUCCESS\":\n print_customer_match_user_list_info(\n client, customer_id, user_list_resource_name\n )\n elif status_name == \"FAILED\":\n print(\n f\"\\tFailure Reason: {offline_user_data_job_result.failure_reason}\"\n )\n elif status_name in (\"PENDING\", \"RUNNING\"):\n print(\n \"To check the status of the job periodically, use the following \"\n f\"GAQL query with GoogleAdsService.Search: {query}\"\n )add_customer_match_user_list.py\n```\n\nExample:\n```text\ndef check_job_status(client, customer_id, offline_user_data_job)\n query = <<~QUERY\n SELECT\n offline_user_data_job.id,\n offline_user_data_job.status,\n offline_user_data_job.type,\n offline_user_data_job.failure_reason,\n offline_user_data_job.customer_match_user_list_metadata.user_list\n FROM\n offline_user_data_job\n WHERE\n offline_user_data_job.resource_name = '#{offline_user_data_job}'\n QUERY\n\n row = client.service.google_ads.search(\n customer_id: customer_id,\n query: query,\n ).first\n\n job = row.offline_user_data_job\n puts \"Offline user data job ID #{job.id} with type '#{job.type}' has status: #{job.status}.\"\n\n case job.status\n when :SUCCESS\n print_customer_match_user_list(client, customer_id, job.customer_match_user_list_metadata.user_list)\n when :FAILED\n puts \" Failure reason: #{job.failure_reason}\"\n else\n puts \" To check the status of the job periodically, use the following GAQL \" \\\n \"query with GoogleAdsService.search:\"\n puts query\n end\nendadd_customer_match_user_list.rb\n```\n\nExample:\n```text\nsub check_job_status {\n my ($api_client, $customer_id, $offline_user_data_job_resource_name) = @_;\n\n my $search_query =\n \"SELECT offline_user_data_job.resource_name, \" .\n \"offline_user_data_job.id, offline_user_data_job.status, \" .\n \"offline_user_data_job.type, offline_user_data_job.failure_reason, \" .\n \"offline_user_data_job.customer_match_user_list_metadata.user_list \" .\n \"FROM offline_user_data_job \" .\n \"WHERE offline_user_data_job.resource_name = \" .\n \"'$offline_user_data_job_resource_name' LIMIT 1\";\n\n my $search_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsRequest\n ->new({\n customerId => $customer_id,\n query => $search_query\n });\n\n # Get the GoogleAdsService.\n my $google_ads_service = $api_client->GoogleAdsService();\n\n my $iterator = Google::Ads::GoogleAds::Utils::SearchGoogleAdsIterator->new({\n service => $google_ads_service,\n request => $search_request\n });\n\n # The results have exactly one row.\n my $google_ads_row = $iterator->next;\n my $offline_user_data_job = $google_ads_row->{offlineUserDataJob};\n my $status = $offline_user_data_job->{status};\n\n printf\n \"Offline user data job ID %d with type %s has status: %s.\\n\",\n $offline_user_data_job->{id},\n $offline_user_data_job->{type},\n $status;\n\n if ($status eq SUCCESS) {\n print_customer_match_user_list_info($api_client, $customer_id,\n $offline_user_data_job->{customerMatchUserListMetadata}{userList});\n } elsif ($status eq FAILED) {\n print \"Failure reason: $offline_user_data_job->{failureReason}\";\n } elsif (grep /$status/, (PENDING, RUNNING)) {\n print\n \"To check the status of the job periodically, use the following GAQL \" .\n \"query with the GoogleAdsService->search() method:\\n$search_query\\n\";\n }\n\n return 1;\n}add_customer_match_user_list.pl\n```\n\nExample:\n```text\ntry (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Creates a query that retrieves the user list.\n String query =\n String.format(\n \"SELECT user_list.size_for_display, user_list.size_for_search \"\n + \"FROM user_list \"\n + \"WHERE user_list.resource_name = '%s'\",\n userListResourceName);\n\n // Constructs the SearchGoogleAdsStreamRequest.\n SearchGoogleAdsStreamRequest request =\n SearchGoogleAdsStreamRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setQuery(query)\n .build();\n\n // Issues the search stream request.\n ServerStream<SearchGoogleAdsStreamResponse> stream =\n googleAdsServiceClient.searchStreamCallable().call(request);AddCustomerMatchUserList.java\n```\n\nExample:\n```text\n// Get the GoogleAdsService.\n GoogleAdsServiceClient service =\n client.GetService(Services.V25.GoogleAdsService);\n\n // Creates a query that retrieves the user list.\n string query =\n \"SELECT user_list.size_for_display, user_list.size_for_search \" +\n \"FROM user_list \" +\n $\"WHERE user_list.resource_name = '{userListResourceName}'\";\n // Issues a search stream request.\n service.SearchStream(customerId.ToString(), query,\n delegate (SearchGoogleAdsStreamResponse resp)\n {\n // Display the results.\n foreach (GoogleAdsRow userListRow in resp.Results)\n {\n UserList userList = userListRow.UserList;\n Console.WriteLine(\"The estimated number of users that the user list \" +\n $\"'{userList.ResourceName}' has is {userList.SizeForDisplay}\" +\n $\" for Display and {userList.SizeForSearch} for Search.\");\n }\n }\n);AddCustomerMatchUserList.cs\n```\n\nExample:\n```text\n$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n\n// Creates a query that retrieves the user list.\n$query =\n \"SELECT user_list.size_for_display, user_list.size_for_search \" .\n \"FROM user_list \" .\n \"WHERE user_list.resource_name = '$userListResourceName'\";\n\n// Issues a search stream request.\n/** @var GoogleAdsServerStreamDecorator $stream */\n$stream = $googleAdsServiceClient->searchStream(\n SearchGoogleAdsStreamRequest::build($customerId, $query)\n);AddCustomerMatchUserList.php\n```\n\nExample:\n```text\ngoogleads_service_client: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n)\n\n# Creates a query that retrieves the user list.\nquery: str = f\"\"\"\n SELECT\n user_list.size_for_display,\n user_list.size_for_search\n FROM user_list\n WHERE user_list.resource_name = '{user_list_resource_name}'\"\"\"\n\n# Issues a search request.\nsearch_results: SearchGoogleAdsStreamResponse = (\n googleads_service_client.search(customer_id=customer_id, query=query)\n)add_customer_match_user_list.py\n```\n\nExample:\n```text\nquery = <<~EOQUERY\n SELECT user_list.size_for_display, user_list.size_for_search\n FROM user_list\n WHERE user_list.resource_name = #{user_list}\nEOQUERY\n\nresponse = client.service.google_ads.search_stream(\n customer_id: customer_id,\n query: query,\n)add_customer_match_user_list.rb\n```\n\nExample:\n```text\n# Create a query that retrieves the user list.\nmy $search_query =\n \"SELECT user_list.size_for_display, user_list.size_for_search \" .\n \"FROM user_list \" .\n \"WHERE user_list.resource_name = '$user_list_resource_name'\";\n\n# Create a search Google Ads stream request that will retrieve the user list.\nmy $search_stream_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsStreamRequest\n ->new({\n customerId => $customer_id,\n query => $search_query,\n });\n\n# Get the GoogleAdsService.\nmy $google_ads_service = $api_client->GoogleAdsService();\n\nmy $search_stream_handler =\n Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({\n service => $google_ads_service,\n request => $search_stream_request\n });add_customer_match_user_list.pl\n```\n\nExample:\n```text\nprivate String targetAdsInAdGroupToUserList(\n GoogleAdsClient googleAdsClient, long customerId, long adGroupId, String userList) {\n // Creates the ad group criterion targeting members of the user list.\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))\n .setUserList(UserListInfo.newBuilder().setUserList(userList).build())\n .build();\n\n // Creates the operation.\n AdGroupCriterionOperation operation =\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterion).build();\n\n // Creates the ad group criterion service.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Adds the ad group criterion.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n // Gets and prints the results.\n String adGroupCriterionResourceName = response.getResults(0).getResourceName();\n System.out.printf(\n \"Successfully created ad group criterion with resource name '%s' \"\n + \"targeting user list with resource name '%s' with ad group with ID %d.%n\",\n adGroupCriterionResourceName, userList, adGroupId);\n return adGroupCriterionResourceName;\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate string TargetAdsInAdGroupToUserList(\n GoogleAdsClient client, long customerId, long adGroupId, string userListResourceName)\n{\n // Get the AdGroupCriterionService client.\n AdGroupCriterionServiceClient adGroupCriterionServiceClient = client.GetService\n (Services.V25.AdGroupCriterionService);\n\n // Create the ad group criterion targeting members of the user list.\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion\n {\n AdGroup = ResourceNames.AdGroup(customerId, adGroupId),\n UserList = new UserListInfo\n {\n UserList = userListResourceName\n }\n };\n\n // Create the operation.\n AdGroupCriterionOperation adGroupCriterionOperation = new AdGroupCriterionOperation\n {\n Create = adGroupCriterion\n };\n\n // Add the ad group criterion, then print and return the new criterion's resource name.\n MutateAdGroupCriteriaResponse mutateAdGroupCriteriaResponse =\n adGroupCriterionServiceClient.MutateAdGroupCriteria(customerId.ToString(),\n new[] { adGroupCriterionOperation });\n\n string adGroupCriterionResourceName =\n mutateAdGroupCriteriaResponse.Results.First().ResourceName;\n Console.WriteLine(\"Successfully created ad group criterion with resource name \" +\n $\"'{adGroupCriterionResourceName}' targeting user list with resource name \" +\n $\"'{userListResourceName}' with ad group with ID {adGroupId}.\");\n return adGroupCriterionResourceName;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function targetAdsInAdGroupToUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n string $userListResourceName\n): string {\n // Creates the ad group criterion targeting members of the user list.\n $adGroupCriterion = new AdGroupCriterion([\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'user_list' => new UserListInfo(['user_list' => $userListResourceName])\n ]);\n\n // Creates the operation.\n $operation = new AdGroupCriterionOperation();\n $operation->setCreate($adGroupCriterion);\n\n // Issues a mutate request to add an ad group criterion.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n /** @var MutateAdGroupCriteriaResponse $adGroupCriterionResponse */\n $adGroupCriterionResponse = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$operation])\n );\n\n $adGroupCriterionResourceName =\n $adGroupCriterionResponse->getResults()[0]->getResourceName();\n printf(\n \"Successfully created ad group criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with ad group with ID %d.%s\",\n $adGroupCriterionResourceName,\n $userListResourceName,\n $adGroupId,\n PHP_EOL\n );\n\n return $adGroupCriterionResourceName;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef target_ads_in_ad_group_to_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n user_list_resource_name: str,\n) -> str:\n \"\"\"Creates an ad group criterion that targets a user list with an ad group.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID used to create an ad group\n criterion.\n ad_group_id: a str ID for an ad group used to create an ad group\n criterion that targets members of a user list.\n user_list_resource_name: a str resource name for a user list.\n\n Returns:\n a str resource name for an ad group criterion.\n \"\"\"\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n # Creates the ad group criterion targeting members of the user list.\n ad_group_criterion: AdGroupCriterion = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = client.get_service(\n \"AdGroupService\"\n ).ad_group_path(customer_id, ad_group_id)\n ad_group_criterion.user_list.user_list = user_list_resource_name\n\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n print(\n \"Successfully created ad group criterion with resource name: \"\n f\"'{resource_name}' targeting user list with resource name: \"\n f\"'{user_list_resource_name}' and with ad group with ID \"\n f\"{ad_group_id}.\"\n )\n return resource_nameset_up_remarketing.py\n```\n\nExample:\n```text\ndef target_ads_in_ad_group_to_user_list(\n client,\n customer_id,\n ad_group_id,\n user_list\n)\n # Creates the ad group criterion targeting members of the user list.\n operation = client.operation.create_resource.ad_group_criterion do |agc|\n agc.ad_group = client.path.ad_group(customer_id, ad_group_id)\n agc.user_list = client.resource.user_list_info do |info|\n info.user_list = user_list\n end\n end\n\n # Issues a mutate request to create the ad group criterion.\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [operation],\n )\n ad_group_criterion_resource_name = response.results.first.resource_name\n puts \"Successfully created ad group criterion with resource name \" \\\n \"'#{ad_group_criterion_resource_name}' targeting user list with resource name \" \\\n \"'#{user_list}' with ad group with ID #{ad_group_id}\"\n\n ad_group_criterion_resource_name\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub target_ads_in_ad_group_to_user_list {\n my ($api_client, $customer_id, $ad_group_id, $user_list_resource_name) = @_;\n\n # Create the ad group criterion targeting members of the user list.\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({\n userList => $user_list_resource_name\n })});\n\n # Create the operation.\n my $ad_group_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion\n });\n\n # Add the ad group criterion, then print and return the new criterion's resource name.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n my $ad_group_criterion_resource_name =\n $ad_group_criteria_response->{results}[0]{resourceName};\n printf \"Successfully created ad group criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with ad group with ID %d.\\n\",\n $ad_group_criterion_resource_name, $user_list_resource_name, $ad_group_id;\n\n return $ad_group_criterion_resource_name;\n}set_up_remarketing.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.548Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":30,"totalLines":1668,"estimatedTokens":16739}}215{"id":"doc-visitors_who_took_specific_actions_google_ads_ap-410f7c3a","source":"documentation","title":"Visitors who Took Specific Actions | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/remarketing/audience-segments/took-specific-actions","text":"Example:\n```text\nSELECT\n remarketing_action.id,\n remarketing_action.name,\n remarketing_action.tag_snippets\nFROM remarketing_action\nWHERE remarketing_action.resource_name = 'REMARKETING_ACTION_RESOURCE_NAME'\n```\n\nExample:\n```text\nprivate void runExample(\n GoogleAdsClient googleAdsClient, long customerId, List<Long> conversionActionIds) {\n List<UserListActionInfo> userListActionInfoList = new ArrayList<>();\n for (long conversionActionId : conversionActionIds) {\n // Creates the UserListActionInfo object for a given conversion action. This specifies the\n // conversion action that, when triggered, will cause a user to be added to a UserList.\n UserListActionInfo userListActionInfo =\n UserListActionInfo.newBuilder()\n .setConversionAction(ResourceNames.conversionAction(customerId, conversionActionId))\n .build();\n userListActionInfoList.add(userListActionInfo);\n }\n\n // Creates a basic user list info object with all of the conversion actions.\n BasicUserListInfo basicUserListInfo =\n BasicUserListInfo.newBuilder().addAllActions(userListActionInfoList).build();\n\n // Creates the basic user list.\n UserList basicUserList =\n UserList.newBuilder()\n .setName(\"Example BasicUserList #\" + getPrintableDateTime())\n .setDescription(\"A list of people who have triggered one or more conversion actions\")\n .setMembershipLifeSpan(365)\n .setBasicUserList(basicUserListInfo)\n .setMembershipStatus(UserListMembershipStatus.OPEN)\n .build();\n\n // Creates the operation.\n UserListOperation operation = UserListOperation.newBuilder().setCreate(basicUserList).build();\n\n // Creates the service client.\n try (UserListServiceClient userListServiceClient =\n googleAdsClient.getLatestVersion().createUserListServiceClient()) {\n // Adds the basic user list.\n MutateUserListsResponse response =\n userListServiceClient.mutateUserLists(\n Long.toString(customerId), ImmutableList.of(operation));\n // Prints the results.\n System.out.printf(\n \"Created basic user list with resource name '%s'.%n\",\n response.getResults(0).getResourceName());\n }\n}AddConversionBasedUserList.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long[] conversionActionIds)\n{\n // Creates the service client.\n UserListServiceClient userListServiceClient =\n client.GetService(Services.V25.UserListService);\n\n List<UserListActionInfo> userListActionInfoList = new List<UserListActionInfo>();\n foreach (long conversionActionId in conversionActionIds)\n {\n // Creates the UserListActionInfo object for a given conversion action. This\n // specifies the conversion action that, when triggered, will cause a user to be\n // added to a UserList.\n userListActionInfoList.Add(new UserListActionInfo\n {\n ConversionAction =\n ResourceNames.ConversionAction(customerId, conversionActionId)\n });\n }\n\n // Creates a basic user list info object with all of the conversion actions.\n BasicUserListInfo basicUserListInfo = new BasicUserListInfo();\n basicUserListInfo.Actions.Add(userListActionInfoList);\n\n // Creates the basic user list.\n UserList basicUserList = new UserList\n {\n Name = $\"Example BasicUserList #{ExampleUtilities.GetShortRandomString()}\",\n Description = \"A list of people who have triggered one or more conversion actions\",\n MembershipLifeSpan = 365L,\n BasicUserList = basicUserListInfo,\n MembershipStatus = UserListMembershipStatus.Open\n };\n\n // Creates the operation.\n UserListOperation operation = new UserListOperation\n {\n Create = basicUserList\n };\n\n try\n {\n // Adds the new user list.\n MutateUserListsResponse response = userListServiceClient.MutateUserLists\n (customerId.ToString(), new[] { operation });\n\n // Prints the result.\n Console.WriteLine(\"Created basic user list with resource name: \" +\n $\"{response.Results.First().ResourceName}\");\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}AddConversionBasedUserList.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n array $conversionActionIds\n) {\n $userListActionInfoList = [];\n foreach ($conversionActionIds as $conversionActionId) {\n // Creates the UserListActionInfo object for a given conversion action. This specifies\n // the conversion action that, when triggered, will cause a user to be added to a\n // UserList.\n $userListActionInfoList[] = new UserListActionInfo([\n 'conversion_action' => ResourceNames::forConversionAction(\n $customerId,\n $conversionActionId\n )\n ]);\n }\n\n // Creates a basic user list info object with all of the conversion actions.\n $basicUserListInfo = new BasicUserListInfo(['actions' => $userListActionInfoList]);\n\n // Creates the basic user list.\n $basicUserList = new UserList([\n 'name' => 'Example BasicUserList #' . Helper::getPrintableDatetime(),\n 'description' => 'A list of people who have triggered one or more conversion actions',\n 'membership_status' => UserListMembershipStatus::OPEN,\n 'membership_life_span' => 365,\n 'basic_user_list' => $basicUserListInfo\n ]);\n\n // Creates the operation.\n $operation = new UserListOperation();\n $operation->setCreate($basicUserList);\n\n // Issues a mutate request to add the user list and prints some information.\n $userListServiceClient = $googleAdsClient->getUserListServiceClient();\n $response = $userListServiceClient->mutateUserLists(\n MutateUserListsRequest::build($customerId, [$operation])\n );\n printf(\n \"Created basic user list with resource name '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n}AddConversionBasedUserList.php\n```\n\nExample:\n```text\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n conversion_action_ids: List[str],\n) -> None:\n \"\"\"Creates a combination user list.\n\n Args:\n client: The Google Ads client.\n customer_id: The customer ID for which to add the user list.\n conversion_action_ids: The IDs of the conversion actions for the basic\n user list.\n \"\"\"\n # Get the UserListService and ConversionActionService clients.\n user_list_service: UserListServiceClient = client.get_service(\n \"UserListService\"\n )\n conversion_action_service: ConversionActionServiceClient = (\n client.get_service(\"ConversionActionService\")\n )\n\n # Create a list of UserListActionInfo objects for the given conversion\n # actions. These specify the conversion actions that, when triggered, will\n # cause a user to be added to a UserList.\n user_list_action_info_list: List[UserListActionInfo] = []\n for conversion_action_id in conversion_action_ids:\n user_list_action_info: UserListActionInfo = client.get_type(\n \"UserListActionInfo\"\n )\n user_list_action_info.conversion_action = (\n conversion_action_service.conversion_action_path(\n customer_id, conversion_action_id\n )\n )\n user_list_action_info_list.append(user_list_action_info)\n\n # Create a UserListOperation and populate the UserList.\n user_list_operation: UserListOperation = client.get_type(\n \"UserListOperation\"\n )\n user_list: UserList = user_list_operation.create\n user_list.name = f\"Example BasicUserList #{uuid4()}\"\n user_list.description = (\n \"A list of people who have triggered one or more conversion actions\"\n )\n user_list.membership_status = client.enums.UserListMembershipStatusEnum.OPEN\n user_list.membership_life_span = 365\n # The basic user list info object contains the conversion action info.\n user_list.basic_user_list.actions.extend(user_list_action_info_list)\n\n # Issue a mutate request to add the user list, then print the results.\n response: MutateUserListsResponse = user_list_service.mutate_user_lists(\n customer_id=customer_id, operations=[user_list_operation]\n )\n print(\n \"Created basic user list with resource name \"\n f\"'{response.results[0].resource_name}.'\"\n )add_conversion_based_user_list.py\n```\n\nExample:\n```text\ndef add_conversion_based_user_list(customer_id, conversion_action_ids)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Creates the basic user list.\n operation = client.operation.create_resource.user_list do |u|\n u.name = \"Example BasicUserList ##{(Time.new.to_f * 100).to_i}\"\n u.description = \"A list of people who have triggered one or more conversion actions\"\n u.membership_status = :OPEN\n u.membership_life_span = 365\n # Creates a basic user list info object with all of the conversion actions.\n u.basic_user_list = client.resource.basic_user_list_info do |info|\n conversion_action_ids.each do |conversion_action_id|\n # Creates the UserListActionInfo object for a given conversion action.\n # This specifies the conversion action that, when triggered, will cause a\n # user to be added to a user_list.\n info.actions << client.resource.user_list_action_info do |action|\n action.conversion_action =\n client.path.conversion_action(customer_id, conversion_action_id)\n end\n end\n end\n end\n\n # Issues a mutate request to add the user list and prints some information.\n response = client.service.user_list.mutate_user_lists(\n customer_id: customer_id,\n operations: [operation],\n )\n\n puts \"Created basic user list with resource name \" \\\n \"#{response.results.first.resource_name}\"\nendadd_conversion_based_user_list.rb\n```\n\nExample:\n```text\nsub add_conversion_based_user_list {\n my ($api_client, $customer_id, $conversion_action_ids) = @_;\n\n my $user_list_action_info_list = [];\n foreach my $conversion_action_id (@$conversion_action_ids) {\n # Create the UserListActionInfo object for a given conversion action. This\n # specifies the conversion action that, when triggered, will cause a user to\n # be added to a UserList.\n push @$user_list_action_info_list,\n Google::Ads::GoogleAds::V25::Common::UserListActionInfo->new({\n conversionAction =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::conversion_action(\n $customer_id, $conversion_action_id\n )});\n }\n\n # Create a basic user list info object with all of the conversion actions.\n my $basic_user_list_info =\n Google::Ads::GoogleAds::V25::Common::BasicUserListInfo->new({\n actions => $user_list_action_info_list\n });\n\n # Create the basic user list.\n my $basic_user_list = Google::Ads::GoogleAds::V25::Resources::UserList->new({\n name => \"Example BasicUserList #\" . uniqid(),\n description =>\n \"A list of people who have triggered one or more conversion actions\",\n membershipStatus => OPEN,\n membershipLifeSpan => 365,\n basicUserList => $basic_user_list_info\n });\n\n # Create the operation.\n my $user_list_operation =\n Google::Ads::GoogleAds::V25::Services::UserListService::UserListOperation->\n new({\n create => $basic_user_list\n });\n\n # Issue a mutate request to add the user list and print some information.\n my $user_lists_response = $api_client->UserListService()->mutate({\n customerId => $customer_id,\n operations => [$user_list_operation]});\n\n printf\n \"Created basic user list with resource name '%s'.\\n\",\n $user_lists_response->{results}[0]{resourceName};\n\n return 1;\n}add_conversion_based_user_list.pl\n```\n\nExample:\n```text\nSELECT\n user_list.name,\n user_list.membership_status,\n user_list.membership_life_span\nFROM user_list\nWHERE\n user_list.resource_name = 'USER_LIST_RESOURCE_NAME'\n```\n\nExample:\n```text\nprivate String targetAdsInAdGroupToUserList(\n GoogleAdsClient googleAdsClient, long customerId, long adGroupId, String userList) {\n // Creates the ad group criterion targeting members of the user list.\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))\n .setUserList(UserListInfo.newBuilder().setUserList(userList).build())\n .build();\n\n // Creates the operation.\n AdGroupCriterionOperation operation =\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterion).build();\n\n // Creates the ad group criterion service.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Adds the ad group criterion.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n // Gets and prints the results.\n String adGroupCriterionResourceName = response.getResults(0).getResourceName();\n System.out.printf(\n \"Successfully created ad group criterion with resource name '%s' \"\n + \"targeting user list with resource name '%s' with ad group with ID %d.%n\",\n adGroupCriterionResourceName, userList, adGroupId);\n return adGroupCriterionResourceName;\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate string TargetAdsInAdGroupToUserList(\n GoogleAdsClient client, long customerId, long adGroupId, string userListResourceName)\n{\n // Get the AdGroupCriterionService client.\n AdGroupCriterionServiceClient adGroupCriterionServiceClient = client.GetService\n (Services.V25.AdGroupCriterionService);\n\n // Create the ad group criterion targeting members of the user list.\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion\n {\n AdGroup = ResourceNames.AdGroup(customerId, adGroupId),\n UserList = new UserListInfo\n {\n UserList = userListResourceName\n }\n };\n\n // Create the operation.\n AdGroupCriterionOperation adGroupCriterionOperation = new AdGroupCriterionOperation\n {\n Create = adGroupCriterion\n };\n\n // Add the ad group criterion, then print and return the new criterion's resource name.\n MutateAdGroupCriteriaResponse mutateAdGroupCriteriaResponse =\n adGroupCriterionServiceClient.MutateAdGroupCriteria(customerId.ToString(),\n new[] { adGroupCriterionOperation });\n\n string adGroupCriterionResourceName =\n mutateAdGroupCriteriaResponse.Results.First().ResourceName;\n Console.WriteLine(\"Successfully created ad group criterion with resource name \" +\n $\"'{adGroupCriterionResourceName}' targeting user list with resource name \" +\n $\"'{userListResourceName}' with ad group with ID {adGroupId}.\");\n return adGroupCriterionResourceName;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function targetAdsInAdGroupToUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n string $userListResourceName\n): string {\n // Creates the ad group criterion targeting members of the user list.\n $adGroupCriterion = new AdGroupCriterion([\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'user_list' => new UserListInfo(['user_list' => $userListResourceName])\n ]);\n\n // Creates the operation.\n $operation = new AdGroupCriterionOperation();\n $operation->setCreate($adGroupCriterion);\n\n // Issues a mutate request to add an ad group criterion.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n /** @var MutateAdGroupCriteriaResponse $adGroupCriterionResponse */\n $adGroupCriterionResponse = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$operation])\n );\n\n $adGroupCriterionResourceName =\n $adGroupCriterionResponse->getResults()[0]->getResourceName();\n printf(\n \"Successfully created ad group criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with ad group with ID %d.%s\",\n $adGroupCriterionResourceName,\n $userListResourceName,\n $adGroupId,\n PHP_EOL\n );\n\n return $adGroupCriterionResourceName;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef target_ads_in_ad_group_to_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n user_list_resource_name: str,\n) -> str:\n \"\"\"Creates an ad group criterion that targets a user list with an ad group.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID used to create an ad group\n criterion.\n ad_group_id: a str ID for an ad group used to create an ad group\n criterion that targets members of a user list.\n user_list_resource_name: a str resource name for a user list.\n\n Returns:\n a str resource name for an ad group criterion.\n \"\"\"\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n # Creates the ad group criterion targeting members of the user list.\n ad_group_criterion: AdGroupCriterion = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = client.get_service(\n \"AdGroupService\"\n ).ad_group_path(customer_id, ad_group_id)\n ad_group_criterion.user_list.user_list = user_list_resource_name\n\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n print(\n \"Successfully created ad group criterion with resource name: \"\n f\"'{resource_name}' targeting user list with resource name: \"\n f\"'{user_list_resource_name}' and with ad group with ID \"\n f\"{ad_group_id}.\"\n )\n return resource_nameset_up_remarketing.py\n```\n\nExample:\n```text\ndef target_ads_in_ad_group_to_user_list(\n client,\n customer_id,\n ad_group_id,\n user_list\n)\n # Creates the ad group criterion targeting members of the user list.\n operation = client.operation.create_resource.ad_group_criterion do |agc|\n agc.ad_group = client.path.ad_group(customer_id, ad_group_id)\n agc.user_list = client.resource.user_list_info do |info|\n info.user_list = user_list\n end\n end\n\n # Issues a mutate request to create the ad group criterion.\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [operation],\n )\n ad_group_criterion_resource_name = response.results.first.resource_name\n puts \"Successfully created ad group criterion with resource name \" \\\n \"'#{ad_group_criterion_resource_name}' targeting user list with resource name \" \\\n \"'#{user_list}' with ad group with ID #{ad_group_id}\"\n\n ad_group_criterion_resource_name\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub target_ads_in_ad_group_to_user_list {\n my ($api_client, $customer_id, $ad_group_id, $user_list_resource_name) = @_;\n\n # Create the ad group criterion targeting members of the user list.\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({\n userList => $user_list_resource_name\n })});\n\n # Create the operation.\n my $ad_group_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n create => $ad_group_criterion\n });\n\n # Add the ad group criterion, then print and return the new criterion's resource name.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n my $ad_group_criterion_resource_name =\n $ad_group_criteria_response->{results}[0]{resourceName};\n printf \"Successfully created ad group criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with ad group with ID %d.\\n\",\n $ad_group_criterion_resource_name, $user_list_resource_name, $ad_group_id;\n\n return $ad_group_criterion_resource_name;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate List<String> getUserListAdGroupCriterion(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n List<String> userListCriteria = new ArrayList<>();\n // Creates the Google Ads service client.\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Creates a request that will retrieve all of the ad group criteria under a campaign.\n SearchGoogleAdsRequest request =\n SearchGoogleAdsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setQuery(\n \"SELECT ad_group_criterion.criterion_id\"\n + \" FROM ad_group_criterion\"\n + \" WHERE campaign.id = \"\n + campaignId\n + \" AND ad_group_criterion.type = 'USER_LIST'\")\n .build();\n // Issues the search request.\n SearchPagedResponse searchPagedResponse = googleAdsServiceClient.search(request);\n // Iterates over all rows in all pages. Prints the results and adds the ad group criteria\n // resource names to the list.\n for (GoogleAdsRow googleAdsRow : searchPagedResponse.iterateAll()) {\n String adGroupCriterionResourceName = googleAdsRow.getAdGroupCriterion().getResourceName();\n System.out.printf(\n \"Ad group criterion with resource name '%s' was found.%n\",\n adGroupCriterionResourceName);\n userListCriteria.add(adGroupCriterionResourceName);\n }\n }\n return userListCriteria;\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate List<string> GetUserListAdGroupCriteria(\n GoogleAdsClient client, long customerId, long campaignId)\n{\n // Get the GoogleAdsService client.\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n List<string> userListCriteriaResourceNames = new List<string>();\n\n // Create a query that will retrieve all of the ad group criteria under a campaign.\n string query = $@\"\n SELECT ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE\n campaign.id = {campaignId}\n AND ad_group_criterion.type = 'USER_LIST'\";\n\n // Issue the search request.\n googleAdsServiceClient.SearchStream(customerId.ToString(), query,\n delegate (SearchGoogleAdsStreamResponse resp)\n {\n // Display the results and add the resource names to the list.\n foreach (GoogleAdsRow googleAdsRow in resp.Results)\n {\n string adGroupCriterionResourceName =\n googleAdsRow.AdGroupCriterion.ResourceName;\n Console.WriteLine(\"Ad group criterion with resource name \" +\n $\"{adGroupCriterionResourceName} was found.\");\n userListCriteriaResourceNames.Add(adGroupCriterionResourceName);\n }\n });\n\n return userListCriteriaResourceNames;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function getUserListAdGroupCriteria(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n): array {\n // Creates a query that retrieves all of the ad group criteria under a campaign.\n $query = sprintf(\n \"SELECT ad_group_criterion.criterion_id \" .\n \"FROM ad_group_criterion \" .\n \"WHERE campaign.id = %d \" .\n \"AND ad_group_criterion.type = 'USER_LIST'\",\n $campaignId\n );\n\n // Creates the Google Ads service client.\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n\n // Issues the search request.\n $response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n $userListCriteria = [];\n // Iterates over all rows in all pages. Prints the user list criteria and adds the ad group\n // criteria resource names to the list.\n foreach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $adGroupCriterionResourceName = $googleAdsRow->getAdGroupCriterion()->getResourceName();\n\n printf(\n \"Ad group criterion with resource name '%s' was found.%s\",\n $adGroupCriterionResourceName,\n PHP_EOL\n );\n\n $userListCriteria[] = $adGroupCriterionResourceName;\n }\n\n return $userListCriteria;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef get_user_list_ad_group_criteria(\n client: GoogleAdsClient, customer_id: str, campaign_id: str\n) -> List[str]:\n \"\"\"Finds all of user list ad group criteria under a campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID.\n campaign_id: a str campaign ID.\n\n Returns:\n a list of ad group criterion resource names.\n \"\"\"\n # Creates a query that retrieves all of the ad group criteria under a\n # campaign.\n query: str = f\"\"\"\n SELECT\n ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE campaign.id = {campaign_id}\n AND ad_group_criterion.type = USER_LIST\"\"\"\n\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n search_request: SearchGoogleAdsRequest = client.get_type(\n \"SearchGoogleAdsRequest\"\n )\n search_request.customer_id = customer_id\n search_request.query = query\n response: SearchGoogleAdsResponse = googleads_service.search(\n request=search_request\n )\n\n # Iterates over all rows in all pages. Prints the user list criteria and\n # adds the ad group criteria resource names to the list.\n user_list_criteria: List[str] = []\n row: GoogleAdsRow\n for row in response:\n resource_name: str = row.ad_group_criterion.resource_name\n print(\n \"Ad group criterion with resource name '{resource_name}' was \"\n \"found.\"\n )\n user_list_criteria.append(resource_name)\n\n return user_list_criteriaset_up_remarketing.py\n```\n\nExample:\n```text\ndef get_user_list_ad_group_criterion(\n client,\n customer_id,\n campaign_id\n)\n user_list_criteria = []\n\n # Creates a query that will retrieve all of the ad group criteria \n # under a campaign.\n query = <<~QUERY\n SELECT ad_group_criterion.criterion_id\n FROM ad_group_criterion\n WHERE campaign.id = #{campaign_id}\n AND ad_group_criterion.type = 'USER_LIST'\n QUERY\n\n # Issues the search request.\n response = client.service.google_ads.search(\n customer_id: customer_id,\n query: query,\n )\n\n # Iterates over all rows in all pages. Prints the results and adds the ad\n # group criteria resource names to the list.\n response.each do |row|\n ad_group_criterion_resource_name = row.ad_group_criterion.resource_name\n puts \"Ad group criterion with resource name \" \\\n \"'#{ad_group_criterion_resource_name}' was found\"\n user_list_criteria << ad_group_criterion_resource_name\n end\n\n user_list_criteria\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub get_user_list_ad_group_criteria {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n my $user_list_criterion_resource_names = [];\n\n # Create a search stream request that will retrieve all of the user list ad\n # group criteria under a campaign.\n my $search_stream_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsStreamRequest\n ->new({\n customerId => $customer_id,\n query => sprintf(\n \"SELECT ad_group_criterion.criterion_id \" .\n \"FROM ad_group_criterion \" .\n \"WHERE campaign.id = %d AND ad_group_criterion.type = 'USER_LIST'\",\n $campaign_id\n )});\n\n my $search_stream_handler =\n Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({\n service => $api_client->GoogleAdsService(),\n request => $search_stream_request\n });\n\n # Issue a search request and process the stream response.\n $search_stream_handler->process_contents(\n sub {\n # Display the results and add the resource names to the list.\n my $google_ads_row = shift;\n\n my $ad_group_criterion_resource_name =\n $google_ads_row->{adGroupCriterion}{resourceName};\n printf \"Ad group criterion with resource name '%s' was found.\\n\",\n $ad_group_criterion_resource_name;\n push(@$user_list_criterion_resource_names,\n $ad_group_criterion_resource_name);\n });\n\n return $user_list_criterion_resource_names;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate void removeExistingListCriteriaFromAdGroup(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n // Retrieves all of the ad group criteria under a campaign.\n List<String> adGroupCriteria =\n getUserListAdGroupCriterion(googleAdsClient, customerId, campaignId);\n\n List<AdGroupCriterionOperation> operations = new ArrayList<>();\n\n // Creates a list of remove operations.\n for (String adGroupCriterion : adGroupCriteria) {\n operations.add(AdGroupCriterionOperation.newBuilder().setRemove(adGroupCriterion).build());\n }\n\n // Creates the ad group criterion service.\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Removes the ad group criterion.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n Long.toString(customerId), operations);\n // Gets and prints the results.\n System.out.printf(\"Removed %d ad group criteria.%n\", response.getResultsCount());\n for (MutateAdGroupCriterionResult result : response.getResultsList()) {\n System.out.printf(\n \"Successfully removed ad group criterion with resource name '%s'.%n\",\n result.getResourceName());\n }\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate void RemoveExistingListCriteriaFromAdGroup(GoogleAdsClient client, long customerId,\n long campaignId)\n{\n // Get the AdGroupCriterionService client.\n AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n client.GetService(Services.V25.AdGroupCriterionService);\n\n // Retrieve all of the ad group criteria under a campaign.\n List<string> adGroupCriteria =\n GetUserListAdGroupCriteria(client, customerId, campaignId);\n\n // Create a list of remove operations.\n List<AdGroupCriterionOperation> operations = adGroupCriteria.Select(adGroupCriterion =>\n new AdGroupCriterionOperation { Remove = adGroupCriterion }).ToList();\n\n // Remove the ad group criteria and print the resource names of the removed criteria.\n MutateAdGroupCriteriaResponse mutateAdGroupCriteriaResponse =\n adGroupCriterionServiceClient.MutateAdGroupCriteria(customerId.ToString(),\n operations);\n\n Console.WriteLine($\"Removed {mutateAdGroupCriteriaResponse.Results.Count} ad group \" +\n \"criteria.\");\n foreach (MutateAdGroupCriterionResult result in mutateAdGroupCriteriaResponse.Results)\n {\n Console.WriteLine(\"Successfully removed ad group criterion with resource name \" +\n $\"'{result.ResourceName}'.\");\n }\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function removeExistingListCriteriaFromAdGroup(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n) {\n // Retrieves all of the ad group criteria under a campaign.\n $allAdGroupCriteria = self::getUserListAdGroupCriteria(\n $googleAdsClient,\n $customerId,\n $campaignId\n );\n\n $removeOperations = [];\n // Creates a list of remove operations.\n foreach ($allAdGroupCriteria as $adGroupCriterionResourceName) {\n $operation = new AdGroupCriterionOperation();\n $operation->setRemove($adGroupCriterionResourceName);\n $removeOperations[] = $operation;\n }\n\n // Issues a mutate request to remove the ad group criteria.\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n /** @var MutateAdGroupCriteriaResponse $adGroupCriteriaResponse */\n $adGroupCriteriaResponse = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, $removeOperations)\n );\n\n foreach ($adGroupCriteriaResponse->getResults() as $adGroupCriteriaResult) {\n printf(\n \"Successfully removed ad group criterion with resource name '%s'.%s\",\n $adGroupCriteriaResult->getResourceName(),\n PHP_EOL\n );\n }\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef remove_existing_criteria_from_ad_group(\n client: GoogleAdsClient, customer_id: str, campaign_id: str\n) -> None:\n \"\"\"Removes all ad group criteria targeting a user list under a campaign.\n\n This is a necessary step before targeting a user list at the campaign level.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID.\n campaign_id: a str ID for a campaign that will have all ad group\n criteria that targets user lists removed.\n \"\"\"\n # Retrieves all of the ad group criteria under a campaign.\n all_ad_group_criteria: List[str] = get_user_list_ad_group_criteria(\n client, customer_id, campaign_id\n )\n\n # Creates a list of remove operations.\n remove_operations: List[AdGroupCriterionOperation] = []\n for ad_group_criterion_resource_name in all_ad_group_criteria:\n remove_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n remove_operation.remove = ad_group_criterion_resource_name\n remove_operations.append(remove_operation)\n\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n response: MutateAdGroupCriteriaResponse = (\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=remove_operations\n )\n )\n print(\n \"Successfully removed ad group criterion with resource name: \"\n f\"'{response.results[0].resource_name}'\"\n )set_up_remarketing.py\n```\n\nExample:\n```text\ndef remove_existing_list_criteria_from_ad_group(\n client,\n customer_id,\n campaign_id\n)\n # Retrieves all of the ad group criteria under a campaign.\n ad_group_criteria = get_user_list_ad_group_criterion(\n client, customer_id, campaign_id)\n\n # Creates a list of remove operations.\n operations = []\n ad_group_criteria.each do |agc|\n operations << client.operation.remove_resource.ad_group_criterion(agc)\n end\n\n # Issues a mutate request to remove all ad group criteria.\n response = client.service.ad_group_criterion.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: operations,\n )\n puts \"Removed #{response.results.size} ad group criteria.\"\n response.results.each do |result|\n puts \"Successfully removed ad group criterion with resource name \" \\\n \"'#{result.resource_name}'\"\n end\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub remove_existing_list_criteria_from_ad_group {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n # Retrieve all of the ad group criteria under a campaign.\n my $ad_group_criteria =\n get_user_list_ad_group_criteria($api_client, $customer_id, $campaign_id);\n\n # Create a list of remove operations.\n my $operations = [];\n foreach my $ad_group_criterion (@$ad_group_criteria) {\n push(\n @$operations,\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({\n remove => $ad_group_criterion\n }));\n }\n\n # Remove the ad group criteria and print the resource names of the removed criteria.\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => $operations\n });\n\n printf \"Removed %d ad group criteria.\\n\",\n scalar @{$ad_group_criteria_response->{results}};\n foreach my $result (@{$ad_group_criteria_response->{results}}) {\n printf \"Successfully removed ad group criterion with resource name '%s'.\\n\",\n $result->{resourceName};\n }\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nprivate String targetAdsInCampaignToUserList(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId, String userList) {\n // Creates the campaign criterion.\n CampaignCriterion campaignCriterion =\n CampaignCriterion.newBuilder()\n .setCampaign(ResourceNames.campaign(customerId, campaignId))\n .setUserList(UserListInfo.newBuilder().setUserList(userList).build())\n .build();\n\n // Creates the operation.\n CampaignCriterionOperation operation =\n CampaignCriterionOperation.newBuilder().setCreate(campaignCriterion).build();\n\n // Creates the campaign criterion service client.\n try (CampaignCriterionServiceClient campaignCriterionServiceClient =\n googleAdsClient.getLatestVersion().createCampaignCriterionServiceClient()) {\n // Adds the campaign criterion.\n MutateCampaignCriteriaResponse response =\n campaignCriterionServiceClient.mutateCampaignCriteria(\n Long.toString(customerId), ImmutableList.of(operation));\n // Gets and prints the campaign criterion resource name.\n String campaignCriterionResourceName = response.getResults(0).getResourceName();\n System.out.printf(\n \"Successfully created campaign criterion with resource name '%s' \"\n + \"targeting user list with resource name '%s' with campaign with ID %d.%n\",\n campaignCriterionResourceName, userList, campaignId);\n return campaignCriterionResourceName;\n }\n}\nSetUpRemarketing.java\n```\n\nExample:\n```text\nprivate string TargetAdsInCampaignToUserList(\n GoogleAdsClient client, long customerId, long campaignId, string userListResourceName)\n{\n // Get the CampaignCriterionService client.\n CampaignCriterionServiceClient campaignCriterionServiceClient =\n client.GetService(Services.V25.CampaignCriterionService);\n\n // Create the campaign criterion.\n CampaignCriterion campaignCriterion = new CampaignCriterion\n {\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n UserList = new UserListInfo\n {\n UserList = userListResourceName\n }\n };\n\n // Create the operation.\n CampaignCriterionOperation campaignCriterionOperation = new CampaignCriterionOperation\n {\n Create = campaignCriterion\n };\n\n // Add the campaign criterion and print the resulting criterion's resource name.\n MutateCampaignCriteriaResponse mutateCampaignCriteriaResponse =\n campaignCriterionServiceClient.MutateCampaignCriteria(customerId.ToString(),\n new[] { campaignCriterionOperation });\n\n string campaignCriterionResourceName =\n mutateCampaignCriteriaResponse.Results.First().ResourceName;\n Console.WriteLine(\"Successfully created campaign criterion with resource name \" +\n $\"'{campaignCriterionResourceName}' targeting user list with resource name \" +\n $\"'{userListResourceName}' with campaign with ID {campaignId}.\");\n\n return campaignCriterionResourceName;\n}SetUpRemarketing.cs\n```\n\nExample:\n```text\nprivate static function targetAdsInCampaignToUserList(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId,\n string $userListResourceName\n): string {\n // Creates the campaign criterion.\n $campaignCriterion = new CampaignCriterion([\n 'campaign' => ResourceNames::forCampaign($customerId, $campaignId),\n 'user_list' => new UserListInfo(['user_list' => $userListResourceName])\n ]);\n\n // Creates the operation.\n $operation = new CampaignCriterionOperation();\n $operation->setCreate($campaignCriterion);\n\n // Issues a mutate request to create a campaign criterion.\n $campaignCriterionServiceClient = $googleAdsClient->getCampaignCriterionServiceClient();\n /** @var MutateCampaignCriteriaResponse $campaignCriteriaResponse */\n $campaignCriteriaResponse = $campaignCriterionServiceClient->mutateCampaignCriteria(\n MutateCampaignCriteriaRequest::build($customerId, [$operation])\n );\n\n $campaignCriterionResourceName =\n $campaignCriteriaResponse->getResults()[0]->getResourceName();\n printf(\n \"Successfully created campaign criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with campaign with ID %d.%s\",\n $campaignCriterionResourceName,\n $userListResourceName,\n $campaignId,\n PHP_EOL\n );\n\n return $campaignCriterionResourceName;\n}SetUpRemarketing.php\n```\n\nExample:\n```text\ndef target_ads_in_campaign_to_user_list(\n client: GoogleAdsClient,\n customer_id: str,\n campaign_id: str,\n user_list_resource_name: str,\n) -> str:\n \"\"\"Creates a campaign criterion that targets a user list with a campaign.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a str client customer ID used to create an campaign\n criterion.\n campaign_id: a str ID for a campaign used to create a campaign\n criterion that targets members of a user list.\n user_list_resource_name: a str resource name for a user list.\n\n Returns:\n a str resource name for a campaign criterion.\n \"\"\"\n campaign_criterion_operation: CampaignCriterionOperation = client.get_type(\n \"CampaignCriterionOperation\"\n )\n campaign_criterion: CampaignCriterion = campaign_criterion_operation.create\n campaign_criterion.campaign = client.get_service(\n \"CampaignService\"\n ).campaign_path(customer_id, campaign_id)\n campaign_criterion.user_list.user_list = user_list_resource_name\n\n campaign_criterion_service: CampaignCriterionServiceClient = (\n client.get_service(\"CampaignCriterionService\")\n )\n response: MutateCampaignCriteriaResponse = (\n campaign_criterion_service.mutate_campaign_criteria(\n customer_id=customer_id, operations=[campaign_criterion_operation]\n )\n )\n resource_name: str = response.results[0].resource_name\n print(\n \"Successfully created campaign criterion with resource name \"\n f\"'{resource_name}' targeting user list with resource name \"\n f\"'{user_list_resource_name}' with campaign with ID {campaign_id}\"\n )\n return resource_nameset_up_remarketing.py\n```\n\nExample:\n```text\ndef target_ads_in_campaign_to_user_list(\n client,\n customer_id,\n campaign_id,\n user_list\n)\n # Creates the campaign criterion targeting members of the user list.\n operation = client.operation.create_resource.campaign_criterion do |cc|\n cc.campaign = client.path.campaign(customer_id, campaign_id)\n cc.user_list = client.resource.user_list_info do |info|\n info.user_list = user_list\n end\n end\n\n # Issues a mutate request to create the campaign criterion.\n response = client.service.campaign_criterion.mutate_campaign_criteria(\n customer_id: customer_id,\n operations: [operation],\n )\n campaign_criterion_resource_name = response.results.first.resource_name\n puts \"Successfully created campaign criterion with resource name \" \\\n \"'#{campaign_criterion_resource_name}' targeting user list with resource name \" \\\n \"'#{user_list}' with campaign with ID #{campaign_id}\"\n\n campaign_criterion_resource_name\nendset_up_remarketing.rb\n```\n\nExample:\n```text\nsub target_ads_in_campaign_to_user_list {\n my ($api_client, $customer_id, $campaign_id, $user_list_resource_name) = @_;\n\n # Create the campaign criterion.\n my $campaign_criterion =\n Google::Ads::GoogleAds::V25::Resources::CampaignCriterion->new({\n campaign => Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $campaign_id\n ),\n userList => Google::Ads::GoogleAds::V25::Common::UserListInfo->new({\n userList => $user_list_resource_name\n })});\n\n # Create the operation.\n my $campaign_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignCriterionService::CampaignCriterionOperation\n ->new({\n create => $campaign_criterion\n });\n\n # Add the campaign criterion and print the resulting criterion's resource name.\n my $campaign_criteria_response =\n $api_client->CampaignCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$campaign_criterion_operation]});\n\n my $campaign_criterion_resource_name =\n $campaign_criteria_response->{results}[0]{resourceName};\n printf \"Successfully created campaign criterion with resource name '%s' \" .\n \"targeting user list with resource name '%s' with campaign with ID %d.\\n\",\n $campaign_criterion_resource_name, $user_list_resource_name, $campaign_id;\n\n return $campaign_criterion_resource_name;\n}set_up_remarketing.pl\n```\n\nExample:\n```text\nSELECT\n ad_group_criterion.criterion_id,\n metrics.conversions,\n metrics.cost_per_conversion\nFROM ad_group_audience_view\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.552Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":33,"totalLines":1265,"estimatedTokens":11490}}216{"id":"doc-using_temporary_ids_google_ads_api_google_for_de-357e35e2","source":"documentation","title":"Using temporary IDs | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/batch-processing/temporary-ids","text":"Example:\n```text\nmutate_operations: [\n {\n campaign_operation: {\n create: {\n resource_name: \"customers/<YOUR_CUSTOMER_ID>/campaigns/-1\",\n ...\n }\n }\n },\n {\n ad_group_operation: {\n create: {\n resource_name: \"customers/<YOUR_CUSTOMER_ID>/adGroups/-2\",\n campaign: \"customers/<YOUR_CUSTOMER_ID>/campaigns/-1\"\n ...\n }\n }\n },\n {\n ad_group_ad_operation: {\n create: {\n ad_group: \"customers/<YOUR_CUSTOMER_ID>/adGroups/-2\"\n ...\n }\n }\n },\n]\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.555Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":32,"estimatedTokens":138}}217{"id":"doc-generate_historical_metrics_google_ads_api_googl-20028069","source":"documentation","title":"Generate Historical Metrics | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/keyword-planning/generate-historical-metrics","text":"Example:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient, Long customerId) {\n GenerateKeywordHistoricalMetricsRequest request =\n GenerateKeywordHistoricalMetricsRequest.newBuilder()\n .setCustomerId(String.valueOf(customerId))\n .addAllKeywords(Arrays.asList(\"mars cruise\", \"cheap cruise\", \"jupiter cruise\"))\n // See https://developers.google.com/google-ads/api/reference/data/geotargets for the\n // list of geo target IDs.\n // Geo target constant 2840 is for USA.\n .addGeoTargetConstants(ResourceNames.geoTargetConstant(2840))\n .setKeywordPlanNetwork(KeywordPlanNetwork.GOOGLE_SEARCH)\n // See\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n // for the list of language constant IDs.\n // Language constant 1000 is for English.\n .setLanguage(ResourceNames.languageConstant(1000))\n .build();\n\n try (KeywordPlanIdeaServiceClient keywordPlanIdeaServiceClient =\n googleAdsClient.getLatestVersion().createKeywordPlanIdeaServiceClient()) {\n GenerateKeywordHistoricalMetricsResponse response =\n keywordPlanIdeaServiceClient.generateKeywordHistoricalMetrics(request);\n for (GenerateKeywordHistoricalMetricsResult result : response.getResultsList()) {\n KeywordPlanHistoricalMetrics metrics = result.getKeywordMetrics();\n System.out.printf(\"The search query: %s%n\", result.getText());\n System.out.printf(\n \"and the following variants: %s%n\", Joiner.on(\",\").join(result.getCloseVariantsList()));\n System.out.println(\"generated the following historical metrics:\");\n\n // Approximate number of monthly searches on this query averaged for the past 12\n // months.\n System.out.printf(\n \"Approximate monthly searches: %s%n\",\n metrics.hasAvgMonthlySearches() ? metrics.getAvgMonthlySearches() : null);\n\n // The competition level for this search query.\n System.out.printf(\"Competition level: %s%n\", metrics.getCompetition());\n\n // The competition index for the query in the range [0,100]. This shows how\n // competitive ad placement is for a keyword. The level of competition from 0-100 is\n // determined by the number of ad slots filled divided by the total number of slots\n // available. If not enough data is available, null will be returned.\n System.out.printf(\n \"Competition index: %s%n\",\n metrics.hasCompetitionIndex() ? metrics.getCompetitionIndex() : null);\n\n // Top of page bid low range (20th percentile) in micros for the keyword.\n System.out.printf(\n \"Top of page bid low range: %s%n\",\n metrics.hasLowTopOfPageBidMicros() ? metrics.getLowTopOfPageBidMicros() : null);\n\n // Top of page bid high range (80th percentile) in micros for the keyword.\n System.out.printf(\n \"Top of page bid high range: %s%n\",\n metrics.hasHighTopOfPageBidMicros() ? metrics.getHighTopOfPageBidMicros() : null);\n\n // Approximate number of searches on this query for the past twelve months.\n metrics.getMonthlySearchVolumesList().stream()\n // Orders the monthly search volumes by descending year, then descending month.\n .sorted(\n (a, b) ->\n ComparisonChain.start()\n .compare(b.getYear(), a.getYear())\n .compare(b.getMonth(), a.getMonth())\n .result())\n // Prints each monthly search volume.\n .forEachOrdered(\n monthlySearchVolume ->\n System.out.printf(\n \"Approximately %d searches in %s, %s%n\",\n monthlySearchVolume.getMonthlySearches(),\n monthlySearchVolume.getMonth(),\n monthlySearchVolume.getYear()));\n }\n }\n}GenerateHistoricalMetrics.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId)\n{\n KeywordPlanIdeaServiceClient keywordPlanIdeaService =\n client.GetService(Services.V25.KeywordPlanIdeaService);\n\n GenerateKeywordHistoricalMetricsRequest request =\n new GenerateKeywordHistoricalMetricsRequest()\n {\n CustomerId = customerId.ToString(),\n Keywords = { \"mars cruise\", \"cheap cruise\", \"jupiter cruise\" },\n // See https://developers.google.com/google-ads/api/reference/data/geotargets\n // for the list of geo target IDs.\n // Geo target constant 2840 is for USA.\n GeoTargetConstants = { ResourceNames.GeoTargetConstant(2840) },\n KeywordPlanNetwork = KeywordPlanNetwork.GoogleSearch,\n // See https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n // for the list of language constant IDs.\n // Language constant 1000 is for English.\n Language = ResourceNames.LanguageConstant(1000)\n };\n\n try\n {\n GenerateKeywordHistoricalMetricsResponse response =\n keywordPlanIdeaService.GenerateKeywordHistoricalMetrics(request);\n\n foreach (GenerateKeywordHistoricalMetricsResult result in response.Results)\n {\n KeywordPlanHistoricalMetrics metrics = result.KeywordMetrics;\n\n Console.WriteLine($\"The search query {result.Text}\");\n Console.WriteLine(\"and the following variants: \" +\n $\"{String.Join(\",\", result.CloseVariants)}\");\n Console.WriteLine(\"Generated the following historical metrics:\");\n\n // Approximate number of monthly searches on this query averaged for the past 12\n // months.\n Console.WriteLine($\"Approximate monthly searches: {metrics.AvgMonthlySearches}\");\n\n // The competition level for this search query.\n Console.WriteLine($\"Competition level: {metrics.Competition}\");\n\n // The competition index for the query in the range [0,100]. This shows how\n // competitive ad placement is for a keyword. The level of competition from 0-100 is\n // determined by the number of ad slots filled divided by the total number of slots\n // available. If not enough data is available, null will be returned.\n Console.WriteLine($\"Competition index: {metrics.CompetitionIndex}\");\n\n // Top of page bid low range (20th percentile) in micros for the keyword.\n Console.WriteLine($\"Top of page bid low range: {metrics.LowTopOfPageBidMicros}\");\n\n // Top of page bid high range (80th percentile) in micros for the keyword.\n Console.WriteLine($\"Top of page bid high range: {metrics.HighTopOfPageBidMicros}\");\n\n // Approximate number of searches on this query for the past twelve months.\n foreach (MonthlySearchVolume month in metrics.MonthlySearchVolumes)\n {\n Console.WriteLine($\"Approximately {month.MonthlySearches} searches in \" +\n $\"{month.Month}, {month.Year}\");\n }\n }\n\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}GenerateHistoricalMetrics.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n): void {\n $keywordPlanIdeaServiceClient = $googleAdsClient->getKeywordPlanIdeaServiceClient();\n // Generates keyword historical metrics based on the specified parameters.\n $response = $keywordPlanIdeaServiceClient->generateKeywordHistoricalMetrics(\n new GenerateKeywordHistoricalMetricsRequest([\n 'customer_id' => $customerId,\n 'keywords' => ['mars cruise', 'cheap cruise', 'jupiter cruise'],\n // See https://developers.google.com/google-ads/api/reference/data/geotargets for\n // the list of geo target IDs.\n // Geo target constant 2840 is for USA.\n 'geo_target_constants' => [ResourceNames::forGeoTargetConstant(2840)],\n 'keyword_plan_network' => KeywordPlanNetwork::GOOGLE_SEARCH,\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n // for the list of language constant IDs.\n // Language constant 1000 is for English.\n 'language' => ResourceNames::forLanguageConstant(1000)\n ])\n );\n\n // Iterates over the results and print its detail.\n foreach ($response->getResults() as $result) {\n /** @var GenerateKeywordHistoricalMetricsResult $result */\n $metrics = $result->getKeywordMetrics();\n printf(\"The search query: '%s' \", $result->getText());\n printf(\n \"and the following variants: '%s' \",\n implode(',', iterator_to_array($result->getCloseVariants()->getIterator()))\n );\n print \"generated the following historical metrics:\" . PHP_EOL;\n\n // Approximate number of monthly searches on this query averaged for the past 12 months.\n printf(\n \"Approximate monthly searches: %s%s\",\n $metrics->hasAvgMonthlySearches()\n ? sprintf(\"%d\", $metrics->getAvgMonthlySearches())\n : \"'none'\",\n PHP_EOL\n );\n\n // The competition level for this search query.\n printf(\n \"Competition level: '%s'%s\",\n KeywordPlanCompetitionLevel::name($metrics->getCompetition()),\n PHP_EOL\n );\n\n // The competition index for the query in the range [0,100]. This shows how\n // competitive ad placement is for a keyword. The level of competition from 0-100 is\n // determined by the number of ad slots filled divided by the total number of slots\n // available. If not enough data is available, null will be returned.\n printf(\n \"Competition index: %s%s\",\n $metrics->hasCompetitionIndex()\n ? sprintf(\"%d\", $metrics->getCompetitionIndex())\n : \"'none'\",\n PHP_EOL\n );\n\n // Top of page bid low range (20th percentile) in micros for the keyword.\n printf(\n \"Top of page bid low range: %s%s\",\n $metrics->hasLowTopOfPageBidMicros()\n ? sprintf(\"%d\", $metrics->getLowTopOfPageBidMicros())\n : \"'none'\",\n PHP_EOL\n );\n\n // Top of page bid high range (80th percentile) in micros for the keyword.\n printf(\n \"Top of page bid high range: %s%s\",\n $metrics->hasHighTopOfPageBidMicros()\n ? sprintf(\"%d\", $metrics->getHighTopOfPageBidMicros())\n : \"'none'\",\n PHP_EOL\n );\n\n // Approximate number of searches on this query for the past twelve months.\n $monthlySearchVolumes =\n iterator_to_array($metrics->getMonthlySearchVolumes()->getIterator());\n usort(\n $monthlySearchVolumes,\n // Orders the monthly search volumes by descending year, then descending month.\n function (MonthlySearchVolume $volume1, MonthlySearchVolume $volume2) {\n $yearsCompared = $volume2->getYear() <=> $volume1->getYear();\n if ($yearsCompared != 0) {\n return $yearsCompared;\n } else {\n return $volume2->getMonth() <=> $volume1->getMonth();\n }\n }\n );\n // Prints each monthly search volume.\n array_walk($monthlySearchVolumes, function (MonthlySearchVolume $monthlySearchVolume) {\n printf(\n \"Approximately %d searches in %s, %s.%s\",\n $monthlySearchVolume->getMonthlySearches(),\n MonthOfYear::name($monthlySearchVolume->getMonth()),\n $monthlySearchVolume->getYear(),\n PHP_EOL\n );\n });\n print PHP_EOL;\n }\n}GenerateHistoricalMetrics.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient, customer_id: str):\n \"\"\"The main method that creates all necessary entities for the example.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n \"\"\"\n generate_historical_metrics(client, customer_id)\n\n\ndef generate_historical_metrics(client: GoogleAdsClient, customer_id: str):\n \"\"\"Generates historical metrics and prints the results.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n keyword_plan_idea_service: KeywordPlanIdeaServiceClient = (\n client.get_service(\"KeywordPlanIdeaService\")\n )\n request: GenerateKeywordHistoricalMetricsRequest = client.get_type(\n \"GenerateKeywordHistoricalMetricsRequest\"\n )\n request.customer_id = customer_id\n request.keywords = [\"mars cruise\", \"cheap cruise\", \"jupiter cruise\"]\n # Geo target constant 2840 is for USA.\n request.geo_target_constants.append(\n googleads_service.geo_target_constant_path(\"2840\")\n )\n request.keyword_plan_network = (\n client.enums.KeywordPlanNetworkEnum.GOOGLE_SEARCH\n )\n # Language criteria 1000 is for English. For the list of language criteria\n # IDs, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n request.language = googleads_service.language_constant_path(\"1000\")\n\n response: GenerateKeywordHistoricalMetricsResponse = (\n keyword_plan_idea_service.generate_keyword_historical_metrics(\n request=request\n )\n )\n\n results: Iterable[GenerateKeywordHistoricalMetricsResult] = response.results\n for result in results:\n metrics: KeywordPlanHistoricalMetrics = result.keyword_metrics\n # These metrics include those for both the search query and any variants\n # included in the response.\n print(\n f\"The search query '{result.text}' (and the following variants: \"\n f\"'{result.close_variants if result.close_variants else 'None'}'), \"\n \"generated the following historical metrics:\\n\"\n )\n\n # Approximate number of monthly searches on this query averaged for the\n # past 12 months.\n print(f\"\\tApproximate monthly searches: {metrics.avg_monthly_searches}\")\n\n # The competition level for this search query.\n print(f\"\\tCompetition level: {metrics.competition}\")\n\n # The competition index for the query in the range [0, 100]. This shows\n # how competitive ad placement is for a keyword. The level of\n # competition from 0-100 is determined by the number of ad slots filled\n # divided by the total number of ad slots available. If not enough data\n # is available, undef will be returned.\n print(f\"\\tCompetition index: {metrics.competition_index}\")\n\n # Top of page bid low range (20th percentile) in micros for the keyword.\n print(\n f\"\\tTop of page bid low range: {metrics.low_top_of_page_bid_micros}\"\n )\n\n # Top of page bid high range (80th percentile) in micros for the\n # keyword.\n print(\n \"\\tTop of page bid high range: \"\n f\"{metrics.high_top_of_page_bid_micros}\"\n )\n\n # Approximate number of searches on this query for the past twelve\n # months.\n months: Iterable[MonthlySearchVolume] = metrics.monthly_search_volumes\n for month in months:\n print(\n f\"\\tApproximately {month.monthly_searches} searches in \"\n f\"{month.month.name}, {month.year}\"\n )generate_historical_metrics.py\n```\n\nExample:\n```text\ndef generate_historical_metrics(customer_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Generates historical metrics and prints the results.\n keyword_plan_idea_service = client.service.keyword_plan_idea\n\n response = keyword_plan_idea_service.generate_keyword_historical_metrics(\n customer_id: customer_id,\n keywords: [\"mars cruise\", \"cheap cruise\", \"jupiter cruise\"],\n keyword_plan_network: :GOOGLE_SEARCH,\n\n # For the list of geo target IDs, see:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # Geo target constant 2840 is for USA.\n geo_target_constants: [client.path.geo_target_constant(\"2840\")],\n\n # Language criteria 1000 is for English.\n # For the list of language criteria IDs, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n language: client.path.language_constant(\"1000\"),\n )\n\n for result in response.results\n metrics = result.keyword_metrics\n # These metrics include those for both the search query and any variants\n # included in the response.\n puts\"The search query '#{result.text}' (and the following variants: \" \\\n \"'#{result.close_variants}'), \" \\\n \"generated the following historical metrics:\\n\"\n\n\n # Approximate number of monthly searches on this query averaged for the\n # past 12 months.\n puts \"\\tApproximate monthly searches: #{metrics.avg_monthly_searches}\"\n\n # The competition level for this search query.\n puts \"\\tCompetition level: #{metrics.competition}\"\n\n # The competition index for the query in the range [0, 100]. This shows\n # how competitive ad placement is for a keyword. The level of\n # competition from 0-100 is determined by the number of ad slots filled\n # divided by the total number of ad slots available. If not enough data\n # is available, undef will be returned.\n puts \"\\tCompetition index: #{metrics.competition_index}\"\n\n # Top of page bid low range (20th percentile) in micros for the keyword.\n puts \"\\tTop of page bid low range: #{metrics.low_top_of_page_bid_micros}\"\n\n # Top of page bid high range (80th percentile) in micros for the\n # keyword.\n puts\"\\tTop of page bid high range: \"\n \"#{metrics.high_top_of_page_bid_micros}\"\n\n # Approximate number of searches on this query for the past twelve\n # months.\n for month in metrics.monthly_search_volumes\n puts \"\\tApproximately #{month.monthly_searches} searches in \"\n \"#{month.month.name}, #{month.year}\"\n end\n end\nendgenerate_historical_metrics.rb\n```\n\nExample:\n```text\nsub generate_historical_metrics {\n my ($api_client, $customer_id) = @_;\n\n my $keyword_historical_metrics_response =\n $api_client->KeywordPlanIdeaService()->generate_keyword_historical_metrics({\n customerId => $customer_id,\n keywords => [\"mars cruise\", \"cheap cruise\", \"jupiter cruise\"],\n # Geo target constant 2840 is for USA.\n geoTargetConstants => [\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n 2840)\n ],\n keywordPlanNetwork => 'GOOGLE_SEARCH',\n # Language criteria 1000 is for English. See\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n # for the list of language criteria IDs.\n language =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::language_constant(\n 1000)});\n\n foreach my $result (@{$keyword_historical_metrics_response->{results}}) {\n my $metric = $result->{keywordMetrics};\n # These metrics include those for both the search query and any\n # variants included in the response.\n # If the metric is undefined, print (undef) as a placeholder.\n printf\n\"The search query, %s, (and the following variants: %s), generated the following historical metrics:\\n\",\n $result->{text},\n $result->{closeVariants}\n ? join(', ', $result->{closeVariants})\n : \"(undef)\";\n\n # Approximate number of monthly searches on this query averaged for\n # the past 12 months.\n printf \"\\tApproximate monthly searches: %s.\\n\",\n value_or_undef($metric->{avgMonthlySearches});\n\n # The competition level for this search query.\n printf \"\\tCompetition level: %s.\\n\", value_or_undef($metric->{competition});\n\n # The competition index for the query in the range [0, 100]. This shows how\n # competitive ad placement is for a keyword. The level of competition from\n # 0-100 is determined by the number of ad slots filled divided by the total\n # number of ad slots available. If not enough data is available, undef will\n # be returned.\n printf \"\\tCompetition index: %s.\\n\",\n value_or_undef($metric->{competitionIndex});\n\n # Top of page bid low range (20th percentile) in micros for the keyword.\n printf \"\\tTop of page bid low range: %s.\\n\",\n value_or_undef($metric->{lowTopOfPageBidMicros});\n\n # Top of page bid high range (80th percentile) in micros for the keyword.\n printf \"\\tTop of page bid high range: %s.\\n\",\n value_or_undef($metric->{highTopOfPageBidMicros});\n\n # Approximate number of searches on this query for the past twelve months.\n foreach my $month (@{$metric->{monthlySearchVolumes}}) {\n printf \"\\tApproximately %d searches in %s, %s.\\n\",\n $month->{monthlySearches}, $month->{month}, $month->{year};\n }\n }\n\n return 1;\n}generate_historical_metrics.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.557Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":498,"estimatedTokens":5358}}218{"id":"doc-generate_forecast_metrics_google_ads_api_google_-e805c684","source":"documentation","title":"Generate Forecast Metrics | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/keyword-planning/generate-forecast-metrics","text":"Example:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient, Long customerId) {\n CampaignToForecast campaignToForecast = createCampaignToForecast(googleAdsClient);\n GenerateKeywordForecastMetricsRequest request =\n GenerateKeywordForecastMetricsRequest.newBuilder()\n .setCustomerId(String.valueOf(customerId))\n .setCampaign(campaignToForecast)\n .setForecastPeriod(\n DateRange.newBuilder()\n // Sets the forecast start date to tomorrow.\n .setStartDate(new DateTime().plusDays(1).toString(\"yyyy-MM-dd\"))\n // Sets the forecast end date to 30 days from today.\n .setEndDate(new DateTime().plusDays(30).toString(\"yyyy-MM-dd\")))\n .build();\n try (KeywordPlanIdeaServiceClient keywordPlanIdeaServiceClient =\n googleAdsClient.getLatestVersion().createKeywordPlanIdeaServiceClient()) {\n GenerateKeywordForecastMetricsResponse response =\n keywordPlanIdeaServiceClient.generateKeywordForecastMetrics(request);\n KeywordForecastMetrics metrics = response.getCampaignForecastMetrics();\n System.out.printf(\n \"Estimated daily clicks: %s%n\", metrics.hasClicks() ? metrics.getClicks() : null);\n System.out.printf(\n \"Estimated average CPC (micros): %s%n\",\n metrics.hasAverageCpcMicros() ? metrics.getAverageCpcMicros() : null);\n }\n}\n\n/**\n * Creates the campaign to forecast. A campaign to forecast lets you try out various\n * configurations and keywords to find the best optimization for your future campaigns. Once\n * you've found the best campaign configuration, create a serving campaign in your Google Ads\n * account with similar values and keywords. For more details, see:\n *\n * <p>https://support.google.com/google-ads/answer/3022575\n */\nprivate CampaignToForecast createCampaignToForecast(GoogleAdsClient googleAdsClient) {\n CampaignToForecast.Builder campaignToForecastBuilder =\n CampaignToForecast.newBuilder()\n .setBiddingStrategy(\n CampaignBiddingStrategy.newBuilder()\n .setManualCpcBiddingStrategy(\n ManualCpcBiddingStrategy.newBuilder().setMaxCpcBidMicros(1_000_000L)));\n\n // See https://developers.google.com/google-ads/api/reference/data/geotargets for the list of\n // geo target IDs. Geo target constant 2840 is for USA.\n campaignToForecastBuilder.addGeoTargetConstants(ResourceNames.geoTargetConstant(2840));\n\n // See https://developers.google.com/google-ads/api/reference/data/codes-formats#languages for\n // the list of language criteria IDs. Language constant 1000 is for English.\n campaignToForecastBuilder.addLanguageConstants(ResourceNames.languageConstant(1000));\n\n // Create forecast ad group based on themes such as creative relevance, product category, or\n // cost per click.\n ForecastAdGroup.Builder forecastAdGroupBuilder = ForecastAdGroup.newBuilder();\n forecastAdGroupBuilder.addKeywords(\n KeywordInfo.newBuilder()\n .setText(\"mars cruise\")\n .setMatchType(KeywordMatchType.BROAD));\n\n forecastAdGroupBuilder.addKeywords(\n KeywordInfo.newBuilder()\n .setText(\"cheap cruise\")\n .setMatchType(KeywordMatchType.PHRASE));\n\n forecastAdGroupBuilder.addKeywords(\n KeywordInfo.newBuilder()\n .setText(\"jupiter cruise\")\n .setMatchType(KeywordMatchType.BROAD));\n\n campaignToForecastBuilder.addAdGroups(forecastAdGroupBuilder.build());\n return campaignToForecastBuilder.build();\n}GenerateForecastMetrics.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId)\n{\n CampaignToForecast campaignToForecast = CreateCampaignToForecast();\n\n KeywordPlanIdeaServiceClient keywordPlanIdeaService =\n client.GetService(Services.V25.KeywordPlanIdeaService);\n\n GenerateKeywordForecastMetricsRequest request = new GenerateKeywordForecastMetricsRequest()\n {\n CustomerId = customerId.ToString(),\n Campaign = campaignToForecast,\n ForecastPeriod = new DateRange()\n {\n // Set the forecast start date to tomorrow.\n StartDate = DateTime.Now.AddDays(1).ToString(\"yyyy-MM-dd\"),\n // Set the forecast end date to 30 days from today.\n EndDate = DateTime.Now.AddDays(30).ToString(\"yyyy-MM-dd\"),\n }\n };\n\n try\n {\n GenerateKeywordForecastMetricsResponse response =\n keywordPlanIdeaService.GenerateKeywordForecastMetrics(request);\n\n KeywordForecastMetrics metrics = response.CampaignForecastMetrics;\n\n Console.WriteLine($\"Estimated daily clicks: {metrics.Clicks}.\"); \n Console.WriteLine($\"Estimated average cpc (micros): {metrics.AverageCpcMicros}.\");\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}\n\n/// <summary>\n/// Creates the campaign to forecast. A campaign to forecast lets you try out\n/// various configuration and keywords to find the best optimization for your\n/// future campaigns. Once you've found the best campaign configuration,\n/// create a serving campaign in your Google Ads account with similar values\n/// and keywords. For more details, see:\n/// https://support.google.com/google-ads/answer/3022575\n/// </summary>\nprivate CampaignToForecast CreateCampaignToForecast()\n{\n CampaignToForecast campaignToForecast = new CampaignToForecast()\n { \n BiddingStrategy = new CampaignToForecast.Types.CampaignBiddingStrategy()\n {\n ManualCpcBiddingStrategy = new ManualCpcBiddingStrategy()\n {\n MaxCpcBidMicros = 1_000_000\n }\n }\n };\n\n // See https://developers.google.com/google-ads/api/reference/data/geotargets\n // for the list of geo target IDs.\n // Geo target constant 2840 is for USA.\n campaignToForecast.GeoTargetConstants.Add(\n ResourceNames.GeoTargetConstant(2840)\n );\n\n // See https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n // for the list of language criteria IDs.\n // Language constant 1000 is for English.\n campaignToForecast.LanguageConstants.Add(ResourceNames.LanguageConstant(1000));\n\n // Create forecast ad group based on themes such as creative relevance, product category,\n // or cost per click.\n ForecastAdGroup forecastAdGroup = new ForecastAdGroup();\n\n KeywordInfo keyword1 = new KeywordInfo()\n {\n Text = \"mars cruise\",\n MatchType = KeywordMatchType.Broad\n };\n forecastAdGroup.Keywords.Add(keyword1);\n\n KeywordInfo keyword2 = new KeywordInfo()\n {\n Text = \"cheap cruise\",\n MatchType = KeywordMatchType.Phrase\n };\n forecastAdGroup.Keywords.Add(keyword2);\n\n KeywordInfo keyword3 = new KeywordInfo()\n {\n Text = \"jupiter cruise\",\n MatchType = KeywordMatchType.Exact\n };\n forecastAdGroup.Keywords.Add(keyword3);\n\n campaignToForecast.AdGroups.Add(forecastAdGroup);\n\n return campaignToForecast;\n}GenerateForecastMetrics.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId\n): void {\n $campaignToForecast = self::createCampaignToForecast();\n $keywordPlanIdeaServiceClient = $googleAdsClient->getKeywordPlanIdeaServiceClient();\n // Generates keyword forecast metrics based on the specified parameters.\n $response = $keywordPlanIdeaServiceClient->generateKeywordForecastMetrics(\n new GenerateKeywordForecastMetricsRequest([\n 'customer_id' => $customerId,\n 'campaign' => $campaignToForecast,\n 'forecast_period' => new DateRange([\n // Sets the forecast start date to tomorrow.\n 'start_date' => date('Ymd', strtotime('+1 day')),\n // Sets the forecast end date to 30 days from today.\n 'end_date' => date('Ymd', strtotime('+30 days'))\n ])\n ])\n );\n\n $metrics = $response->getCampaignForecastMetrics();\n printf(\n \"Estimated daily clicks: %s%s\",\n $metrics->hasClicks() ? sprintf(\"%.2f\", $metrics->getClicks()) : \"'none'\",\n PHP_EOL\n );\n printf(\n \"Estimated daily impressions: %s%s\",\n $metrics->hasImpressions() ? sprintf(\"%.2f\", $metrics->getImpressions()) : \"'none'\",\n PHP_EOL\n );\n printf(\n \"Estimated average CPC (micros): %s%s\",\n $metrics->hasAverageCpcMicros()\n ? sprintf(\"%d\", $metrics->getAverageCpcMicros()) : \"'none'\",\n PHP_EOL\n );\n}\n\n/**\n * Creates the campaign to forecast. A campaign to forecast lets you try out various\n * configurations and keywords to find the best optimization for your future campaigns. Once\n * you've found the best campaign configuration, create a serving campaign in your Google Ads\n * account with similar values and keywords. For more details, see:\n *\n * https://support.google.com/google-ads/answer/3022575\n *\n * @return CampaignToForecast the created campaign to forecast\n */\nprivate static function createCampaignToForecast(): CampaignToForecast\n{\n // Creates a campaign to forecast.\n $campaignToForecast = new CampaignToForecast([\n 'keyword_plan_network' => KeywordPlanNetwork::GOOGLE_SEARCH,\n 'bidding_strategy' => new CampaignBiddingStrategy([\n 'manual_cpc_bidding_strategy' => new ManualCpcBiddingStrategy([\n 'max_cpc_bid_micros' => 1_000_000\n ])\n ]),\n // See https://developers.google.com/google-ads/api/reference/data/geotargets for the\n // list of geo target IDs.\n 'geo_modifiers' => [\n new CriterionBidModifier([\n // Geo target constant 2840 is for USA.\n 'geo_target_constant' => ResourceNames::forGeoTargetConstant(2840)\n ])\n ],\n // See\n // https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n // for the list of language criteria IDs. Language constant 1000 is for English.\n 'language_constants' => [ResourceNames::forLanguageConstant(1000)],\n ]);\n\n // Creates forecast ad group based on themes such as creative relevance, product category,\n // or cost per click.\n $forecastAdGroup = new ForecastAdGroup([\n 'biddable_keywords' => [\n new BiddableKeyword([\n 'max_cpc_bid_micros' => 2_500_000,\n 'keyword' => new KeywordInfo([\n 'text' => 'mars cruise',\n 'match_type' => KeywordMatchType::BROAD\n ])\n ]),\n new BiddableKeyword([\n 'max_cpc_bid_micros' => 1_500_000,\n 'keyword' => new KeywordInfo([\n 'text' => 'cheap cruise',\n 'match_type' => KeywordMatchType::PHRASE\n ])\n ]),\n new BiddableKeyword([\n 'max_cpc_bid_micros' => 1_990_000,\n 'keyword' => new KeywordInfo([\n 'text' => 'jupiter cruise',\n 'match_type' => KeywordMatchType::BROAD\n ])\n ])\n ],\n 'negative_keywords' => [\n new KeywordInfo([\n 'text' => 'moon walk',\n 'match_type' => KeywordMatchType::BROAD\n ])\n ]\n ]);\n $campaignToForecast->setAdGroups([$forecastAdGroup]);\n\n return $campaignToForecast;\n}GenerateForecastMetrics.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient, customer_id: str):\n \"\"\"The main method that creates all necessary entities for the example.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n \"\"\"\n campaign_to_forecast: CampaignToForecast = create_campaign_to_forecast(\n client\n )\n generate_forecast_metrics(client, customer_id, campaign_to_forecast)\n\n\ndef create_campaign_to_forecast(client: GoogleAdsClient) -> CampaignToForecast:\n \"\"\"Creates the campaign to forecast.\n\n A campaign to forecast lets you try out various configurations and keywords\n to find the best optimization for your future campaigns. Once you've found\n the best campaign configuration, create a serving campaign in your Google\n Ads account with similar values and keywords. For more details, see:\n https://support.google.com/google-ads/answer/3022575\n\n Args:\n client: an initialized GoogleAdsClient instance.\n\n Returns:\n An CampaignToForecast instance.\n \"\"\"\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n # Create a campaign to forecast.\n campaign_to_forecast: CampaignToForecast = client.get_type(\n \"CampaignToForecast\"\n )\n # Set the bidding strategy.\n campaign_to_forecast.bidding_strategy.manual_cpc_bidding_strategy.max_cpc_bid_micros = (\n 1000000\n )\n\n # For the list of geo target IDs, see:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # Geo target constant 2840 is for USA.\n campaign_to_forecast.geo_target_constants.append(\n googleads_service.geo_target_constant_path(\"2840\")\n )\n\n # For the list of language criteria IDs, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n # Language criteria 1000 is for English.\n campaign_to_forecast.language_constants.append(\n googleads_service.language_constant_path(\"1000\")\n )\n\n # Create forecast ad groups based on themes such as creative relevance,\n # product category, or cost per click.\n forecast_ad_group: ForecastAdGroup = client.get_type(\"ForecastAdGroup\")\n\n # Create and configure three KeywordInfo instances.\n keyword_1: KeywordInfo = client.get_type(\"KeywordInfo\")\n keyword_1.text = \"mars cruise\"\n keyword_1.match_type = client.enums.KeywordMatchTypeEnum.BROAD\n\n keyword_2: KeywordInfo = client.get_type(\"KeywordInfo\")\n keyword_2.text = \"cheap cruise\"\n keyword_2.match_type = client.enums.KeywordMatchTypeEnum.PHRASE\n\n keyword_3: KeywordInfo = client.get_type(\"KeywordInfo\")\n keyword_3.text = \"cheap cruise\"\n keyword_3.match_type = client.enums.KeywordMatchTypeEnum.EXACT\n\n # Add the keywords to the forecast ad group.\n forecast_ad_group.keywords.extend([keyword_1, keyword_2, keyword_3])\n\n campaign_to_forecast.ad_groups.append(forecast_ad_group)\n\n return campaign_to_forecast\n\n\ndef generate_forecast_metrics(\n client: GoogleAdsClient,\n customer_id: str,\n campaign_to_forecast: CampaignToForecast,\n):\n \"\"\"Generates forecast metrics and prints the results.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n campaign_to_forecast: a CampaignToForecast to generate metrics for.\n \"\"\"\n keyword_plan_idea_service: KeywordPlanIdeaServiceClient = (\n client.get_service(\"KeywordPlanIdeaService\")\n )\n request: GenerateKeywordForecastMetricsRequest = client.get_type(\n \"GenerateKeywordForecastMetricsRequest\"\n )\n request.customer_id = customer_id\n request.campaign = campaign_to_forecast\n # Set the forecast range. Repeat forecasts with different horizons to get a\n # holistic picture.\n # Set the forecast start date to tomorrow.\n tomorrow = datetime.now() + timedelta(days=1)\n request.forecast_period.start_date = tomorrow.strftime(\"%Y-%m-%d\")\n # Set the forecast end date to 30 days from today.\n thirty_days_from_now = datetime.now() + timedelta(days=30)\n request.forecast_period.end_date = thirty_days_from_now.strftime(\"%Y-%m-%d\")\n\n response: GenerateKeywordForecastMetricsResponse = (\n keyword_plan_idea_service.generate_keyword_forecast_metrics(\n request=request\n )\n )\n\n metrics = response.campaign_forecast_metrics\n print(f\"Estimated daily clicks: {metrics.clicks}\")\n print(f\"Estimated daily average CPC: {metrics.average_cpc_micros}\")generate_forecast_metrics.py\n```\n\nExample:\n```text\ndef generate_forecast_metrics(customer_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n campaign_to_forecast = create_campaign_to_forecast(client)\n\n # Generates forecast metrics and prints the results.\n keyword_plan_idea_service = client.service.keyword_plan_idea\n\n # Set the forecast range. Repeat forecasts with different horizons to get a\n # holistic picture.\n\n forecast_period = client.resource.date_range do |p|\n tomorrow = Date.today + 1\n p.start_date = tomorrow.strftime(\"%Y-%m-%d\")\n\n # Set the forecast end date to 30 days from today.\n thirty_days_from_now = Date.today + 30\n p.end_date = thirty_days_from_now.strftime(\"%Y-%m-%d\")\n end\n\n response = keyword_plan_idea_service.generate_keyword_forecast_metrics(\n customer_id: customer_id,\n campaign: campaign_to_forecast,\n forecast_period: forecast_period,\n )\n\n metrics = response.campaign_forecast_metrics\n puts \"Estimated daily clicks: #{metrics.clicks}\"\n puts \"Estimated daily impressions: #{metrics.impressions}\"\n puts \"Estimated daily average CPC: #{metrics.average_cpc_micros}\"\nend\n\ndef create_campaign_to_forecast(client)\n campaign_to_forecast = client.resource.campaign_to_forecast do |c|\n c.keyword_plan_network = :GOOGLE_SEARCH\n\n c.bidding_strategy = client.resource.campaign_bidding_strategy do |bs|\n bs.manual_cpc_bidding_strategy = client.resource.manual_cpc_bidding_strategy do |mbs|\n mbs.max_cpc_bid_micros = 1000000\n end\n end\n\n criterion_bid_modifier = client.resource.criterion_bid_modifier\n\n # For the list of geo target IDs, see:\n # https://developers.google.com/google-ads/api/reference/data/geotargets\n # Geo target constant 2840 is for USA.\n criterion_bid_modifier.geo_target_constant = client.path.geo_target_constant(\"2840\")\n\n c.geo_modifiers << criterion_bid_modifier\n\n # For the list of language criteria IDs, see:\n # https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n # Language criteria 1000 is for English.\n language_constant = client.path.language_constant(\"1000\") # English\n c.language_constants << language_constant\n\n # Create forecast ad groups based on themes such as creative relevance,\n # product category, or cost per click.\n forecast_ad_group = client.resource.forecast_ad_group\n\n biddable_keyword_1 = client.resource.biddable_keyword do |bk|\n bk.max_cpc_bid_micros = 1500000\n bk.keyword = client.resource.keyword_info do |k|\n k.text = \"mars cruise\"\n k.match_type = :BROAD\n end\n end\n\n biddable_keyword_2 = client.resource.biddable_keyword do |bk|\n bk.max_cpc_bid_micros = 2500000\n bk.keyword = client.resource.keyword_info do |k|\n k.text = \"cheap cruise\"\n k.match_type = :PHRASE\n end\n end\n\n biddable_keyword_3 = client.resource.biddable_keyword do |bk|\n bk.max_cpc_bid_micros = 1990000\n bk.keyword = client.resource.keyword_info do |k|\n k.text = \"cheap cruise\"\n k.match_type = :EXACT\n end\n end\n\n forecast_ad_group.biddable_keywords << biddable_keyword_1\n forecast_ad_group.biddable_keywords << biddable_keyword_2\n forecast_ad_group.biddable_keywords << biddable_keyword_3\n\n # Create and configure a negative keyword, then add it to the forecast ad\n # group.\n negative_keyword = client.resource.keyword_info do |k|\n k.text = \"moon walk\"\n k.match_type = :BROAD\n end\n forecast_ad_group.negative_keywords << negative_keyword\n\n c.ad_groups << forecast_ad_group\n end\n\n return campaign_to_forecast\nendgenerate_forecast_metrics.rb\n```\n\nExample:\n```text\nsub generate_forecast_metrics {\n my ($api_client, $customer_id) = @_;\n\n my $campaign_to_forecast = create_campaign_to_forecast();\n\n my $keyword_forecast_metrics_response =\n $api_client->KeywordPlanIdeaService()->generate_keyword_forecast_metrics({\n customerId => $customer_id,\n campaign => $campaign_to_forecast,\n # Set the forecast range. Repeat forecasts with different horizons\n # to get a holistic picture.\n forecastPeriod => Google::Ads::GoogleAds::V25::Common::DateRange->new({\n # Set the forecast start date to tomorrow.\n startDate => strftime(\"%Y-%m-%d\", localtime(time + 60 * 60 * 24)),\n # Set the forecast end date to 30 days from today.\n endDate => strftime(\"%Y-%m-%d\", localtime(time + 60 * 60 * 24 * 30))})\n });\n\n my $metrics = $keyword_forecast_metrics_response->{campaignForecastMetrics};\n\n printf \"Estimated daily clicks: %s.\\n\",\n defined $metrics->{clicks} ? $metrics->{clicks} : \"undef\";\n printf \"Estimated average cpc (micros): %s.\\n\\n\",\n defined $metrics->{averageCpcMicros}\n ? $metrics->{averageCpcMicros}\n : \"undef\";\n\n return 1;\n}\n\n# Creates the campaign to forecast. A campaign to forecast lets you try out\n# various configuration and keywords to find the best optimization for your\n# future campaigns. Once you've found the best campaign configuration,\n# create a serving campaign in your Google Ads account with similar values\n# and keywords. For more details, see:\n# https://support.google.com/google-ads/answer/3022575\nsub create_campaign_to_forecast {\n my ($api_client) = @_;\n\n # Create a campaign to forecast.\n my $campaign_to_forecast =\n Google::Ads::GoogleAds::V25::Services::KeywordPlanIdeaService::CampaignToForecast\n ->new();\n\n # Set the bidding strategy.\n $campaign_to_forecast->{biddingStrategy}->{manualCpcBiddingStrategy} =\n Google::Ads::GoogleAds::V25::Services::KeywordPlanIdeaService::ManualCpcBiddingStrategy\n ->new({maxCpcBidMicros => 1000000});\n\n # See https://developers.google.com/google-ads/api/reference/data/geotargets\n # for the list of geo target IDs.\n # Geo target constant 2840 is for USA.\n $campaign_to_forecast->{geoTargetConstants} = [\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n 2840)];\n\n # See https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n # for the list of language criteria IDs.\n $campaign_to_forecast->{languageConstants} = [\n # Language criteria 1000 is for English.\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::language_constant(1000)];\n\n # Create forecast ad groups based on themes such as creative relevance,\n # product category, or cost per click.\n $campaign_to_forecast->{adGroups} = [\n Google::Ads::GoogleAds::V25::Services::KeywordPlanIdeaService::ForecastAdGroup\n ->new({\n keywords => [\n Google::Ads::GoogleAds::V25::Common::KeywordInfo->new({\n text => \"mars cruise\",\n matchType => 'BROAD'\n }\n ),\n Google::Ads::GoogleAds::V25::Common::KeywordInfo->new({\n text => \"cheap cruise\",\n matchType => 'PHRASE'\n }\n ),\n Google::Ads::GoogleAds::V25::Common::KeywordInfo->new({\n text => \"jupiter cruise\",\n matchType => 'EXACT'\n })]})];\n\n return $campaign_to_forecast;\n}generate_forecast_metrics.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.560Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":614,"estimatedTokens":5918}}219{"id":"doc-best_practices_and_limitations_google_ads_api_go-84d6f83a","source":"documentation","title":"Best Practices and Limitations | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/batch-processing/best-practices","text":"Example:\n```text\nstatic final int MAX_REQUEST_BYTES = 41_937_920;\n\n... (code to get the request object)\n\nint sizeInBytes = request.getSerializedSize();\n```\n\nExample:\n```text\nfrom google.ads.googleads.client import GoogleAdsClient\n\nMAX_REQUEST_BYTES = 41937920\n\n... (code to get the request object)\n\nsize_in_bytes = request._pb.ByteSize()\n```\n\nExample:\n```text\nrequire 'google/ads/google_ads'\n\nMAX_REQUEST_BYTES = 41937920\n\n... (code to get the request object)\n\nsize_in_bytes = request.to_proto.bytesize\n```\n\nExample:\n```text\nuse Google\\Ads\\GoogleAds\\V24\\Resources\\Campaign;\n\nconst MAX_REQUEST_BYTES = 41937920;\n\n... (code to get the request object)\n\n$size_in_bytes = $campaign->byteSize() . PHP_EOL;\n```\n\nExample:\n```text\nusing Google.Protobuf;\nconst int MAX_REQUEST_BYTES = 41937920;\n\n... (code to get the request object)\n\nint sizeInBytes = request.ToByteArray().Length;\n```\n\nExample:\n```text\nuse Devel::Size qw(total_size);\nuse constant MAX_REQUEST_BYTES => 41937920;\n\n... (code to get the request object)\n\nmy $size_in_bytes = total_size($request);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.561Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":63,"estimatedTokens":267}}220{"id":"doc-intra_campaign_experiments_google_ads_api_google-6c917bd4","source":"documentation","title":"Intra-campaign experiments | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/experiments/intra-campaign","text":"Example:\n```text\n// Create the experiment resource name using a temporary ID.\nString experimentResourceName = ResourceNames.experiment(customerId, -1L);\n\n// Create the experiment.\nExperiment experiment =\n Experiment.newBuilder()\n .setResourceName(experimentResourceName)\n .setName(\"ADOPT_AI_MAX Experiment #\" + UUID.randomUUID())\n .setType(ExperimentType.ADOPT_AI_MAX)\n .build();\nMutateOperation experimentOperation =\n MutateOperation.newBuilder()\n .setExperimentOperation(ExperimentOperation.newBuilder().setCreate(experiment).build())\n .build();\n\n// Create the control arm. Both arms in an intra-campaign experiment reference the same base\n// campaign.\nExperimentArm controlArm =\n ExperimentArm.newBuilder()\n .setExperiment(experimentResourceName)\n .setName(\"Control Arm\")\n .setControl(true)\n .setTrafficSplit(50)\n .addCampaigns(ResourceNames.campaign(customerId, campaignId))\n .build();\nMutateOperation controlArmOperation =\n MutateOperation.newBuilder()\n .setExperimentArmOperation(\n ExperimentArmOperation.newBuilder().setCreate(controlArm).build())\n .build();\n\n// Create the treatment arm.\nExperimentArm treatmentArm =\n ExperimentArm.newBuilder()\n .setExperiment(experimentResourceName)\n .setName(\"Treatment Arm\")\n .setControl(false)\n .setTrafficSplit(50)\n .addCampaigns(ResourceNames.campaign(customerId, campaignId))\n .build();\nMutateOperation treatmentArmOperation =\n MutateOperation.newBuilder()\n .setExperimentArmOperation(\n ExperimentArmOperation.newBuilder().setCreate(treatmentArm).build())\n .build();\n\n// Create a campaign operation with an update mask to enable AI Max and configure asset\n// automation settings.\n// Note: For intra-campaign experiments, these settings are applied to the base campaign but are\n// only active for the treatment traffic split.\nCampaign campaign =\n Campaign.newBuilder()\n .setResourceName(ResourceNames.campaign(customerId, campaignId))\n .setAiMaxSetting(AiMaxSetting.newBuilder().setEnableAiMax(true).build())\n .addAssetAutomationSettings(\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(AssetAutomationType.TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN)\n .build())\n .addAssetAutomationSettings(\n AssetAutomationSetting.newBuilder()\n .setAssetAutomationType(\n AssetAutomationType.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION)\n .setAssetAutomationStatus(AssetAutomationStatus.OPTED_IN)\n .build())\n .build();\n\nCampaignOperation campaignOp =\n CampaignOperation.newBuilder()\n .setUpdate(campaign)\n .setUpdateMask(FieldMasks.allSetFieldsOf(campaign))\n .build();\nMutateOperation campaignMutateOperation =\n MutateOperation.newBuilder().setCampaignOperation(campaignOp).build();\n\n// Send all mutate operations in a single Mutate request.\nList<MutateOperation> mutateOperations =\n ImmutableList.of(\n experimentOperation,\n controlArmOperation,\n treatmentArmOperation,\n campaignMutateOperation);\n\ntry (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n\n MutateGoogleAdsRequest request =\n MutateGoogleAdsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .addAllMutateOperations(mutateOperations)\n .build();\n\n MutateGoogleAdsResponse response = googleAdsServiceClient.mutate(request);CreateSearchAdoptAiMaxExperiment.java\n```\n\nExample:\n```text\n// Create the experiment resource name using a temporary ID.\nstring experimentResourceName = ResourceNames.Experiment(customerId, -1);\n\n// Create the experiment.\nMutateOperation experimentOperation = new MutateOperation()\n{\n ExperimentOperation = new ExperimentOperation()\n {\n Create = new Experiment()\n {\n ResourceName = experimentResourceName,\n Name = $\"ADOPT_AI_MAX Experiment #{ExampleUtilities.GetRandomString()}\",\n Type = ExperimentType.AdoptAiMax\n }\n }\n};\n\n// Create the control arm. Both arms in an intra-campaign experiment\n// reference the same base campaign.\nMutateOperation controlArmOperation = new MutateOperation()\n{\n ExperimentArmOperation = new ExperimentArmOperation()\n {\n Create = new ExperimentArm()\n {\n Experiment = experimentResourceName,\n Name = \"Control Arm\",\n Control = true,\n TrafficSplit = 50,\n Campaigns = { ResourceNames.Campaign(customerId, campaignId) }\n }\n }\n};\n\n// Create the treatment arm.\nMutateOperation treatmentArmOperation = new MutateOperation()\n{\n ExperimentArmOperation = new ExperimentArmOperation()\n {\n Create = new ExperimentArm()\n {\n Experiment = experimentResourceName,\n Name = \"Treatment Arm\",\n Control = false,\n TrafficSplit = 50,\n Campaigns = { ResourceNames.Campaign(customerId, campaignId) }\n }\n }\n};\n\n// Create a campaign operation with an update mask to enable AI Max and\n// configure asset automation settings.\n// Note: For intra-campaign experiments, these settings are applied to the\n// base campaign but are only active for the treatment traffic split.\nCampaign campaign = new Campaign()\n{\n ResourceName = ResourceNames.Campaign(customerId, campaignId),\n AiMaxSetting = new Campaign.Types.AiMaxSetting { EnableAiMax = true }\n};\n\ncampaign.AssetAutomationSettings.Add(new Campaign.Types.AssetAutomationSetting\n{\n AssetAutomationType = AssetAutomationType.TextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n});\n\ncampaign.AssetAutomationSettings.Add(new Campaign.Types.AssetAutomationSetting\n{\n AssetAutomationType = AssetAutomationType.FinalUrlExpansionTextAssetAutomation,\n AssetAutomationStatus = AssetAutomationStatus.OptedIn\n});\n\nMutateOperation campaignOperation = new MutateOperation()\n{\n CampaignOperation = new CampaignOperation()\n {\n Update = campaign,\n UpdateMask = FieldMasks.AllSetFieldsOf(campaign)\n }\n};\n\n// Send all mutate operations in a single Mutate request.\nList<MutateOperation> mutateOperations = new List<MutateOperation>\n{\n experimentOperation,\n controlArmOperation,\n treatmentArmOperation,\n campaignOperation\n};\n\nMutateGoogleAdsResponse response = googleAdsService.Mutate(\n customerId.ToString(), mutateOperations);CreateSearchAdoptAiMaxExperiment.cs\n```\n\nExample:\n```text\nThis example is not yet available in PHP; you can take a look at the other languages.\n```\n\nExample:\n```text\n# Create the experiment resource name using a temporary ID.\nexperiment_resource_name = googleads_service.experiment_path(\n customer_id, \"-1\"\n)\n\n# Create the experiment.\nexperiment_operation = client.get_type(\"MutateOperation\")\nexperiment = experiment_operation.experiment_operation.create\nexperiment.resource_name = experiment_resource_name\nexperiment.name = f\"ADOPT_AI_MAX Experiment #{uuid4()}\"\nexperiment.type_ = client.enums.ExperimentTypeEnum.ADOPT_AI_MAX\n\n# Create the control arm. Both arms in an intra-campaign experiment\n# reference the same base campaign.\ncontrol_arm_operation = client.get_type(\"MutateOperation\")\ncontrol_arm = control_arm_operation.experiment_arm_operation.create\ncontrol_arm.experiment = experiment_resource_name\ncontrol_arm.name = \"Control Arm\"\ncontrol_arm.control = True\ncontrol_arm.traffic_split = 50\ncontrol_arm.campaigns.append(\n googleads_service.campaign_path(customer_id, campaign_id)\n)\n\n# Create the treatment arm.\ntreatment_arm_operation = client.get_type(\"MutateOperation\")\ntreatment_arm = treatment_arm_operation.experiment_arm_operation.create\ntreatment_arm.experiment = experiment_resource_name\ntreatment_arm.name = \"Treatment Arm\"\ntreatment_arm.control = False\ntreatment_arm.traffic_split = 50\ntreatment_arm.campaigns.append(\n googleads_service.campaign_path(customer_id, campaign_id)\n)\n\n# Create a campaign operation with an update mask to enable AI Max and\n# configure asset automation settings.\n# Note: For intra-campaign experiments, these settings are applied to the\n# base campaign but are only active for the treatment traffic split.\ncampaign_operation = client.get_type(\"MutateOperation\")\ncampaign = campaign_operation.campaign_operation.update\ncampaign.resource_name = googleads_service.campaign_path(\n customer_id, campaign_id\n)\ncampaign.ai_max_setting.enable_ai_max = True\n\nfor asset_automation_type_enum in [\n client.enums.AssetAutomationTypeEnum.TEXT_ASSET_AUTOMATION,\n client.enums.AssetAutomationTypeEnum.FINAL_URL_EXPANSION_TEXT_ASSET_AUTOMATION,\n]:\n asset_automation_setting = client.get_type(\n \"Campaign\"\n ).AssetAutomationSetting()\n asset_automation_setting.asset_automation_type = (\n asset_automation_type_enum\n )\n asset_automation_setting.asset_automation_status = (\n client.enums.AssetAutomationStatusEnum.OPTED_IN\n )\n campaign.asset_automation_settings.append(asset_automation_setting)\n\nclient.copy_from(\n campaign_operation.campaign_operation.update_mask,\n protobuf_helpers.field_mask(None, campaign._pb),\n)\n\n# Send all mutate operations in a single Mutate request.\nmutate_operations = [\n experiment_operation,\n control_arm_operation,\n treatment_arm_operation,\n campaign_operation,\n]\n\nresponse = googleads_service.mutate(\n customer_id=customer_id,\n mutate_operations=mutate_operations,\n)create_search_adopt_ai_max_experiment.py\n```\n\nExample:\n```text\nThis example is not yet available in Ruby; you can take a look at the other languages.\n```\n\nExample:\n```text\nThis example is not yet available in Perl; you can take a look at the other languages.\n```\n\nExample:\n```text\nSELECT\n experiment.resource_name,\n experiment.name,\n metrics.clicks,\n metrics.control_clicks,\n metrics.clicks_point_estimate,\n metrics.clicks_p_value\nFROM experiment\nWHERE experiment.type = 'ADOPT_AI_MAX'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.562Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":304,"estimatedTokens":2547}}221{"id":"doc-system_managed_experiments_google_ads_api_google-ca9ce574","source":"documentation","title":"System-managed experiments | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/experiments/system-managed","text":"Example:\n```text\nprivate String createExperimentResource(GoogleAdsClient googleAdsClient, long customerId) {\n ExperimentOperation operation =\n ExperimentOperation.newBuilder()\n .setCreate(\n Experiment.newBuilder()\n // Name must be unique.\n .setName(\"Example Experiment #\" + getPrintableDateTime())\n // We specify SEARCH_CUSTOM to create a standard search campaign experiment.\n // This type uses a standard draft-based workflow where the system automatically\n // creates a draft/in-design campaign for the treatment arm.\n .setType(ExperimentType.SEARCH_CUSTOM)\n .setSuffix(\"[experiment]\")\n .setStatus(ExperimentStatus.SETUP)\n .build())\n .build();\n\n try (ExperimentServiceClient experimentServiceClient =\n googleAdsClient.getLatestVersion().createExperimentServiceClient()) {\n MutateExperimentsResponse response =\n experimentServiceClient.mutateExperiments(\n Long.toString(customerId), ImmutableList.of(operation));\n String experiment = response.getResults(0).getResourceName();\n System.out.printf(\"Created experiment with resource name '%s'%n\", experiment);\n return experiment;\n }\n}\nCreateSearchCustomExperiment.java\n```\n\nExample:\n```text\nprivate static string CreateExperimentResource(GoogleAdsClient client, long customerId)\n{\n // Get the ExperimentService.\n ExperimentServiceClient experimentService = client.GetService(\n Services.V25.ExperimentService);\n\n // Creates the experiment.\n Experiment experiment = new Experiment()\n {\n // Name must be unique.\n Name = $\"Example Experiment #{ExampleUtilities.GetRandomString()}\",\n // We specify SearchCustom to create a standard search campaign experiment.\n // This type uses a standard draft-based workflow where the system automatically\n // creates a draft/in-design campaign for the treatment arm.\n Type = ExperimentType.SearchCustom,\n Suffix = \"[experiment]\",\n Status = ExperimentStatus.Setup\n };\n\n // Creates the operation.\n ExperimentOperation operation = new ExperimentOperation()\n {\n Create = experiment\n };\n\n // Makes the API call.\n MutateExperimentsResponse response = experimentService.MutateExperiments(\n customerId.ToString(), new[] { operation });\n\n // Displays the result.\n string experimentResourceName = response.Results.First().ResourceName;\n\n Console.WriteLine($\"Created experiment with resource name \" +\n $\"'{experimentResourceName}'.\");\n return experimentResourceName;\n}CreateSearchCustomExperiment.cs\n```\n\nExample:\n```text\nprivate static function createExperimentResource(\n ExperimentServiceClient $experimentServiceClient,\n int $customerId\n): string {\n // Creates an experiment and its operation.\n $experiment = new Experiment([\n // Name must be unique.\n 'name' => 'Example Experiment #' . Helper::getPrintableDatetime(),\n 'type' => ExperimentType::SEARCH_CUSTOM,\n 'suffix' => '[experiment]',\n 'status' => ExperimentStatus::SETUP\n ]);\n $experimentOperation = new ExperimentOperation(['create' => $experiment]);\n\n // Issues a request to create the experiment.\n $response = $experimentServiceClient->mutateExperiments(\n MutateExperimentsRequest::build($customerId, [$experimentOperation])\n );\n $experimentResourceName = $response->getResults()[0]->getResourceName();\n print \"Created experiment with resource name '$experimentResourceName'\" . PHP_EOL;\n\n return $experimentResourceName;\n}CreateExperiment.php\n```\n\nExample:\n```text\ndef create_experiment_resource(\n client: GoogleAdsClient, customer_id: str\n) -> str:\n \"\"\"Creates a new experiment resource.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n\n Returns:\n the resource name for the new experiment.\n \"\"\"\n experiment_operation: ExperimentOperation = client.get_type(\n \"ExperimentOperation\"\n )\n experiment: Experiment = experiment_operation.create\n\n experiment.name = f\"Example Experiment #{uuid.uuid4()}\"\n # We specify SEARCH_CUSTOM to create a standard search campaign experiment.\n # This type uses a standard draft-based workflow where the system automatically\n # creates a draft/in-design campaign for the treatment arm.\n experiment.type_ = client.enums.ExperimentTypeEnum.SEARCH_CUSTOM\n experiment.suffix = \"[experiment]\"\n experiment.status = client.enums.ExperimentStatusEnum.SETUP\n\n experiment_service: ExperimentServiceClient = client.get_service(\n \"ExperimentService\"\n )\n response: MutateExperimentsResponse = experiment_service.mutate_experiments(\n customer_id=customer_id, operations=[experiment_operation]\n )\n\n experiment_resource_name: str = response.results[0].resource_name\n print(f\"Created experiment with resource name {experiment_resource_name}\")\n\n return experiment_resource_namecreate_search_custom_experiment.py\n```\n\nExample:\n```text\ndef create_experiment_resource(client, customer_id)\n operation = client.operation.create_resource.experiment do |e|\n # Name must be unique.\n e.name = \"Example Experiment #{(Time.new.to_f * 1000).to_i}\"\n e.type = :SEARCH_CUSTOM\n e.suffix = '[experiment]'\n e.status = :SETUP\n end\n\n response = client.service.experiment.mutate_experiments(\n customer_id: customer_id,\n operations: [operation],\n )\n\n experiment = response.results.first.resource_name\n puts \"Created experiment with resource name #{experiment}.\"\n\n experiment\nendcreate_experiment.rb\n```\n\nExample:\n```text\nsub create_experiment_resource {\n my ($api_client, $customer_id) = @_;\n\n my $experiment = Google::Ads::GoogleAds::V25::Resources::Experiment->new({\n # Name must be unique.\n name => \"Example Experiment #\" . uniqid(),\n type => SEARCH_CUSTOM,\n suffix => \"[experiment]\",\n status => SETUP\n });\n\n my $operation =\n Google::Ads::GoogleAds::V25::Services::ExperimentService::ExperimentOperation\n ->new({\n create => $experiment\n });\n\n my $response = $api_client->ExperimentService()->mutate({\n customerId => $customer_id,\n operations => [$operation]});\n\n my $resource_name = $response->{results}[0]{resourceName};\n printf \"Created experiment with resource name '%s'.\\n\", $resource_name;\n return $resource_name;\n}create_experiment.pl\n```\n\nExample:\n```text\nprivate String createExperimentArms(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId, String experiment) {\n List<ExperimentArmOperation> operations = new ArrayList<>();\n operations.add(\n ExperimentArmOperation.newBuilder()\n .setCreate(\n // The \"control\" arm references an already-existing campaign.\n ExperimentArm.newBuilder()\n .setControl(true)\n .addCampaigns(ResourceNames.campaign(customerId, campaignId))\n .setExperiment(experiment)\n .setName(\"control arm\")\n .setTrafficSplit(40)\n .build())\n .build());\n operations.add(\n ExperimentArmOperation.newBuilder()\n .setCreate(\n // In standard campaign experiments, creating the treatment arm automatically\n // generates a draft campaign that you can modify before starting the experiment.\n ExperimentArm.newBuilder()\n .setControl(false)\n .setExperiment(experiment)\n .setName(\"experiment arm\")\n .setTrafficSplit(60)\n .build())\n .build());\n\n try (ExperimentArmServiceClient experimentArmServiceClient =\n googleAdsClient.getLatestVersion().createExperimentArmServiceClient()) {\n // Constructs the mutate request.\n MutateExperimentArmsRequest mutateRequest =\n MutateExperimentArmsRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .addAllOperations(operations)\n // We want to fetch the draft campaign IDs from the treatment arm, so the easiest way\n // to do that is to have the response return the newly created entities.\n .setResponseContentType(ResponseContentType.MUTABLE_RESOURCE)\n .build();\n\n // Sends the mutate request.\n MutateExperimentArmsResponse response =\n experimentArmServiceClient.mutateExperimentArms(mutateRequest);\n\n // Results always return in the order that you specify them in the request. Since we created\n // the treatment arm last, it will be the last result. If you don't remember which arm is the\n // treatment arm, you can always filter the query in the next section with\n // `experiment_arm.control = false`.\n MutateExperimentArmResult controlArmResult = response.getResults(0);\n MutateExperimentArmResult treatmentArmResult =\n response.getResults(response.getResultsCount() - 1);\n\n System.out.printf(\n \"Created control arm with resource name '%s'%n\", controlArmResult.getResourceName());\n System.out.printf(\n \"Created treatment arm with resource name '%s'%n\", treatmentArmResult.getResourceName());\n\n return treatmentArmResult.getExperimentArm().getInDesignCampaigns(0);\n }\n}\nCreateSearchCustomExperiment.java\n```\n\nExample:\n```text\nprivate static (MutateExperimentArmResult, MutateExperimentArmResult)\n CreateExperimentArms(GoogleAdsClient client, long customerId, long baseCampaignId,\n string experimentResourceName)\n{\n // Get the ExperimentArmService.\n ExperimentArmServiceClient experimentService = client.GetService(\n Services.V25.ExperimentArmService);\n\n // Create the control arm. The control arm references an already-existing campaign.\n ExperimentArmOperation controlArmOperation = new ExperimentArmOperation()\n {\n Create = new ExperimentArm()\n {\n Control = true,\n Campaigns = {\n ResourceNames.Campaign(customerId, baseCampaignId)\n },\n Experiment = experimentResourceName,\n Name = \"Control Arm\",\n TrafficSplit = 40\n }\n };\n\n // Create the non-control arm.\n // In standard campaign experiments, creating the treatment arm automatically\n // generates a draft campaign that you can modify before starting the experiment.\n ExperimentArmOperation treatmentArmOperation = new ExperimentArmOperation()\n {\n Create = new ExperimentArm()\n {\n Control = false,\n Experiment = experimentResourceName,\n Name = \"Experiment Arm\",\n TrafficSplit = 60\n }\n };\n\n // We want to fetch the draft campaign IDs from the treatment arm, so the\n // easiest way to do that is to have the response return the newly created\n // entities.\n MutateExperimentArmsRequest request = new MutateExperimentArmsRequest\n {\n CustomerId = customerId.ToString(),\n Operations = { controlArmOperation, treatmentArmOperation },\n ResponseContentType = ResponseContentType.MutableResource\n };\n\n MutateExperimentArmsResponse response = experimentService.MutateExperimentArms(\n request\n );\n\n // Results always return in the order that you specify them in the request.\n // Since we created the treatment arm last, it will be the last result.\n MutateExperimentArmResult controlArm = response.Results.First();\n MutateExperimentArmResult treatmentArm = response.Results.Last();\n\n Console.WriteLine($\"Created control arm with resource name \" +\n $\"'{controlArm.ResourceName}'.\");\n Console.WriteLine($\"Created treatment arm with resource name\" +\n $\" '{treatmentArm.ResourceName}'.\");\n return (controlArm, treatmentArm);\n}CreateSearchCustomExperiment.cs\n```\n\nExample:\n```text\nprivate static function createExperimentArms(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId,\n string $experimentResourceName\n): string {\n $operations = [];\n $experimentArm1 = new ExperimentArm([\n // The \"control\" arm references an already-existing campaign.\n 'control' => true,\n 'campaigns' => [ResourceNames::forCampaign($customerId, $campaignId)],\n 'experiment' => $experimentResourceName,\n 'name' => 'control arm',\n 'traffic_split' => 40\n ]);\n $operations[] = new ExperimentArmOperation(['create' => $experimentArm1]);\n $experimentArm2 = new ExperimentArm([\n // The non-\"control\" arm, also called a \"treatment\" arm, will automatically\n // generate draft campaigns that you can modify before starting the\n // experiment.\n 'control' => false,\n 'experiment' => $experimentResourceName,\n 'name' => 'experiment arm',\n 'traffic_split' => 60\n ]);\n $operations[] = new ExperimentArmOperation(['create' => $experimentArm2]);\n\n // Issues a request to create the experiment arms.\n $experimentArmServiceClient = $googleAdsClient->getExperimentArmServiceClient();\n $response = $experimentArmServiceClient->mutateExperimentArms(\n MutateExperimentArmsRequest::build($customerId, $operations)\n // We want to fetch the draft campaign IDs from the treatment arm, so the easiest\n // way to do that is to have the response return the newly created entities.\n ->setResponseContentType(ResponseContentType::MUTABLE_RESOURCE)\n );\n // Results always return in the order that you specify them in the request.\n // Since we created the treatment arm last, it will be the last result.\n $controlArmResourceName = $response->getResults()[0]->getResourceName();\n $treatmentArm = $response->getResults()[count($operations) - 1];\n print \"Created control arm with resource name '$controlArmResourceName'\" . PHP_EOL;\n print \"Created treatment arm with resource name '{$treatmentArm->getResourceName()}'\"\n . PHP_EOL;\n\n return $treatmentArm->getExperimentArm()->getInDesignCampaigns()[0];\n}CreateExperiment.php\n```\n\nExample:\n```text\ndef create_experiment_arms(\n client: GoogleAdsClient,\n customer_id: str,\n base_campaign_id: str,\n experiment: str,\n) -> str:\n \"\"\"Creates a control and treatment experiment arms.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n base_campaign_id: the campaign ID to associate with the control arm of\n the experiment.\n experiment: the resource name for an experiment.\n\n Returns:\n the resource name for the new treatment experiment arm.\n \"\"\"\n operations: List[ExperimentArmOperation] = []\n\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n\n # The \"control\" arm references an already-existing campaign.\n operation_1: ExperimentArmOperation = client.get_type(\n \"ExperimentArmOperation\"\n )\n exa_1: ExperimentArm = operation_1.create\n exa_1.control = True\n exa_1.campaigns.append(\n campaign_service.campaign_path(customer_id, base_campaign_id)\n )\n exa_1.experiment = experiment\n exa_1.name = \"control arm\"\n exa_1.traffic_split = 40\n operations.append(operation_1)\n\n # In standard campaign experiments, creating the treatment arm automatically\n # generates a draft campaign that you can modify before starting the experiment.\n operation_2: ExperimentArmOperation = client.get_type(\n \"ExperimentArmOperation\"\n )\n exa_2: ExperimentArm = operation_2.create\n exa_2.control = False\n exa_2.experiment = experiment\n exa_2.name = \"experiment arm\"\n exa_2.traffic_split = 60\n operations.append(operation_2)\n\n experiment_arm_service: ExperimentArmServiceClient = client.get_service(\n \"ExperimentArmService\"\n )\n request: MutateExperimentArmsRequest = client.get_type(\n \"MutateExperimentArmsRequest\"\n )\n request.customer_id = customer_id\n request.operations = operations\n # We want to fetch the draft campaign IDs from the treatment arm, so the\n # easiest way to do that is to have the response return the newly created\n # entities.\n request.response_content_type = (\n client.enums.ResponseContentTypeEnum.MUTABLE_RESOURCE\n )\n response: MutateExperimentArmsResponse = (\n experiment_arm_service.mutate_experiment_arms(request=request)\n )\n\n # Results always return in the order that you specify them in the request.\n # Since we created the treatment arm second, it will be the second result.\n control_arm_result: Any = response.results[0]\n treatment_arm_result: Any = response.results[1]\n\n print(\n f\"Created control arm with resource name {control_arm_result.resource_name}\"\n )\n print(\n f\"Created treatment arm with resource name {treatment_arm_result.resource_name}\"\n )\n\n return treatment_arm_result.experiment_arm.in_design_campaigns[0]create_search_custom_experiment.py\n```\n\nExample:\n```text\ndef create_experiment_arms(client, customer_id, base_campaign_id, experiment)\n operations = []\n operations << client.operation.create_resource.experiment_arm do |ea|\n # The \"control\" arm references an already-existing campaign.\n ea.control = true\n ea.campaigns << client.path.campaign(customer_id, base_campaign_id)\n ea.experiment = experiment\n ea.name = 'control arm'\n ea.traffic_split = 40\n end\n operations << client.operation.create_resource.experiment_arm do |ea|\n # The non-\"control\" arm, also called a \"treatment\" arm, will automatically\n # generate draft campaigns that you can modify before starting the\n # experiment.\n ea.control = false\n ea.experiment = experiment\n ea.name = 'experiment arm'\n ea.traffic_split = 60\n end\n\n response = client.service.experiment_arm.mutate_experiment_arms(\n customer_id: customer_id,\n operations: operations,\n # We want to fetch the draft campaign IDs from the treatment arm, so the\n # easiest way to do that is to have the response return the newly created\n # entities.\n response_content_type: :MUTABLE_RESOURCE,\n )\n\n # Results always return in the order that you specify them in the request.\n # Since we created the treatment arm last, it will be the last result.\n control_arm_result = response.results.first\n treatment_arm_result = response.results.last\n\n puts \"Created control arm with resource name #{control_arm_result.resource_name}.\"\n puts \"Created treatment arm with resource name #{treatment_arm_result.resource_name}.\"\n\n treatment_arm_result.experiment_arm.in_design_campaigns.first\nendcreate_experiment.rb\n```\n\nExample:\n```text\nsub create_experiment_arms {\n my ($api_client, $customer_id, $base_campaign_id, $experiment) = @_;\n\n my $operations = [];\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::ExperimentArmService::ExperimentArmOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::ExperimentArm->new({\n # The \"control\" arm references an already-existing campaign.\n control => \"true\",\n campaigns => [\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $base_campaign_id\n )\n ],\n experiment => $experiment,\n name => \"control arm\",\n trafficSplit => 40\n })});\n\n push @$operations,\n Google::Ads::GoogleAds::V25::Services::ExperimentArmService::ExperimentArmOperation\n ->new({\n create => Google::Ads::GoogleAds::V25::Resources::ExperimentArm->new({\n # The non-\"control\" arm, also called a \"treatment\" arm, will automatically\n # generate draft campaigns that you can modify before starting the\n # experiment.\n control => \"false\",\n experiment => $experiment,\n name => \"experiment arm\",\n trafficSplit => 60\n })});\n\n my $response = $api_client->ExperimentArmService()->mutate({\n customerId => $customer_id,\n operations => $operations,\n # We want to fetch the draft campaign IDs from the treatment arm, so the\n # easiest way to do that is to have the response return the newly created\n # entities.\n responseContentType => MUTABLE_RESOURCE\n });\n\n # Results always return in the order that you specify them in the request.\n # Since we created the treatment arm last, it will be the last result.\n my $control_arm_result = $response->{results}[0];\n my $treatment_arm_result = $response->{results}[1];\n\n printf \"Created control arm with resource name '%s'.\\n\",\n $control_arm_result->{resourceName};\n printf \"Created treatment arm with resource name '%s'.\\n\",\n $treatment_arm_result->{resourceName};\n return $treatment_arm_result->{experimentArm}{inDesignCampaigns}[0];\n}create_experiment.pl\n```\n\nExample:\n```text\nSELECT experiment_arm.in_design_campaigns\nFROM experiment_arm\nWHERE experiment_arm.resource_name = \"TREATMENT_ARM_RESOURCE_NAME\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.564Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":563,"estimatedTokens":5271}}222{"id":"doc-keyword_ideas_google_ads_api_google_for_develope-c33cbc25","source":"documentation","title":"Keyword Ideas | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/keyword-planning/generate-keyword-ideas","text":"Example:\n```text\nprivate void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n long languageId,\n List<Long> locationIds,\n List<String> keywords,\n @Nullable String pageUrl) {\n try (KeywordPlanIdeaServiceClient keywordPlanServiceClient =\n googleAdsClient.getLatestVersion().createKeywordPlanIdeaServiceClient()) {\n GenerateKeywordIdeasRequest.Builder requestBuilder =\n GenerateKeywordIdeasRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n // Sets the language resource using the provided language ID.\n .setLanguage(ResourceNames.languageConstant(languageId))\n // Sets the network. To restrict to only Google Search, change the parameter below to\n // KeywordPlanNetwork.GOOGLE_SEARCH.\n .setKeywordPlanNetwork(KeywordPlanNetwork.GOOGLE_SEARCH_AND_PARTNERS);\n\n // Adds the resource name of each location ID to the request.\n for (Long locationId : locationIds) {\n requestBuilder.addGeoTargetConstants(ResourceNames.geoTargetConstant(locationId));\n }\n\n // Makes sure that keywords and/or page URL were specified. The request must have exactly one\n // of urlSeed, keywordSeed, or keywordAndUrlSeed set.\n if (keywords.isEmpty() && pageUrl == null) {\n throw new IllegalArgumentException(\n \"At least one of keywords or page URL is required, but neither was specified.\");\n }\n\n if (keywords.isEmpty()) {\n // Only page URL was specified, so use a UrlSeed.\n requestBuilder.getUrlSeedBuilder().setUrl(pageUrl);\n } else if (pageUrl == null) {\n // Only keywords were specified, so use a KeywordSeed.\n requestBuilder.getKeywordSeedBuilder().addAllKeywords(keywords);\n } else {\n // Both page URL and keywords were specified, so use a KeywordAndUrlSeed.\n requestBuilder.getKeywordAndUrlSeedBuilder().setUrl(pageUrl).addAllKeywords(keywords);\n }\n\n // Sends the keyword ideas request.\n GenerateKeywordIdeasPagedResponse response =\n keywordPlanServiceClient.generateKeywordIdeas(requestBuilder.build());\n // Prints each result in the response.\n for (GenerateKeywordIdeaResult result : response.iterateAll()) {\n System.out.printf(\n \"Keyword idea text '%s' has %d average monthly searches and '%s' competition.%n\",\n result.getText(),\n result.getKeywordIdeaMetrics().getAvgMonthlySearches(),\n result.getKeywordIdeaMetrics().getCompetition());\n }\n }\n}GenerateKeywordIdeas.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long[] locationIds,\n long languageId, string[] keywordTexts, string pageUrl)\n{\n KeywordPlanIdeaServiceClient keywordPlanIdeaService =\n client.GetService(Services.V25.KeywordPlanIdeaService);\n\n // Make sure that keywords and/or page URL were specified. The request must have\n // exactly one of urlSeed, keywordSeed, or keywordAndUrlSeed set.\n if (keywordTexts.Length == 0 && string.IsNullOrEmpty(pageUrl))\n {\n throw new ArgumentException(\"At least one of keywords or page URL is required, \" +\n \"but neither was specified.\");\n }\n\n // Specify the optional arguments of the request as a keywordSeed, UrlSeed,\n // or KeywordAndUrlSeed.\n GenerateKeywordIdeasRequest request = new GenerateKeywordIdeasRequest()\n {\n CustomerId = customerId.ToString(),\n };\n\n if (keywordTexts.Length == 0)\n {\n // Only page URL was specified, so use a UrlSeed.\n request.UrlSeed = new UrlSeed()\n {\n Url = pageUrl\n };\n }\n else if (string.IsNullOrEmpty(pageUrl))\n {\n // Only keywords were specified, so use a KeywordSeed.\n request.KeywordSeed = new KeywordSeed();\n request.KeywordSeed.Keywords.AddRange(keywordTexts);\n }\n else\n {\n // Both page URL and keywords were specified, so use a KeywordAndUrlSeed.\n request.KeywordAndUrlSeed = new KeywordAndUrlSeed();\n request.KeywordAndUrlSeed.Url = pageUrl;\n request.KeywordAndUrlSeed.Keywords.AddRange(keywordTexts);\n }\n\n // Create a list of geo target constants based on the resource name of specified\n // location IDs.\n foreach (long locationId in locationIds)\n {\n request.GeoTargetConstants.Add(ResourceNames.GeoTargetConstant(locationId));\n }\n\n request.Language = ResourceNames.LanguageConstant(languageId);\n // Set the network. To restrict to only Google Search, change the parameter below to\n // KeywordPlanNetwork.GoogleSearch.\n request.KeywordPlanNetwork = KeywordPlanNetwork.GoogleSearchAndPartners;\n\n try\n {\n // Generate keyword ideas based on the specified parameters.\n var response =\n keywordPlanIdeaService.GenerateKeywordIdeas(request);\n\n // Iterate over the results and print its detail.\n foreach (GenerateKeywordIdeaResult result in response)\n {\n KeywordPlanHistoricalMetrics metrics = result.KeywordIdeaMetrics;\n Console.WriteLine($\"Keyword idea text '{result.Text}' has \" +\n $\"{metrics.AvgMonthlySearches} average monthly searches and competition \" +\n $\"is {metrics.Competition}.\");\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}GenerateKeywordIdeas.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n array $locationIds,\n int $languageId,\n array $keywords,\n ?string $pageUrl\n) {\n $keywordPlanIdeaServiceClient = $googleAdsClient->getKeywordPlanIdeaServiceClient();\n\n // Make sure that keywords and/or page URL were specified. The request must have exactly one\n // of urlSeed, keywordSeed, or keywordAndUrlSeed set.\n if (empty($keywords) && is_null($pageUrl)) {\n throw new \\InvalidArgumentException(\n 'At least one of keywords or page URL is required, but neither was specified.'\n );\n }\n\n // Specify the optional arguments of the request as a keywordSeed, urlSeed,\n // or keywordAndUrlSeed.\n $requestOptionalArgs = [];\n if (empty($keywords)) {\n // Only page URL was specified, so use a UrlSeed.\n $requestOptionalArgs['url_seed'] = new UrlSeed(['url' => $pageUrl]);\n } elseif (is_null($pageUrl)) {\n // Only keywords were specified, so use a KeywordSeed.\n $requestOptionalArgs['keyword_seed'] = new KeywordSeed(['keywords' => $keywords]);\n } else {\n // Both page URL and keywords were specified, so use a KeywordAndUrlSeed.\n $requestOptionalArgs['keyword_and_url_seed'] =\n new KeywordAndUrlSeed(['url' => $pageUrl, 'keywords' => $keywords]);\n }\n\n // Create a list of geo target constants based on the resource name of specified location\n // IDs.\n $geoTargetConstants = array_map(function ($locationId) {\n return ResourceNames::forGeoTargetConstant($locationId);\n }, $locationIds);\n\n // Generate keyword ideas based on the specified parameters.\n $response = $keywordPlanIdeaServiceClient->generateKeywordIdeas(\n new GenerateKeywordIdeasRequest([\n // Set the language resource using the provided language ID.\n 'language' => ResourceNames::forLanguageConstant($languageId),\n 'customer_id' => $customerId,\n // Add the resource name of each location ID to the request.\n 'geo_target_constants' => $geoTargetConstants,\n // Set the network. To restrict to only Google Search, change the parameter below to\n // KeywordPlanNetwork::GOOGLE_SEARCH.\n 'keyword_plan_network' => KeywordPlanNetwork::GOOGLE_SEARCH_AND_PARTNERS\n ] + $requestOptionalArgs)\n );\n\n // Iterate over the results and print its detail.\n foreach ($response->iterateAllElements() as $result) {\n /** @var GenerateKeywordIdeaResult $result */\n // Note that the competition printed below is enum value.\n // For example, a value of 2 will be returned when the competition is 'LOW'.\n // A mapping of enum names to values can be found at KeywordPlanCompetitionLevel.php.\n printf(\n \"Keyword idea text '%s' has %d average monthly searches and competition as %d.%s\",\n $result->getText(),\n is_null($result->getKeywordIdeaMetrics()) ?\n 0 : $result->getKeywordIdeaMetrics()->getAvgMonthlySearches(),\n is_null($result->getKeywordIdeaMetrics()) ?\n 0 : $result->getKeywordIdeaMetrics()->getCompetition(),\n PHP_EOL\n );\n }\n}GenerateKeywordIdeas.php\n```\n\nExample:\n```text\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n location_ids: list[str],\n language_id: str,\n keyword_texts: list[str],\n page_url: str,\n):\n keyword_plan_idea_service: KeywordPlanIdeaServiceClient = (\n client.get_service(\"KeywordPlanIdeaService\")\n )\n keyword_competition_level_enum: KeywordPlanCompetitionLevelEnum = (\n client.enums.KeywordPlanCompetitionLevelEnum\n )\n keyword_plan_network: KeywordPlanNetworkEnum = (\n client.enums.KeywordPlanNetworkEnum.GOOGLE_SEARCH_AND_PARTNERS\n )\n location_rns: list[str] = map_locations_ids_to_resource_names(\n client, location_ids\n )\n google_ads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n language_rn: str = google_ads_service.language_constant_path(language_id)\n\n # Either keywords or a page_url are required to generate keyword ideas\n # so this raises an error if neither are provided.\n if not (keyword_texts or page_url):\n raise ValueError(\n \"At least one of keywords or page URL is required, \"\n \"but neither was specified.\"\n )\n\n # Only one of the fields \"url_seed\", \"keyword_seed\", or\n # \"keyword_and_url_seed\" can be set on the request, depending on whether\n # keywords, a page_url or both were passed to this function.\n request: GenerateKeywordIdeasRequest = client.get_type(\n \"GenerateKeywordIdeasRequest\"\n )\n request.customer_id = customer_id\n request.language = language_rn\n request.geo_target_constants = location_rns\n request.include_adult_keywords = False\n request.keyword_plan_network = keyword_plan_network\n\n # To generate keyword ideas with only a page_url and no keywords we need\n # to initialize a UrlSeed object with the page_url as the \"url\" field.\n if not keyword_texts and page_url:\n request.url_seed.url = page_url\n\n # To generate keyword ideas with only a list of keywords and no page_url\n # we need to initialize a KeywordSeed object and set the \"keywords\" field\n # to be a list of StringValue objects.\n if keyword_texts and not page_url:\n request.keyword_seed.keywords.extend(keyword_texts)\n\n # To generate keyword ideas using both a list of keywords and a page_url we\n # need to initialize a KeywordAndUrlSeed object, setting both the \"url\" and\n # \"keywords\" fields.\n if keyword_texts and page_url:\n request.keyword_and_url_seed.url = page_url\n request.keyword_and_url_seed.keywords.extend(keyword_texts)\n\n keyword_ideas = keyword_plan_idea_service.generate_keyword_ideas(\n request=request\n )\n\n idea: GenerateKeywordIdeaResult\n for idea in keyword_ideas:\n competition_value = idea.keyword_idea_metrics.competition.name\n print(\n f'Keyword idea text \"{idea.text}\" has '\n f'\"{idea.keyword_idea_metrics.avg_monthly_searches}\" '\n f'average monthly searches and \"{competition_value}\" '\n \"competition.\\n\"\n )generate_keyword_ideas.py\n```\n\nExample:\n```text\ndef generate_keyword_ideas(customer_id, location_ids, language_id, keywords,\n page_url)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n # Make sure that keywords and/or page URL were specified. The request must\n # have exactly one of urlSeed, keywordSeed, or keywordAndUrlSeed set.\n if keywords.reject {|k| k.nil?}.empty? && page_url.nil?\n raise \"At least one of keywords or page URL is required.\"\n end\n\n kp_idea_service = client.service.keyword_plan_idea\n\n options_hash = if keywords.empty?\n seed = client.resource.url_seed do |seed|\n seed.url = page_url\n end\n {url_seed: seed}\n elsif page_url.nil?\n seed = client.resource.keyword_seed do |seed|\n keywords.each do |keyword|\n seed.keywords << keyword\n end\n end\n {keyword_seed: seed}\n else\n seed = client.resource.keyword_and_url_seed do |seed|\n seed.url = page_url\n keywords.each do |keyword|\n seed.keywords << keyword\n end\n end\n {keyword_and_url_seed: seed}\n end\n\n geo_target_constants = location_ids.map do |location_id|\n client.path.geo_target_constant(location_id)\n end\n\n include_adult_keywords = true\n\n response = kp_idea_service.generate_keyword_ideas(\n customer_id: customer_id,\n language: client.path.language_constant(language_id),\n geo_target_constants: geo_target_constants,\n include_adult_keywords: include_adult_keywords,\n # To restrict to only Google Search, change the parameter below to\n # :GOOGLE_SEARCH\n keyword_plan_network: :GOOGLE_SEARCH_AND_PARTNERS,\n **options_hash\n )\n\n response.each do |result|\n monthly_searches = if result.keyword_idea_metrics.nil?\n 0\n else\n result.keyword_idea_metrics.avg_monthly_searches\n end\n competition = if result.keyword_idea_metrics.nil?\n :UNSPECIFIED\n else\n result.keyword_idea_metrics.competition\n end\n puts \"Keyword idea text #{result.text} has #{monthly_searches} average \" +\n \"monthly searches and competition as #{competition}.\"\n end\nendgenerate_keyword_ideas.rb\n```\n\nExample:\n```text\nsub generate_keyword_ideas {\n my (\n $api_client, $customer_id, $location_ids,\n $language_id, $keyword_texts, $page_url\n ) = @_;\n\n # Make sure that keywords and/or page URL were specified. The request must have\n # exactly one of urlSeed, keywordSeed, or keywordAndUrlSeed set.\n if (not scalar @$keyword_texts and not $page_url) {\n die \"At least one of keywords or page URL is required, \" .\n \"but neither was specified.\";\n }\n\n # Specify the optional arguments of the request as a keywordSeed, urlSeed,\n # or keywordAndUrlSeed.\n my $request_option_args = {};\n if (!scalar @$keyword_texts) {\n # Only page URL was specified, so use a UrlSeed.\n $request_option_args->{urlSeed} =\n Google::Ads::GoogleAds::V25::Services::KeywordPlanIdeaService::UrlSeed->\n new({\n url => $page_url\n });\n } elsif (not $page_url) {\n # Only keywords were specified, so use a KeywordSeed.\n $request_option_args->{keywordSeed} =\n Google::Ads::GoogleAds::V25::Services::KeywordPlanIdeaService::KeywordSeed\n ->new({\n keywords => $keyword_texts\n });\n } else {\n # Both page URL and keywords were specified, so use a KeywordAndUrlSeed.\n $request_option_args->{keywordAndUrlSeed} =\n Google::Ads::GoogleAds::V25::Services::KeywordPlanIdeaService::KeywordAndUrlSeed\n ->new({\n url => $page_url,\n keywords => $keyword_texts\n });\n }\n\n # Create a list of geo target constants based on the resource name of specified\n # location IDs.\n my $geo_target_constants = [\n map (\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::geo_target_constant(\n $_),\n @$location_ids)];\n\n # Generate keyword ideas based on the specified parameters.\n my $keyword_ideas_response =\n $api_client->KeywordPlanIdeaService()->generate_keyword_ideas({\n customerId => $customer_id,\n # Set the language resource using the provided language ID.\n language =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::language_constant(\n $language_id),\n # Add the resource name of each location ID to the request.\n geoTargetConstants => $geo_target_constants,\n # Set the network. To restrict to only Google Search, change the parameter below\n # to GOOGLE_SEARCH.\n keywordPlanNetwork => GOOGLE_SEARCH_AND_PARTNERS,\n %$request_option_args\n });\n\n # Iterate over the results and print its detail.\n foreach my $result (@{$keyword_ideas_response->{results}}) {\n printf \"Keyword idea text '%s' has %d average monthly searches \" .\n \"and '%s' competition.\\n\", $result->{text},\n $result->{keywordIdeaMetrics}{avgMonthlySearches}\n ? $result->{keywordIdeaMetrics}{avgMonthlySearches}\n : 0,\n $result->{keywordIdeaMetrics}{competition}\n ? $result->{keywordIdeaMetrics}{competition}\n : \"undef\";\n }\n\n return 1;\n}generate_keyword_ideas.pl\n```\n\nExample:\n```text\n# This code example generates keyword ideas.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n#\n# LANGUAGE: The resource name of the language to target. This is in the format\n# of \"languageConstants/{criterion_id}\". See\n# https://developers.google.com/google-ads/api/data/codes-formats#languages\n# for the available criterion_id values.\n# GEO_TARGET_CONSTANT: The resource name of the geo target constant to\n# generate keyword ideas for. This is in the format of\n# \"geoTargetConstants/{criterion_id}\". See\n# https://developers.google.com/google-ads/api/data/geotargets for the\n# available criterion_id values.\n# KEYWORD: The keyword to generate keyword ideas for.\n# URL: The URL of the website to generate keyword ideas for.\ncurl -f --request POST \\\n\"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}:generateKeywordIdeas\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data @- <<EOF\n{\n \"language\": \"${LANGUAGE}\",\n \"geoTargetConstants\": [\n \"${GEO_TARGET_CONSTANT}\"\n ],\n \"includeAdultKeywords\": false,\n \"keywordPlanNetwork\": \"GOOGLE_SEARCH\",\n \"keywordAndUrlSeed\": {\n \"keywords\": [\n \"${KEYWORD}\"\n ],\n \"url\": \"${URL}\"\n }\n}\nEOFgenerate_keyword_ideas.sh\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.566Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":498,"estimatedTokens":4763}}223{"id":"doc-optimization_score_and_recommendations_google_ad-f37c40f5","source":"documentation","title":"Optimization score and recommendations | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/recommendations","text":"Example:\n```text\ntry (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient();\n RecommendationServiceClient recommendationServiceClient =\n googleAdsClient.getLatestVersion().createRecommendationServiceClient()) {\n // Creates a query that retrieves keyword recommendations.\n String query =\n \"SELECT recommendation.resource_name, \"\n + \" recommendation.campaign, \"\n + \" recommendation.keyword_recommendation \"\n + \"FROM recommendation \"\n + \"WHERE recommendation.type = KEYWORD\";\n // Constructs the SearchGoogleAdsStreamRequest.\n SearchGoogleAdsStreamRequest request =\n SearchGoogleAdsStreamRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setQuery(query)\n .build();\n\n // Issues the search stream request to detect keyword recommendations that exist for the\n // customer account.\n ServerStream<SearchGoogleAdsStreamResponse> stream =\n googleAdsServiceClient.searchStreamCallable().call(request);\n\n // Creates apply operations for all the recommendations found.\n List<ApplyRecommendationOperation> applyRecommendationOperations = new ArrayList<>();\n for (SearchGoogleAdsStreamResponse response : stream) {\n for (GoogleAdsRow googleAdsRow : response.getResultsList()) {\n Recommendation recommendation = googleAdsRow.getRecommendation();\n System.out.printf(\n \"Keyword recommendation '%s' was found for campaign '%s'%n\",\n recommendation.getResourceName(), recommendation.getCampaign());\n KeywordInfo keyword = recommendation.getKeywordRecommendation().getKeyword();\n System.out.printf(\"\\tKeyword = '%s'%n\", keyword.getText());\n System.out.printf(\"\\tMatch type = '%s'%n\", keyword.getMatchType());\n\n // Creates an ApplyRecommendationOperation that will apply this recommendation, and adds\n // it to the list of operations.\n applyRecommendationOperations.add(buildRecommendationOperation(recommendation));\n }\n }DetectAndApplyRecommendations.java\n```\n\nExample:\n```text\n// Get the GoogleAdsServiceClient.\nGoogleAdsServiceClient googleAdsService = client.GetService(\n Services.V25.GoogleAdsService);\n\n// Creates a query that retrieves keyword recommendations.\nstring query = \"SELECT recommendation.resource_name, \" +\n \"recommendation.campaign, recommendation.keyword_recommendation \" +\n \"FROM recommendation WHERE \" +\n $\"recommendation.type = KEYWORD\";\n\nList<ApplyRecommendationOperation> operations =\n new List<ApplyRecommendationOperation>();\n\ntry\n{\n // Issue a search request.\n googleAdsService.SearchStream(customerId.ToString(), query,\n delegate (SearchGoogleAdsStreamResponse resp)\n {\n Console.WriteLine($\"Found {resp.Results.Count} recommendations.\");\n foreach (GoogleAdsRow googleAdsRow in resp.Results)\n {\n Recommendation recommendation = googleAdsRow.Recommendation;\n Console.WriteLine(\"Keyword recommendation \" +\n $\"{recommendation.ResourceName} was found for campaign \" +\n $\"{recommendation.Campaign}.\");\n\n if (recommendation.KeywordRecommendation != null)\n {\n KeywordInfo keyword =\n recommendation.KeywordRecommendation.Keyword;\n Console.WriteLine($\"Keyword = {keyword.Text}, type = \" +\n \"{keyword.MatchType}\");\n }\n\n operations.Add(\n BuildApplyRecommendationOperation(recommendation.ResourceName)\n );\n }\n }\n );\n}\ncatch (GoogleAdsException e)\n{\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n}DetectAndApplyRecommendations.cs\n```\n\nExample:\n```text\n$googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n// Creates a query that retrieves keyword recommendations.\n$query = 'SELECT recommendation.resource_name, recommendation.campaign, '\n . 'recommendation.keyword_recommendation '\n . 'FROM recommendation '\n . 'WHERE recommendation.type = KEYWORD ';\n// Issues a search request to detect keyword recommendations that exist for the\n// customer account.\n$response =\n $googleAdsServiceClient->search(SearchGoogleAdsRequest::build($customerId, $query));\n\n$operations = [];\n// Iterates over all rows in all pages and prints the requested field values for\n// the recommendation in each row.\nforeach ($response->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $recommendation = $googleAdsRow->getRecommendation();\n printf(\n \"Keyword recommendation with resource name '%s' was found for campaign \"\n . \"with resource name '%s':%s\",\n $recommendation->getResourceName(),\n $recommendation->getCampaign(),\n PHP_EOL\n );\n if (!is_null($recommendation->getKeywordRecommendation())) {\n $keyword = $recommendation->getKeywordRecommendation()->getKeyword();\n printf(\n \"\\tKeyword = '%s'%s\\ttype = '%s'%s\",\n $keyword->getText(),\n PHP_EOL,\n KeywordMatchType::name($keyword->getMatchType()),\n PHP_EOL\n );\n }\n // Creates an ApplyRecommendationOperation that will be used to apply this\n // recommendation, and adds it to the list of operations.\n $operations[] = self::buildRecommendationOperation($recommendation->getResourceName());\n}DetectAndApplyRecommendations.php\n```\n\nExample:\n```text\ngoogleads_service = client.get_service(\"GoogleAdsService\")\nquery: str = \"\"\"\n SELECT\n recommendation.campaign,\n recommendation.keyword_recommendation\n FROM recommendation\n WHERE\n recommendation.type = KEYWORD\"\"\"\n\n# Detects keyword recommendations that exist for the customer account.\nresponse: Iterable[GoogleAdsRow] = googleads_service.search(\n customer_id=customer_id, query=query\n)\n\noperations: List[ApplyRecommendationOperation] = []\nfor row in response:\n recommendation = row.recommendation\n print(\n f\"Keyword recommendation ('{recommendation.resource_name}') \"\n f\"was found for campaign '{recommendation.campaign}.\"\n )\n\n keyword = recommendation.keyword_recommendation.keyword\n print(\n f\"\\tKeyword = '{keyword.text}'\\n\" f\"\\tType = '{keyword.match_type}'\"\n )\n\n # Create an ApplyRecommendationOperation that will be used to apply\n # this recommendation, and add it to the list of operations.\n operations.append(\n build_recommendation_operation(client, recommendation.resource_name)\n )detect_and_apply_recommendations.py\n```\n\nExample:\n```text\nquery = <<~QUERY\n SELECT recommendation.resource_name, recommendation.campaign,\n recommendation.keyword_recommendation\n FROM recommendation\n WHERE recommendation.type = KEYWORD\nQUERY\n\ngoogle_ads_service = client.service.google_ads\n\nresponse = google_ads_service.search(\n customer_id: customer_id,\n query: query,\n)\n\noperations = response.each do |row|\n recommendation = row.recommendation\n\n puts \"Keyword recommendation ('#{recommendation.resource_name}') was found for \"\\\n \"campaign '#{recommendation.campaign}'.\"\n\n if recommendation.keyword_recommendation\n keyword = recommendation.keyword_recommendation.keyword\n puts \"\\tKeyword = '#{keyword.text}'\"\n puts \"\\ttype = '#{keyword.match_type}'\"\n end\n\n build_recommendation_operation(client, recommendation.resource_name)\nenddetect_and_apply_recommendations.rb\n```\n\nExample:\n```text\n# Create the search query.\nmy $search_query =\n \"SELECT recommendation.resource_name, \" .\n \"recommendation.campaign, recommendation.keyword_recommendation \" .\n \"FROM recommendation \" .\n \"WHERE recommendation.type = KEYWORD\";\n\n# Get the GoogleAdsService.\nmy $google_ads_service = $api_client->GoogleAdsService();\n\nmy $search_stream_handler =\n Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({\n service => $google_ads_service,\n request => {\n customerId => $customer_id,\n query => $search_query\n }});\n\n# Create apply operations for all the recommendations found.\nmy $apply_recommendation_operations = ();\n$search_stream_handler->process_contents(\n sub {\n my $google_ads_row = shift;\n my $recommendation = $google_ads_row->{recommendation};\n printf \"Keyword recommendation '%s' was found for campaign '%s'.\\n\",\n $recommendation->{resourceName}, $recommendation->{campaign};\n my $keyword = $recommendation->{keywordRecommendation}{keyword};\n printf \"\\tKeyword = '%s'\\n\", $keyword->{text};\n printf \"\\tMatch type = '%s'\\n\", $keyword->{matchType};\n # Creates an ApplyRecommendationOperation that will apply this recommendation, and adds\n # it to the list of operations.\n push @$apply_recommendation_operations,\n build_recommendation_operation($recommendation);\n });detect_and_apply_recommendations.pl\n```\n\nExample:\n```text\n# Gets keyword recommendations.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\ncurl -f --request POST \\\n\"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:search\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data @- <<EOF\n{\n\"query\": \"\n SELECT\n recommendation.campaign,\n recommendation.keyword_recommendation\n FROM recommendation\n WHERE\n recommendation.type = KEYWORD\n\"\n}\nEOFdetect_and_apply_recommendations.sh\n```\n\nExample:\n```text\n/** Creates and returns an ApplyRecommendationOperation to apply the given recommendation. */\nprivate ApplyRecommendationOperation buildRecommendationOperation(Recommendation recommendation) {\n // If you have a recommendation ID instead of a resource name, you can create a resource name\n // like this:\n // String resourceName = ResourceNames.recommendation(customerId, recommendationId);\n\n // Creates a builder to construct the operation.\n Builder operationBuilder = ApplyRecommendationOperation.newBuilder();\n\n // Each recommendation type has optional parameters to override the recommended values. Below is\n // an example showing how to override a recommended ad when a TextAdRecommendation is applied.\n // operationBuilder.getTextAdBuilder().getAdBuilder().setResourceName(\"INSERT_AD_RESOURCE_NAME\");\n\n // Sets the operation's resource name to the resource name of the recommendation to apply.\n operationBuilder.setResourceName(recommendation.getResourceName());\n return operationBuilder.build();\n}DetectAndApplyRecommendations.java\n```\n\nExample:\n```text\nprivate ApplyRecommendationOperation BuildApplyRecommendationOperation(\n string recommendationResourceName\n)\n{\n // If you have a recommendation_id instead of the resource_name you can create a\n // resource name from it like this:\n // string recommendationResourceName =\n // ResourceNames.Recommendation(customerId, recommendationId)\n\n // Each recommendation type has optional parameters to override the recommended values.\n // This is an example to override a recommended ad when a TextAdRecommendation is\n // applied.\n // For details, please read\n // https://developers.google.com/google-ads/api/reference/rpc/latest/ApplyRecommendationOperation.\n /*\n Ad overridingAd = new Ad()\n {\n Id = \"INSERT_AD_ID_AS_LONG_HERE\"\n };\n applyRecommendationOperation.TextAd = new TextAdParameters()\n {\n Ad = overridingAd\n };\n */\n\n ApplyRecommendationOperation applyRecommendationOperation =\n new ApplyRecommendationOperation()\n {\n ResourceName = recommendationResourceName\n };\n\n return applyRecommendationOperation;\n}DetectAndApplyRecommendations.cs\n```\n\nExample:\n```text\nprivate static function buildRecommendationOperation(\n string $recommendationResourceName\n): ApplyRecommendationOperation {\n // If you have a recommendation_id instead of the resource name, you can create a resource\n // name from it like this:\n /*\n $recommendationResourceName =\n ResourceNames::forRecommendation($customerId, $recommendationId);\n */\n\n // Each recommendation type has optional parameters to override the recommended values.\n // This is an example to override a recommended ad when a TextAdRecommendation is applied.\n // For details, please read\n // https://developers.google.com/google-ads/api/reference/rpc/latest/ApplyRecommendationOperation.\n /*\n $overridingAd = new Ad([\n 'id' => 'INSERT_AD_ID_AS_INTEGER_HERE'\n ]);\n $applyRecommendationOperation->setTextAd(new TextAdParameters(['ad' => $overridingAd]));\n */\n\n // Issues a mutate request to apply the recommendation.\n $applyRecommendationOperation = new ApplyRecommendationOperation();\n $applyRecommendationOperation->setResourceName($recommendationResourceName);\n return $applyRecommendationOperation;\n}DetectAndApplyRecommendations.php\n```\n\nExample:\n```text\ndef build_recommendation_operation(\n client: GoogleAdsClient, recommendation: str\n) -> ApplyRecommendationOperation:\n \"\"\"Creates a ApplyRecommendationOperation to apply the given recommendation.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n recommendation: a resource name for the recommendation to be applied.\n \"\"\"\n # If you have a recommendation ID instead of a resource name, you can create\n # a resource name like this:\n #\n # googleads_service = client.get_service(\"GoogleAdsService\")\n # resource_name = googleads_service.recommendation_path(\n # customer_id, recommendation.id\n # )\n\n operation: ApplyRecommendationOperation = client.get_type(\n \"ApplyRecommendationOperation\"\n )\n\n # Each recommendation type has optional parameters to override the\n # recommended values. Below is an example showing how to override a\n # recommended ad when a TextAdRecommendation is applied.\n #\n # operation.text_ad.ad.resource_name = \"INSERT_AD_RESOURCE_NAME\"\n #\n # For more details, see:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/ApplyRecommendationOperation#apply_parameters\n\n operation.resource_name = recommendation\n return operationdetect_and_apply_recommendations.py\n```\n\nExample:\n```text\ndef build_recommendation_operation(client, recommendation)\n # If you have a recommendation_id instead of the resource_name\n # you can create a resource name from it like this:\n # recommendation_resource =\n # client.path.recommendation(customer_id, recommendation_id)\n\n operations = client.operation.apply_recommendation\n operations.resource_name = recommendation_resource\n\n # Each recommendation type has optional parameters to override the recommended\n # values. This is an example to override a recommended ad when a\n # TextAdRecommendation is applied.\n #\n # text_ad_parameters = client.resource.text_ad_parameters do |tap|\n # tap.ad = client.resource.ad do |ad|\n # ad.id = \"INSERT_AD_ID_AS_INTEGER_HERE\"\n # end\n # end\n # operation.text_ad = text_ad_parameters\n #\n # For more details, see:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/ApplyRecommendationOperation#apply_parameters\n\n return operation\nenddetect_and_apply_recommendations.rb\n```\n\nExample:\n```text\nsub build_recommendation_operation {\n my ($recommendation) = @_;\n\n # If you have a recommendation ID instead of a resource name, you can create a resource\n # name like this:\n # my $recommendation_resource_name =\n # Google::Ads::GoogleAds::V25::Utils::ResourceNames::recommendation(\n # $customer_id, $recommendation_id);\n\n # Each recommendation type has optional parameters to override the recommended values.\n # Below is an example showing how to override a recommended ad when a TextAdRecommendation\n # is applied.\n # my $overriding_ad = Google::Ads::GoogleAds::V25::Resources::Ad->new({\n # id => \"INSERT_AD_ID_AS_INTEGER_HERE\"\n # });\n # my $text_ad_parameters =\n # Google::Ads::GoogleAds::V25::Services::RecommendationService::TextAdParameters\n # ->new({ad => $overriding_ad});\n # $apply_recommendation_operation->{textAd} = $text_ad_parameters;\n\n # Create an apply recommendation operation.\n my $apply_recommendation_operation =\n Google::Ads::GoogleAds::V25::Services::RecommendationService::ApplyRecommendationOperation\n ->new({\n resourceName => $recommendation->{resourceName}});\n\n return $apply_recommendation_operation;\n}detect_and_apply_recommendations.pl\n```\n\nExample:\n```text\n// Issues a mutate request to apply the recommendations.\nApplyRecommendationResponse applyRecommendationsResponse =\n recommendationServiceClient.applyRecommendation(\n Long.toString(customerId), applyRecommendationOperations);\nfor (ApplyRecommendationResult applyRecommendationResult :\n applyRecommendationsResponse.getResultsList()) {\n System.out.printf(\n \"Applied recommendation with resource name: '%s'.%n\",\n applyRecommendationResult.getResourceName());\n}DetectAndApplyRecommendations.java\n```\n\nExample:\n```text\nprivate void ApplyRecommendation(GoogleAdsClient client, long customerId,\n List<ApplyRecommendationOperation> operations)\n{\n // Get the RecommendationServiceClient.\n RecommendationServiceClient recommendationService = client.GetService(\n Services.V25.RecommendationService);\n\n ApplyRecommendationRequest applyRecommendationRequest = new ApplyRecommendationRequest()\n {\n CustomerId = customerId.ToString(),\n };\n\n applyRecommendationRequest.Operations.AddRange(operations);\n\n ApplyRecommendationResponse response =\n recommendationService.ApplyRecommendation(applyRecommendationRequest);\n foreach (ApplyRecommendationResult result in response.Results)\n {\n Console.WriteLine(\"Applied a recommendation with resource name: \" +\n result.ResourceName);\n }\n}DetectAndApplyRecommendations.cs\n```\n\nExample:\n```text\nprivate static function applyRecommendations(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n array $operations\n): void {\n // Issues a mutate request to apply the recommendations.\n $recommendationServiceClient = $googleAdsClient->getRecommendationServiceClient();\n $response = $recommendationServiceClient->applyRecommendation(\n ApplyRecommendationRequest::build($customerId, $operations)\n );\n foreach ($response->getResults() as $appliedRecommendation) {\n /** @var Recommendation $appliedRecommendation */\n printf(\n \"Applied a recommendation with resource name: '%s'.%s\",\n $appliedRecommendation->getResourceName(),\n PHP_EOL\n );\n }\n}DetectAndApplyRecommendations.php\n```\n\nExample:\n```text\ndef apply_recommendations(\n client: GoogleAdsClient,\n customer_id: str,\n operations: List[ApplyRecommendationOperation],\n) -> None:\n \"\"\"Applies a batch of recommendations.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n operations: a list of ApplyRecommendationOperation messages.\n \"\"\"\n # Issues a mutate request to apply the recommendations.\n recommendation_service = client.get_service(\"RecommendationService\")\n response: ApplyRecommendationResult = (\n recommendation_service.apply_recommendation(\n customer_id=customer_id, operations=operations\n )\n )\n\n for result in response.results:\n print(\n \"Applied a recommendation with resource name: \"\n f\"'{result.resource_name}'.\"\n )detect_and_apply_recommendations.py\n```\n\nExample:\n```text\ndef apply_recommendations(client, customer_id, operations)\n # Issues a mutate request to apply the recommendation.\n recommendation_service = client.service.recommendation\n\n response = recommendation_service.apply_recommendation(\n customer_id: customer_id,\n operations: [operations],\n )\n\n response.results.each do |applied_recommendation|\n puts \"Applied recommendation with resource name: '#{applied_recommendation.resource_name}'.\"\n end\nenddetect_and_apply_recommendations.rb\n```\n\nExample:\n```text\n# Issue a mutate request to apply the recommendations.\nmy $apply_recommendation_response =\n $api_client->RecommendationService()->apply({\n customerId => $customer_id,\n operations => $apply_recommendation_operations\n });\n\nforeach my $result (@{$apply_recommendation_response->{results}}) {\n printf \"Applied recommendation with resource name: '%s'.\\n\",\n $result->{resourceName};\n}detect_and_apply_recommendations.pl\n```\n\nExample:\n```text\n# Applies a recommendation.\n#\n# Variables:\n# API_VERSION,\n# CUSTOMER_ID,\n# DEVELOPER_TOKEN,\n# MANAGER_CUSTOMER_ID,\n# OAUTH2_ACCESS_TOKEN:\n# See https://developers.google.com/google-ads/api/rest/auth#request_headers\n# for details.\n#\n# RECOMMENDATION_RESOURCE_NAME: The resource name of the recommendation to\n# apply, from the previous request.\ncurl -f --request POST \\\n\"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/recommendations:apply\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data @- <<EOF\n{\n \"operations\": [\n {\n \"resourceName\": \"${RECOMMENDATION_RESOURCE_NAME}\"\n }\n ]\n}\nEOFdetect_and_apply_recommendations.sh\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.569Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":20,"totalLines":620,"estimatedTokens":5448}}224{"id":"doc-report_on_experiments_google_ads_api_google_for_-1562f09c","source":"documentation","title":"Report on experiments | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/experiments/reporting","text":"Example:\n```text\nSELECT\n experiment.experiment_id,\n experiment.name,\n experiment.type,\n metrics.clicks,\n metrics.control_clicks,\n metrics.clicks_point_estimate,\n metrics.clicks_margin_of_error,\n metrics.clicks_p_value,\n metrics.conversions,\n metrics.control_conversions,\n metrics.conversions_absolute_change_point_estimate,\n metrics.conversions_absolute_change_margin_of_error,\n metrics.conversions_absolute_change_p_value\nFROM experiment\nWHERE experiment.experiment_id = EXPERIMENT_ID\n```\n\nExample:\n```text\nprivate void evaluateExperiment(\n GoogleAdsClient googleAdsClient, long customerId, GoogleAdsRow row) {\n Metrics metrics = row.getMetrics();\n String experimentResourceName = row.getExperiment().getResourceName();\n\n // 1. Evaluate conversion success as a primary success signal if available.\n // - Point Estimate: Represents the estimated average lift or difference in conversions.\n // - Margin of Error: Outlines the confidence interval bounds. Note that the margin_of_error\n // provided by the API is calculated for a preset confidence level which is set based on the\n // experiment type.\n // - Lower Bound: (Point Estimate - Margin of Error). If this value is above 0,\n // we have statistical significance that performance has improved.\n double convPValue = metrics.getConversionsAbsoluteChangePValue();\n double convLift = metrics.getConversionsAbsoluteChangePointEstimate();\n double convError = metrics.getConversionsAbsoluteChangeMarginOfError();\n double convLowerBound = convLift - convError;\n\n if (convPValue <= P_VALUE_THRESHOLD) {\n if (convLowerBound > 0) {\n System.out.printf(\n \"Significant Success: Conversions increased. Even at the lower bound, the lift is %.2f.\"\n + \" Promoting changes.%n\",\n convLowerBound);\n promoteExperiment(googleAdsClient, customerId, experimentResourceName);\n return;\n } else if ((convLift + convError) < 0) {\n System.out.printf(\n \"Significant Decline: Even the upper bound (%.2f) is below zero. Ending experiment.%n\",\n convLift + convError);\n endExperiment(googleAdsClient, customerId, experimentResourceName);\n return;\n }\n }\n\n // 2. Fall back to evaluating click metrics if conversions are inconclusive.\n double clickPValue = metrics.getClicksPValue();\n double clickLift = metrics.getClicksPointEstimate();\n double clickError = metrics.getClicksMarginOfError();\n double clickLowerBound = clickLift - clickError;\n\n if (clickPValue <= P_VALUE_THRESHOLD && clickLowerBound > 0) {\n System.out.printf(\"Click volume is significantly up (+%.1f%%).%n\", clickLift * 100);\n\n // Graduation is only supported for separate campaign experiments, not\n // intra-campaign experiments where there is no separate treatment campaign.\n ExperimentType experimentType = row.getExperiment().getType();\n if (experimentType != ExperimentType.ADOPT_BROAD_MATCH_KEYWORDS\n && experimentType != ExperimentType.ADOPT_AI_MAX) {\n System.out.println(\"Graduating treatment campaign for further manual analysis.\");\n graduateExperiment(googleAdsClient, customerId, experimentResourceName);\n } else {\n System.out.println(\n \"Intra-campaign trial detected: graduation is not supported. Continuing to run the\"\n + \" experiment to gather more conversion data.\");\n }\n } else {\n // 3. Print status if no action was taken.\n System.out.printf(\n \"Inconclusive: No significant lift in Conversions (p=%.2f) or Clicks (p=%.2f). Current\"\n + \" estimated lift: %.2f +/- %.2f. Allowing the experiment to continue running.%n\",\n convPValue, clickPValue, convLift, convError);\n }\n}\nEvaluateAndUpdateExperiment.java\n```\n\nExample:\n```text\nprivate static void EvaluateExperiment(GoogleAdsClient client, long customerId, GoogleAdsRow row)\n{\n // This function evaluates performance metrics and immediately takes action\n // to update the experiment's status (promote, end, or graduate) if\n // statistical significance thresholds are met.\n var metrics = row.Metrics;\n string experimentResourceName = row.Experiment.ResourceName;\n\n bool hasConvMetrics = metrics.HasConversionsAbsoluteChangePValue\n && metrics.HasConversionsAbsoluteChangePointEstimate\n && metrics.HasConversionsAbsoluteChangeMarginOfError;\n\n bool hasClickMetrics = metrics.HasClicksPValue\n && metrics.HasClicksPointEstimate\n && metrics.HasClicksMarginOfError;\n\n // 1. Evaluate conversion success as a primary success signal if available.\n // - Point Estimate: Represents the estimated average lift or difference in conversions.\n // - Margin of Error: Outlines the confidence interval bounds. Note that the margin_of_error\n // provided by the API is calculated for a preset confidence level which is set based on\n // the experiment type.\n // - Lower Bound: (Point Estimate - Margin of Error). If this value is above 0,\n // we have statistical significance that performance has improved.\n if (hasConvMetrics)\n {\n double convPValue = metrics.ConversionsAbsoluteChangePValue;\n double convLift = metrics.ConversionsAbsoluteChangePointEstimate;\n double convError = metrics.ConversionsAbsoluteChangeMarginOfError;\n double convLowerBound = convLift - convError;\n\n if (convPValue <= P_VALUE_THRESHOLD)\n {\n if (convLowerBound > 0)\n {\n Console.WriteLine(\n $\"Significant Success: Conversions increased. Even at the lower\" +\n $\" bound, the lift is {convLowerBound:F2}. Promoting changes.\");\n PromoteExperiment(client, customerId, experimentResourceName);\n return;\n }\n else if ((convLift + convError) < 0)\n {\n Console.WriteLine(\n $\"Significant Decline: Even the upper bound ({convLift + convError:F2}) \" +\n $\"is below zero. Ending experiment.\");\n EndExperiment(client, customerId, experimentResourceName);\n return;\n }\n }\n }\n\n // 2. Evaluate click volume as a secondary signal.\n // This is helpful as an early indicator or for lower-volume accounts.\n if (hasClickMetrics)\n {\n double clickPValue = metrics.ClicksPValue;\n double clickLift = metrics.ClicksPointEstimate;\n double clickError = metrics.ClicksMarginOfError;\n double clickLowerBound = clickLift - clickError;\n\n if (clickPValue <= P_VALUE_THRESHOLD && clickLowerBound > 0)\n {\n // We have a directional winner: high confidence in more traffic,\n // but not enough data to confirm conversion impact yet.\n Console.WriteLine(\n $\"Click volume is significantly up (+{clickLift * 100:F1}%).\");\n\n // Graduation is only supported for separate campaign experiments, not\n // intra-campaign experiments where there is no separate treatment campaign.\n if (row.Experiment.Type != ExperimentType.AdoptBroadMatchKeywords\n && row.Experiment.Type != ExperimentType.AdoptAiMax)\n {\n Console.WriteLine(\"Graduating treatment campaign for further manual analysis.\");\n GraduateExperiment(client, customerId, experimentResourceName);\n }\n else\n {\n Console.WriteLine(\n \"Intra-campaign trial detected: graduation is not supported. \" +\n \"Continuing to run the experiment to gather more conversion data.\");\n }\n return;\n }\n }\n\n // 3. Print status if no action was taken.\n if (hasConvMetrics || hasClickMetrics)\n {\n string convStatus = hasConvMetrics\n ? $\"Conversions (p={metrics.ConversionsAbsoluteChangePValue:F2}, \" +\n $\"lift={metrics.ConversionsAbsoluteChangePointEstimate:F2} +/- \" +\n $\"{metrics.ConversionsAbsoluteChangeMarginOfError:F2})\"\n : \"Conversions (not populated)\";\n\n string clickStatus = hasClickMetrics\n ? $\"Clicks (p={metrics.ClicksPValue:F2}, \" +\n $\"lift={metrics.ClicksPointEstimate:F2} +/- \" +\n $\"{metrics.ClicksMarginOfError:F2})\"\n : \"Clicks (not populated)\";\n\n Console.WriteLine(\n $\"Inconclusive: No significant action taken. {convStatus}, {clickStatus}. \" +\n \"Allowing the experiment to continue running.\");\n }\n else\n {\n Console.WriteLine(\n \"Conversion and click performance metrics are not yet populated. \" +\n \"Allowing the experiment to continue running.\");\n }\n}EvaluateAndUpdateExperiment.cs\n```\n\nExample:\n```text\nThis example is not yet available in PHP; you can take a look at the other languages.\n```\n\nExample:\n```text\ndef evaluate_experiment(\n client: GoogleAdsClient, customer_id: str, row: GoogleAdsRow\n) -> None:\n \"\"\"Evaluates the performance of the experiment and updates it accordingly\n (for example, promotes, ends, or graduates).\n\n Checks conversion and click metrics against statistical significance thresholds\n to determine the appropriate action to take on the experiment.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: a client customer ID.\n row: a GoogleAdsRow containing the experiment and metrics.\n \"\"\"\n # This function evaluates performance metrics and immediately takes action\n # to update the experiment's status (promote, end, or graduate) if\n # statistical significance thresholds are met.\n metrics = row.metrics\n experiment_resource_name = row.experiment.resource_name\n\n has_conv_metrics = (\n \"conversions_absolute_change_p_value\" in metrics\n and \"conversions_absolute_change_point_estimate\" in metrics\n and \"conversions_absolute_change_margin_of_error\" in metrics\n )\n has_click_metrics = (\n \"clicks_p_value\" in metrics\n and \"clicks_point_estimate\" in metrics\n and \"clicks_margin_of_error\" in metrics\n )\n\n # 1. Evaluate conversion success as a primary success signal if available.\n # - Point Estimate: Represents the estimated average lift or difference in conversions.\n # - Margin of Error: Outlines the confidence interval bounds. Note that the margin_of_error provided by the API is calculated for a preset confidence level which is set based on the experiment type.\n # - Lower Bound: (Point Estimate - Margin of Error). If this value is above 0,\n # we have statistical significance that performance has improved.\n if has_conv_metrics:\n conv_p_value = metrics.conversions_absolute_change_p_value\n conv_lift = metrics.conversions_absolute_change_point_estimate\n conv_error = metrics.conversions_absolute_change_margin_of_error\n conv_lower_bound = conv_lift - conv_error\n\n if conv_p_value <= P_VALUE_THRESHOLD:\n if conv_lower_bound > 0:\n print(\n \"Significant Success: Conversions increased. Even at the lower\"\n f\" bound, the lift is {conv_lower_bound:.2f}. Promoting\"\n \" changes.\"\n )\n promote_experiment(\n client, customer_id, experiment_resource_name\n )\n return\n elif (conv_lift + conv_error) < 0:\n print(\n \"Significant Decline: Even the upper bound\"\n f\" ({conv_lift + conv_error:.2f}) is below zero. Ending\"\n \" experiment.\"\n )\n end_experiment(client, customer_id, experiment_resource_name)\n return\n\n # 2. Evaluate click volume as a secondary signal.\n # This is helpful as an early indicator or for lower-volume accounts.\n click_p_value = metrics.clicks_p_value\n click_lift = metrics.clicks_point_estimate\n click_error = metrics.clicks_margin_of_error\n click_lower_bound = click_lift - click_error\n\n if click_p_value <= P_VALUE_THRESHOLD and click_lower_bound > 0:\n # We have a directional winner: high confidence in more traffic,\n # but not enough data to confirm conversion impact yet.\n print(f\"Click volume is significantly up (+{click_lift*100:.1f}%).\")\n\n # Graduation is only supported for separate campaign experiments, not\n # intra-campaign experiments where there is no separate treatment campaign.\n experiment_type_name = row.experiment.type_.name\n if (\n experiment_type_name != \"ADOPT_BROAD_MATCH_KEYWORDS\"\n and experiment_type_name != \"ADOPT_AI_MAX\"\n ):\n print(\n \"Graduating treatment campaign for further manual analysis.\"\n )\n graduate_experiment(\n client, customer_id, experiment_resource_name\n )\n else:\n print(\n \"Intra-campaign trial detected: graduation is not supported. \"\n \"Continuing to run the experiment to gather more conversion data.\"\n )\n return\n\n # 3. Print status if no action was taken.\n if has_conv_metrics or has_click_metrics:\n conv_status = (\n f\"Conversions (p={metrics.conversions_absolute_change_p_value:.2f}, \"\n f\"lift={metrics.conversions_absolute_change_point_estimate:.2f} +/- \"\n f\"{metrics.conversions_absolute_change_margin_of_error:.2f})\"\n if has_conv_metrics\n else \"Conversions (not populated)\"\n )\n click_status = (\n f\"Clicks (p={metrics.clicks_p_value:.2f}, \"\n f\"lift={metrics.clicks_point_estimate:.2f} +/- \"\n f\"{metrics.clicks_margin_of_error:.2f})\"\n if has_click_metrics\n else \"Clicks (not populated)\"\n )\n print(\n f\"Inconclusive: No significant action taken. {conv_status}, {click_status}.\"\n \" Allowing the experiment to continue running.\"\n )\n else:\n print(\n \"Conversion and click performance metrics are not yet populated. \"\n \"Allowing the experiment to continue running.\"\n )evaluate_and_update_experiment.py\n```\n\nExample:\n```text\nThis example is not yet available in Ruby; you can take a look at the other languages.\n```\n\nExample:\n```text\nThis example is not yet available in Perl; you can take a look at the other languages.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.572Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":342,"estimatedTokens":3661}}225{"id":"doc-criteria_metrics_google_ads_api_google_for_devel-6a00a5d3","source":"documentation","title":"Criteria Metrics | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/reporting/criteria-metrics","text":"Example:\n```text\nSELECT\n ad_group_criterion.keyword.text,\n ad_group.name,\n campaign.name,\n metrics.impressions,\n metrics.clicks,\n metrics.ctr,\n metrics.average_cpc\nFROM keyword_view\nWHERE segments.date DURING LAST_30_DAYS\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.572Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":15,"estimatedTokens":62}}226{"id":"doc-zero_metrics_google_ads_api_google_for_developer-1659f2ff","source":"documentation","title":"Zero Metrics | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/reporting/zero-metrics","text":"Example:\n```text\nSELECT\n campaign.id,\n metrics.impressions\nFROM campaign\nWHERE metrics.impressions > 0\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n metrics.impressions,\n segments.date\nFROM campaign\nWHERE segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n ad_group_criterion.criterion_id,\n metrics.impressions,\n metrics.clicks,\n metrics.conversions,\n segments.date\nFROM keyword_view\nWHERE segments.date BETWEEN <date1> AND <date2>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.574Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":32,"estimatedTokens":118}}227{"id":"doc-common_use_case_example_google_ads_api_google_fo-ea13b4cf","source":"documentation","title":"Common Use Case Example | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/reporting/example","text":"Example:\n```text\nSELECT\n campaign.name,\n campaign.status,\n segments.device,\n metrics.impressions,\n metrics.clicks,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros\nFROM campaign\nWHERE segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nhttps://googleads.googleapis.com/v25/customers/{customer_id}/googleAds:searchStream\n```\n\nExample:\n```text\nPOST /v25/customers/{customer_id}/googleAds:searchStream HTTP/1.1\nHost: googleads.googleapis.com\nUser-Agent: curl\nContent-Type: application/json\nAccept: application/json\nAuthorization: Bearer [Enter OAuth 2.0 access token here]\ndeveloper-token: [Enter developerToken here]\n\nParameters:\n{\n \"query\" : \"SELECT campaign.name, campaign.status, segments.device,\n metrics.impressions, metrics.clicks, metrics.ctr,\n metrics.average_cpc, metrics.cost_micros\n FROM campaign\n WHERE segments.date DURING LAST_30_DAYS\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.576Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":41,"estimatedTokens":237}}228{"id":"doc-paging_through_results_google_ads_api_google_for-2721923e","source":"documentation","title":"Paging through results | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/reporting/paging","text":"Example:\n```text\nSELECT\n ad_group.id,\n ad_group_criterion.type,\n ad_group_criterion.criterion_id,\n ad_group_criterion.keyword.text,\n ad_group_criterion.keyword.match_type\nFROM ad_group_criterion\nWHERE ad_group_criterion.type = KEYWORD\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.578Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":65}}229{"id":"doc-generate_a_reach_curve_google_ads_api_google_for-2196763c","source":"documentation","title":"Generate a reach curve | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/reach-forecasting/generate-curve","text":"Example:\n```text\nprivate void getReachCurve(\n ReachPlanServiceClient reachPlanServiceClient, GenerateReachForecastRequest request) {\n GenerateReachForecastResponse response = reachPlanServiceClient.generateReachForecast(request);\n System.out.println(\"Reach curve output:\");\n System.out.println(\n \"Currency, Cost Micros, On-Target Reach, On-Target Imprs, Total Reach, Total Imprs,\"\n + \" Products\");\n for (ReachForecast point : response.getReachCurve().getReachForecastsList()) {\n System.out.printf(\n \"%s, \\\"\",\n Joiner.on(\", \")\n .join(\n request.getCurrencyCode(),\n String.valueOf(point.getCostMicros()),\n String.valueOf(point.getForecast().getOnTargetReach()),\n String.valueOf(point.getForecast().getOnTargetImpressions()),\n String.valueOf(point.getForecast().getTotalReach()),\n String.valueOf(point.getForecast().getTotalImpressions())));\n for (PlannedProductReachForecast product : point.getPlannedProductReachForecastsList()) {\n System.out.printf(\"[Product: %s, \", product.getPlannableProductCode());\n System.out.printf(\"Budget Micros: %s]\", product.getCostMicros());\n }\n System.out.printf(\"\\\"%n\");\n }\n}ForecastReach.java\n```\n\nExample:\n```text\npublic void GetReachCurve(ReachPlanServiceClient reachPlanService,\n GenerateReachForecastRequest request)\n{\n GenerateReachForecastResponse response = reachPlanService.GenerateReachForecast(\n request);\n Console.WriteLine(\"Reach curve output:\");\n Console.WriteLine(\n \"Currency, Cost Micros, On-Target Reach, On-Target Impressions, Total Reach,\" +\n \" Total Impressions, Products\");\n foreach (ReachForecast point in response.ReachCurve.ReachForecasts)\n {\n Console.Write($\"{request.CurrencyCode}, \");\n Console.Write($\"{point.CostMicros}, \");\n Console.Write($\"{point.Forecast.OnTargetReach}, \");\n Console.Write($\"{point.Forecast.OnTargetImpressions}, \");\n Console.Write($\"{point.Forecast.TotalReach}, \");\n Console.Write($\"{point.Forecast.TotalImpressions}, \");\n Console.Write(\"\\\"[\");\n foreach (PlannedProductReachForecast productReachForecast in\n point.PlannedProductReachForecasts)\n {\n Console.Write($\"(Product: {productReachForecast.PlannableProductCode}, \");\n Console.Write($\"Budget Micros: {productReachForecast.CostMicros}), \");\n }\n\n Console.WriteLine(\"]\\\"\");\n }\n}ForecastReach.cs\n```\n\nExample:\n```text\nprivate static function getReachCurve(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n array $productMix,\n string $locationId,\n string $currencyCode\n) {\n // Valid durations are between 1 and 90 days.\n $duration = new CampaignDuration(['duration_in_days' => 28]);\n $targeting = new Targeting([\n 'plannable_location_id' => $locationId,\n 'age_range' => ReachPlanAgeRange::AGE_RANGE_18_65_UP,\n 'genders' => [\n new GenderInfo(['type' => GenderType::FEMALE]),\n new GenderInfo(['type' => GenderType::MALE])\n ],\n 'devices' => [\n new DeviceInfo(['type' => Device::DESKTOP]),\n new DeviceInfo(['type' => Device::MOBILE]),\n new DeviceInfo(['type' => Device::TABLET])\n ]\n ]);\n\n // See the docs for defaults and valid ranges:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/GenerateReachForecastRequest\n $response = $googleAdsClient->getReachPlanServiceClient()->generateReachForecast(\n GenerateReachForecastRequest::build($customerId, $duration, $productMix)\n ->setCurrencyCode($currencyCode)\n ->setTargeting($targeting)\n );\n\n printf(\n \"Reach curve output:%sCurrency, Cost Micros, On-Target Reach, On-Target Imprs,\" .\n \" Total Reach, Total Imprs, Products%s\",\n PHP_EOL,\n PHP_EOL\n );\n foreach ($response->getReachCurve()->getReachForecasts() as $point) {\n $products = '';\n /** @var ReachForecast $point */\n foreach ($point->getPlannedProductReachForecasts() as $plannedProductReachForecast) {\n /** @var PlannedProductReachForecast $plannedProductReachForecast */\n $products .= sprintf(\n '(Product: %s, Budget Micros: %s)',\n $plannedProductReachForecast->getPlannableProductCode(),\n $plannedProductReachForecast->getCostMicros()\n );\n }\n printf(\n \"%s, %d, %d, %d, %d, %d, %s%s\",\n $currencyCode,\n $point->getCostMicros(),\n $point->getForecast()->getOnTargetReach(),\n $point->getForecast()->getOnTargetImpressions(),\n $point->getForecast()->getTotalReach(),\n $point->getForecast()->getTotalImpressions(),\n $products,\n PHP_EOL\n );\n }\n}ForecastReach.php\n```\n\nExample:\n```text\ndef request_reach_curve(\n client: GoogleAdsClient,\n customer_id: str,\n product_mix: list[PlannedProduct],\n location_id: str,\n currency_code: str,\n):\n \"\"\"Creates a sample request for a given product mix.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: The customer ID for the reach forecast.\n product_mix: The product mix for the reach forecast.\n location_id: The location ID to plan for.\n currency_code: Three-character ISO 4217 currency code.\n \"\"\"\n # See the docs for defaults and valid ranges:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/GenerateReachForecastRequest\n request: GenerateReachForecastRequest = client.get_type(\n \"GenerateReachForecastRequest\"\n )\n request.customer_id = customer_id\n # Valid durations are between 1 and 90 days.\n request.campaign_duration.duration_in_days = 28\n request.currency_code = currency_code\n request.cookie_frequency_cap = 0\n request.min_effective_frequency = 1\n request.planned_products = product_mix\n\n request.targeting.plannable_location_id = location_id\n request.targeting.age_range = (\n client.enums.ReachPlanAgeRangeEnum.AGE_RANGE_18_65_UP\n )\n\n # Add gender targeting to the request.\n gender_type: GenderTypeEnum\n for gender_type in [\n client.enums.GenderTypeEnum.FEMALE,\n client.enums.GenderTypeEnum.MALE,\n ]:\n gender: GenderInfo = client.get_type(\"GenderInfo\")\n gender.type_ = gender_type\n request.targeting.genders.append(gender)\n\n # Add device targeting to the request.\n device_type: DeviceEnum\n for device_type in [\n client.enums.DeviceEnum.DESKTOP,\n client.enums.DeviceEnum.MOBILE,\n client.enums.DeviceEnum.TABLET,\n ]:\n device: DeviceInfo = client.get_type(\"DeviceInfo\")\n device.type_ = device_type\n request.targeting.devices.append(device)\n\n reach_plan_service: ReachPlanServiceClient = client.get_service(\n \"ReachPlanService\"\n )\n response: GenerateReachForecastResponse = (\n reach_plan_service.generate_reach_forecast(request=request)\n )\n\n print(\n \"Currency, Cost, On-Target Reach, On-Target Imprs, Total Reach,\"\n \" Total Imprs, Products\"\n )\n point: ReachForecast\n for point in response.reach_curve.reach_forecasts:\n product_splits = []\n p: PlannedProductReachForecast\n for p in point.planned_product_reach_forecasts:\n product_splits.append(\n {p.plannable_product_code: p.cost_micros / ONE_MILLION}\n )\n print(\n [\n currency_code,\n point.cost_micros / ONE_MILLION,\n point.forecast.on_target_reach,\n point.forecast.on_target_impressions,\n point.forecast.total_reach,\n point.forecast.total_impressions,\n product_splits,\n ]\n )forecast_reach.py\n```\n\nExample:\n```text\ndef get_reach_curve(\n client,\n reach_plan_service,\n customer_id,\n product_mix,\n location_id,\n currency_code)\n duration = client.resource.campaign_duration do |d|\n # Valid durations are between 1 and 90 days.\n d.duration_in_days = 28\n end\n\n targeting = client.resource.targeting do |t|\n t.plannable_location_id = location_id\n t.age_range = :AGE_RANGE_18_65_UP\n t.genders << client.resource.gender_info do |gender|\n gender.type = :FEMALE\n end\n t.genders << client.resource.gender_info do |gender|\n gender.type = :MALE\n end\n t.devices << client.resource.device_info do |device|\n device.type = :DESKTOP\n end\n t.devices << client.resource.device_info do |device|\n device.type = :MOBILE\n end\n t.devices << client.resource.device_info do |device|\n device.type = :TABLET\n end\n end\n\n # See the docs for defaults and valid ranges:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/GenerateReachForecastRequest\n response = reach_plan_service.generate_reach_forecast(\n customer_id: customer_id,\n campaign_duration: duration,\n planned_products: product_mix,\n currency_code: currency_code,\n targeting: targeting,\n )\n\n puts \"Reach curve output:\"\n puts \"Currency, Cost Micros, On-Target Reach, On-Target Imprs, \" \\\n \"Total Reach, Total Imprs, Products\"\n\n response.reach_curve.reach_forecasts.each do |point|\n products = \"\"\n point.planned_product_reach_forecasts.each do |product|\n products += \"(Product: #{product.plannable_product_code}, \"\\\n \"Cost Micros: #{product.cost_micros})\"\n end\n puts \"#{currency_code}, #{point.cost_micros}, \" \\\n \"#{point.forecast.on_target_reach}, \" \\\n \"#{point.forecast.on_target_impressions}, \" \\\n \"#{point.forecast.total_reach}, \" \\\n \"#{point.forecast.total_impressions}, \" \\\n \"#{products}\"\n end\nendforecast_reach.rb\n```\n\nExample:\n```text\nsub pull_reach_curve {\n my ($reach_plan_service, $reach_request) = @_;\n\n my $response = $reach_plan_service->generate_reach_forecast($reach_request);\n print \"Reach curve output:\\n\";\n print \"Currency,\\tCost Micros,\\tOn-Target Reach,\\tOn-Target Imprs,\\t\" .\n \"Total Reach,\\tTotal Imprs,\\tProducts\\n\";\n foreach my $point (@{$response->{reachCurve}{reachForecasts}}) {\n printf \"%s,\\t%d,\\t%d,\\t%d,\\t%d,\\t%d,\\t'[\", $reach_request->{currencyCode},\n $point->{costMicros}, $point->{forecast}{onTargetReach},\n $point->{forecast}{onTargetImpressions}, $point->{forecast}{totalReach},\n $point->{forecast}{totalImpressions};\n foreach my $productReachForecast (@{$point->{plannedProductReachForecasts}})\n {\n printf \"(Product: %s, Budget Micros: %d), \",\n $productReachForecast->{plannableProductCode},\n $productReachForecast->{costMicros};\n }\n print \"]'\\n\";\n }\n}forecast_reach.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.579Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":305,"estimatedTokens":2716}}230{"id":"doc-labels_google_ads_api_google_for_developers-e93a2c32","source":"documentation","title":"Labels | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/reporting/labels","text":"Example:\n```text\nprivate void runExample(\n GoogleAdsClient googleAdsClient, long customerId, List<Long> campaignIds, Long labelId) {\n // Gets the resource name of the label to be added across all given campaigns.\n String labelResourceName = ResourceNames.label(customerId, labelId);\n\n List<CampaignLabelOperation> operations = new ArrayList<>(campaignIds.size());\n // Creates a campaign label operation for each campaign.\n for (Long campaignId : campaignIds) {\n // Gets the resource name of the given campaign.\n String campaignResourceName = ResourceNames.campaign(customerId, campaignId);\n // Creates the campaign label.\n CampaignLabel campaignLabel =\n CampaignLabel.newBuilder()\n .setCampaign(campaignResourceName)\n .setLabel(labelResourceName)\n .build();\n\n operations.add(CampaignLabelOperation.newBuilder().setCreate(campaignLabel).build());\n }\n\n try (CampaignLabelServiceClient campaignLabelServiceClient =\n googleAdsClient.getLatestVersion().createCampaignLabelServiceClient()) {\n MutateCampaignLabelsResponse response =\n campaignLabelServiceClient.mutateCampaignLabels(Long.toString(customerId), operations);\n System.out.printf(\"Added %d campaign labels:%n\", response.getResultsCount());\n for (MutateCampaignLabelResult result : response.getResultsList()) {\n System.out.println(result.getResourceName());\n }\n }\n}AddCampaignLabels.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long[] campaignIds, long labelId)\n{\n // Get the CampaignLabelServiceClient.\n CampaignLabelServiceClient campaignLabelService =\n client.GetService(Services.V25.CampaignLabelService);\n\n // Gets the resource name of the label to be added across all given campaigns.\n string labelResourceName = ResourceNames.Label(customerId, labelId);\n\n List<CampaignLabelOperation> operations = new List<CampaignLabelOperation>();\n // Creates a campaign label operation for each campaign.\n foreach (long campaignId in campaignIds)\n {\n // Gets the resource name of the given campaign.\n string campaignResourceName = ResourceNames.Campaign(customerId, campaignId);\n // Creates the campaign label.\n CampaignLabel campaignLabel = new CampaignLabel()\n {\n Campaign = campaignResourceName,\n Label = labelResourceName\n };\n\n operations.Add(new CampaignLabelOperation()\n {\n Create = campaignLabel\n });\n }\n\n // Send the operation in a mutate request.\n try\n {\n MutateCampaignLabelsResponse response =\n campaignLabelService.MutateCampaignLabels(customerId.ToString(), operations);\n Console.WriteLine($\"Added {response.Results} campaign labels:\");\n\n foreach (MutateCampaignLabelResult result in response.Results)\n {\n Console.WriteLine(result.ResourceName);\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n}AddCampaignLabels.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n array $campaignIds,\n int $labelId\n) {\n // Gets the resource name of the label to be added across all given campaigns.\n $labelResourceName = ResourceNames::forLabel($customerId, $labelId);\n\n // Creates a campaign label operation for each campaign.\n $operations = [];\n foreach ($campaignIds as $campaignId) {\n // Creates the campaign label.\n $campaignLabel = new CampaignLabel([\n 'campaign' => ResourceNames::forCampaign($customerId, $campaignId),\n 'label' => $labelResourceName\n ]);\n $campaignLabelOperation = new CampaignLabelOperation();\n $campaignLabelOperation->setCreate($campaignLabel);\n $operations[] = $campaignLabelOperation;\n }\n\n // Issues a mutate request to add the labels to the campaigns.\n $campaignLabelServiceClient = $googleAdsClient->getCampaignLabelServiceClient();\n $response = $campaignLabelServiceClient->mutateCampaignLabels(\n MutateCampaignLabelsRequest::build($customerId, $operations)\n );\n\n printf(\"Added %d campaign labels:%s\", $response->getResults()->count(), PHP_EOL);\n\n foreach ($response->getResults() as $addedCampaignLabel) {\n /** @var CampaignLabel $addedCampaignLabel */\n printf(\n \"New campaign label added with resource name: '%s'.%s\",\n $addedCampaignLabel->getResourceName(),\n PHP_EOL\n );\n }\n}AddCampaignLabels.php\n```\n\nExample:\n```text\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n label_id: str,\n campaign_ids: List[str],\n) -> None:\n \"\"\"This code example adds a campaign label to a list of campaigns.\n\n Args:\n client: An initialized GoogleAdsClient instance.\n customer_id: A client customer ID str.\n label_id: The ID of the label to attach to campaigns.\n campaign_ids: A list of campaign IDs to which the label will be added.\n \"\"\"\n\n # Get an instance of CampaignLabelService client.\n campaign_label_service: CampaignLabelServiceClient = client.get_service(\n \"CampaignLabelService\"\n )\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n label_service: LabelServiceClient = client.get_service(\"LabelService\")\n\n # Build the resource name of the label to be added across the campaigns.\n label_resource_name: str = label_service.label_path(customer_id, label_id)\n\n operations: List[Any] = []\n\n for campaign_id in campaign_ids:\n campaign_resource_name: str = campaign_service.campaign_path(\n customer_id, campaign_id\n )\n campaign_label_operation: Any = client.get_type(\n \"CampaignLabelOperation\"\n )\n\n campaign_label: CampaignLabel = campaign_label_operation.create\n campaign_label.campaign = campaign_resource_name\n campaign_label.label = label_resource_name\n operations.append(campaign_label_operation)\n\n response: MutateCampaignLabelsResponse = (\n campaign_label_service.mutate_campaign_labels(\n customer_id=customer_id, operations=operations\n )\n )\n print(f\"Added {len(response.results)} campaign labels:\")\n for result in response.results:\n print(result.resource_name)add_campaign_labels.py\n```\n\nExample:\n```text\ndef add_campaign_label(customer_id, label_id, campaign_ids)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n label_resource_name = client.path.label(customer_id, label_id)\n\n labels = campaign_ids.map { |campaign_id|\n client.resource.campaign_label do |label|\n campaign_resource_name = client.path.campaign(customer_id, campaign_id)\n label.campaign = campaign_resource_name\n label.label = label_resource_name\n end\n }\n\n ops = labels.map { |label|\n client.operation.create_resource.campaign_label(label)\n }\n\n response = client.service.campaign_label.mutate_campaign_labels(\n customer_id: customer_id,\n operations: ops,\n )\n response.results.each do |result|\n puts(\"Created campaign label with id: #{result.resource_name}\")\n end\nendadd_campaign_labels.rb\n```\n\nExample:\n```text\nsub add_campaign_labels {\n my ($api_client, $customer_id, $campaign_ids, $label_id) = @_;\n\n my $label_resource_name =\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::label($customer_id,\n $label_id);\n\n my $campaign_label_operations = [];\n\n # Create a campaign label operation for each campaign.\n foreach my $campaign_id (@$campaign_ids) {\n # Create a campaign label.\n my $campaign_label =\n Google::Ads::GoogleAds::V25::Resources::CampaignLabel->new({\n campaign => Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, $campaign_id\n ),\n label => $label_resource_name\n });\n\n # Create a campaign label operation.\n my $campaign_label_operation =\n Google::Ads::GoogleAds::V25::Services::CampaignLabelService::CampaignLabelOperation\n ->new({\n create => $campaign_label\n });\n\n push @$campaign_label_operations, $campaign_label_operation;\n }\n\n # Add the campaign labels to the campaigns.\n my $campaign_labels_response = $api_client->CampaignLabelService()->mutate({\n customerId => $customer_id,\n operations => $campaign_label_operations\n });\n\n my $campaign_label_results = $campaign_labels_response->{results};\n printf \"Added %d campaign labels:\\n\", scalar @$campaign_label_results;\n\n foreach my $campaign_label_result (@$campaign_label_results) {\n printf \"Created campaign label '%s'.\\n\",\n $campaign_label_result->{resourceName};\n }\n\n return 1;\n}add_campaign_labels.pl\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n label.id,\n label.name\nFROM campaign_label\nWHERE label.id IN (123456, 789012, 345678)\n```\n\nExample:\n```text\nSELECT\n label.id,\n label.name\nFROM label\nWHERE label.name = \"LABEL_NAME\"\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n label.id,\n label.name\nFROM campaign_label\nWHERE label.id = LABEL_ID\nORDER BY campaign.id\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.581Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":298,"estimatedTokens":2369}}231{"id":"doc-segmentation_google_ads_api_google_for_developer-2b10bd10","source":"documentation","title":"Segmentation | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/reporting/segmentation","text":"Example:\n```text\nSELECT\n campaign.name,\n campaign.status,\n segments.device,\n metrics.impressions\nFROM campaign\n```\n\nExample:\n```text\n{\n \"results\":[\n {\n \"campaign\":{\n \"resourceName\":\"customers/1234567890/campaigns/111111111\",\n \"name\":\"Test campaign\",\n \"status\":\"ENABLED\"\n },\n \"metrics\":{\n \"impressions\":\"10922\"\n },\n \"segments\":{\n \"device\":\"MOBILE\"\n }\n },\n {\n \"campaign\":{\n \"resourceName\":\"customers/1234567890/campaigns/111111111\",\n \"name\":\"Test campaign\",\n \"status\":\"ENABLED\"\n },\n \"metrics\":{\n \"impressions\":\"28297\"\n },\n \"segments\":{\n \"device\":\"DESKTOP\"\n }\n },\n ...\n ]\n}\n```\n\nExample:\n```text\nSELECT metrics.impressions\nFROM ad_group\n```\n\nExample:\n```text\n{\n \"results\":[\n {\n \"adGroup\":{\n \"resourceName\":\"customers/1234567890/adGroups/2222222222\"\n },\n \"metrics\":{\n \"impressions\":\"237\"\n }\n },\n {\n \"adGroup\":{\n \"resourceName\":\"customers/1234567890/adGroups/33333333333\"\n },\n \"metrics\":{\n \"impressions\":\"15\"\n }\n },\n {\n \"adGroup\":{\n \"resourceName\":\"customers/1234567890/adGroups/44444444444\"\n },\n \"metrics\":{\n \"impressions\":\"0\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n metrics.clicks,\n segments.date\nFROM campaign\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n metrics.clicks\nFROM campaign\nWHERE segments.date > '2024-01-01'\n AND segments.date < '2024-02-01'\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n metrics.clicks,\n segments.date\nFROM campaign\nWHERE segments.date > '2024-01-01'\n AND segments.date < '2024-02-01'\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n metrics.clicks,\n segments.month\nFROM campaign\nWHERE segments.date > '2024-01-01'\n AND segments.date < '2024-02-01'\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n metrics.clicks,\n segments.quarter,\n segments.month\nFROM campaign\nWHERE segments.year > 2019\n AND segments.year < 2024\n```\n\nExample:\n```text\n{\n \"results\":[\n {\n \"searchTermView\":{\n \"resourceName\":\"customers/1234567890/searchTermViews/111111111~2222222222~Z29vZ2xlIHBob3RvcyBpb3M\",\n \"searchTerm\":\"google photos\"\n },\n \"metrics\":{\n \"impressions\":\"3\"\n },\n \"segments\":{\n \"date\":\"2024-06-15\"\n }\n },\n {\n \"searchTermView\":{\n \"resourceName\":\"customers/1234567890/searchTermViews/111111111~33333333333~Z29vZ2xlIHBob3RvcyBpb3M\",\n \"searchTerm\":\"google photos\"\n },\n \"metrics\":{\n \"impressions\":\"2\"\n },\n \"segments\":{\n \"date\":\"2024-06-15\"\n }\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.582Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":169,"estimatedTokens":677}}232{"id":"doc-google_ads_query_language_google_ads_api_google_-ba6b3525","source":"documentation","title":"Google Ads Query Language | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/query/overview","text":"Example:\n```text\nSELECT\n campaign.id,\n campaign.name,\n campaign.status\nFROM campaign\nORDER BY campaign.id\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n campaign.status,\n metrics.impressions\nFROM campaign\nWHERE campaign.status = 'PAUSED'\n AND metrics.impressions > 1000\nORDER BY campaign.id\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n campaign.status,\n metrics.impressions,\n segments.date,\nFROM campaign\nWHERE campaign.status = 'PAUSED'\n AND metrics.impressions > 1000\n AND segments.date during LAST_30_DAYS\nORDER BY campaign.id\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n campaign.status,\n bidding_strategy.name\nFROM campaign\nORDER BY campaign.id\n```\n\nExample:\n```text\nSELECT\n name,\n category,\n selectable,\n filterable,\n sortable,\n selectable_with,\n data_type,\n is_repeated\nWHERE name = \"<INSERT_RESOURCE_OR_FIELD>\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.583Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":64,"estimatedTokens":227}}233{"id":"doc-retrieve_bid_simulations_google_ads_api_google_f-e150c87a","source":"documentation","title":"Retrieve bid simulations | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/bid-simulations/retrieve-bid-simulations","text":"Example:\n```text\nprivate void runExample(GoogleAdsClient googleAdsClient, long customerId, long adGroupId) {\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n // Creates a query that retrieves the ad group criterion CPC bid simulations.\n String query =\n String.format(\n \"SELECT ad_group_criterion_simulation.ad_group_id, \"\n + \"ad_group_criterion_simulation.criterion_id, \"\n + \"ad_group_criterion_simulation.start_date, \"\n + \"ad_group_criterion_simulation.end_date, \"\n + \"ad_group_criterion_simulation.cpc_bid_point_list.points \"\n + \"FROM ad_group_criterion_simulation \"\n + \"WHERE ad_group_criterion_simulation.type = CPC_BID \"\n + \"AND ad_group_criterion_simulation.ad_group_id = %d\",\n adGroupId);\n // Constructs the SearchGoogleAdsStreamRequest.\n SearchGoogleAdsStreamRequest request =\n SearchGoogleAdsStreamRequest.newBuilder()\n .setCustomerId(Long.toString(customerId))\n .setQuery(query)\n .build();\n\n // Issues the search stream request.\n ServerStream<SearchGoogleAdsStreamResponse> stream =\n googleAdsServiceClient.searchStreamCallable().call(request);\n\n // Iterates over all rows in all messages and prints the requested field values for\n // the ad group criterion CPC bid simulation in each row.\n for (SearchGoogleAdsStreamResponse response : stream) {\n for (GoogleAdsRow googleAdsRow : response.getResultsList()) {\n AdGroupCriterionSimulation simulation = googleAdsRow.getAdGroupCriterionSimulation();\n System.out.printf(\n \"Found ad group criterion CPC bid simulation for ad group ID %d, \"\n + \"criterion ID %d, start date '%s', end date '%s', and points:%n\",\n simulation.getAdGroupId(),\n simulation.getCriterionId(),\n simulation.getStartDate(),\n simulation.getEndDate());\n for (CpcBidSimulationPoint point : simulation.getCpcBidPointList().getPointsList()) {\n System.out.printf(\n \" bid: %d => clicks: %d, cost: %d, impressions: %d, \"\n + \"biddable conversions: %.2f, biddable conversions value: %.2f%s\",\n point.getCpcBidMicros(),\n point.getClicks(),\n point.getCostMicros(),\n point.getImpressions(),\n point.getBiddableConversions(),\n point.getBiddableConversions());\n }\n }\n }\n }\n}GetAdGroupCriterionCpcBidSimulations.java\n```\n\nExample:\n```text\npublic void Run(GoogleAdsClient client, long customerId, long adGroupId)\n{\n // Get the GoogleAdsService.\n GoogleAdsServiceClient googleAdsService =\n client.GetService(Services.V25.GoogleAdsService);\n\n try\n {\n // Creates a query that retrieves the ad group criterion CPC bid simulations.\n string query = $@\"\n SELECT\n ad_group_criterion_simulation.ad_group_id,\n ad_group_criterion_simulation.criterion_id,\n ad_group_criterion_simulation.start_date,\n ad_group_criterion_simulation.end_date,\n ad_group_criterion_simulation.cpc_bid_point_list.points\n FROM\n ad_group_criterion_simulation\n WHERE\n ad_group_criterion_simulation.type = CPC_BID AND\n ad_group_criterion_simulation.ad_group_id = {adGroupId}\";\n\n // Issue a search stream request.\n googleAdsService.SearchStream(customerId.ToString(), query,\n delegate (SearchGoogleAdsStreamResponse response)\n {\n // Iterates over all rows in all messages and prints the requested field\n // values for the ad group criterion CPC bid simulation in each row.\n foreach (GoogleAdsRow googleAdsRow in response.Results)\n {\n AdGroupCriterionSimulation simulation =\n googleAdsRow.AdGroupCriterionSimulation;\n\n Console.WriteLine(\"Found ad group criterion CPC bid simulation for \" +\n $\"ad group ID {simulation.AdGroupId}, \" +\n $\"criterion ID {simulation.CriterionId}, \" +\n $\"start date {simulation.StartDate}, \" +\n $\"end date {simulation.EndDate}\");\n\n foreach (CpcBidSimulationPoint point in\n simulation.CpcBidPointList.Points)\n {\n Console.WriteLine($\"\\tbid: {point.CpcBidMicros} => \" +\n $\"clicks: {point.Clicks}, \" +\n $\"cost: {point.CostMicros}, \" +\n $\"impressions: {point.Impressions}, \" +\n $\"biddable conversions: {point.BiddableConversions}, \" +\n \"biddable conversions value: \" +\n $\"{point.BiddableConversionsValue}\");\n }\n\n Console.WriteLine();\n }\n }\n );\n }GetAdGroupCriterionCpcBidSimulations.cs\n```\n\nExample:\n```text\npublic static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId\n) {\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n\n // Creates a query that retrieves the ad group criterion CPC bid simulations.\n $query = sprintf(\n 'SELECT ad_group_criterion_simulation.ad_group_id, ' .\n 'ad_group_criterion_simulation.criterion_id, ' .\n 'ad_group_criterion_simulation.start_date, ' .\n 'ad_group_criterion_simulation.end_date, ' .\n 'ad_group_criterion_simulation.cpc_bid_point_list.points ' .\n 'FROM ad_group_criterion_simulation ' .\n 'WHERE ad_group_criterion_simulation.type = CPC_BID ' .\n 'AND ad_group_criterion_simulation.ad_group_id = %d',\n $adGroupId\n );\n\n // Issues a search stream request.\n /** @var GoogleAdsServerStreamDecorator $stream */\n $stream = $googleAdsServiceClient->searchStream(\n SearchGoogleAdsStreamRequest::build($customerId, $query)\n );\n\n // Iterates over all rows in all messages and prints the requested field values for\n // the ad group criterion CPC bid simulation in each row.\n foreach ($stream->iterateAllElements() as $googleAdsRow) {\n /** @var GoogleAdsRow $googleAdsRow */\n $simulation = $googleAdsRow->getAdGroupCriterionSimulation();\n printf(\n 'Found ad group criterion CPC bid simulation for ad group ID %d, ' .\n 'criterion ID %d, start date \"%s\", end date \"%s\", and points:%s',\n $simulation->getAdGroupId(),\n $simulation->getCriterionId(),\n $simulation->getStartDate(),\n $simulation->getEndDate(),\n PHP_EOL\n );\n foreach ($simulation->getCpcBidPointList()->getPoints() as $point) {\n /** @var CpcBidSimulationPoint $point */\n printf(\n ' bid: %d => clicks: %d, cost: %d, impressions: %d, ' .\n 'biddable conversions: %.2f, biddable conversions value: %.2f%s',\n $point->getCpcBidMicros(),\n $point->getClicks(),\n $point->getCostMicros(),\n $point->getImpressions(),\n $point->getBiddableConversions(),\n $point->getBiddableConversionsValue(),\n PHP_EOL\n );\n }\n\n print PHP_EOL;\n }\n}GetAdGroupCriterionCpcBidSimulations.php\n```\n\nExample:\n```text\ndef main(client: GoogleAdsClient, customer_id: str, ad_group_id: str):\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n query = f\"\"\"\n SELECT\n ad_group_criterion_simulation.ad_group_id,\n ad_group_criterion_simulation.criterion_id,\n ad_group_criterion_simulation.start_date,\n ad_group_criterion_simulation.end_date,\n ad_group_criterion_simulation.cpc_bid_point_list.points\n FROM ad_group_criterion_simulation\n WHERE\n ad_group_criterion_simulation.type = CPC_BID\n AND ad_group_criterion_simulation.ad_group_id = {ad_group_id}\"\"\"\n\n # Issues a search request using streaming.\n stream: Iterable[SearchGoogleAdsStreamResponse] = (\n googleads_service.search_stream(customer_id=customer_id, query=query)\n )\n\n # Iterates over all rows in all messages and prints the requested field\n # values for the ad group criterion CPC bid simulation in each row.\n batch: SearchGoogleAdsStreamResponse\n for batch in stream:\n row: GoogleAdsRow\n for row in batch.results:\n simulation: AdGroupCriterionSimulation = (\n row.ad_group_criterion_simulation\n )\n\n print(\n \"found ad group criterion CPC bid simulation for \"\n f\"ad group ID {simulation.ad_group_id}, \"\n f\"criterion ID {simulation.criterion_id}, \"\n f\"start date {simulation.start_date}, \"\n f\"end date {simulation.end_date}\"\n )\n\n point: CpcBidSimulationPoint\n for point in simulation.cpc_bid_point_list.points:\n print(\n f\"\\tbid: {point.cpc_bid_micros} => \"\n f\"clicks: {point.clicks}\",\n f\"cost: {point.cost_micros}, \"\n f\"impressions: {point.impressions},\"\n \"biddable conversions: \"\n f\"{point.biddable_conversions},\"\n f\"biddable conversions value: \"\n f\"{point.biddable_conversions_value}\",\n )\n\n print()get_ad_group_criterion_cpc_bid_simulations.py\n```\n\nExample:\n```text\ndef get_ad_group_criterion_cpc_bid_simulations(customer_id, ad_group_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n query = <<~QUERY\n SELECT ad_group_criterion_simulation.ad_group_id,\n ad_group_criterion_simulation.criterion_id,\n ad_group_criterion_simulation.start_date,\n ad_group_criterion_simulation.end_date,\n ad_group_criterion_simulation.cpc_bid_point_list.points\n FROM ad_group_criterion_simulation\n WHERE ad_group_criterion_simulation.type = CPC_BID \n AND ad_group_criterion_simulation.ad_group_id = #{ad_group_id}\n QUERY\n\n responses = client.service.google_ads.search_stream(\n customer_id: customer_id,\n query: query,\n )\n\n responses.each do |response|\n response.results.each do |row|\n simulation = row.ad_group_criterion_simulation\n\n puts \"Found ad group criterion CPC bid simulation for \" \\\n \"ad group ID #{simulation.ad_group_id}, \" \\\n \"criterion ID #{simulation.criterion_id}, \" \\\n \"start date '#{simulation.start_date}', \" \\\n \"end date '#{simulation.end_date}', and points:\"\n\n simulation.cpc_bid_point_list.points.each do |point|\n puts \" bid: #{point.cpc_bid_micros} => \" \\\n \"clicks: #{point.clicks}, \" \\\n \"cost: #{point.cost_micros}, \" \\\n \"impressions: #{point.impressions}, \" \\\n \"biddable conversions: #{point.biddable_conversions.round(2)}, \" \\\n \"biddable conversions value: #{point.biddable_conversions_value.round(2)}\"\n end\n end\n end\nendget_ad_group_criterion_cpc_bid_simulations.rb\n```\n\nExample:\n```text\nsub get_ad_group_criterion_cpc_bid_simulations {\n my ($api_client, $customer_id, $ad_group_id) = @_;\n\n # Create a query that retrieves the ad group criterion CPC bid simulations.\n my $search_query =\n \"SELECT ad_group_criterion_simulation.ad_group_id, \" .\n \"ad_group_criterion_simulation.criterion_id, \" .\n \"ad_group_criterion_simulation.start_date, \" .\n \"ad_group_criterion_simulation.end_date, \" .\n \"ad_group_criterion_simulation.cpc_bid_point_list.points \" .\n \"FROM ad_group_criterion_simulation \" .\n \"WHERE ad_group_criterion_simulation.type = CPC_BID \" .\n \"AND ad_group_criterion_simulation.ad_group_id = $ad_group_id\";\n\n my $search_stream_request =\n Google::Ads::GoogleAds::V25::Services::GoogleAdsService::SearchGoogleAdsStreamRequest\n ->new({\n customerId => $customer_id,\n query => $search_query\n });\n\n # Get the GoogleAdsService.\n my $google_ads_service = $api_client->GoogleAdsService();\n\n my $search_stream_handler =\n Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({\n service => $google_ads_service,\n request => $search_stream_request\n });\n\n # Issue a search stream request, iterate over all rows in all messages and\n # print the requested field values for the ad group criterion CPC bid\n # simulation in each row.\n $search_stream_handler->process_contents(\n sub {\n my $google_ads_row = shift;\n my $simulation = $google_ads_row->{adGroupCriterionSimulation};\n\n printf\n \"Found ad group criterion CPC bid simulation for ad group ID %d, \" .\n \"criterion ID %d, start date '%s', end date '%s', and points:\\n\",\n $simulation->{adGroupId}, $simulation->{criterionId},\n $simulation->{startDate}, $simulation->{endDate};\n\n foreach my $point (@{$simulation->{cpcBidPointList}{points}}) {\n printf \" bid: %d => clicks: %d, cost: %d, impressions: %d, \" .\n \"biddable conversions: %.2f, biddable conversions value: %.2f\\n\",\n $point->{cpcBidMicros},\n $point->{clicks},\n $point->{costMicros},\n $point->{impressions},\n $point->{biddableConversions},\n $point->{biddableConversionsValue};\n }\n });\n\n return 1;\n}get_ad_group_criterion_cpc_bid_simulations.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.584Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":346,"estimatedTokens":3481}}234{"id":"doc-specify_a_media_plan_google_ads_api_google_for_d-3b117fb4","source":"documentation","title":"Specify a media plan | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/reach-forecasting/media-plan","text":"Example:\n```text\nprivate void showPlannableProducts(\n ReachPlanServiceClient reachPlanServiceClient, String locationId) {\n ListPlannableProductsRequest request =\n ListPlannableProductsRequest.newBuilder().setPlannableLocationId(locationId).build();\n\n ListPlannableProductsResponse response = reachPlanServiceClient.listPlannableProducts(request);\n\n System.out.printf(\"Plannable Products for location %s:%n\", locationId);\n for (ProductMetadata product : response.getProductMetadataList()) {\n System.out.printf(\"%s:%n\", product.getPlannableProductCode());\n System.out.println(\"Age Ranges:\");\n for (ReachPlanAgeRange ageRange : product.getPlannableTargeting().getAgeRangesList()) {\n System.out.printf(\"\\t- %s%n\", ageRange);\n }\n System.out.println(\"Genders:\");\n for (GenderInfo gender : product.getPlannableTargeting().getGendersList()) {\n System.out.printf(\"\\t- %s%n\", gender.getType());\n }\n System.out.println(\"Devices:\");\n for (DeviceInfo device : product.getPlannableTargeting().getDevicesList()) {\n System.out.printf(\"\\t- %s%n\", device.getType());\n }\n }\n}ForecastReach.java\n```\n\nExample:\n```text\npublic void ShowPlannableProducts(\n ReachPlanServiceClient reachPlanService, string locationId)\n{\n ListPlannableProductsRequest request = new ListPlannableProductsRequest\n {\n PlannableLocationId = locationId\n };\n ListPlannableProductsResponse response = reachPlanService.ListPlannableProducts(\n request);\n\n Console.WriteLine($\"Plannable Products for location {locationId}:\");\n foreach (ProductMetadata product in response.ProductMetadata)\n {\n Console.WriteLine($\"{product.PlannableProductCode}:\");\n Console.WriteLine(\"Age Ranges:\");\n foreach (ReachPlanAgeRange ageRange in product.PlannableTargeting.AgeRanges)\n {\n Console.WriteLine($\"\\t- {ageRange}\");\n }\n\n Console.WriteLine(\"Genders:\");\n foreach (GenderInfo gender in product.PlannableTargeting.Genders)\n {\n Console.WriteLine($\"\\t- {gender.Type}\");\n }\n\n Console.WriteLine(\"Devices:\");\n foreach (DeviceInfo device in product.PlannableTargeting.Devices)\n {\n Console.WriteLine($\"\\t- {device.Type}\");\n }\n }\n}ForecastReach.cs\n```\n\nExample:\n```text\nprivate static function showPlannableProducts(GoogleAdsClient $googleAdsClient)\n{\n $response = $googleAdsClient->getReachPlanServiceClient()->listPlannableProducts(\n ListPlannableProductsRequest::build(self::LOCATION_ID)\n );\n\n print 'Plannable Products for Location ID ' . self::LOCATION_ID . ':' . PHP_EOL;\n foreach ($response->getProductMetadata() as $product) {\n /** @var ProductMetadata $product */\n print $product->getPlannableProductCode() . ':' . PHP_EOL;\n print 'Age Ranges:' . PHP_EOL;\n foreach ($product->getPlannableTargeting()->getAgeRanges() as $ageRange) {\n /** @var ReachPlanAgeRange $ageRange */\n printf(\"\\t- %s%s\", ReachPlanAgeRange::name($ageRange), PHP_EOL);\n }\n print 'Genders:' . PHP_EOL;\n foreach ($product->getPlannableTargeting()->getGenders() as $gender) {\n /** @var GenderInfo $gender */\n printf(\"\\t- %s%s\", GenderType::name($gender->getType()), PHP_EOL);\n }\n print 'Devices:' . PHP_EOL;\n foreach ($product->getPlannableTargeting()->getDevices() as $device) {\n /** @var DeviceInfo $device */\n printf(\"\\t- %s%s\", Device::name($device->getType()), PHP_EOL);\n }\n }\n}ForecastReach.php\n```\n\nExample:\n```text\ndef show_plannable_products(client: GoogleAdsClient, location_id: str):\n \"\"\"Lists plannable products for a given location.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n location_id: The location ID to plan for.\n \"\"\"\n reach_plan_service: ReachPlanServiceClient = client.get_service(\n \"ReachPlanService\"\n )\n response: ListPlannableProductsResponse = (\n reach_plan_service.list_plannable_products(\n plannable_location_id=location_id\n )\n )\n print(f\"Plannable Products for Location ID {location_id}\")\n\n product_metadata: ProductMetadata\n for product_metadata in response.product_metadata:\n print(\n f\"{product_metadata.plannable_product_code} : \"\n f\"{product_metadata.plannable_product_name}\"\n )\n\n print(\"Age Ranges:\")\n age_range: ReachPlanAgeRangeEnum\n for age_range in product_metadata.plannable_targeting.age_ranges:\n print(f\"\\t- {age_range.name}\")\n\n print(\"Genders:\")\n gender: GenderInfo\n for gender in product_metadata.plannable_targeting.genders:\n print(f\"\\t- {gender.type_.name}\")\n\n print(\"Devices:\")\n device: DeviceInfo\n for device in product_metadata.plannable_targeting.devices:\n print(f\"\\t- {device.type_.name}\")forecast_reach.py\n```\n\nExample:\n```text\ndef show_plannable_products(reach_plan_service)\n response = reach_plan_service.list_plannable_products(\n plannable_location_id: LOCATION_ID,\n )\n\n puts \"Plannable Products for Location ID #{LOCATION_ID}:\"\n\n response.product_metadata.each do |product|\n puts \"#{product.plannable_product_code}:\"\n puts \"Age Ranges:\"\n product.plannable_targeting.age_ranges.each do |age_range|\n puts \"\\t- #{age_range}\"\n end\n puts \"Genders:\"\n product.plannable_targeting.genders.each do |gender|\n puts \"\\t- #{gender.type}\"\n end\n puts \"Devices:\"\n product.plannable_targeting.devices.each do |device|\n puts \"\\t- #{device.type}\"\n end\n end\nendforecast_reach.rb\n```\n\nExample:\n```text\nsub show_plannable_products {\n my ($reach_plan_service, $location_id) = @_;\n\n my $response = $reach_plan_service->list_plannable_products({\n plannableLocationId => $location_id\n });\n\n printf \"Plannable Products for location %d:\\n\", $location_id;\n foreach my $product (@{$response->{productMetadata}}) {\n printf \"%s : '%s'\\n\", $product->{plannableProductCode},\n $product->{plannableProductName};\n print \"Age Ranges:\\n\";\n foreach my $age_range (@{$product->{plannableTargeting}{ageRanges}}) {\n printf \"\\t- %s\\n\", $age_range;\n }\n print \"Genders:\\n\";\n foreach my $gender (@{$product->{plannableTargeting}{genders}}) {\n printf \"\\t- %s\\n\", $gender->{type};\n }\n print \"Devices:\\n\";\n foreach my $device (@{$product->{plannableTargeting}{devices}}) {\n printf \"\\t- %s\\n\", $device->{type};\n }\n }\n}forecast_reach.pl\n```\n\nExample:\n```text\nprivate void forecastManualMix(\n ReachPlanServiceClient reachPlanServiceClient,\n long customerId,\n String locationId,\n String currencyCode,\n long budgetMicros) {\n List<PlannedProduct> productMix = new ArrayList<>();\n\n // Set up a ratio to split the budget between two products.\n double trueviewAllocation = 0.15;\n double bumperAllocation = 1 - trueviewAllocation;\n\n // See listPlannableProducts on ReachPlanService to retrieve a list\n // of valid PlannableProductCode's for a given location:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/ReachPlanService\n productMix.add(\n PlannedProduct.newBuilder()\n .setPlannableProductCode(\"TRUEVIEW_IN_STREAM\")\n .setBudgetMicros((long) (budgetMicros * bumperAllocation))\n .build());\n productMix.add(\n PlannedProduct.newBuilder()\n .setPlannableProductCode(\"BUMPER\")\n .setBudgetMicros((long) (budgetMicros * bumperAllocation))\n .build());\n\n GenerateReachForecastRequest request =\n buildReachRequest(customerId, productMix, locationId, currencyCode);\n\n getReachCurve(reachPlanServiceClient, request);\n}ForecastReach.java\n```\n\nExample:\n```text\npublic void ForecastMix(ReachPlanServiceClient reachPlanService, string customerId,\n string locationId, string currencyCode, long budgetMicros)\n{\n List<PlannedProduct> productMix = new List<PlannedProduct>();\n\n // Set up a ratio to split the budget between two products.\n double trueviewAllocation = 0.15;\n double bumperAllocation = 1 - trueviewAllocation;\n\n // See listPlannableProducts on ReachPlanService to retrieve a list\n // of valid PlannableProductCode's for a given location:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/ReachPlanService\n productMix.Add(new PlannedProduct\n {\n PlannableProductCode = \"TRUEVIEW_IN_STREAM\",\n BudgetMicros = Convert.ToInt64(budgetMicros * trueviewAllocation)\n });\n productMix.Add(new PlannedProduct\n {\n PlannableProductCode = \"BUMPER\",\n BudgetMicros = Convert.ToInt64(budgetMicros * bumperAllocation)\n });\n\n GenerateReachForecastRequest request =\n BuildReachRequest(customerId, productMix, locationId, currencyCode);\n\n GetReachCurve(reachPlanService, request);\n}ForecastReach.cs\n```\n\nExample:\n```text\nprivate static function forecastManualMix(GoogleAdsClient $googleAdsClient, int $customerId)\n{\n // Set up a ratio to split the budget between two products.\n $trueviewAllocation = floatval(0.15);\n $bumperAllocation = floatval(1 - $trueviewAllocation);\n\n // See listPlannableProducts on ReachPlanService to retrieve a list\n // of valid PlannableProductCode's for a given location:\n // https://developers.google.com/google-ads/api/reference/rpc/latest/ReachPlanService\n $productMix = [\n new PlannedProduct([\n 'plannable_product_code' => 'TRUEVIEW_IN_STREAM',\n 'budget_micros' => self::BUDGET_MICROS * $trueviewAllocation\n ]),\n new PlannedProduct([\n 'plannable_product_code' => 'BUMPER',\n 'budget_micros' => self::BUDGET_MICROS * $bumperAllocation\n ])\n ];\n\n self::getReachCurve(\n $googleAdsClient,\n $customerId,\n $productMix,\n self::LOCATION_ID,\n self::CURRENCY_CODE\n );\n}ForecastReach.php\n```\n\nExample:\n```text\ndef forecast_manual_mix(\n client: GoogleAdsClient,\n customer_id: str,\n location_id: str,\n currency_code: str,\n budget: int,\n):\n \"\"\"Pulls a forecast for product mix created manually.\n\n Args:\n client: an initialized GoogleAdsClient instance.\n customer_id: The customer ID for the reach forecast.\n location_id: The location ID to plan for.\n currency_code: Three-character ISO 4217 currency code.\n budget: Budget to allocate to the plan.\n \"\"\"\n product_mix: list[PlannedProduct] = []\n trueview_allocation = 0.15\n bumper_allocation = 1 - trueview_allocation\n product_splits = [\n (\"TRUEVIEW_IN_STREAM\", trueview_allocation),\n (\"BUMPER\", bumper_allocation),\n ]\n product: str\n split: float\n for product, split in product_splits:\n planned_product: PlannedProduct = client.get_type(\"PlannedProduct\")\n planned_product.plannable_product_code = product\n planned_product.budget_micros = math.trunc(budget * ONE_MILLION * split)\n product_mix.append(planned_product)\n\n request_reach_curve(\n client, customer_id, product_mix, location_id, currency_code\n )forecast_reach.py\n```\n\nExample:\n```text\ndef forecast_manual_mix(client, reach_plan_service, customer_id)\n # Set up a ratio to split the budget between two products.\n trueview_allocation = 0.15\n bumper_allocation = 1 - trueview_allocation\n\n # See listPlannableProducts on ReachPlanService to retrieve a list\n # of valid PlannableProductCode's for a given location:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/ReachPlanService\n product_mix = []\n\n product_mix << client.resource.planned_product do |p|\n p.plannable_product_code = 'TRUEVIEW_IN_STREAM'\n p.budget_micros = BUDGET_MICROS * trueview_allocation\n end\n\n product_mix << client.resource.planned_product do |p|\n p.plannable_product_code = 'BUMPER'\n p.budget_micros = BUDGET_MICROS * bumper_allocation\n end\n\n get_reach_curve(\n client,\n reach_plan_service,\n customer_id,\n product_mix,\n LOCATION_ID,\n CURRENCY_CODE,\n )\nendforecast_reach.rb\n```\n\nExample:\n```text\nsub forecast_mix {\n my (\n $reach_plan_service, $customer_id, $location_id,\n $currency_code, $budget_micros\n ) = @_;\n\n my $product_mix = [];\n\n # Set up a ratio to split the budget between two products.\n my $trueview_allocation = 0.15;\n my $bumper_allocation = 1 - $trueview_allocation;\n\n # See list_plannable_products on ReachPlanService to retrieve a list of valid\n # plannable product codes for a given location:\n # https://developers.google.com/google-ads/api/reference/rpc/latest/ReachPlanService\n push @$product_mix,\n Google::Ads::GoogleAds::V25::Services::ReachPlanService::PlannedProduct->\n new({\n plannableProductCode => \"TRUEVIEW_IN_STREAM\",\n budgetMicros => int($budget_micros * $trueview_allocation)});\n push @$product_mix,\n Google::Ads::GoogleAds::V25::Services::ReachPlanService::PlannedProduct->\n new({\n plannableProductCode => \"BUMPER\",\n budgetMicros => int($budget_micros * $bumper_allocation)});\n\n my $reach_request =\n build_reach_request($customer_id, $product_mix, $location_id,\n $currency_code);\n\n pull_reach_curve($reach_plan_service, $reach_request);\n}forecast_reach.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.586Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":401,"estimatedTokens":3329}}235{"id":"doc-google_ads_api_google_for_developers-a6101084","source":"documentation","title":"Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/query/ordering-limiting","text":"Example:\n```text\nFieldName ('ASC' | 'DESC')?\n```\n\nExample:\n```text\nORDER BY metrics.impressions DESC, campaign.name ASC\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n metrics.impressions\nFROM campaign\nWHERE segments.date DURING LAST_30_DAYS\nORDER BY metrics.impressions DESC\nLIMIT 5\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.586Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":23,"estimatedTokens":79}}236{"id":"doc-date_ranges_google_ads_api_google_for_developers-64f3ea9c","source":"documentation","title":"Date Ranges | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/query/date-ranges","text":"Example:\n```text\nsegments.date BETWEEN '2024-01-01' AND '2024-01-31'\n```\n\nExample:\n```text\nsegments.date >= '20241001' AND segments.date <= '20241031'\n```\n\nExample:\n```text\nsegments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nsegments.month = '2024-05-01'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.587Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":21,"estimatedTokens":69}}237{"id":"doc-query_structure_google_ads_api_google_for_develo-23144327","source":"documentation","title":"Query structure | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/query/structure","text":"Example:\n```text\nSELECT\n campaign.id,\n campaign.name\nFROM campaign\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n bidding_strategy.id,\n bidding_strategy.name,\n segments.device,\n segments.date,\n metrics.impressions,\n metrics.clicks\nFROM campaign\nWHERE segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n ad_group.id\nFROM ad_group\n```\n\nExample:\n```text\nSELECT ad_group.id\nFROM ad_group\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n metrics.impressions\nFROM campaign\nWHERE segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n segments.device,\n metrics.clicks\nFROM campaign\nWHERE metrics.impressions > 0\n AND segments.device = MOBILE\n AND segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name,\n segments.date,\n metrics.clicks\nFROM campaign\nWHERE segments.date DURING LAST_30_DAYS\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n metrics.clicks\nFROM campaign\nORDER BY metrics.clicks DESC\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n ad_group.name,\n metrics.impressions,\n metrics.clicks\nFROM ad_group\nORDER BY\n campaign.name,\n metrics.impressions DESC,\n metrics.clicks DESC\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n ad_group.name,\n segments.device,\n metrics.impressions\nFROM ad_group\nORDER BY metrics.impressions DESC\nLIMIT 50\n```\n\nExample:\n```text\nSELECT campaign.name\nFROM campaign\nPARAMETERS include_drafts=true\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n customer.id\nFROM campaign\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n customer.id\nFROM campaign\nPARAMETERS omit_unselected_resource_names = true\n```\n\nExample:\n```text\nSELECT\n campaign.name,\n campaign.resource_name\nFROM campaign\nPARAMETERS omit_unselected_resource_names = true\n```\n\nExample:\n```text\nSELECT campaign.id\nFROM ad_group\nWHERE ad_group.status = PAUSED\n```\n\nExample:\n```text\nSELECT\n metrics.impressions,\n metrics.clicks,\n metrics.cost_micros\nFROM campaign\n```\n\nExample:\n```text\nSELECT segments.device FROM campaign\n```\n\nExample:\n```text\nSELECT\n campaign.id,\n campaign.name\nFROM campaign\nWHERE campaign.resource_name = 'customers/1234567/campaigns/987654'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.588Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":170,"estimatedTokens":556}}238{"id":"doc-case_sensitivity_google_ads_api_google_for_devel-816532af","source":"documentation","title":"Case Sensitivity | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/query/case-sensitivity","text":"Example:\n```text\nSELECT campaign.id\nFROM campaign\nWHERE campaign.name REGEXP_MATCH \"(?i).*test.*\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.589Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":8,"estimatedTokens":29}}239{"id":"doc-reports_in_the_google_ads_ui_google_ads_api_goog-36057972","source":"documentation","title":"Reports in the Google Ads UI | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/reporting/uireports","text":"Example:\n```text\nSELECT\n customer.descriptive_name,\n customer.id,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n metrics.absolute_top_impression_percentage,\n metrics.top_impression_percentage,\n metrics.average_cpm\nFROM customer\nWHERE segments.date DURING LAST_7_DAYS\n```\n\nExample:\n```text\nSELECT campaign.name\nFROM campaign\nWHERE campaign.status != 'REMOVED'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.590Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":25,"estimatedTokens":110}}240{"id":"doc-google_ads_query_language_grammar_google_ads_api-a2c7fd38","source":"documentation","title":"Google Ads Query Language Grammar | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/query/grammar","text":"Example:\n```text\nQuery -> SelectClause FromClause WhereClause? OrderByClause?\n LimitClause? ParametersClause?\nSelectClause -> SELECT FieldName (, FieldName)*\nFromClause -> FROM ResourceName\nWhereClause -> WHERE Condition (AND Condition)*\nOrderByClause -> ORDER BY Ordering (, Ordering)*\nLimitClause -> LIMIT PositiveInteger\nParametersClause -> PARAMETERS Literal = Value (, Literal = Value)*\n\nCondition -> FieldName Operator Value\nOperator -> = | != | > | >= | < | <= | IN | NOT IN |\n LIKE | NOT LIKE | CONTAINS ANY | CONTAINS ALL |\n CONTAINS NONE | IS NULL | IS NOT NULL | DURING |\n BETWEEN | REGEXP_MATCH | NOT REGEXP_MATCH\nValue -> Literal | LiteralList | Number | NumberList | String |\n StringList | Function\nOrdering -> FieldName (ASC | DESC)?\n\nFieldName -> [a-z] ([a-zA-Z0-9._])*\nResourceName -> [a-z] ([a-zA-Z_])*\n\nStringList -> ( String (, String)* )\nLiteralList -> ( Literal (, Literal)* )\nNumberList -> ( Number (, Number)* )\n\nPositiveInteger -> [1-9] ([0-9])*\nNumber -> -? [0-9]+ (. [0-9] [0-9]*)?\nString -> (' Char* ') | (\" Char* \")\nLiteral -> [a-zA-Z0-9_]*\n\nFunction -> LAST_14_DAYS | LAST_30_DAYS | LAST_7_DAYS |\n LAST_BUSINESS_WEEK | LAST_MONTH | LAST_WEEK_MON_SUN |\n LAST_WEEK_SUN_SAT | THIS_MONTH | THIS_WEEK_MON_TODAY |\n THIS_WEEK_SUN_TODAY | TODAY | YESTERDAY\n```\n\nExample:\n```text\ncampaign.name LIKE '[[]Earth[_]to[_]Mars[]]%'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.592Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":44,"estimatedTokens":413}}241{"id":"doc-query_cookbook_google_ads_api_google_for_develop-f526f0f2","source":"documentation","title":"Query Cookbook | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/query/cookbook","text":"Example:\n```text\nSELECT campaign.name,\n campaign_budget.amount_micros,\n campaign.status,\n campaign.optimization_score,\n campaign.advertising_channel_type,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n campaign.bidding_strategy_type\nFROM campaign\nWHERE segments.date DURING LAST_7_DAYS\n AND campaign.status != 'REMOVED'\n```\n\nExample:\n```text\ncurl -f --request POST \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:searchStream\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data '{\n\"query\": \"\n SELECT campaign.name,\n campaign_budget.amount_micros,\n campaign.status,\n campaign.optimization_score,\n campaign.advertising_channel_type,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n campaign.bidding_strategy_type\n FROM campaign\n WHERE segments.date DURING LAST_7_DAYS\n AND campaign.status != 'REMOVED'\n\"\n}'\n```\n\nExample:\n```text\nSELECT ad_group.name,\n campaign.name,\n ad_group.status,\n ad_group.type,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros\nFROM ad_group\nWHERE segments.date DURING LAST_7_DAYS\n AND ad_group.status != 'REMOVED'\n```\n\nExample:\n```text\ncurl -f --request POST \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:searchStream\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data '{\n\"query\": \"\n SELECT ad_group.name,\n campaign.name,\n ad_group.status,\n ad_group.type,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros\n FROM ad_group\n WHERE segments.date DURING LAST_7_DAYS\n AND ad_group.status != 'REMOVED'\n\"\n}'\n```\n\nExample:\n```text\nSELECT ad_group_ad.ad.expanded_text_ad.headline_part1,\n ad_group_ad.ad.expanded_text_ad.headline_part2,\n ad_group_ad.ad.expanded_text_ad.headline_part3,\n ad_group_ad.ad.final_urls,\n ad_group_ad.ad.expanded_text_ad.description,\n ad_group_ad.ad.expanded_text_ad.description2,\n campaign.name,\n ad_group.name,\n ad_group_ad.policy_summary.approval_status,\n ad_group_ad.ad.type,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros\nFROM ad_group_ad\nWHERE segments.date DURING LAST_7_DAYS\n AND ad_group_ad.status != 'REMOVED'\n```\n\nExample:\n```text\ncurl -f --request POST \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:searchStream\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data '{\n\"query\": \"\n SELECT ad_group_ad.ad.expanded_text_ad.headline_part1,\n ad_group_ad.ad.expanded_text_ad.headline_part2,\n ad_group_ad.ad.expanded_text_ad.headline_part3,\n ad_group_ad.ad.final_urls,\n ad_group_ad.ad.expanded_text_ad.description,\n ad_group_ad.ad.expanded_text_ad.description2,\n campaign.name,\n ad_group.name,\n ad_group_ad.policy_summary.approval_status,\n ad_group_ad.ad.type,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros\n FROM ad_group_ad\n WHERE segments.date DURING LAST_7_DAYS\n AND ad_group_ad.status != 'REMOVED'\n\"\n}'\n```\n\nExample:\n```text\nSELECT ad_group_criterion.keyword.text,\n campaign.name,\n ad_group.name,\n ad_group_criterion.system_serving_status,\n ad_group_criterion.keyword.match_type,\n ad_group_criterion.approval_status,\n ad_group_criterion.final_urls,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros\nFROM keyword_view\nWHERE segments.date DURING LAST_7_DAYS\n AND ad_group_criterion.status != 'REMOVED'\n```\n\nExample:\n```text\ncurl -f --request POST \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:searchStream\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data '{\n\"query\": \"\n SELECT ad_group_criterion.keyword.text,\n campaign.name,\n ad_group.name,\n ad_group_criterion.system_serving_status,\n ad_group_criterion.keyword.match_type,\n ad_group_criterion.approval_status,\n ad_group_criterion.final_urls,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros\n FROM keyword_view\n WHERE segments.date DURING LAST_7_DAYS\n AND ad_group_criterion.status != 'REMOVED'\n\"\n}'\n```\n\nExample:\n```text\nSELECT search_term_view.search_term,\n segments.keyword.info.match_type,\n search_term_view.status,\n campaign.name,\n ad_group.name,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n campaign.advertising_channel_type\nFROM search_term_view\nWHERE segments.date DURING LAST_7_DAYS\n```\n\nExample:\n```text\ncurl -f --request POST \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:searchStream\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data '{\n\"query\": \"\n SELECT search_term_view.search_term,\n segments.keyword.info.match_type,\n search_term_view.status,\n campaign.name,\n ad_group.name,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n campaign.advertising_channel_type\n FROM search_term_view\n WHERE segments.date DURING LAST_7_DAYS\n\"\n}'\n```\n\nExample:\n```text\nSELECT ad_group_criterion.resource_name,\n ad_group_criterion.type,\n campaign.name,\n ad_group.name,\n ad_group_criterion.system_serving_status,\n ad_group_criterion.bid_modifier,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n campaign.advertising_channel_type\nFROM ad_group_audience_view\nWHERE segments.date DURING LAST_7_DAYS\n```\n\nExample:\n```text\ncurl -f --request POST \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:searchStream\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data '{\n\"query\": \"\n SELECT ad_group_criterion.resource_name,\n ad_group_criterion.type,\n campaign.name,\n ad_group.name,\n ad_group_criterion.system_serving_status,\n ad_group_criterion.bid_modifier,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n campaign.advertising_channel_type\n FROM ad_group_audience_view\n WHERE segments.date DURING LAST_7_DAYS\n\"\n}'\n```\n\nExample:\n```text\nSELECT ad_group_criterion.age_range.type,\n campaign.name,\n ad_group.name,\n ad_group_criterion.system_serving_status,\n ad_group_criterion.bid_modifier,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n campaign.advertising_channel_type\nFROM age_range_view\nWHERE segments.date DURING LAST_7_DAYS\n```\n\nExample:\n```text\ncurl -f --request POST \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:searchStream\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data '{\n\"query\": \"\n SELECT ad_group_criterion.age_range.type,\n campaign.name,\n ad_group.name,\n ad_group_criterion.system_serving_status,\n ad_group_criterion.bid_modifier,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n campaign.advertising_channel_type\n FROM age_range_view\n WHERE segments.date DURING LAST_7_DAYS\n\"\n}'\n```\n\nExample:\n```text\nSELECT ad_group_criterion.gender.type,\n campaign.name,\n ad_group.name,\n ad_group_criterion.system_serving_status,\n ad_group_criterion.bid_modifier,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n campaign.advertising_channel_type\nFROM gender_view\nWHERE segments.date DURING LAST_7_DAYS\n```\n\nExample:\n```text\ncurl -f --request POST \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:searchStream\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data '{\n\"query\": \"\n SELECT ad_group_criterion.gender.type,\n campaign.name,\n ad_group.name,\n ad_group_criterion.system_serving_status,\n ad_group_criterion.bid_modifier,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros,\n campaign.advertising_channel_type\n FROM gender_view\n WHERE segments.date DURING LAST_7_DAYS\n\"\n}'\n```\n\nExample:\n```text\nSELECT campaign_criterion.location.geo_target_constant,\n campaign.name,\n campaign_criterion.bid_modifier,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros\nFROM location_view\nWHERE segments.date DURING LAST_7_DAYS\n AND campaign_criterion.status != 'REMOVED'\n```\n\nExample:\n```text\ncurl -f --request POST \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:searchStream\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data '{\n\"query\": \"\n SELECT campaign_criterion.location.geo_target_constant,\n campaign.name,\n campaign_criterion.bid_modifier,\n metrics.clicks,\n metrics.impressions,\n metrics.ctr,\n metrics.average_cpc,\n metrics.cost_micros\n FROM location_view\n WHERE segments.date DURING LAST_7_DAYS\n AND campaign_criterion.status != 'REMOVED'\n\"\n}'\n```\n\nExample:\n```text\nSELECT geo_target_constant.canonical_name,\n geo_target_constant.country_code,\n geo_target_constant.id,\n geo_target_constant.name,\n geo_target_constant.status,\n geo_target_constant.target_type\nFROM geo_target_constant\nWHERE geo_target_constant.resource_name = 'geoTargetConstants/1014044'\n```\n\nExample:\n```text\ncurl -f --request POST \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:searchStream\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data \"{\n\\\"query\\\": \\\"\n SELECT geo_target_constant.canonical_name,\n geo_target_constant.country_code,\n geo_target_constant.id,\n geo_target_constant.name,\n geo_target_constant.status,\n geo_target_constant.target_type\n FROM geo_target_constant\n WHERE geo_target_constant.resource_name = 'geoTargetConstants/1014044'\n\\\"\n}\"\n```\n\nExample:\n```text\nSELECT geo_target_constant.canonical_name,\n geo_target_constant.country_code,\n geo_target_constant.id,\n geo_target_constant.name,\n geo_target_constant.status,\n geo_target_constant.target_type\nFROM geo_target_constant\nWHERE geo_target_constant.country_code = 'US'\n AND geo_target_constant.target_type = 'City'\n AND geo_target_constant.name = 'Mountain View'\n AND geo_target_constant.status = 'ENABLED'\n```\n\nExample:\n```text\ncurl -f --request POST \"https://googleads.googleapis.com/v${API_VERSION}/customers/${CUSTOMER_ID}/googleAds:searchStream\" \\\n--header \"Content-Type: application/json\" \\\n--header \"developer-token: ${DEVELOPER_TOKEN}\" \\\n--header \"login-customer-id: ${MANAGER_CUSTOMER_ID}\" \\\n--header \"Authorization: Bearer ${OAUTH2_ACCESS_TOKEN}\" \\\n--data \"{\n\\\"query\\\": \\\"\n SELECT geo_target_constant.canonical_name,\n geo_target_constant.country_code,\n geo_target_constant.id,\n geo_target_constant.name,\n geo_target_constant.status,\n geo_target_constant.target_type\n FROM geo_target_constant\n WHERE geo_target_constant.country_code = 'US'\n AND geo_target_constant.target_type = 'City'\n AND geo_target_constant.name = 'Mountain View'\n AND geo_target_constant.status = 'ENABLED'\n\\\"\n}\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.595Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":22,"totalLines":472,"estimatedTokens":3211}}242{"id":"doc-understand_api_errors_google_ads_api_google_for_-aa6c40c9","source":"documentation","title":"Understand API errors | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/best-practices/understand-api-errors","text":"Example:\n```text\n{\n \"code\": 3,\n \"message\": \"The request was invalid.\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.ads.googleads.v24.errors.GoogleAdsFailure\",\n \"errors\": [\n {\n \"errorCode\": {\n \"fieldError\": \"REQUIRED\"\n },\n \"message\": \"The required field was not present.\",\n \"location\": {\n \"fieldPathElements\": [\n { \"fieldName\": \"operations\" },\n { \"fieldName\": \"create\" },\n { \"fieldName\": \"name\" }\n ]\n }\n },\n {\n \"errorCode\": {\n \"stringLengthError\": \"TOO_SHORT\"\n },\n \"message\": \"The provided string is too short.\",\n \"trigger\": {\n \"stringValue\": \"\"\n },\n \"location\": {\n \"fieldPathElements\": [\n { \"fieldName\": \"operations\" },\n { \"fieldName\": \"create\" },\n { \"fieldName\": \"description\" }\n ]\n }\n }\n ]\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.601Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":45,"estimatedTokens":261}}243{"id":"doc-troubleshooting_google_ads_api_google_for_develo-9f81a67f","source":"documentation","title":"Troubleshooting | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/best-practices/troubleshooting","text":"Example:\n```text\n{\n \"error\": {\n \"code\": 401,\n \"message\": \"Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. Visit https://developers.google.com/identity/sign-in/web/devconsole-project.\",\n \"status\": \"UNAUTHENTICATED\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.DebugInfo\",\n \"detail\": \"Authentication error: 2\"\n }\n ]\n }\n}\n```\n\nExample:\n```text\n{\n \"errors\": [\n {\n \"errorCode\": { \"fieldMaskError\": \"FIELD_NOT_FOUND\" },\n \"message\": \"The field mask contained an invalid field: 'keyword/matchtype'.\",\n \"location\": { \"operationIndex\": \"1\" }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"errors\": [\n {\n \"errorCode\": {\"criterionError\": \"CANNOT_ADD_CRITERIA_TYPE\"},\n \"message\": \"Criteria type can not be targeted.\",\n \"trigger\": { \"stringValue\": \"\" },\n \"location\": {\n \"operationIndex\": \"0\",\n \"fieldPathElements\": [ { \"fieldName\": \"keyword\" } ]\n }\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.603Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":48,"estimatedTokens":265}}244{"id":"doc-import_store_sales_conversions_google_ads_api_go-3860a40d","source":"documentation","title":"Import store sales conversions | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/conversions/upload-store-sales-transactions","text":"Example:\n```text\n// Copyright 2020 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.remarketing;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.Consent;\nimport com.google.ads.googleads.v25.common.OfflineUserAddressInfo;\nimport com.google.ads.googleads.v25.common.StoreSalesMetadata;\nimport com.google.ads.googleads.v25.common.StoreSalesThirdPartyMetadata;\nimport com.google.ads.googleads.v25.common.TransactionAttribute;\nimport com.google.ads.googleads.v25.common.UserData;\nimport com.google.ads.googleads.v25.common.UserIdentifier;\nimport com.google.ads.googleads.v25.enums.ConsentStatusEnum.ConsentStatus;\nimport com.google.ads.googleads.v25.enums.OfflineUserDataJobStatusEnum.OfflineUserDataJobStatus;\nimport com.google.ads.googleads.v25.enums.OfflineUserDataJobTypeEnum.OfflineUserDataJobType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.errors.GoogleAdsFailure;\nimport com.google.ads.googleads.v25.resources.OfflineUserDataJob;\nimport com.google.ads.googleads.v25.services.AddOfflineUserDataJobOperationsRequest;\nimport com.google.ads.googleads.v25.services.AddOfflineUserDataJobOperationsResponse;\nimport com.google.ads.googleads.v25.services.CreateOfflineUserDataJobResponse;\nimport com.google.ads.googleads.v25.services.GoogleAdsRow;\nimport com.google.ads.googleads.v25.services.GoogleAdsServiceClient;\nimport com.google.ads.googleads.v25.services.OfflineUserDataJobOperation;\nimport com.google.ads.googleads.v25.services.OfflineUserDataJobServiceClient;\nimport com.google.ads.googleads.v25.utils.ErrorUtils;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeoutException;\n\n/**\n * Uploads offline data for store sales transactions.\n *\n * <p>This feature is only available to allowlisted accounts. See\n * https://support.google.com/google-ads/answer/7620302 for more details.\n */\npublic class UploadStoreSalesTransactions {\n\n private static class UploadStoreSalesTransactionsParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(\n names = ArgumentNames.OFFLINE_USER_DATA_JOB_TYPE,\n required = false,\n description =\n \"The type of user data in the job (first or third party). If you have an official\"\n + \" store sales partnership with Google, use STORE_SALES_UPLOAD_THIRD_PARTY.\"\n + \" Otherwise, use STORE_SALES_UPLOAD_FIRST_PARTY or omit this parameter.\")\n private OfflineUserDataJobType offlineUserDataJobType =\n OfflineUserDataJobType.STORE_SALES_UPLOAD_FIRST_PARTY;\n\n @Parameter(\n names = ArgumentNames.EXTERNAL_ID,\n description =\n \"Optional (but recommended) external ID to identify the offline user data job\")\n private Long externalId;\n\n @Parameter(\n names = ArgumentNames.CONVERSION_ACTION_ID,\n required = true,\n description = \"The ID of a store sales conversion action\")\n private Long conversionActionId;\n\n @Parameter(\n names = ArgumentNames.CUSTOM_KEY,\n required = false,\n description =\n \"Only required after creating a custom key and custom values in the account.\"\n + \" Custom key and values are used to segment store sales conversions.\"\n + \" This measurement can be used to provide more advanced insights.\")\n private String customKey;\n\n @Parameter(\n names = ArgumentNames.ADVERTISER_UPLOAD_DATE_TIME,\n description = \"Only required if uploading third party data\")\n private String advertiserUploadDateTime;\n\n @Parameter(\n names = ArgumentNames.BRIDGE_MAP_VERSION_ID,\n description = \"Only required if uploading third party data\")\n private String bridgeMapVersionId;\n\n @Parameter(\n names = ArgumentNames.PARTNER_ID,\n description = \"Only required if uploading third party data\")\n private Long partnerId;\n\n @Parameter(\n names = ArgumentNames.ITEM_ID,\n description =\n \"Specify a unique identifier of a product, either the Merchant Center Item ID or\"\n + \" Global Trade Item Number (GTIN). Only required if uploading with item\"\n + \" attributes.\")\n private String itemId;\n\n @Parameter(\n names = ArgumentNames.MERCHANT_CENTER_ACCOUNT_ID,\n description =\n \"A Merchant Center Account ID. Only required if uploading with item attributes.\")\n private Long merchantCenterAccountId;\n\n @Parameter(\n names = ArgumentNames.COUNTRY_CODE,\n description =\n \"A two-letter country code of the location associated with the feed where your items\"\n + \" are uploaded. Only required if uploading with item attributes. For a list of\"\n + \" country codes see the country codes here:\"\n + \" https://developers.google.com/google-ads/api/reference/data/codes-formats#country-codes\")\n private String countryCode;\n\n @Parameter(\n names = ArgumentNames.LANGUAGE_CODE,\n description =\n \"A two-letter language code of the language associated with the feed where your items\"\n + \" are uploaded. Only required if uploading with item attributes. For a list of\"\n + \" language codes see:\"\n + \" https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\")\n private String languageCode;\n\n @Parameter(\n names = ArgumentNames.QUANTITY,\n description =\n \"The number of items sold. Can only be set when at least one other item attribute has\"\n + \" been provided. Only required if uploading with item attributes.\")\n private int quantity;\n\n @Parameter(names = ArgumentNames.AD_PERSONALIZATION_CONSENT, required = false)\n private ConsentStatus adPersonalizationConsent;\n\n @Parameter(names = ArgumentNames.AD_USER_DATA_CONSENT, required = false)\n private ConsentStatus adUserDataConsent;\n }\n\n /** Specifies the value to use if uploading data with custom key and values. */\n private static final String CUSTOM_VALUE = null;\n\n public static void main(String[] args)\n throws InterruptedException,\n ExecutionException,\n TimeoutException,\n UnsupportedEncodingException {\n UploadStoreSalesTransactionsParams params = new UploadStoreSalesTransactionsParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.offlineUserDataJobType =\n OfflineUserDataJobType.valueOf(\"INSERT_OFFLINE_USER_DATA_JOB_TYPE_HERE\");\n params.conversionActionId = Long.parseLong(\"INSERT_CONVERSION_ACTION_ID_HERE\");\n // OPTIONAL (but recommended): Specify an external ID for the job.\n // params.externalId = Long.parseLong(\"INSERT_EXTERNAL_ID_HERE\");\n\n // OPTIONAL: specify the ad user data consent.\n // params.adUserDataConsent = ConsentStatus.valueOf(\"INSERT_AD_USER_DATA_CONSENT_HERE\");\n\n // OPTIONAL: If uploading data with custom key and values, also specify the following value:\n // params.customKey = \"INSERT_CUSTOM_KEY_HERE\";\n\n // OPTIONAL: If uploading third party data, also specify the following values:\n // params.advertiserUploadDateTime = \"INSERT_ADVERTISER_UPLOAD_DATE_TIME_HERE\";\n // params.bridgeMapVersionId = \"INSERT_BRIDGE_MAP_VERSION_ID_HERE\";\n // params.partnerId = Long.parseLong(\"INSERT_PARTNER_ID_HERE\");\n\n // OPTIONAL: Specify a unique identifier of a product, either the Merchant Center\n // Item ID or Global Trade Item Number (GTIN). Only required if uploading with\n // item attributes.\n // params.itemId = Long.parseLong(\"INSERT_ITEM_ID_HERE\");\n\n // OPTIONAL: Specify a Merchant Center Account ID. Only required if uploading\n // with item attributes.\n // params.merchantCenterAccountId = Long.parseLong(\"INSERT_MERCHANT_CENTER_ID_HERE\");\n\n // OPTIONAL: Specify a two-letter country code of the location associated with the\n // feed where your items are uploaded. Only required if uploading with item\n // attributes.\n // params.countryCode = \"INSERT_COUNTRY_CODE_HERE\";\n\n // OPTIONAL: Specify a two-letter language code of the language associated with\n // the feed where your items are uploaded. Only required if uploading with item\n // attributes.\n // params.languageCode = \"INSERT_LANGUAGE_CODE_HERE\";\n\n // OPTIONAL: Specify a number of items sold. Only required if uploading with item\n // attributes.\n // params.quantity = 1;\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new UploadStoreSalesTransactions()\n .runExample(\n googleAdsClient,\n params.customerId,\n params.offlineUserDataJobType,\n params.externalId,\n params.conversionActionId,\n params.adPersonalizationConsent,\n params.adUserDataConsent,\n params.customKey,\n params.advertiserUploadDateTime,\n params.bridgeMapVersionId,\n params.partnerId,\n params.itemId,\n params.merchantCenterAccountId,\n params.countryCode,\n params.languageCode,\n params.quantity);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param offlineUserDataJobType the type of offline user data in the job (first party or third\n * party). If you have an official store sales partnership with Google, use {@code\n * STORE_SALES_UPLOAD_THIRD_PARTY}. Otherwise, use {@code STORE_SALES_UPLOAD_FIRST_PARTY}.\n * @param externalId optional (but recommended) external ID for the offline user data job.\n * @param conversionActionId the ID of a store sales conversion action.\n * @param adPersonalizationConsent the ad personalization consent status.\n * @param adUserDataConsent the ad user data consent status.\n * @param customKey to segment store sales conversions. Only required after creating a custom key\n * and custom values in the account.\n * @param advertiserUploadDateTime date and time the advertiser uploaded data to the partner. Only\n * required for third party uploads.\n * @param bridgeMapVersionId version of partner IDs to be used for uploads. Only required for\n * third party uploads.\n * @param partnerId ID of the third party partner. Only required for third party uploads.\n * @param itemId the ID of the item in merchant center (optional).\n * @param merchantCenterAccountId the ID of the merchant center account (optional).\n * @param countryCode the country code of the item for sale in merchant center.\n * @param languageCode the language of the item for sale in merchant center.\n * @param quantity the number of items that we sold.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n OfflineUserDataJobType offlineUserDataJobType,\n Long externalId,\n long conversionActionId,\n ConsentStatus adPersonalizationConsent,\n ConsentStatus adUserDataConsent,\n String customKey,\n String advertiserUploadDateTime,\n String bridgeMapVersionId,\n Long partnerId,\n String itemId,\n Long merchantCenterAccountId,\n String countryCode,\n String languageCode,\n int quantity)\n throws InterruptedException,\n ExecutionException,\n TimeoutException,\n UnsupportedEncodingException {\n String offlineUserDataJobResourceName;\n try (OfflineUserDataJobServiceClient offlineUserDataJobServiceClient =\n googleAdsClient.getLatestVersion().createOfflineUserDataJobServiceClient()) {\n // Creates an offline user data job for uploading transactions.\n offlineUserDataJobResourceName =\n createOfflineUserDataJob(\n offlineUserDataJobServiceClient,\n customerId,\n offlineUserDataJobType,\n externalId,\n customKey,\n advertiserUploadDateTime,\n bridgeMapVersionId,\n partnerId);\n\n // Adds transactions to the job.\n addTransactionsToOfflineUserDataJob(\n offlineUserDataJobServiceClient,\n customerId,\n offlineUserDataJobResourceName,\n conversionActionId,\n adPersonalizationConsent,\n adUserDataConsent,\n customKey,\n itemId,\n merchantCenterAccountId,\n countryCode,\n languageCode,\n quantity);\n\n // Issues an asynchronous request to run the offline user data job.\n offlineUserDataJobServiceClient.runOfflineUserDataJobAsync(offlineUserDataJobResourceName);\n\n // BEWARE! The above call returns an OperationFuture. The execution of that future depends on\n // the thread pool which is owned by offlineUserDataJobServiceClient. If you use this future,\n // you *must* keep the service client in scope too.\n // See https://developers.google.com/google-ads/api/docs/client-libs/java/lro for more detail.\n\n System.out.printf(\n \"Sent request to asynchronously run offline user data job: %s%n\",\n offlineUserDataJobResourceName);\n }\n\n // Offline user data jobs may take up to 24 hours to complete, so instead of waiting for the job\n // to complete, retrieves and displays the job status once and then prints the query to use to\n // check the job again later.\n checkJobStatus(googleAdsClient, customerId, offlineUserDataJobResourceName);\n }\n\n /**\n * Creates an offline user data job for uploading store sales transactions.\n *\n * @return the resource name of the created job.\n */\n private String createOfflineUserDataJob(\n OfflineUserDataJobServiceClient offlineUserDataJobServiceClient,\n long customerId,\n OfflineUserDataJobType offlineUserDataJobType,\n Long externalId,\n String customKey,\n String advertiserUploadDateTime,\n String bridgeMapVersionId,\n Long partnerId) {\n // TIP: If you are migrating from the AdWords API, please note that Google Ads API uses the\n // term \"fraction\" instead of \"rate\". For example, loyaltyRate in the AdWords API is called\n // loyaltyFraction in the Google Ads API.\n StoreSalesMetadata.Builder storeSalesMetadataBuilder =\n // Please refer to https://support.google.com/google-ads/answer/7506124 for additional\n // details.\n StoreSalesMetadata.newBuilder()\n // Sets the fraction of your overall sales that you (or the advertiser, in the third\n // party case) can associate with a customer (email, phone number, address, etc.) in\n // your database or loyalty program.\n // For example, set this to 0.7 if you have 100 transactions over 30 days, and out of\n // those 100 transactions, you can identify 70 by an email address or phone number.\n .setLoyaltyFraction(0.7)\n // Sets the fraction of sales you're uploading out of the overall sales that you (or the\n // advertiser, in the third party case) can associate with a customer. In most cases,\n // you will set this to 1.0.\n // Continuing the example above for loyalty fraction, a value of 1.0 here indicates that\n // you are uploading all 70 of the transactions that can be identified by an email\n // address or phone number.\n .setTransactionUploadFraction(1.0);\n\n if (customKey != null && !customKey.isEmpty()) {\n storeSalesMetadataBuilder.setCustomKey(customKey);\n }\n\n if (OfflineUserDataJobType.STORE_SALES_UPLOAD_THIRD_PARTY == offlineUserDataJobType) {\n // Creates additional metadata required for uploading third party data.\n StoreSalesThirdPartyMetadata storeSalesThirdPartyMetadata =\n StoreSalesThirdPartyMetadata.newBuilder()\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n .setAdvertiserUploadDateTime(advertiserUploadDateTime)\n\n // Sets the fraction of transactions you received from the advertiser that have valid\n // formatting and values. This captures any transactions the advertiser provided to\n // you but which you are unable to upload to Google due to formatting errors or\n // missing data.\n // In most cases, you will set this to 1.0.\n .setValidTransactionFraction(1.0)\n // Sets the fraction of valid transactions (as defined above) you received from the\n // advertiser that you (the third party) have matched to an external user ID on your\n // side.\n // In most cases, you will set this to 1.0.\n .setPartnerMatchFraction(1.0)\n\n // Sets the fraction of transactions you (the third party) are uploading out of the\n // transactions you received from the advertiser that meet both of the following\n // criteria:\n // 1. Are valid in terms of formatting and values. See valid transaction fraction\n // above.\n // 2. You matched to an external user ID on your side. See partner match fraction\n // above.\n // In most cases, you will set this to 1.0.\n .setPartnerUploadFraction(1.0)\n\n // Please speak with your Google representative to get the values to use for the\n // bridge map version and partner IDs.\n\n // Sets the version of partner IDs to be used for uploads.\n .setBridgeMapVersionId(bridgeMapVersionId)\n // Sets the third party partner ID uploading the transactions.\n .setPartnerId(partnerId)\n .build();\n storeSalesMetadataBuilder.setThirdPartyMetadata(storeSalesThirdPartyMetadata);\n }\n\n // Creates a new offline user data job.\n OfflineUserDataJob.Builder offlineUserDataJobBuilder =\n OfflineUserDataJob.newBuilder()\n .setType(offlineUserDataJobType)\n .setStoreSalesMetadata(storeSalesMetadataBuilder);\n if (externalId != null) {\n offlineUserDataJobBuilder.setExternalId(externalId);\n }\n\n // Issues a request to create the offline user data job.\n CreateOfflineUserDataJobResponse createOfflineUserDataJobResponse =\n offlineUserDataJobServiceClient.createOfflineUserDataJob(\n Long.toString(customerId), offlineUserDataJobBuilder.build());\n String offlineUserDataJobResourceName = createOfflineUserDataJobResponse.getResourceName();\n System.out.printf(\n \"Created an offline user data job with resource name: %s.%n\",\n offlineUserDataJobResourceName);\n return offlineUserDataJobResourceName;\n }\n\n /** Adds operations to the job for a set of sample transactions. */\n private void addTransactionsToOfflineUserDataJob(\n OfflineUserDataJobServiceClient offlineUserDataJobServiceClient,\n long customerId,\n String offlineUserDataJobResourceName,\n long conversionActionId,\n ConsentStatus adPersonalizationConsent,\n ConsentStatus adUserDataConsent,\n String customKey,\n String itemId,\n Long merchantId,\n String countryCode,\n String languageCode,\n Integer quantity)\n throws InterruptedException,\n ExecutionException,\n TimeoutException,\n UnsupportedEncodingException {\n // Constructs the operation for each transaction.\n List<OfflineUserDataJobOperation> userDataJobOperations =\n buildOfflineUserDataJobOperations(\n customerId,\n conversionActionId,\n adPersonalizationConsent,\n adUserDataConsent,\n customKey,\n itemId,\n merchantId,\n countryCode,\n languageCode,\n quantity);\n\n // Issues a request to add the operations to the offline user data job.\n AddOfflineUserDataJobOperationsResponse response =\n offlineUserDataJobServiceClient.addOfflineUserDataJobOperations(\n AddOfflineUserDataJobOperationsRequest.newBuilder()\n .setResourceName(offlineUserDataJobResourceName)\n .setEnablePartialFailure(true)\n // Enables warnings (optional).\n .setEnableWarnings(true)\n .addAllOperations(userDataJobOperations)\n .build());\n\n // Prints the status message if any partial failure error is returned.\n // NOTE: The details of each partial failure error are not printed here, you can refer to\n // the example HandlePartialFailure.java to learn more.\n if (response.hasPartialFailureError()) {\n GoogleAdsFailure googleAdsFailure =\n ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError());\n googleAdsFailure\n .getErrorsList()\n .forEach(e -> System.out.println(\"Partial failure occurred: \" + e.getMessage()));\n System.out.printf(\n \"Encountered %d partial failure errors while adding %d operations to the offline user \"\n + \"data job: '%s'. Only the successfully added operations will be executed when \"\n + \"the job runs.%n\",\n ErrorUtils.getInstance().getFailedOperationIndices(googleAdsFailure).size(),\n userDataJobOperations.size(),\n response.getPartialFailureError().getMessage());\n\n // Checks if any warnings occurred and displays details.\n if (response.hasWarning()) {\n // Converts the Any in response back to a GoogleAdsFailure object.\n GoogleAdsFailure warningsFailure =\n ErrorUtils.getInstance().getGoogleAdsFailure(response.getWarning());\n // Prints some information about the warnings encountered.\n System.out.println(\n System.out.printf(\"Encountered %d warning(s).%n\", warningsFailure.getErrorsCount()));\n }\n } else {\n System.out.printf(\n \"Successfully added %d operations to the offline user data job.%n\",\n userDataJobOperations.size());\n }\n }\n\n /**\n * Creates a list of offline user data job operations for sample transactions.\n *\n * @return a list of operations.\n */\n private List<OfflineUserDataJobOperation> buildOfflineUserDataJobOperations(\n long customerId,\n long conversionActionId,\n ConsentStatus adPersonalizationConsent,\n ConsentStatus adUserDataConsent,\n String customKey,\n String itemId,\n Long merchantId,\n String countryCode,\n String languageCode,\n Integer quantity)\n throws UnsupportedEncodingException {\n MessageDigest sha256Digest;\n try {\n // Gets a digest for generating hashed values using SHA-256. You must normalize and hash the\n // the value for any field where the name begins with \"hashed\". See the normalizeAndHash()\n // method.\n sha256Digest = MessageDigest.getInstance(\"SHA-256\");\n } catch (NoSuchAlgorithmException e) {\n throw new RuntimeException(\"Missing SHA-256 algorithm implementation\", e);\n }\n\n // Create the first transaction for upload based on an email address and state.\n UserData.Builder userDataWithEmailAddress =\n UserData.newBuilder()\n .addAllUserIdentifiers(\n ImmutableList.of(\n UserIdentifier.newBuilder()\n .setHashedEmail(\n // Email addresses must be normalized and hashed.\n normalizeAndHash(sha256Digest, \"dana@example.com\"))\n .build(),\n UserIdentifier.newBuilder()\n .setAddressInfo(OfflineUserAddressInfo.newBuilder().setState(\"NY\"))\n .build()))\n .setTransactionAttribute(\n TransactionAttribute.newBuilder()\n .setConversionAction(\n ResourceNames.conversionAction(customerId, conversionActionId))\n .setCurrencyCode(\"USD\")\n // Converts the transaction amount from $200 USD to micros.\n .setTransactionAmountMicros(200L * 1_000_000L)\n // Specifies the date and time of the transaction. The format is\n // \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional\n // timezone offset from UTC. If the offset is absent, the API will\n // use the account's timezone as default. Examples: \"2018-03-05 09:15:00\"\n // or \"2018-02-01 14:34:30+03:00\".\n .setTransactionDateTime(\"2020-05-01 23:52:12\"));\n\n // Adds consent information if specified.\n if (adPersonalizationConsent != null || adUserDataConsent != null) {\n Consent.Builder consentBuilder = Consent.newBuilder();\n if (adPersonalizationConsent != null) {\n consentBuilder.setAdPersonalization(adPersonalizationConsent);\n }\n if (adUserDataConsent != null) {\n consentBuilder.setAdUserData(adUserDataConsent);\n }\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy for details.\n userDataWithEmailAddress.setConsent(consentBuilder);\n }\n\n // Optional: If uploading data with custom key and values, also assign the custom value.\n if (customKey != null) {\n userDataWithEmailAddress.getTransactionAttributeBuilder().setCustomValue(CUSTOM_VALUE);\n }\n\n // Creates the second transaction for upload based on a physical address.\n UserData.Builder userDataWithPhysicalAddress =\n UserData.newBuilder()\n .addUserIdentifiers(\n UserIdentifier.newBuilder()\n .setAddressInfo(\n OfflineUserAddressInfo.newBuilder()\n .setHashedFirstName(normalizeAndHash(sha256Digest, \"Dana\"))\n .setHashedLastName(normalizeAndHash(sha256Digest, \"Quinn\"))\n .setCountryCode(\"US\")\n .setPostalCode(\"10011\")))\n .setTransactionAttribute(\n TransactionAttribute.newBuilder()\n .setConversionAction(\n ResourceNames.conversionAction(customerId, conversionActionId))\n .setCurrencyCode(\"EUR\")\n // Converts the transaction amount from 450 EUR to micros.\n .setTransactionAmountMicros(450L * 1_000_000L)\n // Specifies the date and time of the transaction. This date and time will be\n // interpreted by the API using the Google Ads customer's time zone.\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n .setTransactionDateTime(\"2020-05-14 19:07:02\"));\n\n if (itemId != null) {\n userDataWithPhysicalAddress\n .getTransactionAttributeBuilder()\n .getItemAttributeBuilder()\n .setItemId(itemId)\n .setMerchantId(merchantId)\n .setCountryCode(countryCode)\n .setLanguageCode(languageCode)\n .setQuantity(quantity);\n }\n\n // Creates the operations to add the two transactions.\n List<OfflineUserDataJobOperation> operations = new ArrayList<>();\n for (UserData userData :\n Arrays.asList(userDataWithEmailAddress.build(), userDataWithPhysicalAddress.build())) {\n operations.add(OfflineUserDataJobOperation.newBuilder().setCreate(userData).build());\n }\n\n return operations;\n }\n\n /**\n * Returns the result of normalizing and then hashing the string using the provided digest.\n * Private customer data must be hashed during upload, as described at\n * https://support.google.com/google-ads/answer/7506124.\n *\n * @param digest the digest to use to hash the normalized string.\n * @param s the string to normalize and hash.\n */\n private String normalizeAndHash(MessageDigest digest, String s)\n throws UnsupportedEncodingException {\n // Normalizes by removing leading and trailing whitespace and converting all characters to\n // lower case.\n String normalized = s.trim().toLowerCase();\n // Hashes the normalized string using the hashing algorithm.\n byte[] hash = digest.digest(normalized.getBytes(\"UTF-8\"));\n StringBuilder result = new StringBuilder();\n for (byte b : hash) {\n result.append(String.format(\"%02x\", b));\n }\n\n return result.toString();\n }\n\n /** Retrieves, checks, and prints the status of the offline user data job. */\n private void checkJobStatus(\n GoogleAdsClient googleAdsClient, long customerId, String offlineUserDataJobResourceName) {\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n String query =\n String.format(\n \"SELECT offline_user_data_job.resource_name, \"\n + \"offline_user_data_job.id, \"\n + \"offline_user_data_job.status, \"\n + \"offline_user_data_job.type, \"\n + \"offline_user_data_job.failure_reason \"\n + \"FROM offline_user_data_job \"\n + \"WHERE offline_user_data_job.resource_name = '%s'\",\n offlineUserDataJobResourceName);\n // Issues the query and gets the GoogleAdsRow containing the job from the response.\n GoogleAdsRow googleAdsRow =\n googleAdsServiceClient\n .search(Long.toString(customerId), query)\n .iterateAll()\n .iterator()\n .next();\n OfflineUserDataJob offlineUserDataJob = googleAdsRow.getOfflineUserDataJob();\n System.out.printf(\n \"Offline user data job ID %d with type '%s' has status: %s%n\",\n offlineUserDataJob.getId(), offlineUserDataJob.getType(), offlineUserDataJob.getStatus());\n OfflineUserDataJobStatus jobStatus = offlineUserDataJob.getStatus();\n if (OfflineUserDataJobStatus.FAILED == jobStatus) {\n System.out.printf(\" Failure reason: %s%n\", offlineUserDataJob.getFailureReason());\n } else if (OfflineUserDataJobStatus.PENDING == jobStatus\n || OfflineUserDataJobStatus.RUNNING == jobStatus) {\n System.out.println();\n System.out.printf(\n \"To check the status of the job periodically, use the following GAQL query with\"\n + \" GoogleAdsService.search:%n%s%n\",\n query);\n }\n }\n }\n}\nUploadStoreSalesTransactions.java\n```\n\nExample:\n```text\n// Copyright 2020 Google LLC\n//\n// 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// http://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.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Security.Cryptography;\nusing System.Text;\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing static Google.Ads.GoogleAds.V25.Enums.ConsentStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.OfflineUserDataJobStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.OfflineUserDataJobTypeEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example uploads offline data for store sales transactions.\n /// This feature is only available to allowlisted accounts. See\n /// https://support.google.com/google-ads/answer/7620302 for more details.\n /// </summary>\n public class UploadStoreSalesTransactions : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"UploadStoreSalesTransactions\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// The ID of a store sales conversion action.\n /// </summary>\n [Option(\"conversionActionId\", Required = true, HelpText =\n \"The ID of a store sales conversion action.\")]\n public long ConversionActionId { get; set; }\n\n /// <summary>\n /// The type of user data in the job (first or third party). If you have an official\n /// store sales partnership with Google, use StoreSalesUploadThirdParty. Otherwise,\n /// use StoreSalesUploadFirstParty or omit this parameter.\n /// </summary>\n [Option(\"offlineUserDataJobType\", Required = false, HelpText =\n \"The type of user data in the job (first or third party). If you have an\" +\n \" official store sales partnership with Google, use \" +\n \"StoreSalesUploadThirdParty. Otherwise, use StoreSalesUploadFirstParty or \" +\n \"omit this parameter.\",\n Default = OfflineUserDataJobType.StoreSalesUploadFirstParty)]\n public OfflineUserDataJobType OfflineUserDataJobType { get; set; }\n\n /// <summary>\n /// Optional (but recommended) external ID to identify the offline user data job.\n /// </summary>\n [Option(\"externalId\", Required = false, HelpText =\n \"Optional (but recommended) external ID to identify the offline user data job.\",\n Default = null)]\n public long? ExternalId { get; set; }\n\n /// <summary>\n /// Date and time the advertiser uploaded data to the partner. Only required if\n /// uploading third party data.\n /// </summary>\n [Option(\"advertiserUploadDateTime\", Required = false, HelpText =\n \"Date and time the advertiser uploaded data to the partner. Only required if \" +\n \"uploading third party data.\", Default = null)]\n public string AdvertiserUploadDateTime { get; set; }\n\n /// <summary>\n /// Version of partner IDs to be used for uploads. Only required if uploading third\n /// party data.\n /// </summary>\n [Option(\"bridgeMapVersionId\", Required = false, HelpText =\n \"Version of partner IDs to be used for uploads. Only required if uploading \" +\n \"third party data.\", Default = null)]\n public string BridgeMapVersionId { get; set; }\n\n /// <summary>\n /// ID of the third party partner. Only required if uploading third party data.\n /// </summary>\n [Option(\"partnerId\", Required = false, HelpText =\n \"ID of the third party partner. Only required if uploading third party data.\",\n Default = null)]\n public long? PartnerId { get; set; }\n\n /// <summary>\n /// Optional custom key name. Only required if uploading data with custom key and\n /// values.\n /// </summary>\n [Option(\"customKey\", Required = false, HelpText =\n \"Optional custom key name. Only required if uploading data with custom key and\" +\n \" values.\", Default = null)]\n public string CustomKey { get; set; }\n\n /// <summary>\n /// A unique identifier of a product, either the Merchant Center Item ID or Global Trade\n /// Item Number (GTIN). Only required if uploading with item attributes.\n /// </summary>\n [Option(\"itemId\", Required = false, HelpText =\n \"A unique identifier of a product, either the Merchant Center Item ID or \" +\n \"Global Trade Item Number (GTIN). Only required if uploading with item \" +\n \"attributes.\",\n Default = null)]\n public string ItemId { get; set; }\n\n /// <summary>\n /// A Merchant Center Account ID. Only required if uploading with item attributes.\n /// </summary>\n [Option(\"merchantCenterAccountId\", Required = false, HelpText =\n \"A Merchant Center Account ID. Only required if uploading with item \" +\n \"attributes.\",\n Default = null)]\n public long? MerchantCenterAccountId { get; set; }\n\n /// <summary>\n /// A two-letter country code of the location associated with the feed where your items\n /// are uploaded. Only required if uploading with item attributes.\n /// For a list of country codes see:\n /// https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-16\n /// </summary>\n [Option(\"countryCode\", Required = false, HelpText =\n \"A two-letter country code of the location associated with the feed where your \" +\n \"items are uploaded. Only required if uploading with item attributes.\\nFor a \" +\n \"list of country codes see: \" +\n \"https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-16\",\n Default = null)]\n public string CountryCode { get; set; }\n\n /// <summary>\n /// A two-letter language code of the language associated with the feed where your items\n /// are uploaded. Only required if uploading with item attributes. For a list of\n /// language codes see:\n /// https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n /// </summary>\n [Option(\"languageCode\", Required = false, HelpText =\n \"A two-letter language code of the language associated with the feed where \" +\n \"your items are uploaded. Only required if uploading with item attributes.\\n\" +\n \"For a list of language codes see: \" +\n \"https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\",\n Default = null)]\n public string LanguageCode { get; set; }\n\n /// <summary>\n /// The number of items sold. Can only be set when at least one other item attribute has\n /// been provided. Only required if uploading with item attributes.\n /// </summary>\n [Option(\"quantity\", Required = false, HelpText =\n \"The number of items sold. Only required if uploading with item attributes.\",\n Default = 1)]\n public long Quantity { get; set; }\n\n /// <summary>\n /// The consent status for ad personalization.\n /// </summary>\n [Option(\"adPersonalizationConsent\", Required = false, HelpText =\n \"The consent status for ad user data.\")]\n public ConsentStatus? AdPersonalizationConsent { get; set; }\n\n /// <summary>\n /// The consent status for ad user data.\n /// </summary>\n [Option(\"adUserDataConsent\", Required = false, HelpText =\n \"The consent status for ad user data.\")]\n public ConsentStatus? AdUserDataConsent { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n UploadStoreSalesTransactions codeExample = new UploadStoreSalesTransactions();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.ConversionActionId,\n options.OfflineUserDataJobType, options.ExternalId,\n options.AdvertiserUploadDateTime, options.BridgeMapVersionId, options.PartnerId,\n options.CustomKey, options.ItemId, options.MerchantCenterAccountId,\n options.CountryCode,\n options.LanguageCode, options.Quantity, options.AdPersonalizationConsent,\n options.AdUserDataConsent);\n }\n\n // Gets a digest for generating hashed values using SHA-256. You must normalize and hash the\n // the value for any field where the name begins with \"hashed\". See the normalizeAndHash()\n // method.\n private static readonly SHA256 _digest = SHA256.Create();\n\n // If uploading data with custom key and values, specify the value:\n private const string CUSTOM_VALUE = \"INSERT_CUSTOM_VALUE_HERE\";\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example uploads offline data for store sales transactions. This feature \" +\n \"is only available to allowlisted accounts. See \" +\n \"https://support.google.com/google-ads/answer/7620302 for more details.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"conversionActionId\">The ID of a store sales conversion action.</param>\n /// <param name=\"offlineUserDataJobType\">The type of user data in the job (first or third\n /// party). If you have an official store sales partnership with Google, use\n /// StoreSalesUploadThirdParty. Otherwise, use StoreSalesUploadFirstParty or\n /// omit this parameter.</param>\n /// <param name=\"externalId\">Optional (but recommended) external ID to identify the offline\n /// user data job.</param>\n /// <param name=\"advertiserUploadDateTime\">Date and time the advertiser uploaded data to the\n /// partner. Only required if uploading third party data.</param>\n /// <param name=\"bridgeMapVersionId\">Version of partner IDs to be used for uploads. Only\n /// required if uploading third party data.</param>\n /// <param name=\"partnerId\">ID of the third party partner. Only required if uploading third\n /// party data.</param>\n /// <param name=\"customKey\">Optional custom key name. Only required if uploading data\n /// with custom key and values.</param>\n /// <param name=\"itemId\">A unique identifier of a product, either the Merchant Center Item\n /// ID or Global Trade Item Number (GTIN). Only required if uploading with item\n /// attributes.</param>\n /// <param name=\"merchantCenterAccountId\">A Merchant Center Account ID. Only required if uploading with\n /// item attributes.</param>\n /// <param name=\"countryCode\">A two-letter country code of the location associated with the\n /// feed where your items are uploaded. Only required if uploading with item\n /// attributes.</param>\n /// <param name=\"languageCode\">A two-letter language code of the language associated with\n /// the feed where your items are uploaded. Only required if uploading with item\n /// attributes.</param>\n /// <param name=\"quantity\">The number of items sold. Only required if uploading with item\n /// attributes.</param>\n /// <param name=\"adPersonalizationConsent\">The consent status for ad personalization.\n /// </param>\n /// <param name=\"adUserDataConsent\">The consent status for ad user data.</param>\n public void Run(GoogleAdsClient client, long customerId, long conversionActionId,\n OfflineUserDataJobType offlineUserDataJobType, long? externalId,\n string advertiserUploadDateTime, string bridgeMapVersionId, long? partnerId,\n string customKey, string itemId, long? merchantCenterAccountId, string countryCode,\n string languageCode, long quantity, ConsentStatus? adPersonalizationConsent,\n ConsentStatus? adUserDataConsent)\n {\n // Get the OfflineUserDataJobServiceClient.\n OfflineUserDataJobServiceClient offlineUserDataJobServiceClient =\n client.GetService(Services.V25.OfflineUserDataJobService);\n\n // Ensure that a valid job type is provided.\n if (offlineUserDataJobType != OfflineUserDataJobType.StoreSalesUploadFirstParty &\n offlineUserDataJobType != OfflineUserDataJobType.StoreSalesUploadThirdParty)\n {\n Console.WriteLine(\"Invalid job type specified, defaulting to First Party.\");\n offlineUserDataJobType = OfflineUserDataJobType.StoreSalesUploadFirstParty;\n }\n\n try\n {\n // Creates an offline user data job for uploading transactions.\n string offlineUserDataJobResourceName =\n CreateOfflineUserDataJob(offlineUserDataJobServiceClient, customerId,\n offlineUserDataJobType, externalId, advertiserUploadDateTime,\n bridgeMapVersionId, partnerId, customKey);\n\n // Adds transactions to the job.\n AddTransactionsToOfflineUserDataJob(offlineUserDataJobServiceClient, customerId,\n offlineUserDataJobResourceName, conversionActionId, customKey, itemId,\n merchantCenterAccountId, countryCode, languageCode, quantity,\n adPersonalizationConsent, adUserDataConsent);\n\n // Issues an asynchronous request to run the offline user data job.\n offlineUserDataJobServiceClient.RunOfflineUserDataJobAsync(\n offlineUserDataJobResourceName);\n\n Console.WriteLine(\"Sent request to asynchronously run offline user data job \" +\n $\"{offlineUserDataJobResourceName}.\");\n\n // Offline user data jobs may take up to 24 hours to complete, so instead of waiting\n // for the job to complete, retrieves and displays the job status once and then\n // prints the query to use to check the job again later.\n CheckJobStatus(client, customerId, offlineUserDataJobResourceName);\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Creates an offline user data job for uploading store sales transactions.\n /// </summary>\n /// <param name=\"offlineUserDataJobServiceClient\">The offline user data job service\n /// client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"offlineUserDataJobType\">The type of user data in the job (first or third\n /// party). If you have an official store sales partnership with Google, use\n /// StoreSalesUploadThirdParty. Otherwise, use StoreSalesUploadFirstParty or\n /// omit this parameter.</param>\n /// <param name=\"externalId\">Optional (but recommended) external ID to identify the offline\n /// user data job.</param>\n /// <param name=\"advertiserUploadDateTime\">Date and time the advertiser uploaded data to the\n /// partner. Only required if uploading third party data.</param>\n /// <param name=\"bridgeMapVersionId\">Version of partner IDs to be used for uploads. Only\n /// required if uploading third party data.</param>\n /// <param name=\"partnerId\">ID of the third party partner. Only required if uploading third\n /// party data.</param>\n /// <param name=\"customKey\">The custom key, or null if not uploading data with custom key\n /// and value.</param>\n /// <returns>The resource name of the created job.</returns>\n private string CreateOfflineUserDataJob(\n OfflineUserDataJobServiceClient offlineUserDataJobServiceClient, long customerId,\n OfflineUserDataJobType offlineUserDataJobType, long? externalId,\n string advertiserUploadDateTime, string bridgeMapVersionId, long? partnerId,\n string customKey)\n {\n // TIP: If you are migrating from the AdWords API, please note that Google Ads API uses\n // the term \"fraction\" instead of \"rate\". For example, loyaltyRate in the AdWords API is\n // called loyaltyFraction in the Google Ads API.\n\n // Please refer to https://support.google.com/google-ads/answer/7506124 for additional\n // details.\n StoreSalesMetadata storeSalesMetadata = new StoreSalesMetadata()\n {\n // Sets the fraction of your overall sales that you (or the advertiser, in the third\n // party case) can associate with a customer (email, phone number, address, etc.) in\n // your database or loyalty program.\n // For example, set this to 0.7 if you have 100 transactions over 30 days, and out\n // of those 100 transactions, you can identify 70 by an email address or phone\n // number.\n LoyaltyFraction = 0.7,\n // Sets the fraction of sales you're uploading out of the overall sales that you (or\n // the advertiser, in the third party case) can associate with a customer. In most\n // cases, you will set this to 1.0.\n // Continuing the example above for loyalty fraction, a value of 1.0 here indicates\n // that you are uploading all 70 of the transactions that can be identified by an\n // email address or phone number.\n TransactionUploadFraction = 1.0\n };\n\n // Apply the custom key if provided.\n if (!string.IsNullOrEmpty(customKey))\n {\n storeSalesMetadata.CustomKey = customKey;\n }\n\n // Creates additional metadata required for uploading third party data.\n if (offlineUserDataJobType == OfflineUserDataJobType.StoreSalesUploadThirdParty)\n {\n StoreSalesThirdPartyMetadata storeSalesThirdPartyMetadata =\n new StoreSalesThirdPartyMetadata()\n {\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n AdvertiserUploadDateTime = advertiserUploadDateTime,\n\n // Sets the fraction of transactions you received from the advertiser that\n // have valid formatting and values. This captures any transactions the\n // advertiser provided to you but which you are unable to upload to Google\n // due to formatting errors or missing data.\n // In most cases, you will set this to 1.0.\n ValidTransactionFraction = 1.0,\n\n // Sets the fraction of valid transactions (as defined above) you received\n // from the advertiser that you (the third party) have matched to an\n // external user ID on your side.\n // In most cases, you will set this to 1.0.\n PartnerMatchFraction = 1.0,\n\n // Sets the fraction of transactions you (the third party) are uploading out\n // of the transactions you received from the advertiser that meet both of\n // the following criteria:\n // 1. Are valid in terms of formatting and values. See valid transaction\n // fraction above.\n // 2. You matched to an external user ID on your side. See partner match\n // fraction above.\n // In most cases, you will set this to 1.0.\n PartnerUploadFraction = 1.0,\n\n // Sets the version of partner IDs to be used for uploads.\n // Please speak with your Google representative to get the values to use for\n // the bridge map version and partner IDs.\n BridgeMapVersionId = bridgeMapVersionId,\n };\n\n // Sets the third party partner ID uploading the transactions.\n if (partnerId.HasValue)\n {\n storeSalesThirdPartyMetadata.PartnerId = partnerId.Value;\n }\n\n storeSalesMetadata.ThirdPartyMetadata = storeSalesThirdPartyMetadata;\n }\n\n // Creates a new offline user data job.\n OfflineUserDataJob offlineUserDataJob = new OfflineUserDataJob()\n {\n Type = offlineUserDataJobType,\n StoreSalesMetadata = storeSalesMetadata\n };\n\n if (externalId.HasValue)\n {\n offlineUserDataJob.ExternalId = externalId.Value;\n }\n\n // Issues a request to create the offline user data job.\n CreateOfflineUserDataJobResponse createOfflineUserDataJobResponse =\n offlineUserDataJobServiceClient.CreateOfflineUserDataJob(\n customerId.ToString(), offlineUserDataJob);\n string offlineUserDataJobResourceName = createOfflineUserDataJobResponse.ResourceName;\n Console.WriteLine(\"Created an offline user data job with resource name: \" +\n $\"{offlineUserDataJobResourceName}.\");\n return offlineUserDataJobResourceName;\n }\n\n /// <summary>\n /// Adds operations to a job for a set of sample transactions.\n /// </summary>\n /// <param name=\"offlineUserDataJobServiceClient\">The offline user data job service\n /// client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"offlineUserDataJobResourceName\">The resource name of the job to which to\n /// add transactions.</param>\n /// <param name=\"conversionActionId\">The ID of a store sales conversion action.</param>\n /// <param name=\"customKey\">The custom key, or null if not uploading data with custom key\n /// and value.</param>\n /// <param name=\"itemId\">A unique identifier of a product, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"merchantCenterAccountId\">A Merchant Center Account ID, or null if not\n /// uploading with item attributes.</param>\n /// <param name=\"countryCode\">A two-letter country code, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"languageCode\">A two-letter language code, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"quantity\">The number of items sold, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"adPersonalizationConsent\">The consent status for ad personalization.\n /// </param>\n /// <param name=\"adUserDataConsent\">The consent status for ad user data.</param>\n private void AddTransactionsToOfflineUserDataJob(\n OfflineUserDataJobServiceClient offlineUserDataJobServiceClient, long customerId,\n string offlineUserDataJobResourceName, long conversionActionId, string customKey,\n string itemId, long? merchantCenterAccountId, string countryCode, string languageCode,\n long quantity, ConsentStatus? adPersonalizationConsent,\n ConsentStatus? adUserDataConsent)\n {\n // Constructions an operation for each transaction.\n List<OfflineUserDataJobOperation> userDataJobOperations =\n BuildOfflineUserDataJobOperations(customerId, conversionActionId, customKey, itemId,\n merchantCenterAccountId, countryCode, languageCode, quantity,\n adPersonalizationConsent, adUserDataConsent);\n\n // Constructs a request with partial failure enabled to add the operations to the\n // offline user data job, and enable_warnings set to true to retrieve warnings.\n AddOfflineUserDataJobOperationsRequest request =\n new AddOfflineUserDataJobOperationsRequest()\n {\n EnablePartialFailure = true,\n ResourceName = offlineUserDataJobResourceName,\n Operations = { userDataJobOperations },\n EnableWarnings = true,\n };\n\n AddOfflineUserDataJobOperationsResponse response = offlineUserDataJobServiceClient\n .AddOfflineUserDataJobOperations(request);\n\n // Prints the status message if any partial failure error is returned.\n // NOTE: The details of each partial failure error are not printed here, you can refer\n // to the example HandlePartialFailure.cs to learn more.\n if (response.PartialFailureError != null)\n {\n Console.WriteLine($\"Encountered {response.PartialFailureError.Details.Count} \" +\n $\"partial failure errors while adding {userDataJobOperations.Count} \" +\n \"operations to the offline user data job: \" +\n $\"'{response.PartialFailureError.Message}'. Only the successfully added \" +\n \"operations will be executed when the job runs.\");\n }\n else\n {\n Console.WriteLine($\"Successfully added {userDataJobOperations.Count} operations \" +\n \"to the offline user data job.\");\n }\n\n // Prints the number of warnings if any warnings are returned. You can access\n // details of each warning using the same approach you'd use for partial failure\n // errors.\n if (request.EnableWarnings && response.Warnings != null)\n {\n // Extracts the warnings from the response.\n GoogleAdsFailure warnings = response.Warnings;\n Console.WriteLine($\"{warnings.Errors.Count} warning(s) occurred\");\n }\n }\n\n /// <summary>\n /// Creates a list of offline user data job operations for sample transactions.\n /// </summary>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"conversionActionId\">The ID of a store sales conversion action.</param>\n /// <param name=\"customKey\">The custom key, or null if not uploading data with custom key\n /// and value.</param>\n /// <param name=\"itemId\">A unique identifier of a product, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"merchantCenterAccountId\">A Merchant Center Account ID, or null if not\n /// uploading with item attributes.</param>\n /// <param name=\"countryCode\">A two-letter country code, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"languageCode\">A two-letter language code, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"quantity\">The number of items sold, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"adPersonalizationConsent\">The consent status for ad personalization.\n /// </param>\n /// <param name=\"adUserDataConsent\">The consent status for ad user data.</param>\n /// <returns>A list of operations.</returns>\n private List<OfflineUserDataJobOperation> BuildOfflineUserDataJobOperations(long customerId,\n long conversionActionId, string customKey, string itemId, long? merchantCenterAccountId,\n string countryCode, string languageCode, long quantity,\n ConsentStatus? adPersonalizationConsent, ConsentStatus? adUserDataConsent)\n {\n // Create the first transaction for upload based on an email address and state.\n UserData userDataWithEmailAddress = new UserData()\n {\n UserIdentifiers =\n {\n new UserIdentifier()\n {\n // Email addresses must be normalized and hashed.\n HashedEmail = NormalizeAndHash(\"dana@example.com\")\n },\n new UserIdentifier()\n {\n AddressInfo = new OfflineUserAddressInfo()\n {\n State = \"NY\"\n }\n },\n },\n TransactionAttribute = new TransactionAttribute()\n {\n ConversionAction =\n ResourceNames.ConversionAction(customerId, conversionActionId),\n CurrencyCode = \"USD\",\n // Converts the transaction amount from $200 USD to micros.\n // If item attributes are provided, this value represents the total value of the\n // items after multiplying the unit price per item by the quantity provided in\n // the ItemAttribute.\n TransactionAmountMicros = 200L * 1_000_000L,\n // Specifies the date and time of the transaction. The format is\n // \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional\n // timezone offset from UTC. If the offset is absent, the API will\n // use the account's timezone as default. Examples: \"2018-03-05 09:15:00\"\n // or \"2018-02-01 14:34:30+03:00\".\n TransactionDateTime =\n DateTime.Today.AddDays(-2).ToString(\"yyyy-MM-dd HH:mm:ss\")\n }\n };\n\n // Set the custom value if a custom key was provided.\n if (!string.IsNullOrEmpty(customKey))\n {\n userDataWithEmailAddress.TransactionAttribute.CustomValue = CUSTOM_VALUE;\n }\n\n if (adUserDataConsent != null || adPersonalizationConsent != null)\n {\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy\n // for details.\n userDataWithEmailAddress.Consent = new Consent();\n\n if (adPersonalizationConsent != null)\n {\n userDataWithEmailAddress.Consent.AdPersonalization =\n (ConsentStatus)adPersonalizationConsent;\n }\n\n if (adUserDataConsent != null)\n {\n userDataWithEmailAddress.Consent.AdUserData = (ConsentStatus)adUserDataConsent;\n }\n }\n\n // Creates the second transaction for upload based on a physical address.\n UserData userDataWithPhysicalAddress = new UserData()\n {\n UserIdentifiers =\n {\n new UserIdentifier()\n {\n AddressInfo = new OfflineUserAddressInfo()\n {\n // Names must be normalized and hashed.\n HashedFirstName = NormalizeAndHash(\"Alex\"),\n HashedLastName = NormalizeAndHash(\"Quinn\"),\n CountryCode = \"US\",\n PostalCode = \"10011\"\n }\n }\n },\n TransactionAttribute = new TransactionAttribute()\n {\n ConversionAction =\n ResourceNames.ConversionAction(customerId, conversionActionId),\n CurrencyCode = \"EUR\",\n // Converts the transaction amount from 450 EUR to micros.\n TransactionAmountMicros = 450L * 1_000_000L,\n // Specifies the date and time of the transaction. This date and time will be\n // interpreted by the API using the Google Ads customer's time zone.\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n // e.g. \"2020-05-14 19:07:02\".\n TransactionDateTime = DateTime.Today.AddDays(-1).ToString(\"yyyy-MM-dd HH:mm:ss\")\n }\n };\n\n // Set the item attribute if provided.\n if (!string.IsNullOrEmpty(itemId))\n {\n userDataWithPhysicalAddress.TransactionAttribute.ItemAttribute = new ItemAttribute\n {\n ItemId = itemId,\n MerchantId = merchantCenterAccountId.Value,\n CountryCode = countryCode,\n LanguageCode = languageCode,\n // Quantity field should only be set when at least one of the other item\n // attributes is present.\n Quantity = quantity\n };\n }\n\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy\n // for details.\n userDataWithPhysicalAddress.Consent = new Consent()\n {\n AdPersonalization = ConsentStatus.Granted,\n AdUserData = ConsentStatus.Denied\n };\n\n\n // Creates the operations to add the two transactions.\n List<OfflineUserDataJobOperation> operations = new List<OfflineUserDataJobOperation>()\n {\n new OfflineUserDataJobOperation()\n {\n Create = userDataWithEmailAddress\n },\n new OfflineUserDataJobOperation()\n {\n Create = userDataWithPhysicalAddress\n }\n };\n\n return operations;\n }\n\n /// <summary>\n /// Normalizes and hashes a string value.\n /// </summary>\n /// <param name=\"value\">The value to normalize and hash.</param>\n /// <returns>The normalized and hashed value.</returns>\n private static string NormalizeAndHash(string value)\n {\n return ToSha256String(_digest, ToNormalizedValue(value));\n }\n\n /// <summary>\n /// Hash a string value using SHA-256 hashing algorithm.\n /// </summary>\n /// <param name=\"digest\">Provides the algorithm for SHA-256.</param>\n /// <param name=\"value\">The string value (e.g. an email address) to hash.</param>\n /// <returns>The hashed value.</returns>\n private static string ToSha256String(SHA256 digest, string value)\n {\n byte[] digestBytes = digest.ComputeHash(Encoding.UTF8.GetBytes(value));\n // Convert the byte array into an unhyphenated hexadecimal string.\n return BitConverter.ToString(digestBytes).Replace(\"-\", string.Empty);\n }\n\n /// <summary>\n /// Removes leading and trailing whitespace and converts all characters to\n /// lower case.\n /// </summary>\n /// <param name=\"value\">The value to normalize.</param>\n /// <returns>The normalized value.</returns>\n private static string ToNormalizedValue(string value)\n {\n return value.Trim().ToLower();\n }\n\n /// <summary>\n /// Retrieves, checks, and prints the status of the offline user data job.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"offlineUserDataJobResourceName\">The resource name of the job whose status\n /// you wish to check.</param>\n private void CheckJobStatus(GoogleAdsClient client, long customerId,\n string offlineUserDataJobResourceName)\n {\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n string query = $@\"SELECT offline_user_data_job.resource_name,\n offline_user_data_job.id,\n offline_user_data_job.status,\n offline_user_data_job.type,\n offline_user_data_job.failure_reason\n FROM offline_user_data_job\n WHERE offline_user_data_job.resource_name = '{offlineUserDataJobResourceName}'\";\n\n // Issues the query and gets the GoogleAdsRow containing the job from the response.\n GoogleAdsRow googleAdsRow = googleAdsServiceClient.Search(\n customerId.ToString(), query).First();\n\n OfflineUserDataJob offlineUserDataJob = googleAdsRow.OfflineUserDataJob;\n\n OfflineUserDataJobStatus jobStatus = offlineUserDataJob.Status;\n Console.WriteLine($\"Offline user data job ID {offlineUserDataJob.Id} with type \" +\n $\"'{offlineUserDataJob.Type}' has status {offlineUserDataJob.Status}.\");\n\n if (jobStatus == OfflineUserDataJobStatus.Failed)\n {\n Console.WriteLine($\"\\tFailure reason: {offlineUserDataJob.FailureReason}\");\n }\n else if (jobStatus == OfflineUserDataJobStatus.Pending |\n jobStatus == OfflineUserDataJobStatus.Running)\n {\n Console.WriteLine(\"\\nTo check the status of the job periodically, use the\" +\n $\"following GAQL query with GoogleAdsService.Search:\\n{query}\\n\");\n }\n }\n }\n}\nUploadStoreSalesTransactions.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2020 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\Remarketing;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsServerStreamDecorator;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\GoogleAdsFailures;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\Consent;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ItemAttribute;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\OfflineUserAddressInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\StoreSalesMetadata;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\StoreSalesThirdPartyMetadata;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\TransactionAttribute;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\UserData;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\UserIdentifier;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\ConsentStatusEnum\\ConsentStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\OfflineUserDataJobFailureReasonEnum\\OfflineUserDataJobFailureReason;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\OfflineUserDataJobStatusEnum\\OfflineUserDataJobStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\OfflineUserDataJobTypeEnum\\OfflineUserDataJobType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\OfflineUserDataJob;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AddOfflineUserDataJobOperationsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AddOfflineUserDataJobOperationsResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\Client\\OfflineUserDataJobServiceClient;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CreateOfflineUserDataJobRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CreateOfflineUserDataJobResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\GoogleAdsRow;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\OfflineUserDataJobOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\RunOfflineUserDataJobRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\SearchGoogleAdsStreamRequest;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * Uploads offline data for store sales transactions.\n *\n * This feature is only available to allowlisted accounts. See\n * https://support.google.com/google-ads/answer/7620302 for more details.\n */\nclass UploadStoreSalesTransactions\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n\n /**\n * The type of user data in the job (first or third party). If you have an official\n * store sales partnership with Google, use STORE_SALES_UPLOAD_THIRD_PARTY.\n * Otherwise, use STORE_SALES_UPLOAD_FIRST_PARTY or omit this parameter.\n */\n private const OFFLINE_USER_DATA_JOB_TYPE = 'STORE_SALES_UPLOAD_FIRST_PARTY';\n /** The ID of a store sales conversion action. */\n private const CONVERSION_ACTION_ID = 'INSERT_CONVERSION_ACTION_ID_HERE';\n /**\n * Optional (but recommended) external ID to identify the offline user data job.\n * The external ID for the offline user data job.\n */\n private const EXTERNAL_ID = null;\n /**\n * Only required after creating a custom key and custom values in the account.\n * Custom key and values are used to segment store sales conversions.\n * This measurement can be used to provide more advanced insights.\n */\n private const CUSTOM_KEY = null;\n\n // Optional: If uploading third party data, also specify the following values:\n /**\n * The date and time the advertiser uploaded data to the partner.\n * The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n */\n private const ADVERTISER_UPLOAD_DATE_TIME = null;\n /** The version of partner IDs to be used for uploads. */\n private const BRIDGE_MAP_VERSION_ID = null;\n /** The ID of the third party partner. */\n private const PARTNER_ID = null;\n // Optional: The consent status for ad personalization.\n private const AD_PERSONALIZATION_CONSENT = null;\n // Optional: The consent status for ad user data.\n private const AD_USER_DATA_CONSENT = null;\n\n // Optional: Below constants are only required if uploading with item attributes.\n /**\n * Specify a unique identifier of a product, either the Merchant Center\n * Item ID or Global Trade Item Number (GTIN).\n */\n private const ITEM_ID = null;\n /**\n * Specify a Merchant Center Account ID.\n */\n private const MERCHANT_CENTER_ACCOUNT_ID = null;\n /**\n * Specify a two-letter country code of the location associated with the\n * feed where your items are uploaded.\n * For a list of country codes see:\n * https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-16\n */\n private const COUNTRY_CODE = null;\n /**\n * Specify a two-letter language code of the language associated with\n * the feed where your items are uploaded.\n * For a list of language codes see:\n * https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n */\n private const LANGUAGE_CODE = null;\n /**\n * Specify a number of items sold.\n */\n private const QUANTITY = 1;\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::OFFLINE_USER_DATA_JOB_TYPE => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::CONVERSION_ACTION_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::AD_PERSONALIZATION_CONSENT => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::AD_USER_DATA_CONSENT => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::EXTERNAL_ID => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::CUSTOM_KEY => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::ADVERTISER_UPLOAD_DATE_TIME => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::BRIDGE_MAP_VERSION_ID => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::PARTNER_ID => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::ITEM_ID => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::MERCHANT_CENTER_ACCOUNT_ID => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::COUNTRY_CODE => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::LANGUAGE_CODE => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::QUANTITY => GetOpt::OPTIONAL_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())\n ->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::OFFLINE_USER_DATA_JOB_TYPE]\n ?: self::OFFLINE_USER_DATA_JOB_TYPE,\n $options[ArgumentNames::CONVERSION_ACTION_ID] ?: self::CONVERSION_ACTION_ID,\n $options[ArgumentNames::AD_PERSONALIZATION_CONSENT]\n ? ConsentStatus::value($options[ArgumentNames::AD_PERSONALIZATION_CONSENT])\n : self::AD_PERSONALIZATION_CONSENT,\n $options[ArgumentNames::AD_USER_DATA_CONSENT]\n ? ConsentStatus::value($options[ArgumentNames::AD_USER_DATA_CONSENT])\n : self::AD_USER_DATA_CONSENT,\n $options[ArgumentNames::EXTERNAL_ID] ?: self::EXTERNAL_ID,\n $options[ArgumentNames::CUSTOM_KEY] ?: self::CUSTOM_KEY,\n $options[ArgumentNames::ADVERTISER_UPLOAD_DATE_TIME]\n ?: self::ADVERTISER_UPLOAD_DATE_TIME,\n $options[ArgumentNames::BRIDGE_MAP_VERSION_ID] ?: self::BRIDGE_MAP_VERSION_ID,\n $options[ArgumentNames::PARTNER_ID] ?: self::PARTNER_ID,\n $options[ArgumentNames::ITEM_ID] ?: self::ITEM_ID,\n $options[ArgumentNames::MERCHANT_CENTER_ACCOUNT_ID]\n ?: self::MERCHANT_CENTER_ACCOUNT_ID,\n $options[ArgumentNames::COUNTRY_CODE] ?: self::COUNTRY_CODE,\n $options[ArgumentNames::LANGUAGE_CODE] ?: self::LANGUAGE_CODE,\n $options[ArgumentNames::QUANTITY] ?: self::QUANTITY\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param string|null $offlineUserDataJobType the type of offline user data in the job (first\n * party or third party). If you have an official store sales partnership with Google, use\n * `STORE_SALES_UPLOAD_THIRD_PARTY`. Otherwise, use `STORE_SALES_UPLOAD_FIRST_PARTY`\n * @param int $conversionActionId the ID of a store sales conversion action\n * @param int|null $adPersonalizationConsent the ad personalization consent status\n * @param int|null $adUserDataConsent the ad user data consent status\n * @param int|null $externalId optional (but recommended) external ID for the offline user data\n * job\n * @param string|null $customKey the custom key to segment store sales conversions. Only\n * required after creating a custom key and custom values in the account.\n * @param string|null $advertiserUploadDateTime date and time the advertiser uploaded data to\n * the partner. Only required for third party uploads\n * @param string|null $bridgeMapVersionId version of partner IDs to be used for uploads. Only\n * required for third party uploads\n * @param int|null $partnerId ID of the third party partner. Only required for third party\n * uploads\n * @param string|null $itemId a unique identifier of a product, either the Merchant Center Item\n * ID or Global Trade Item Number (GTIN)\n * @param int|null $merchantCenterAccountId a Merchant Center Account ID\n * @param string|null $countryCode a two-letter country code of the location associated with the\n * feed where your items are uploaded\n * @param string|null $languageCode a two-letter language code of the language associated with\n * the feed where your items are uploaded\n * @param int|null $quantity the number of items sold. Can only be set when at least one other\n * item attribute has been provided\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n ?string $offlineUserDataJobType,\n int $conversionActionId,\n ?int $adPersonalizationConsent,\n ?int $adUserDataConsent,\n ?int $externalId,\n ?string $customKey,\n ?string $advertiserUploadDateTime,\n ?string $bridgeMapVersionId,\n ?int $partnerId,\n ?string $itemId,\n ?int $merchantCenterAccountId,\n ?string $countryCode,\n ?string $languageCode,\n ?int $quantity\n ) {\n $offlineUserDataJobServiceClient = $googleAdsClient->getOfflineUserDataJobServiceClient();\n\n // Creates an offline user data job for uploading transactions.\n $offlineUserDataJobResourceName = self::createOfflineUserDataJob(\n $offlineUserDataJobServiceClient,\n $customerId,\n $offlineUserDataJobType,\n $externalId,\n $customKey,\n $advertiserUploadDateTime,\n $bridgeMapVersionId,\n $partnerId\n );\n\n // Adds transactions to the job.\n self::addTransactionsToOfflineUserDataJob(\n $offlineUserDataJobServiceClient,\n $customerId,\n $offlineUserDataJobResourceName,\n $conversionActionId,\n $adPersonalizationConsent,\n $adUserDataConsent,\n $itemId,\n $merchantCenterAccountId,\n $countryCode,\n $languageCode,\n $quantity\n );\n\n // Issues an asynchronous request to run the offline user data job.\n $offlineUserDataJobServiceClient->runOfflineUserDataJob(\n RunOfflineUserDataJobRequest::build($offlineUserDataJobResourceName)\n );\n\n printf(\n \"Sent request to asynchronously run offline user data job: '%s'.%s\",\n $offlineUserDataJobResourceName,\n PHP_EOL\n );\n\n // Offline user data jobs may take up to 24 hours to complete, so instead of waiting for the\n // job to complete, retrieves and displays the job status once and then prints the query to\n // use to check the job again later.\n self::checkJobStatus($googleAdsClient, $customerId, $offlineUserDataJobResourceName);\n }\n\n /**\n * Creates an offline user data job for uploading store sales transactions.\n *\n * @param OfflineUserDataJobServiceClient $offlineUserDataJobServiceClient the offline user\n * data job service client\n * @param int $customerId the customer ID\n * @param string|null $offlineUserDataJobType the type of offline user data in the job (first\n * party or third party). If you have an official store sales partnership with Google, use\n * `STORE_SALES_UPLOAD_THIRD_PARTY`. Otherwise, use `STORE_SALES_UPLOAD_FIRST_PARTY`\n * @param int|null $externalId optional (but recommended) external ID for the offline user data\n * job\n * @param string|null $customKey the custom key to segment store sales conversions. Only\n * required after creating a custom key and custom values in the account.\n * @param string|null $advertiserUploadDateTime date and time the advertiser uploaded data to\n * the partner. Only required for third party uploads\n * @param string|null $bridgeMapVersionId version of partner IDs to be used for uploads. Only\n * required for third party uploads\n * @param int|null $partnerId ID of the third party partner. Only required for third party\n * uploads\n * @return string the resource name of the created job\n */\n private static function createOfflineUserDataJob(\n OfflineUserDataJobServiceClient $offlineUserDataJobServiceClient,\n int $customerId,\n ?string $offlineUserDataJobType,\n ?int $externalId,\n ?string $customKey,\n ?string $advertiserUploadDateTime,\n ?string $bridgeMapVersionId,\n ?int $partnerId\n ): string {\n // TIP: If you are migrating from the AdWords API, please note that Google Ads API uses the\n // term \"fraction\" instead of \"rate\". For example, loyaltyRate in the AdWords API is called\n // loyaltyFraction in the Google Ads API.\n // Please refer to https://support.google.com/google-ads/answer/7506124 for additional\n // details.\n $storeSalesMetadata = new StoreSalesMetadata([\n // Sets the fraction of your overall sales that you (or the advertiser, in the third\n // party case) can associate with a customer (email, phone number, address, etc.) in\n // your database or loyalty program.\n // For example, set this to 0.7 if you have 100 transactions over 30 days, and out of\n // those 100 transactions, you can identify 70 by an email address or phone number.\n 'loyalty_fraction' => 0.7,\n // Sets the fraction of sales you're uploading out of the overall sales that you (or the\n // advertiser, in the third party case) can associate with a customer. In most cases,\n // you will set this to 1.0.\n // Continuing the example above for loyalty fraction, a value of 1.0 here indicates that\n // you are uploading all 70 of the transactions that can be identified by an email\n // address or phone number.\n 'transaction_upload_fraction' => 1.0,\n ]);\n if (!is_null($customKey)) {\n $storeSalesMetadata->setCustomKey($customKey);\n }\n if (\n OfflineUserDataJobType::value($offlineUserDataJobType)\n === OfflineUserDataJobType::STORE_SALES_UPLOAD_THIRD_PARTY\n ) {\n // Creates additional metadata required for uploading third party data.\n $storeSalesThirdPartyMetadata = new StoreSalesThirdPartyMetadata([\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n 'advertiser_upload_date_time' => $advertiserUploadDateTime,\n // Sets the fraction of transactions you received from the advertiser that have\n // valid formatting and values. This captures any transactions the advertiser\n // provided to you but which you are unable to upload to Google due to formatting\n // errors or missing data.\n // In most cases, you will set this to 1.0.\n 'valid_transaction_fraction' => 1.0,\n // Sets the fraction of valid transactions (as defined above) you received from the\n // advertiser that you (the third party) have matched to an external user ID on your\n // side.\n // In most cases, you will set this to 1.0.\n 'partner_match_fraction' => 1.0,\n // Sets the fraction of transactions you (the third party) are uploading out of the\n // transactions you received from the advertiser that meet both of the following\n // criteria:\n // 1. Are valid in terms of formatting and values. See valid transaction fraction\n // above.\n // 2. You matched to an external user ID on your side. See partner match fraction\n // above.\n // In most cases, you will set this to 1.0.\n 'partner_upload_fraction' => 1.0,\n // Please speak with your Google representative to get the values to use for the\n // bridge map version and partner IDs.\n // Sets the version of partner IDs to be used for uploads.\n 'bridge_map_version_id' => $bridgeMapVersionId,\n // Sets the third party partner ID uploading the transactions.\n 'partner_id' => $partnerId,\n ]);\n $storeSalesMetadata->setThirdPartyMetadata($storeSalesThirdPartyMetadata);\n }\n // Creates a new offline user data job.\n $offlineUserDataJob = new OfflineUserDataJob([\n 'type' => OfflineUserDataJobType::value($offlineUserDataJobType),\n 'store_sales_metadata' => $storeSalesMetadata\n ]);\n if (!is_null($externalId)) {\n $offlineUserDataJob->setExternalId($externalId);\n }\n\n // Issues a request to create the offline user data job.\n /** @var CreateOfflineUserDataJobResponse $createOfflineUserDataJobResponse */\n $createOfflineUserDataJobResponse =\n $offlineUserDataJobServiceClient->createOfflineUserDataJob(\n CreateOfflineUserDataJobRequest::build($customerId, $offlineUserDataJob)\n );\n $offlineUserDataJobResourceName = $createOfflineUserDataJobResponse->getResourceName();\n printf(\n \"Created an offline user data job with resource name: '%s'.%s\",\n $offlineUserDataJobResourceName,\n PHP_EOL\n );\n\n return $offlineUserDataJobResourceName;\n }\n\n /**\n * Adds operations to the job for a set of sample transactions.\n *\n * @param OfflineUserDataJobServiceClient $offlineUserDataJobServiceClient the offline user\n * data job service client\n * @param int $customerId the customer ID\n * @param string $offlineUserDataJobResourceName the resource name of the created offline user\n * data job\n * @param int $conversionActionId the ID of a store sales conversion action\n * @param int|null $adPersonalizationConsent the ad personalization consent status\n * @param int|null $adUserDataConsent the ad user data consent status\n * @param string|null $itemId a unique identifier of a product, either the Merchant Center Item\n * ID or Global Trade Item Number (GTIN)\n * @param int|null $merchantCenterAccountId a Merchant Center Account ID\n * @param string|null $countryCode a two-letter country code of the location associated with the\n * feed where your items are uploaded\n * @param string|null $languageCode a two-letter language code of the language associated with\n * the feed where your items are uploaded\n * @param int|null $quantity the number of items sold. Can only be set when at least one other\n * item attribute has been provided\n */\n private static function addTransactionsToOfflineUserDataJob(\n OfflineUserDataJobServiceClient $offlineUserDataJobServiceClient,\n int $customerId,\n string $offlineUserDataJobResourceName,\n int $conversionActionId,\n ?int $adPersonalizationConsent,\n ?int $adUserDataConsent,\n ?string $itemId,\n ?int $merchantCenterAccountId,\n ?string $countryCode,\n ?string $languageCode,\n ?int $quantity\n ) {\n // Constructs the operation for each transaction.\n $userDataJobOperations = self::buildOfflineUserDataJobOperations(\n $customerId,\n $conversionActionId,\n $adPersonalizationConsent,\n $adUserDataConsent,\n $itemId,\n $merchantCenterAccountId,\n $countryCode,\n $languageCode,\n $quantity\n );\n\n // Issues a request to add the operations to the offline user data job.\n /** @var AddOfflineUserDataJobOperationsResponse $operationResponse */\n $request = AddOfflineUserDataJobOperationsRequest::build(\n $offlineUserDataJobResourceName,\n $userDataJobOperations\n );\n // (Optional) Enables partial failure and warnings.\n $request->setEnablePartialFailure(true)->setEnableWarnings(true);\n $response = $offlineUserDataJobServiceClient->addOfflineUserDataJobOperations($request);\n\n // Prints the status message if any partial failure error is returned.\n // NOTE: The details of each partial failure error are not printed here, you can refer to\n // the example HandlePartialFailure.php to learn more.\n if ($response->hasPartialFailureError()) {\n printf(\n \"Encountered %d partial failure errors while adding %d operations to the \"\n . \"offline user data job: '%s'. Only the successfully added operations will be \"\n . \"executed when the job runs.%s\",\n count($response->getPartialFailureError()->getDetails()),\n count($userDataJobOperations),\n $response->getPartialFailureError()->getMessage(),\n PHP_EOL\n );\n } else {\n printf(\n \"Successfully added %d operations to the offline user data job.%s\",\n count($userDataJobOperations),\n PHP_EOL\n );\n }\n\n // Prints the number of warnings if any warnings are returned. You can access\n // details of each warning using the same approach you'd use for partial failure\n // errors.\n if ($response->hasWarning()) {\n // Extracts all the warning errors from the response details into a single\n // GoogleAdsFailure object.\n $warningFailure = GoogleAdsFailures::fromAnys($response->getWarning()->getDetails());\n // Prints some information about the warnings encountered.\n printf(\n \"Encountered %d warning(s).%s\",\n count($warningFailure->getErrors()),\n PHP_EOL\n );\n }\n }\n\n /**\n * Creates a list of offline user data job operations for sample transactions.\n *\n * @param int $customerId the customer ID\n * @param int $conversionActionId the ID of a store sales conversion action\n * @param int|null $adPersonalizationConsent the ad personalization consent status\n * @param int|null $adUserDataConsent the ad user data consent status\n * @return OfflineUserDataJobOperation[] an array with the operations\n * @param string|null $itemId a unique identifier of a product, either the Merchant Center Item\n * ID or Global Trade Item Number (GTIN)\n * @param int|null $merchantCenterAccountId a Merchant Center Account ID\n * @param string|null $countryCode a two-letter country code of the location associated with the\n * feed where your items are uploaded\n * @param string|null $languageCode a two-letter language code of the language associated with\n * the feed where your items are uploaded\n * @param int|null $quantity the number of items sold. Can only be set when at least one other\n * item attribute has been provided\n */\n private static function buildOfflineUserDataJobOperations(\n $customerId,\n $conversionActionId,\n ?int $adPersonalizationConsent,\n ?int $adUserDataConsent,\n ?string $itemId,\n ?int $merchantCenterAccountId,\n ?string $countryCode,\n ?string $languageCode,\n ?int $quantity\n ): array {\n // Creates the first transaction for upload based on an email address and state.\n $userDataWithEmailAddress = new UserData([\n 'user_identifiers' => [\n new UserIdentifier([\n // Email addresses must be normalized and hashed.\n 'hashed_email' => self::normalizeAndHash('dana@example.com')\n ]),\n new UserIdentifier([\n 'address_info' => new OfflineUserAddressInfo(['state' => 'NY'])\n ])\n ],\n 'transaction_attribute' => new TransactionAttribute([\n 'conversion_action'\n => ResourceNames::forConversionAction($customerId, $conversionActionId),\n 'currency_code' => 'USD',\n // Converts the transaction amount from $200 USD to micros.\n 'transaction_amount_micros' => Helper::baseToMicro(200),\n // Specifies the date and time of the transaction. The format is\n // \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional\n // timezone offset from UTC. If the offset is absent, the API will\n // use the account's timezone as default. Examples: \"2018-03-05 09:15:00\"\n // or \"2018-02-01 14:34:30+03:00\".\n 'transaction_date_time' => '2020-05-01 23:52:12'\n // OPTIONAL: If uploading data with custom key and values, also specify the\n // following value:\n // 'custom_value' => 'INSERT_CUSTOM_VALUE_HERE'\n ])\n ]);\n\n // Adds consent information if specified.\n if (!empty($adPersonalizationConsent) || !empty($adUserDataConsent)) {\n $consent = new Consent();\n if (!empty($adPersonalizationConsent)) {\n $consent->setAdPersonalization($adPersonalizationConsent);\n }\n if (!empty($adUserDataConsent)) {\n $consent->setAdUserData($adUserDataConsent);\n }\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy for details.\n $userDataWithEmailAddress->setConsent($consent);\n }\n\n // Creates the second transaction for upload based on a physical address.\n $userDataWithPhysicalAddress = new UserData([\n 'user_identifiers' => [\n new UserIdentifier([\n 'address_info' => new OfflineUserAddressInfo([\n // First and last name must be normalized and hashed.\n 'hashed_first_name' => self::normalizeAndHash('Dana'),\n 'hashed_last_name' => self::normalizeAndHash('Quinn'),\n // Country code and zip code are sent in plain text.\n 'country_code' => 'US',\n 'postal_code' => '10011'\n ])\n ])\n ],\n 'transaction_attribute' => new TransactionAttribute([\n 'conversion_action'\n => ResourceNames::forConversionAction($customerId, $conversionActionId),\n 'currency_code' => 'EUR',\n // Converts the transaction amount from 450 EUR to micros.\n 'transaction_amount_micros' => Helper::baseToMicro(450),\n // Specifies the date and time of the transaction. This date and time will be\n // interpreted by the API using the Google Ads customer's time zone.\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n 'transaction_date_time' => '2020-05-14 19:07:02'\n ])\n ]);\n\n // Optional: If uploading data with item attributes, also assign these values\n // in the transaction attribute.\n if (!empty($itemId)) {\n $userDataWithPhysicalAddress->getTransactionAttribute()->setItemAttribute(\n new ItemAttribute([\n 'item_id' => $itemId,\n 'merchant_id' => $merchantCenterAccountId,\n 'country_code' => $countryCode,\n 'language_code' => $languageCode,\n // Quantity field should only be set when at least one of the other item\n // attribute fields is present.\n 'quantity' => $quantity\n ])\n );\n }\n\n // Creates the operations to add the two transactions.\n $operations = [];\n foreach ([$userDataWithEmailAddress, $userDataWithPhysicalAddress] as $userData) {\n $operations[] = new OfflineUserDataJobOperation(['create' => $userData]);\n }\n\n return $operations;\n }\n\n /**\n * Returns the result of normalizing and then hashing the string.\n * Private customer data must be hashed during upload, as described at\n * https://support.google.com/google-ads/answer/7506124.\n *\n * @param string $value the value to normalize and hash\n * @return string the normalized and hashed value\n */\n private static function normalizeAndHash(string $value): string\n {\n return hash('sha256', strtolower(trim($value)));\n }\n\n /**\n * Retrieves, checks, and prints the status of the offline user data job.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param string $offlineUserDataJobResourceName the resource name of the created offline user\n * data job\n */\n private static function checkJobStatus(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $offlineUserDataJobResourceName\n ) {\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n\n // Creates a query that retrieves the offline user data.\n $query = \"SELECT offline_user_data_job.resource_name, \"\n . \"offline_user_data_job.id, \"\n . \"offline_user_data_job.status, \"\n . \"offline_user_data_job.type, \"\n . \"offline_user_data_job.failure_reason \"\n . \"FROM offline_user_data_job \"\n . \"WHERE offline_user_data_job.resource_name = '$offlineUserDataJobResourceName'\";\n\n // Issues a search stream request.\n /** @var GoogleAdsServerStreamDecorator $stream */\n $stream = $googleAdsServiceClient->searchStream(\n SearchGoogleAdsStreamRequest::build($customerId, $query)\n );\n\n // Prints out some information about the offline user data.\n /** @var GoogleAdsRow $googleAdsRow */\n $googleAdsRow = $stream->iterateAllElements()->current();\n $offlineUserDataJob = $googleAdsRow->getOfflineUserDataJob();\n printf(\n \"Offline user data job ID %d with type '%s' has status: %s.%s\",\n $offlineUserDataJob->getId(),\n OfflineUserDataJobType::name($offlineUserDataJob->getType()),\n OfflineUserDataJobStatus::name($offlineUserDataJob->getStatus()),\n PHP_EOL\n );\n\n if (OfflineUserDataJobStatus::FAILED === $offlineUserDataJob->getStatus()) {\n printf(\n \" Failure reason: %s%s\",\n OfflineUserDataJobFailureReason::name($offlineUserDataJob->getFailureReason()),\n PHP_EOL\n );\n } elseif (\n OfflineUserDataJobStatus::PENDING === $offlineUserDataJob->getStatus()\n || OfflineUserDataJobStatus::RUNNING === $offlineUserDataJob->getStatus()\n ) {\n printf(\n '%1$sTo check the status of the job periodically, use the following GAQL '\n . 'query with GoogleAdsService.search:%1$s%2$s%1$s.',\n PHP_EOL,\n $query\n );\n }\n }\n}\n\nUploadStoreSalesTransactions::main();\nUploadStoreSalesTransactions.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2020 Google LLC\n#\n# 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.\n\"\"\"This example uploads offline conversion data for store sales transactions.\n\nThis feature is only available to allowlisted accounts.\nSee https://support.google.com/google-ads/answer/7620302 for more details.\n\"\"\"\n\nimport argparse\nfrom datetime import datetime\nimport hashlib\nimport logging\nimport sys\nfrom typing import List, Optional, Tuple\n\nfrom google.protobuf.any_pb2 import Any\nfrom google.rpc import status_pb2\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.common.types.offline_user_data import (\n ItemAttribute,\n StoreSalesMetadata,\n StoreSalesThirdPartyMetadata,\n UserData,\n UserIdentifier,\n)\nfrom google.ads.googleads.v24.enums.types.offline_user_data_job_status import (\n OfflineUserDataJobStatusEnum,\n)\nfrom google.ads.googleads.v24.enums.types.offline_user_data_job_type import (\n OfflineUserDataJobTypeEnum,\n)\nfrom google.ads.googleads.v24.errors.types.errors import (\n GoogleAdsError,\n GoogleAdsFailure,\n)\nfrom google.ads.googleads.v24.resources.types.offline_user_data_job import (\n OfflineUserDataJob,\n)\nfrom google.ads.googleads.v24.services.services.google_ads_service import (\n GoogleAdsServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.offline_user_data_job_service import (\n OfflineUserDataJobServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.google_ads_service import (\n GoogleAdsRow,\n)\nfrom google.ads.googleads.v24.services.types.offline_user_data_job_service import (\n AddOfflineUserDataJobOperationsRequest,\n AddOfflineUserDataJobOperationsResponse,\n CreateOfflineUserDataJobResponse,\n OfflineUserDataJobOperation,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n conversion_action_id: int,\n offline_user_data_job_type: int,\n external_id: Optional[int],\n advertiser_upload_date_time: Optional[str],\n bridge_map_version_id: Optional[str],\n partner_id: Optional[int],\n custom_key: Optional[str],\n custom_value: Optional[str],\n item_id: Optional[str],\n merchant_center_account_id: Optional[int],\n country_code: Optional[str],\n language_code: Optional[str],\n quantity: int,\n ad_user_data_consent: Optional[str],\n ad_personalization_consent: Optional[str],\n) -> None:\n \"\"\"Uploads offline conversion data for store sales transactions.\n\n Args:\n client: An initialized Google Ads client.\n customer_id: The Google Ads customer ID.\n conversion_action_id: The ID of a store sales conversion action.\n offline_user_data_job_type: Optional type of offline user data in the\n job (first party or third party). If you have an official store\n sales partnership with Google, use STORE_SALES_UPLOAD_THIRD_PARTY.\n Otherwise, use STORE_SALES_UPLOAD_FIRST_PARTY.\n external_id: Optional, but recommended, external ID for the offline\n user data job.\n advertiser_upload_date_time: Optional date and time the advertiser\n uploaded data to the partner. Only required for third party uploads.\n The format is 'yyyy-mm-dd hh:mm:ss+|-hh:mm', e.g.\n '2019-01-01 12:32:45-08:00'.\n bridge_map_version_id: Optional version of partner IDs to be used for\n uploads. Only required for third party uploads.\n partner_id: Optional ID of the third party partner. Only required for\n third party uploads.\n custom_key: A custom key str to segment store sales conversions. Only\n required after creating a custom key and custom values in the\n account.\n custom_value: A custom value str to segment store sales conversions.\n Only required after creating a custom key and custom values in the\n account.\n item_id: Optional str ID of the product. Either the Merchant Center Item\n ID or the Global Trade Item Number (GTIN). Only required if\n uploading with item attributes.\n merchant_center_account_id: Optional Merchant Center Account ID. Only\n required if uploading with item attributes.\n country_code: Optional two-letter country code of the location associated\n with the feed where your items are uploaded. Only required if\n uploading with item attributes.\n language_code: Optional two-letter country code of the language\n associated with the feed where your items are uploaded. Only\n required if uploading with item attributes.\n quantity: Optional number of items sold. Only required if uploading with\n item attributes.\n ad_user_data_consent: The consent status for ad user data for all\n members in the job.\n ad_personalization_consent: The personalization consent status for ad\n user data for all members in the job.\n \"\"\"\n # Get the OfflineUserDataJobService client.\n offline_user_data_job_service: OfflineUserDataJobServiceClient = (\n client.get_service(\"OfflineUserDataJobService\")\n )\n\n # Create an offline user data job for uploading transactions.\n offline_user_data_job_resource_name: str = create_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_type,\n external_id,\n advertiser_upload_date_time,\n bridge_map_version_id,\n partner_id,\n custom_key,\n )\n\n # Add transactions to the job.\n add_transactions_to_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_resource_name,\n conversion_action_id,\n custom_value,\n item_id,\n merchant_center_account_id,\n country_code,\n language_code,\n quantity,\n ad_user_data_consent,\n ad_personalization_consent,\n )\n\n # Issue an asynchronous request to run the offline user data job.\n offline_user_data_job_service.run_offline_user_data_job(\n resource_name=offline_user_data_job_resource_name\n )\n\n # Offline user data jobs may take up to 24 hours to complete, so\n # instead of waiting for the job to complete, retrieves and displays\n # the job status once and then prints the query to use to check the job\n # again later.\n check_job_status(client, customer_id, offline_user_data_job_resource_name)\n\n\ndef create_offline_user_data_job(\n client: GoogleAdsClient,\n offline_user_data_job_service: OfflineUserDataJobServiceClient,\n customer_id: str,\n offline_user_data_job_type: int,\n external_id: Optional[int],\n advertiser_upload_date_time: Optional[str],\n bridge_map_version_id: Optional[str],\n partner_id: Optional[int],\n custom_key: Optional[str],\n) -> str:\n \"\"\"Creates an offline user data job for uploading store sales transactions.\n\n Args:\n client: An initialized Google Ads API client.\n offline_user_data_job_service: The offline user data job service client.\n customer_id: The Google Ads customer ID.\n offline_user_data_job_type: Optional type of offline user data in the\n job (first party or third party).\n external_id: Optional external ID for the offline user data job.\n advertiser_upload_date_time: Optional date and time the advertiser\n uploaded data to the partner. Only required for third party uploads.\n bridge_map_version_id: Optional version of partner IDs to be used for\n uploads. Only required for third party uploads.\n partner_id: Optional ID of the third party partner. Only required for\n third party uploads.\n custom_key: A custom key str to segment store sales conversions. Only\n required after creating a custom key and custom values in the\n account.\n\n Returns:\n The string resource name of the created job.\n \"\"\"\n # TIP: If you are migrating from the AdWords API, please note that Google\n # Ads API uses the term \"fraction\" instead of \"rate\". For example,\n # loyalty_rate in the AdWords API is called loyalty_fraction in the Google\n # Ads API.\n\n # Create a new offline user data job.\n offline_user_data_job: OfflineUserDataJob = client.get_type(\n \"OfflineUserDataJob\"\n )\n offline_user_data_job.type_ = offline_user_data_job_type\n if external_id is not None:\n offline_user_data_job.external_id = external_id\n\n # Please refer to https://support.google.com/google-ads/answer/7506124 for\n # additional details.\n store_sales_metadata: StoreSalesMetadata = (\n offline_user_data_job.store_sales_metadata\n )\n # Set the fraction of your overall sales that you (or the advertiser,\n # in the third party case) can associate with a customer (email, phone\n # number, address, etc.) in your database or loyalty program.\n # For example, set this to 0.7 if you have 100 transactions over 30\n # days, and out of those 100 transactions, you can identify 70 by an\n # email address or phone number.\n store_sales_metadata.loyalty_fraction = 0.7\n # Set the fraction of sales you're uploading out of the overall sales\n # that you (or the advertiser, in the third party case) can associate\n # with a customer. In most cases, you will set this to 1.0.\n # Continuing the example above for loyalty fraction, a value of 1.0 here\n # indicates that you are uploading all 70 of the transactions that can\n # be identified by an email address or phone number.\n store_sales_metadata.transaction_upload_fraction = 1.0\n\n if custom_key:\n store_sales_metadata.custom_key = custom_key\n\n if (\n offline_user_data_job_type\n == client.enums.OfflineUserDataJobTypeEnum.STORE_SALES_UPLOAD_THIRD_PARTY\n ):\n # Create additional metadata required for uploading third party data.\n store_sales_third_party_metadata: StoreSalesThirdPartyMetadata = (\n store_sales_metadata.third_party_metadata\n )\n # The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n store_sales_third_party_metadata.advertiser_upload_date_time = (\n advertiser_upload_date_time\n )\n # Set the fraction of transactions you received from the advertiser\n # that have valid formatting and values. This captures any transactions\n # the advertiser provided to you but which you are unable to upload to\n # Google due to formatting errors or missing data.\n # In most cases, you will set this to 1.0.\n store_sales_third_party_metadata.valid_transaction_fraction = 1.0\n # Set the fraction of valid transactions (as defined above) you\n # received from the advertiser that you (the third party) have matched\n # to an external user ID on your side.\n # In most cases, you will set this to 1.0.\n store_sales_third_party_metadata.partner_match_fraction = 1.0\n # Set the fraction of transactions you (the third party) are uploading\n # out of the transactions you received from the advertiser that meet\n # both of the following criteria:\n # 1. Are valid in terms of formatting and values. See valid transaction\n # fraction above.\n # 2. You matched to an external user ID on your side. See partner match\n # fraction above.\n # In most cases, you will set this to 1.0.\n store_sales_third_party_metadata.partner_upload_fraction = 1.0\n # Set the version of partner IDs to be used for uploads.\n # Please speak with your Google representative to get the values to use\n # for the bridge map version and partner IDs.\n store_sales_third_party_metadata.bridge_map_version_id = (\n bridge_map_version_id\n )\n # Set the third party partner ID uploading the transactions.\n store_sales_third_party_metadata.partner_id = partner_id\n\n create_offline_user_data_job_response: CreateOfflineUserDataJobResponse = (\n offline_user_data_job_service.create_offline_user_data_job(\n customer_id=customer_id, job=offline_user_data_job\n )\n )\n offline_user_data_job_resource_name: str = (\n create_offline_user_data_job_response.resource_name\n )\n print(\n \"Created an offline user data job with resource name \"\n f\"'{offline_user_data_job_resource_name}'.\"\n )\n return offline_user_data_job_resource_name\n\n\ndef add_transactions_to_offline_user_data_job(\n client: GoogleAdsClient,\n offline_user_data_job_service: OfflineUserDataJobServiceClient,\n customer_id: str,\n offline_user_data_job_resource_name: str,\n conversion_action_id: int,\n custom_value: Optional[str],\n item_id: Optional[str],\n merchant_center_account_id: Optional[int],\n country_code: Optional[str],\n language_code: Optional[str],\n quantity: int,\n ad_user_data_consent: Optional[str],\n ad_personalization_consent: Optional[str],\n) -> None:\n \"\"\"Add operations to the job for a set of sample transactions.\n\n Args:\n client: An initialized Google Ads API client.\n offline_user_data_job_service: The offline user data job service client.\n customer_id: The Google Ads customer ID.\n offline_user_data_job_resource_name: The string resource name of the\n offline user data job that will receive the transactions.\n conversion_action_id: The ID of a store sales conversion action.\n custom_value: A custom value str to segment store sales conversions.\n Only required after creating a custom key and custom values in the\n account.\n item_id: Optional str ID of the product. Either the Merchant Center Item\n ID or the Global Trade Item Number (GTIN). Only required if\n uploading with item attributes.\n merchant_center_account_id: Optional Merchant Center Account ID. Only\n required if uploading with item attributes.\n country_code: Optional two-letter country code of the location associated\n with the feed where your items are uploaded. Only required if\n uploading with item attributes.\n language_code: Optional two-letter country code of the language\n associated with the feed where your items are uploaded. Only\n required if uploading with item attributes.\n quantity: Optional number of items sold. Only required if uploading with\n item attributes.\n ad_user_data_consent: The consent status for ad user data for all\n members in the job.\n ad_personalization_consent: The personalization consent status for ad\n user data for all members in the job.\n \"\"\"\n # Construct some sample transactions.\n operations: List[OfflineUserDataJobOperation] = (\n build_offline_user_data_job_operations(\n client,\n customer_id,\n conversion_action_id,\n custom_value,\n item_id,\n merchant_center_account_id,\n country_code,\n language_code,\n quantity,\n ad_user_data_consent,\n ad_personalization_consent,\n )\n )\n\n # Constructs a request with partial failure enabled to add the operations\n # to the offline user data job, and enable_warnings set to true to retrieve\n # warnings.\n request: AddOfflineUserDataJobOperationsRequest = client.get_type(\n \"AddOfflineUserDataJobOperationsRequest\"\n )\n request.resource_name = offline_user_data_job_resource_name\n request.enable_partial_failure = True\n request.enable_warnings = True\n request.operations = operations\n\n response: AddOfflineUserDataJobOperationsResponse = (\n offline_user_data_job_service.add_offline_user_data_job_operations(\n request=request,\n )\n )\n\n # Print the error message for any partial failure error that is returned.\n if response.partial_failure_error:\n print_google_ads_failures(client, response.partial_failure_error)\n else:\n print(\n f\"Successfully added {len(operations)} to the offline user data \"\n \"job.\"\n )\n\n # Print the message for any warnings that are returned.\n if response.warning:\n print_google_ads_failures(client, response.warning)\n\n\ndef print_google_ads_failures(\n client: GoogleAdsClient, status: status_pb2.Status\n) -> None:\n \"\"\"Prints the details for partial failure errors and warnings.\n\n Both partial failure errors and warnings are returned as Status instances,\n which include serialized GoogleAdsFailure objects. Here we deserialize\n each GoogleAdsFailure and print the error details it includes.\n\n Args:\n client: An initialized Google Ads API client.\n status: a google.rpc.Status instance.\n \"\"\"\n detail: Any\n for detail in status.details:\n google_ads_failure: GoogleAdsFailure = client.get_type(\n \"GoogleAdsFailure\"\n )\n # Retrieve the class definition of the GoogleAdsFailure instance\n # with type() in order to use the \"deserialize\" class method to parse\n # the detail string into a protobuf message instance.\n failure_instance: GoogleAdsFailure = type(\n google_ads_failure\n ).deserialize(detail.value)\n error: GoogleAdsError\n for error in failure_instance.errors:\n print(\n \"A partial failure or warning at index \"\n f\"{error.location.field_path_elements[0].index} occurred.\\n\"\n f\"Message: {error.message}\\n\"\n f\"Code: {error.error_code}\"\n )\n\n\ndef build_offline_user_data_job_operations(\n client: GoogleAdsClient,\n customer_id: str,\n conversion_action_id: int,\n custom_value: Optional[str],\n item_id: Optional[str],\n merchant_center_account_id: Optional[int],\n country_code: Optional[str],\n language_code: Optional[str],\n quantity: int,\n ad_user_data_consent: Optional[str],\n ad_personalization_consent: Optional[str],\n) -> List[OfflineUserDataJobOperation]:\n \"\"\"Create offline user data job operations for sample transactions.\n\n Args:\n client: An initialized Google Ads API client.\n customer_id: The Google Ads customer ID.\n conversion_action_id: The ID of a store sales conversion action.\n custom_value: A custom value str to segment store sales conversions.\n Only required after creating a custom key and custom values in the\n account.\n item_id: Optional str ID of the product. Either the Merchant Center Item\n ID or the Global Trade Item Number (GTIN). Only required if\n uploading with item attributes.\n merchant_center_account_id: Optional Merchant Center Account ID. Only\n required if uploading with item attributes.\n country_code: Optional two-letter country code of the location associated\n with the feed where your items are uploaded. Only required if\n uploading with item attributes.\n language_code: Optional two-letter country code of the language\n associated with the feed where your items are uploaded. Only\n required if uploading with item attributes.\n quantity: Optional number of items sold. Only required if uploading with\n item attributes.\n ad_user_data_consent: The consent status for ad user data for all\n members in the job.\n ad_personalization_consent: The personalization consent status for ad\n user data for all members in the job.\n\n Returns:\n A list of OfflineUserDataJobOperations.\n \"\"\"\n # Create the first transaction for upload with an email address and state.\n user_data_with_email_address_operation: OfflineUserDataJobOperation = (\n client.get_type(\"OfflineUserDataJobOperation\")\n )\n user_data_with_email_address: UserData = (\n user_data_with_email_address_operation.create\n )\n email_identifier: UserIdentifier = client.get_type(\"UserIdentifier\")\n # Hash normalized email addresses based on SHA-256 hashing algorithm.\n email_identifier.hashed_email = normalize_and_hash(\"dana@example.com\")\n state_identifier: UserIdentifier = client.get_type(\"UserIdentifier\")\n state_identifier.address_info.state = \"NY\"\n user_data_with_email_address.user_identifiers.extend(\n [email_identifier, state_identifier]\n )\n user_data_with_email_address.transaction_attribute.conversion_action = (\n client.get_service(\"ConversionActionService\").conversion_action_path(\n customer_id, conversion_action_id\n )\n )\n user_data_with_email_address.transaction_attribute.currency_code = \"USD\"\n # Convert the transaction amount from $200 USD to micros.\n user_data_with_email_address.transaction_attribute.transaction_amount_micros = (\n 200000000\n )\n # Specify the date and time of the transaction. The format is\n # \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional timezone\n # offset from UTC. If the offset is absent, the API will use the account's\n # timezone as default. Examples: \"2018-03-05 09:15:00\" or\n # \"2018-02-01 14:34:30+03:00\".\n user_data_with_email_address.transaction_attribute.transaction_date_time = (\n datetime.now() - datetime.timedelta(months=1)\n ).strftime(\"%Y-%m-%d %H:%M:%S\")\n\n # Specifies whether user consent was obtained for the data you are\n # uploading. For more details, see:\n # https://www.google.com/about/company/user-consent-policy\n if ad_user_data_consent:\n user_data_with_email_address.consent.ad_user_data = (\n client.enums.ConsentStatusEnum[ad_user_data_consent]\n )\n if ad_personalization_consent:\n user_data_with_email_address.consent.ad_personalization = (\n client.enums.ConsentStatusEnum[ad_personalization_consent]\n )\n\n if custom_value:\n user_data_with_email_address.transaction_attribute.custom_value = (\n custom_value\n )\n\n # Create the second transaction for upload based on a physical address.\n user_data_with_physical_address_operation: OfflineUserDataJobOperation = (\n client.get_type(\"OfflineUserDataJobOperation\")\n )\n user_data_with_physical_address: UserData = (\n user_data_with_physical_address_operation.create\n )\n address_identifier: UserIdentifier = client.get_type(\"UserIdentifier\")\n # First and last name must be normalized and hashed.\n address_identifier.address_info.hashed_first_name = normalize_and_hash(\n \"Dana\"\n )\n address_identifier.address_info.hashed_last_name = normalize_and_hash(\n \"Quinn\"\n )\n # Country and zip codes are sent in plain text.\n address_identifier.address_info.country_code = \"US\"\n address_identifier.address_info.postal_code = \"10011\"\n user_data_with_physical_address.user_identifiers.append(address_identifier)\n user_data_with_physical_address.transaction_attribute.conversion_action = (\n client.get_service(\"ConversionActionService\").conversion_action_path(\n customer_id, conversion_action_id\n )\n )\n user_data_with_physical_address.transaction_attribute.currency_code = \"EUR\"\n # Convert the transaction amount from 450 EUR to micros.\n user_data_with_physical_address.transaction_attribute.transaction_amount_micros = (\n 450000000\n )\n # Specify the date and time of the transaction. This date and time\n # will be interpreted by the API using the Google Ads customer's\n # time zone. The date/time must be in the format\n # \"yyyy-MM-dd hh:mm:ss\".\n user_data_with_physical_address.transaction_attribute.transaction_date_time = (\n datetime.now() - datetime.timedelta(days=1)\n ).strftime(\n \"%Y-%m-%d %H:%M:%S\"\n )\n\n # Optional: If uploading data with item attributes, also assign these\n # values in the transaction attribute\n if item_id:\n item_attribute: ItemAttribute = (\n user_data_with_physical_address.transaction_attribute.item_attribute\n )\n item_attribute.item_id = item_id\n item_attribute.merchant_id = merchant_center_account_id\n item_attribute.country_code = country_code\n item_attribute.language_code = language_code\n item_attribute.quantity = quantity\n\n return [\n user_data_with_email_address_operation,\n user_data_with_physical_address_operation,\n ]\n\n\ndef normalize_and_hash(s: str) -> str:\n \"\"\"Normalizes and hashes a string with SHA-256.\n\n Args:\n s: The string to perform this operation on.\n\n Returns:\n A normalized (lowercase, remove whitespace) and SHA-256 hashed string.\n \"\"\"\n return hashlib.sha256(s.strip().lower().encode()).hexdigest()\n\n\ndef check_job_status(\n client: GoogleAdsClient,\n customer_id: str,\n offline_user_data_job_resource_name: str,\n) -> None:\n \"\"\"Retrieves, checks, and prints the status of the offline user data job.\n\n Args:\n client: An initialized Google Ads API client.\n customer_id: The Google Ads customer ID.\n offline_user_data_job_resource_name: The resource name of the job whose\n status you wish to check.\n \"\"\"\n # Get the GoogleAdsService client.\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n # Construct a query to fetch the job status.\n query: str = f\"\"\"\n SELECT\n offline_user_data_job.resource_name,\n offline_user_data_job.id,\n offline_user_data_job.status,\n offline_user_data_job.type,\n offline_user_data_job.failure_reason\n FROM offline_user_data_job\n WHERE offline_user_data_job.resource_name =\n '{offline_user_data_job_resource_name}'\"\"\"\n\n # Issue the query and get the GoogleAdsRow containing the job.\n googleads_row: GoogleAdsRow = next(\n iter(googleads_service.search(customer_id=customer_id, query=query))\n )\n offline_user_data_job: OfflineUserDataJob = (\n googleads_row.offline_user_data_job\n )\n\n offline_user_data_job_type_enum: (\n OfflineUserDataJobTypeEnum.OfflineUserDataJobType\n ) = client.enums.OfflineUserDataJobTypeEnum.OfflineUserDataJobType\n offline_user_data_job_status_enum: (\n OfflineUserDataJobStatusEnum.OfflineUserDataJobStatus\n ) = client.enums.OfflineUserDataJobStatusEnum.OfflineUserDataJobStatus\n\n job_status: int = offline_user_data_job.status\n print(\n f\"Offline user data job ID {offline_user_data_job.id} with type \"\n f\"'{offline_user_data_job_type_enum.Name(offline_user_data_job.type)}' \"\n f\"has status {offline_user_data_job_status_enum.Name(job_status)}.\"\n )\n\n offline_user_data_job_status_enum_wrapper: OfflineUserDataJobStatusEnum = (\n client.enums.OfflineUserDataJobStatusEnum\n )\n if job_status == offline_user_data_job_status_enum_wrapper.FAILED:\n print(f\"\\tFailure reason: {offline_user_data_job.failure_reason}\")\n elif (\n job_status == offline_user_data_job_status_enum_wrapper.PENDING\n or job_status == offline_user_data_job_status_enum_wrapper.RUNNING\n ):\n print(\n \"\\nTo check the status of the job periodically, use the \"\n f\"following GAQL query with GoogleAdsService.Search:\\n{query}\\n\"\n )\n elif job_status == offline_user_data_job_status_enum_wrapper.SUCCESS:\n print(\"\\nThe requested job has completed successfully.\")\n else:\n raise ValueError(\"Requested job has UNKNOWN or UNSPECIFIED status.\")\n\n\nif __name__ == \"__main__\":\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n parser: argparse.ArgumentParser = argparse.ArgumentParser(\n description=\"This example uploads offline data for store sales \"\n \"transactions.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-a\",\n \"--conversion_action_id\",\n type=int,\n required=True,\n help=\"The ID of a store sales conversion action.\",\n )\n group: argparse._MutuallyExclusiveGroup = (\n parser.add_mutually_exclusive_group(required=False)\n )\n group.add_argument(\n \"-k\",\n \"--custom_key\",\n type=str,\n help=\"Only required after creating a custom key and custom values in \"\n \"the account. Custom key and values are used to segment store sales \"\n \"conversions. This measurement can be used to provide more advanced \"\n \"insights. If provided, a custom value must also be provided\",\n )\n group.add_argument(\n \"-v\",\n \"--custom_value\",\n type=str,\n help=\"Only required after creating a custom key and custom values in \"\n \"the account. Custom key and values are used to segment store sales \"\n \"conversions. This measurement can be used to provide more advanced \"\n \"insights. If provided, a custom key must also be provided\",\n )\n parser.add_argument(\n \"-o\",\n \"--offline_user_data_job_type\",\n type=int,\n required=False,\n default=googleads_client.enums.OfflineUserDataJobTypeEnum.STORE_SALES_UPLOAD_FIRST_PARTY,\n help=\"Optional type of offline user data in the job (first party or \"\n \"third party). If you have an official store sales partnership with \"\n \"Google, use STORE_SALES_UPLOAD_THIRD_PARTY. Otherwise, defaults to \"\n \"STORE_SALES_UPLOAD_FIRST_PARTY.\",\n )\n parser.add_argument(\n \"-e\",\n \"--external_id\",\n type=int,\n required=False,\n default=None,\n help=\"Optional, but recommended, external ID for the offline user data \"\n \"job.\",\n )\n parser.add_argument(\n \"-d\",\n \"--advertiser_upload_date_time\",\n type=str,\n required=False,\n default=None,\n help=\"Optional date and time the advertiser uploaded data to the \"\n \"partner. Only required for third party uploads. The format is \"\n \"'yyyy-mm-dd hh:mm:ss+|-hh:mm', e.g. '2021-01-01 12:32:45-08:00'.\",\n )\n parser.add_argument(\n \"-b\",\n \"--bridge_map_version_id\",\n type=str,\n required=False,\n default=None,\n help=\"Optional version of partner IDs to be used for uploads. Only \"\n \"required for third party uploads.\",\n )\n parser.add_argument(\n \"-p\",\n \"--partner_id\",\n type=int,\n required=False,\n default=None,\n help=\"Optional ID of the third party partner. Only required for third \"\n \"party uploads.\",\n )\n parser.add_argument(\n \"-i\",\n \"--item_id\",\n type=str,\n required=False,\n default=None,\n help=\"Optional ID of the product. Either the Merchant Center Item ID \"\n \"or the Global Trade Item Number (GTIN). Only required if uploading \"\n \"with item attributes.\",\n )\n parser.add_argument(\n \"-m\",\n \"--merchant_center_account_id\",\n type=int,\n required=False,\n default=None,\n help=\"Optional Merchant Center Account ID. Only required if uploading \"\n \"with item attributes.\",\n )\n parser.add_argument(\n \"-r\",\n \"--country_code\",\n type=str,\n required=False,\n default=None,\n help=\"Optional two-letter country code of the location associated with \"\n \"the feed where your items are uploaded. Only required if uploading \"\n \"with item attributes.\",\n )\n parser.add_argument(\n \"-l\",\n \"--language_code\",\n type=str,\n required=False,\n default=None,\n help=\"Optional two-letter language code of the language associated \"\n \"with the feed where your items are uploaded. Only required if \"\n \"uploading with item attributes.\",\n )\n parser.add_argument(\n \"-q\",\n \"--quantity\",\n type=int,\n required=False,\n default=1,\n help=\"Optional number of items sold. Only required if uploading with \"\n \"item attributes.\",\n )\n parser.add_argument(\n \"--ad_user_data_consent\",\n type=str,\n choices=[\n e.name\n for e in googleads_client.enums.ConsentStatusEnum\n if e.name not in (\"UNSPECIFIED\", \"UNKNOWN\")\n ],\n help=(\n \"The data consent status for ad user data for all members in \"\n \"the job.\"\n ),\n )\n parser.add_argument(\n \"--ad_personalization_consent\",\n type=str,\n choices=[\n e.name\n for e in googleads_client.enums.ConsentStatusEnum\n if e.name not in (\"UNSPECIFIED\", \"UNKNOWN\")\n ],\n help=(\n \"The personalization consent status for ad user data for all \"\n \"members in the job.\"\n ),\n )\n args: argparse.Namespace = parser.parse_args()\n\n # Additional check to make sure that custom_key and custom_value are either\n # not provided or both provided together.\n required_together: Tuple[str, str] = (\"custom_key\", \"custom_value\")\n required_custom_vals: List[Optional[str]] = [\n getattr(args, field, None) for field in required_together\n ]\n if any(required_custom_vals) and not all(required_custom_vals):\n parser.error(\n \"--custom_key (-k) and --custom_value (-v) must be passed \"\n \"in together\"\n )\n\n try:\n main(\n googleads_client,\n args.customer_id,\n args.conversion_action_id,\n args.offline_user_data_job_type,\n args.external_id,\n args.advertiser_upload_date_time,\n args.bridge_map_version_id,\n args.partner_id,\n args.custom_key,\n args.custom_value,\n args.item_id,\n args.merchant_center_account_id,\n args.country_code,\n args.language_code,\n args.quantity,\n args.ad_user_data_consent,\n args.ad_personalization_consent,\n )\n except GoogleAdsException as ex:\n print(\n f\"Request with ID '{ex.request_id}' failed with status \"\n f\"'{ex.error.code().name}' and includes the following errors:\"\n )\n for error in ex.failure.errors:\n print(f\"\\tError with message '{error.message}'.\")\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nupload_store_sales_transactions.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2020 Google LLC\n#\n# 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.\n#\n# Uploads offline data for store sales transactions.\n#\n# This feature is only available to allowlisted accounts. See\n# https://support.google.com/google-ads/answer/7620302 for more details.\n\nrequire 'date'\nrequire 'digest'\nrequire 'google/ads/google_ads'\nrequire 'optparse'\n\ndef upload_store_sales_transactions(\n customer_id,\n offline_user_data_job_type,\n conversion_action_id,\n external_id,\n advertiser_upload_date_time,\n bridge_map_version_id,\n partner_id,\n custom_key,\n custom_value,\n item_id,\n merchant_center_account_id,\n region_code,\n language_code,\n quantity,\n ad_user_data_consent,\n ad_personalization_consent)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n offline_user_data_job_service = client.service.offline_user_data_job\n\n # Creates an offline user data job for uploading transactions.\n offline_user_data_job_resource_name = create_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_type,\n external_id,\n advertiser_upload_date_time,\n bridge_map_version_id,\n partner_id,\n custom_key,\n )\n\n # Add transactions to the job\n add_transactions_to_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_resource_name,\n conversion_action_id,\n custom_value,\n ad_user_data_consent,\n ad_personalization_consent\n )\n\n # Issues an asynchronous request to run the offline user data job.\n offline_user_data_job_service.run_offline_user_data_job(\n resource_name: offline_user_data_job_resource_name,\n )\n\n puts \"Sent request to asynchronously run offline user data job: \" \\\n \"#{offline_user_data_job_resource_name}\"\n\n # Offline user data jobs may take up to 24 hours to complete, so instead of\n # waiting for the job to complete, retrieves and displays the job status once\n # and then prints the query to use to check the job again later.\n check_job_status(client, customer_id, offline_user_data_job_resource_name)\nend\n\n# Creates an offline user data job for uploading store sales transactions.\ndef create_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_type,\n external_id,\n advertiser_upload_date_time,\n bridge_map_version_id,\n partner_id,\n custom_key)\n # TIP: If you are migrating from the AdWords API, please note tha Google Ads\n # API uses the term \"fraction\" instead of \"rate\". For example, loyalty_rate\n # in the AdWords API is called loyalty_fraction in the Google Ads API.\n # Please refer to https://support.google.com/google-ads/answer/7506124 for\n # additional details.\n store_sales_metadata = client.resource.store_sales_metadata do |s|\n # Sets the fraction of your overall sales that you (or the advertiser, in\n # the third party case) can associate with a customer (email, phone number,\n # address, etc.) in your database or loyalty program.\n # For example, set this to 0.7 if you have 100 transactions over 30 days,\n # and out of those 100 transactions, you can identify 70 by an email address\n # or phone number.\n s.loyalty_fraction = 0.7\n # Sets the fraction of sales you're uploading out of the overall sales that\n # you (or the advertiser, in the third party case) can associate with a\n # customer. In most cases, you will set this to 1.0.\n # Continuing the example above for loyalty fraction, a value of 1.0 here\n # indicates that you are uploading all 70 of the transactions that can be\n # identified by an email address or phone number.\n s.transaction_upload_fraction = 1.0\n s.custom_key = custom_key unless custom_key.nil?\n end\n\n # Creates additional metadata required for uploading third party data.\n if offline_user_data_job_type == :STORE_SALES_UPLOAD_THIRD_PARTY\n store_sales_metadata.third_party_metadata =\n client.resource.store_sales_third_party_metadata do |t|\n # The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n t.advertiser_upload_date_time = advertiser_upload_date_time\n # Sets the fraction of transactions you received from the advertiser\n # that have valid formatting and values. This captures any transactions\n # the advertiser provided to you but which you are unable to upload to\n # Google due to formatting errors or missing data.\n # In most cases, you will set this to 1.0.\n t.valid_transaction_fraction = 1.0\n # Sets the fraction of valid transactions (as defined above) you received\n # from the advertiser that you (the third party) have matched to an\n # external user ID on your side.\n # In most cases, you will set this to 1.0.\n t.partner_match_fraction = 1.0\n # Sets the fraction of transactions you (the third party) are uploading\n # out of the transactions you received from the advertiser that meet both\n # of the following criteria:\n # 1. Are valid in terms of formatting and values. See valid transaction\n # fraction above.\n # 2. You matched to an external user ID on your side. See partner match\n # fraction above.\n # In most cases, you will set this to 1.0.\n t.partner_upload_fraction = 1.0\n # Please speak with your Google representative to get the values to use\n # for the bridge map version and partner IDs.\n # Sets the version of partner IDs to be used for uploads.\n t.bridge_map_version_id = bridge_map_version_id\n # Sets the third party partner ID uploading the transactions.\n t.partner_id = partner_id.to_i\n end\n end\n\n # Creates a new offline user data job.\n offline_user_data_job = client.resource.offline_user_data_job do |job|\n job.type = offline_user_data_job_type\n job.store_sales_metadata = store_sales_metadata\n end\n\n unless external_id.nil?\n offline_user_data_job.external_id = external_id.to_i\n end\n\n # Issues a request to create the offline user data job.\n response = offline_user_data_job_service.create_offline_user_data_job(\n customer_id: customer_id,\n job: offline_user_data_job,\n )\n\n offline_user_data_job_resource_name = response.resource_name\n puts \"Created an offline user data job with resource name: \" \\\n \"#{offline_user_data_job_resource_name}.\"\n\n offline_user_data_job_resource_name\nend\n\n# Adds operations to the job for a set of sample transactions.\ndef add_transactions_to_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_resource_name,\n conversion_action_id,\n custom_value,\n ad_user_data_consent,\n ad_personalization_consent)\n # Constructs the operation for each transaction.\n user_data_job_operations = build_offline_user_data_job_operations(\n client, customer_id, conversion_action_id, custom_value, ad_user_data_consent,\n ad_personalization_consent)\n\n # Issues a request to add the operations to the offline user data job.\n response = offline_user_data_job_service.add_offline_user_data_job_operations(\n resource_name: offline_user_data_job_resource_name,\n operations: user_data_job_operations,\n enable_partial_failure: true,\n enable_warnings: true,\n )\n\n # Prints errors if any partial failure error is returned.\n if response.partial_failure_error\n failures = client.decode_partial_failure_error(response.partial_failure_error)\n failures.each do |failure|\n failure.errors.each do |error|\n human_readable_error_path = error\n .location\n .field_path_elements\n .map { |location_info|\n if location_info.index\n \"#{location_info.field_name}[#{location_info.index}]\"\n else\n \"#{location_info.field_name}\"\n end\n }.join(\" > \")\n\n errmsg = \"error occured while adding operations \" \\\n \"#{human_readable_error_path}\" \\\n \" with value: #{error.trigger&.string_value}\" \\\n \" because #{error.message.downcase}\"\n puts errmsg\n end\n end\n end\n\n if response.warning\n # Convert to a GoogleAdsFailure.\n warnings = client.decode_warning(response.warning)\n puts \"Encountered #{warnings.errors.size} warning(s).\"\n end\n\n puts \"Successfully added #{user_data_job_operations.size} operations to \" \\\n \"the offline user data job.\"\nend\n\n# Creates a list of offline user data job operations for sample transactions.\ndef build_offline_user_data_job_operations(\n client,\n customer_id,\n conversion_action_id,\n custom_value,\n ad_user_data_consent,\n ad_personalization_consent)\n operations = []\n\n # Creates the first transaction for upload based on an email address\n # and state.\n operations << client.operation.create_resource.offline_user_data_job do |op|\n op.user_identifiers << client.resource.user_identifier do |id|\n # Email addresses must be normalized and hashed.\n id.hashed_email = normalize_and_hash(\"dana@example.com\")\n end\n op.user_identifiers << client.resource.user_identifier do |id|\n id.address_info = client.resource.offline_user_address_info do |info|\n info.state = \"NY\"\n end\n end\n op.transaction_attribute = client.resource.transaction_attribute do |t|\n t.conversion_action = client.path.conversion_action(\n customer_id, conversion_action_id)\n t.currency_code = \"USD\"\n # Converts the transaction amount from $200 USD to micros.\n t.transaction_amount_micros = 200_000_000\n # Specifies the date and time of the transaction. The format is\n # \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional timezone\n # offset from UTC. If the offset is absent, the API will use the\n # account's timezone as default. Examples: \"2018-03-05 09:15:00\" or\n # \"2018-02-01 14:34:30+03:00\".\n t.transaction_date_time = \"2020-05-01 23:52:12\"\n t.custom_value = custom_value unless custom_value.nil?\n end\n if !ad_user_data_consent.nil? || !ad_personalization_consent.nil?\n op.consent = client.resource.consent do |c|\n # Specifies whether user consent was obtained for the data you are\n # uploading. For more details, see:\n # https://www.google.com/about/company/user-consent-policy\n unless ad_user_data_consent.nil?\n c.ad_user_data = ad_user_data_consent\n end\n unless ad_personalization_consent.nil?\n c.ad_personalization = ad_personalization_consent\n end\n end\n end\n end\n\n # Creates the second transaction for upload based on a physical address.\n operations << client.operation.create_resource.offline_user_data_job do |op|\n op.user_identifiers << client.resource.user_identifier do |id|\n id.address_info = client.resource.offline_user_address_info do |info|\n # First and last name must be normalized and hashed.\n info.hashed_first_name = normalize_and_hash(\"Dana\")\n info.hashed_last_name = normalize_and_hash(\"Quinn\")\n # Country code and zip code are sent in plain text.\n info.country_code = \"US\"\n info.postal_code = \"10011\"\n end\n end\n op.transaction_attribute = client.resource.transaction_attribute do |t|\n t.conversion_action = client.path.conversion_action(\n customer_id, conversion_action_id)\n t.currency_code = \"EUR\"\n # Converts the transaction amount from 450 EUR to micros.\n t.transaction_amount_micros = 450_000_000\n # Specifies the date and time of the transaction. This date and time will\n # be interpreted by the API using the Google Ads customer's time zone.\n # The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n t.transaction_date_time = \"2020-05-14 19:07:02\"\n t.custom_value = custom_value unless custom_value.nil?\n if item_id\n t.item_attribute = client.resource.item_attribute do |item|\n item.item_id = item_id\n item.merchant_id = merchant_center_account_id.to_i\n item.region_code = region_code\n item.language_code = language_code\n item.quantity = quantity.to_i\n end\n end\n end\n end\n\n # Returns the operations containing the two transactions.\n operations\nend\n\n# Returns the result of normalizing and then hashing the string.\n# Private customer data must be hashed during upload, as described at\n# https://support.google.com/google-ads/answer/7506124.\ndef normalize_and_hash(str)\n Digest::SHA256.hexdigest(str.strip.downcase)\nend\n\n# Retrieves, checks, and prints the status of the offline user data job.\ndef check_job_status(\n client,\n customer_id,\n offline_user_data_job_resource_name)\n # Creates a query that retrieves the offline user data.\n query = <<~QUERY\n SELECT offline_user_data_job.resource_name,\n offline_user_data_job.id,\n offline_user_data_job.status,\n offline_user_data_job.type,\n offline_user_data_job.failure_reason\n FROM offline_user_data_job\n WHERE offline_user_data_job.resource_name = \"#{offline_user_data_job_resource_name}\"\n QUERY\n\n puts query\n\n # Issues a search stream request.\n responses = client.service.google_ads.search_stream(\n customer_id: customer_id,\n query: query,\n )\n\n # Prints out some information about the offline user data.\n offline_user_data_job = responses.first.results.first.offline_user_data_job\n puts \"Offline user data job ID #{offline_user_data_job.id} \" \\\n \"with type #{offline_user_data_job.type} \" \\\n \"has status: #{offline_user_data_job.status}\"\n\n if offline_user_data_job.status == :FAILED\n puts \" Failure reason: #{offline_user_data_job.failure_reason}\"\n elsif offline_user_data_job.status == :PENDING \\\n || offline_user_data_job.status == :RUNNING\n puts \"To check the status of the job periodically, use the following GAQL \" \\\n \"query with google_ads.search:\"\n puts query\n end\nend\n\nif __FILE__ == $0\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:conversion_action_id] = 'INSERT_CONVERSION_ACTION_ID_HERE'\n options[:offline_user_data_job_type] = \"STORE_SALES_UPLOAD_FIRST_PARTY\"\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-c', '--conversion-action-id CONVERSION-ACTION-ID', String,\n 'The ID of a store sales conversion action') do |v|\n options[:conversion_action_id] = v\n end\n\n opts.on('-T', '--offline-user-data-job-type OFFLINE-USER-DATA-JOB-TYPE', String,\n '(Optional) The type of user data in the job (first or third party). ' \\\n 'If you have an official store sales partnership with Google, ' \\\n 'use STORE_SALES_UPLOAD_THIRD_PARTY. Otherwise, use ' \\\n 'STORE_SALES_UPLOAD_FIRST_PARTY or omit this parameter.') do |v|\n options[:offline_user_data_job_type] = v\n end\n\n opts.on('-E', '--external-id EXTERNAL-ID', String,\n '(Optional, but recommended) external ID to identify the offline ' \\\n 'user data job') do |v|\n options[:external_id] = v\n end\n\n opts.on('-U', '--advertiser-upload-date-time ADVERTISER-UPLOAD-DATE-TIME', String,\n '(Only required if uploading third party data) Specify the date and time ' \\\n 'the advertiser uploaded data to the partner. ' \\\n 'The format is \"yyyy-mm-dd hh:mm:ss\"') do |v|\n options[:advertiser_upload_date_time] = v\n end\n\n opts.on('-B', '--bridge-map-version-id BRIDGE-MAP-VERSION-ID', String,\n '(Only required if uploading third party data) ' \\\n 'The version of partner IDs to be used for uploads.') do |v|\n options[:bridge_map_version_id] = v\n end\n\n opts.on('-P', '--partner-id PARTNER-ID', String,\n '(Only required if uploading third party data) ' \\\n 'The ID of the third party partner. ') do |v|\n options[:partner_id] = v\n end\n\n opts.on('-k' '--custom-key CUSTOM-KEY', String,\n 'Only required after creating a custom key and custom values in ' \\\n 'the account. Custom key and values are used to segment store sales ' \\\n 'conversions. This measurement can be used to provide more advanced ' \\\n 'insights. If provided, a custom value must also be provided') do |v|\n options[:custom_key] = v\n end\n\n opts.on('-v' '--custom-value CUSTOM-VALUE', String,\n 'Only required after creating a custom key and custom values in ' \\\n 'the account. Custom key and values are used to segment store sales ' \\\n 'conversions. This measurement can be used to provide more advanced ' \\\n 'insights. If provided, a custom key must also be provided') do |v|\n options[:custom_value] = v\n end\n\n opts.on('-i', '--item-id ITEM-ID', String,\n 'Optional: Specify a unique identifier of a product, either the ' \\\n 'Merchant Center Item ID or Global Trade Item Number (GTIN). ' \\\n 'Only required if uploading with item attributes.') do |v|\n options[:item_id] = v\n end\n\n opts.on('-m', '--merchant-center-account-id MERCHANT-CENTER-ACCOUNT-ID', String,\n 'Optional: Specify a Merchant Center Account ID. Only required if ' \\\n 'uploading with item attributes.') do |v|\n options[:merchant_center_account_id] = v\n end\n\n opts.on('-r', '--region-code REGION-CODE', String,\n 'Optional: Specify a two-letter region code of the location associated ' \\\n 'with the feed where your items are uploaded. Only required if ' \\\n 'uploading with item attributes.') do |v|\n options[:region_code] = v\n end\n\n opts.on('-L', '--language-code LANGUAGE-CODE', String,\n 'Optional: Specify a two-letter language code of the language ' \\\n 'associated with the feed where your items are uploaded. Only required ' \\\n 'if uploading with item attributes.') do |v|\n options[:language_code] = v\n end\n\n opts.on('-q', '--quantity QUANTITY', String,\n 'Optional: Specify a number of items sold. Only required if uploading ' \\\n 'with item attributes.') do |v|\n options[:quantity] = v\n end\n\n opts.on('-d', '--ad-user-data-consent [AD-USER-DATA_CONSENT]', String,\n 'The personalization consent status for ad user data for all members in the job.' \\\n 'e.g. UNKNOWN, GRANTED, DENIED') do |v|\n options[:ad_user_data_consent] = v\n end\n\n opts.on('-p', '--ad-personalization-consent [AD-PERSONALIZATION-CONSENT]', String,\n 'The personalization consent status for ad user data for all members in the job.' \\\n 'e.g. UNKNOWN, GRANTED, DENIED') do |v|\n options[:ad_personalization_consent] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n upload_store_sales_transactions(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options[:offline_user_data_job_type].to_sym,\n options.fetch(:conversion_action_id),\n options[:external_id],\n options[:advertiser_upload_date_time],\n options[:bridge_map_version_id],\n options[:partner_id],\n options[:custom_key],\n options[:custom_value],\n options[:item_id],\n options[:merchant_center_account_id],\n options[:region_code],\n options[:language_code],\n options[:quantity],\n options[:ad_user_data_consent],\n options[:ad_personalization_consent],\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n end\nend\nupload_store_sales_transactions.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2020, Google LLC\n#\n# 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# http://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.\n#\n# This example uploads offline data for store sales transactions.\n#\n# This feature is only available to allowlisted accounts.\n# See https://support.google.com/google-ads/answer/7620302 for more details.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\n\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::OfflineUserDataJob;\nuse Google::Ads::GoogleAds::V25::Common::Consent;\nuse Google::Ads::GoogleAds::V25::Common::ItemAttribute;\nuse Google::Ads::GoogleAds::V25::Common::OfflineUserAddressInfo;\nuse Google::Ads::GoogleAds::V25::Common::StoreSalesMetadata;\nuse Google::Ads::GoogleAds::V25::Common::StoreSalesThirdPartyMetadata;\nuse Google::Ads::GoogleAds::V25::Common::TransactionAttribute;\nuse Google::Ads::GoogleAds::V25::Common::UserData;\nuse Google::Ads::GoogleAds::V25::Common::UserIdentifier;\nuse Google::Ads::GoogleAds::V25::Enums::OfflineUserDataJobTypeEnum\n qw(STORE_SALES_UPLOAD_FIRST_PARTY STORE_SALES_UPLOAD_THIRD_PARTY);\nuse\n Google::Ads::GoogleAds::V25::Services::OfflineUserDataJobService::OfflineUserDataJobOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Digest::SHA qw(sha256_hex);\n\nuse constant POLL_FREQUENCY_SECONDS => 1;\nuse constant POLL_TIMEOUT_SECONDS => 60;\n# If uploading data with custom key and values, specify the value.\nuse constant CUSTOM_VALUE => \"INSERT_CUSTOM_VALUE_HERE\";\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\nmy $conversion_action_id = \"INSERT_CONVERSION_ACTION_ID_HERE\";\n\n# Optional: Specify the type of user data in the job (first or third party).\n# If you have an official store sales partnership with Google, use\n# STORE_SALES_UPLOAD_THIRD_PARTY.\n# Otherwise, use STORE_SALES_UPLOAD_FIRST_PARTY or omit this parameter.\nmy $offline_user_data_job_type = STORE_SALES_UPLOAD_FIRST_PARTY;\n# Optional: Specify an external ID below to identify the offline user data job.\n# If none is specified, this example will create an external ID.\nmy $external_id = undef;\n# Optional: Specify the custom key if uploading data with custom key and values.\nmy $custom_key = undef;\n# Optional: Specify an advertiser upload date time for third party data.\nmy $advertiser_upload_date_time = undef;\n# Optional: Specify a bridge map version ID for third party data.\nmy $bridge_map_version_id = undef;\n# Optional: Specify a partner ID for third party data.\nmy $partner_id = undef;\n# Optional: Specify a unique identifier of a product, either the Merchant Center\n# Item ID or Global Trade Item Number (GTIN). Only required if uploading with\n# item attributes.\nmy $item_id = undef;\n# Optional: Specify a Merchant Center Account ID. Only required if uploading\n# with item attributes.\nmy $merchant_center_account_id = undef;\n# Optional: Specify a two-letter country code of the location associated with the\n# feed where your items are uploaded. Only required if uploading with item\n# attributes.\nmy $country_code = undef;\n# Optional: Specify a two-letter language code of the language associated with\n# the feed where your items are uploaded. Only required if uploading with item\n# attributes.\nmy $language_code = undef;\n# Optional: Specify a number of items sold. Only required if uploading with item\n# attributes.\nmy $quantity = 1;\n# Optional: Specify the ad personalization consent status.\nmy $ad_personalization_consent = undef;\n# Optional: Specify the ad user data consent status.\nmy $ad_user_data_consent = undef;\n\nsub upload_store_sales_transactions {\n my (\n $api_client, $customer_id,\n $offline_user_data_job_type, $conversion_action_id,\n $external_id, $custom_key,\n $advertiser_upload_date_time, $bridge_map_version_id,\n $partner_id, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity,\n $$ad_personalization_consent, $ad_user_data_consent\n ) = @_;\n\n my $offline_user_data_job_service = $api_client->OfflineUserDataJobService();\n\n # Create an offline user data job for uploading transactions.\n my $offline_user_data_job_resource_name = create_offline_user_data_job(\n $offline_user_data_job_service, $customer_id,\n $offline_user_data_job_type, $external_id,\n $custom_key, $advertiser_upload_date_time,\n $bridge_map_version_id, $partner_id\n );\n\n # Add transactions to the job.\n add_transactions_to_offline_user_data_job(\n $offline_user_data_job_service, $customer_id,\n $offline_user_data_job_resource_name, $conversion_action_id,\n $custom_key, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity,\n );\n\n # Issue an asynchronous request to run the offline user data job.\n my $operation_response = $offline_user_data_job_service->run({\n resourceName => $offline_user_data_job_resource_name\n });\n print \"Asynchronous request to execute the added operations started.\\n\";\n print \"Waiting until operation completes.\\n\";\n\n # poll_until_done() implements a default back-off policy for retrying. You can\n # tweak the parameters like the poll timeout seconds by passing them to the\n # poll_until_done() method. Visit the OperationService.pm file for more details.\n my $lro = $api_client->OperationService()->poll_until_done({\n name => $operation_response->{name},\n pollFrequencySeconds => POLL_FREQUENCY_SECONDS,\n pollTimeoutSeconds => POLL_TIMEOUT_SECONDS\n });\n if ($lro->{done}) {\n printf \"Offline user data job with resource name '%s' has finished.\\n\",\n $offline_user_data_job_resource_name;\n } else {\n printf\n \"Offline user data job with resource name '%s' still pending after %d \" .\n \"seconds, continuing the execution of the code example anyway.\\n\",\n $offline_user_data_job_resource_name,\n POLL_TIMEOUT_SECONDS;\n }\n\n return 1;\n}\n\n# Creates an offline user data job for uploading store sales transactions.\n# Returns the resource name of the created job.\nsub create_offline_user_data_job {\n my (\n $offline_user_data_job_service, $customer_id,\n $offline_user_data_job_type, $external_id,\n $custom_key, $advertiser_upload_date_time,\n $bridge_map_version_id, $partner_id\n ) = @_;\n\n # TIP: If you are migrating from the AdWords API, please note that Google Ads\n # API uses the term \"fraction\" instead of \"rate\". For example, loyaltyRate in\n # the AdWords API is called loyaltyFraction in the Google Ads API.\n my $store_sales_metadata =\n # Please refer to https://support.google.com/google-ads/answer/7506124 for\n # additional details.\n Google::Ads::GoogleAds::V25::Common::StoreSalesMetadata->new({\n # Set the fraction of your overall sales that you (or the advertiser,\n # in the third party case) can associate with a customer (email, phone\n # number, address, etc.) in your database or loyalty program.\n # For example, set this to 0.7 if you have 100 transactions over 30\n # days, and out of those 100 transactions, you can identify 70 by an\n # email address or phone number.\n loyaltyFraction => 0.7,\n # Set the fraction of sales you're uploading out of the overall sales\n # that you (or the advertiser, in the third party case) can associate\n # with a customer. In most cases, you will set this to 1.0.\n # Continuing the example above for loyalty fraction, a value of 1.0 here\n # indicates that you are uploading all 70 of the transactions that can\n # be identified by an email address or phone number.\n transactionUploadFraction => 1.0\n });\n\n # Apply the custom key if provided.\n $store_sales_metadata->{customKey} = $custom_key if defined $custom_key;\n\n if ($offline_user_data_job_type eq STORE_SALES_UPLOAD_THIRD_PARTY) {\n # Create additional metadata required for uploading third party data.\n my $store_sales_third_party_metadata =\n Google::Ads::GoogleAds::V25::Common::StoreSalesThirdPartyMetadata->new({\n # The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n advertiserUploadDateTime => $advertiser_upload_date_time,\n\n # Set the fraction of transactions you received from the advertiser\n # that have valid formatting and values. This captures any transactions\n # the advertiser provided to you but which you are unable to upload to\n # Google due to formatting errors or missing data.\n # In most cases, you will set this to 1.0.\n validTransactionFraction => 1.0,\n # Set the fraction of valid transactions (as defined above) you received\n # from the advertiser that you (the third party) have matched to an\n # external user ID on your side.\n # In most cases, you will set this to 1.0.\n partnerMatchFraction => 1.0,\n\n # Set the fraction of transactions you (the third party) are uploading\n # out of the transactions you received from the advertiser that meet\n # both of the following criteria:\n # 1. Are valid in terms of formatting and values. See valid transaction\n # fraction above.\n # 2. You matched to an external user ID on your side. See partner match\n # fraction above.\n # In most cases, you will set this to 1.0.\n partnerUploadFraction => 1.0,\n\n # Please speak with your Google representative to get the values to use\n # for the bridge map version and partner IDs.\n\n # Set the version of partner IDs to be used for uploads.\n bridgeMapVersionId => $bridge_map_version_id,\n # Set the third party partner ID uploading the transactions.\n partnerId => $partner_id,\n });\n $store_sales_metadata->{thirdPartyMetadata} =\n $store_sales_third_party_metadata;\n }\n\n # Create a new offline user data job.\n my $offline_user_data_job =\n Google::Ads::GoogleAds::V25::Resources::OfflineUserDataJob->new({\n type => $offline_user_data_job_type,\n storeSalesMetadata => $store_sales_metadata,\n external_id => $external_id,\n });\n\n # Issue a request to create the offline user data job.\n my $create_offline_user_data_job_response =\n $offline_user_data_job_service->create({\n customerId => $customer_id,\n job => $offline_user_data_job\n });\n my $offline_user_data_job_resource_name =\n $create_offline_user_data_job_response->{resourceName};\n printf\n \"Created an offline user data job with resource name: '%s'.\\n\",\n $offline_user_data_job_resource_name;\n return $offline_user_data_job_resource_name;\n}\n\n# Adds operations to the job for a set of sample transactions.\nsub add_transactions_to_offline_user_data_job {\n my (\n $offline_user_data_job_service, $customer_id,\n $offline_user_data_job_resource_name, $conversion_action_id,\n $custom_key, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity\n ) = @_;\n\n # Construct the operation for each transaction.\n my $user_data_job_operations = build_offline_user_data_job_operations(\n $customer_id, $conversion_action_id,\n $custom_key, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity,\n $ad_personalization_consent, $ad_user_data_consent\n );\n\n # Issue a request to add the operations to the offline user data job.\n my $response = $offline_user_data_job_service->add_operations({\n resourceName => $offline_user_data_job_resource_name,\n enablePartialFailure => \"true\",\n # Enable warnings (optional).\n enableWarnings => \"true\",\n operations => $user_data_job_operations\n });\n\n # Print the status message if any partial failure error is returned.\n # Note: The details of each partial failure error are not printed here, you\n # can refer to the example handle_partial_failure.pl to learn more.\n if ($response->{partialFailureError}) {\n # Extract the partial failure from the response status.\n my $partial_failure = $response->{partialFailureError}{details}[0];\n foreach my $error (@{$partial_failure->{errors}}) {\n printf \"Partial failure occurred: '%s'\\n\", $error->{message};\n }\n printf \"Encountered %d partial failure errors while adding %d operations \" .\n \"to the offline user data job: '%s'. Only the successfully added \" .\n \"operations will be executed when the job runs.\\n\",\n scalar @{$partial_failure->{errors}}, scalar @$user_data_job_operations,\n $response->{partialFailureError}{message};\n } else {\n printf \"Successfully added %d operations to the offline user data job.\\n\",\n scalar @$user_data_job_operations;\n }\n\n # Print the number of warnings if any warnings are returned. You can access\n # details of each warning using the same approach you'd use for partial failure\n # errors.\n if ($response->{warning}) {\n # Extract the warnings from the response status.\n my $warnings_failure = $response->{warning}{details}[0];\n printf \"Encountered %d warning(s).\\n\",\n scalar @{$warnings_failure->{errors}};\n }\n}\n\n# Creates a list of offline user data job operations for sample transactions.\n# Returns a list of operations.\nsub build_offline_user_data_job_operations {\n my (\n $customer_id, $conversion_action_id,\n $custom_key, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity,\n $ad_personalization_consent, $ad_user_data_consent\n ) = @_;\n\n # Create the first transaction for upload based on an email address and state.\n my $user_data_with_email_address =\n Google::Ads::GoogleAds::V25::Common::UserData->new({\n userIdentifiers => [\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n # Hash normalized email addresses based on SHA-256 hashing algorithm.\n hashedEmail => normalize_and_hash('dana@example.com')}\n ),\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n addressInfo =>\n Google::Ads::GoogleAds::V25::Common::OfflineUserAddressInfo->new({\n state => \"NY\"\n })})\n ],\n transactionAttribute =>\n Google::Ads::GoogleAds::V25::Common::TransactionAttribute->new({\n conversionAction =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::conversion_action(\n $customer_id, $conversion_action_id\n ),\n currencyCode => \"USD\",\n # Convert the transaction amount from $200 USD to micros.\n transactionAmountMicros => 200000000,\n # Specify the date and time of the transaction. The format is\n # \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional timezone\n # offset from UTC. If the offset is absent, the API will use the\n # account's timezone as default. Examples: \"2018-03-05 09:15:00\"\n # or \"2018-02-01 14:34:30+03:00\".\n transactionDateTime => \"2020-05-01 23:52:12\",\n })});\n\n # Add consent information if specified.\n if ($ad_personalization_consent or $ad_user_data_consent) {\n # Specify whether user consent was obtained for the data you are uploading.\n # See https://www.google.com/about/company/user-consent-policy for details.\n $user_data_with_email_address->{consent} =\n Google::Ads::GoogleAds::V25::Common::Consent({\n adPersonalization => $ad_personalization_consent,\n adUserData => $ad_user_data_consent\n });\n }\n\n # Optional: If uploading data with custom key and values, also assign the\n # custom value.\n if (defined($custom_key)) {\n $user_data_with_email_address->{transactionAttribute}{customValue} =\n CUSTOM_VALUE;\n }\n\n # Create the second transaction for upload based on a physical address.\n my $user_data_with_physical_address =\n Google::Ads::GoogleAds::V25::Common::UserData->new({\n userIdentifiers => [\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n addressInfo =>\n Google::Ads::GoogleAds::V25::Common::OfflineUserAddressInfo->new({\n # First and last name must be normalized and hashed.\n hashedFirstName => normalize_and_hash(\"Dana\"),\n hashedLastName => normalize_and_hash(\"Quinn\"),\n # Country code and zip code are sent in plain text.\n countryCode => \"US\",\n postalCode => \"10011\"\n })})\n ],\n transactionAttribute =>\n Google::Ads::GoogleAds::V25::Common::TransactionAttribute->new({\n conversionAction =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::conversion_action(\n $customer_id,\n $conversion_action_id\n ),\n currencyCode => \"EUR\",\n # Convert the transaction amount from 450 EUR to micros.\n transactionAmountMicros => 450000000,\n # Specify the date and time of the transaction. This date and time\n # will be interpreted by the API using the Google Ads customer's\n # time zone. The date/time must be in the format\n # \"yyyy-MM-dd hh:mm:ss\".\n transactionDateTime => \"2020-05-14 19:07:02\",\n })});\n\n # Optional: If uploading data with item attributes, also assign these values\n # in the transaction attribute.\n if (defined($item_id)) {\n $user_data_with_physical_address->{transactionAttribute}{itemAttribute} =\n Google::Ads::GoogleAds::V25::Common::ItemAttribute->new({\n itemId => $item_id,\n merchantId => $merchant_center_account_id,\n countryCode => $country_code,\n languageCode => $language_code,\n # Quantity field should only be set when at least one of the other item\n # attributes is present.\n quantity => $quantity\n });\n\n }\n\n # Create the operations to add the two transactions.\n my $operations = [\n Google::Ads::GoogleAds::V25::Services::OfflineUserDataJobService::OfflineUserDataJobOperation\n ->new({\n create => $user_data_with_email_address\n }\n ),\n Google::Ads::GoogleAds::V25::Services::OfflineUserDataJobService::OfflineUserDataJobOperation\n ->new({\n create => $user_data_with_physical_address\n })];\n\n return $operations;\n}\n\n# Returns the result of normalizing and then hashing the string using the\n# provided digest. Private customer data must be hashed during upload, as\n# described at https://support.google.com/google-ads/answer/7506124\nsub normalize_and_hash {\n my $value = shift;\n\n $value =~ s/^\\s+|\\s+$//g;\n return sha256_hex(lc $value);\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"offline_user_data_job_type=s\" => \\$offline_user_data_job_type,\n \"conversion_action_id=i\" => \\$conversion_action_id,\n \"external_id=i\" => \\$external_id,\n \"custom_key=s\" => \\$custom_key,\n \"advertiser_upload_date_time=s\" => \\$advertiser_upload_date_time,\n \"bridge_map_version_id=i\" => \\$bridge_map_version_id,\n \"partner_id=i\" => \\$partner_id,\n \"item_id=s\" => \\$item_id,\n \"merchant_center_account_id=i\" => \\$merchant_center_account_id,\n \"country_code=s\" => \\$country_code,\n \"language_code=s\" => \\$language_code,\n \"quantity=i\" => \\$quantity,\n \"ad_personalization_consent=s\" => \\$ad_personalization_consent,\n \"ad_user_data_consent=s\" => \\$ad_user_data_consent\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2)\n if not check_params($customer_id, $conversion_action_id);\n\n# Call the example.\nupload_store_sales_transactions(\n $api_client, $customer_id =~ s/-//gr,\n $offline_user_data_job_type, $conversion_action_id,\n $external_id, $custom_key,\n $advertiser_upload_date_time, $bridge_map_version_id,\n $partner_id, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity,\n $ad_personalization_consent, $ad_user_data_consent\n);\n\n=pod\n\n=head1 NAME\n\nupload_store_sales_transactions\n\n=head1 DESCRIPTION\n\nThis example uploads offline data for store sales transactions.\n\nThis feature is only available to allowlisted accounts.\nSee https://support.google.com/google-ads/answer/7620302 for more details.\n\n=head1 SYNOPSIS\n\nupload_store_sales_transactions.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -conversion_action_id The ID of a store sales conversion action.\n -offline_user_data_job_type [optional] The type of offline user data in the job (first party or third party).\n If you have an official store sales partnership with Google, use STORE_SALES_UPLOAD_THIRD_PARTY.\n Otherwise, use STORE_SALES_UPLOAD_FIRST_PARTY.\n -external_id [optional] (but recommended) external ID for the offline user data job.\n -custom_key [optional] Only required after creating a custom key and custom values in the account. Custom key\n and values are used to segment store sales conversions. This measurement can be used to provide\n more advanced insights.\n -advertiser_upload_date_time [optional] Date and time the advertiser uploaded data to the partner. Only required for third party uploads.\n The format is \"yyyy-mm-dd hh:mm:ss+|-hh:mm\", e.g. \"2019-01-01 12:32:45-08:00\".\n -bridge_map_version_id [optional] Version of partner IDs to be used for uploads. Only required for third party uploads.\n -partner_id [optional] ID of the third party partner. Only required for third party uploads.\n -item_id [optional] A unique identifier of a product, either the Merchant Center Item ID or Global Trade Item Number (GTIN).\n Only required if uploading with item attributes.\n -merchant_center_account_id [optional] A Merchant Center Account ID. Only required if uploading with item attributes.\n -country_code [optional] A two-letter country code of the location associated with the feed where your items are uploaded.\n Only required if uploading with item attributes.\n For a list of country codes see: https://developers.google.com/google-ads/api/reference/data/codes-formats#country-codes\n -language_code [optional] A two-letter language code of the language associated with the feed where your items are uploaded.\n Only required if uploading with item attributes.\n For a list of language codes see: https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n -quantity [optional] The number of items sold. Can only be set when at least one other item attribute has been provided.\n Only required if uploading with item attributes.\n -ad_personalization_consent\t\t[optional] The ad personalization consent status.\n\t-ad_user_data_consent\t\t\t[optional] The ad user data consent status.\n\n=cut\nupload_store_sales_transactions.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.616Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":4194,"estimatedTokens":47570}}245{"id":"doc-partial_failure_google_ads_api_google_for_develo-763e0aa1","source":"documentation","title":"Partial Failure | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/best-practices/partial-failures","text":"Example:\n```text\nmutateAdGroups(String.valueOf(customerId), operations, true)\n```\n\nExample:\n```text\nMutateAdGroups(customerId.ToString(), operations, true, false)\n```\n\nExample:\n```text\nmutateAdGroups($customerId, $operations, ['partialFailure' => true])\n```\n\nExample:\n```text\nmutate_ad_groups(customer_id, operations, partial_failure=True)\n```\n\nExample:\n```text\nmutate_ad_groups(customer_id, operations, partial_failure: true)\n```\n\nExample:\n```text\nmutate({customerId => $customer_id, operations => $operations, partialFailure => 'true'})\n```\n\nExample:\n```text\nprivate MutateAdGroupsResponse createAdGroups(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n // This AdGroup should be created successfully - assuming the campaign in the params exists.\n AdGroup group1 =\n AdGroup.newBuilder()\n .setCampaign(ResourceNames.campaign(customerId, campaignId))\n .setName(\"Valid AdGroup: \" + getPrintableDateTime())\n .build();\n // This AdGroup will always fail - campaign ID 0 in resource names is never valid.\n AdGroup group2 =\n AdGroup.newBuilder()\n .setCampaign(ResourceNames.campaign(customerId, 0L))\n .setName(\"Broken AdGroup: \" + getPrintableDateTime())\n .build();\n // This AdGroup will always fail - duplicate ad group names are not allowed.\n AdGroup group3 =\n AdGroup.newBuilder()\n .setCampaign(ResourceNames.campaign(customerId, campaignId))\n .setName(group1.getName())\n .build();\n\n AdGroupOperation op1 = AdGroupOperation.newBuilder().setCreate(group1).build();\n AdGroupOperation op2 = AdGroupOperation.newBuilder().setCreate(group2).build();\n AdGroupOperation op3 = AdGroupOperation.newBuilder().setCreate(group3).build();\n\n try (AdGroupServiceClient service =\n googleAdsClient.getLatestVersion().createAdGroupServiceClient()) {\n // Issues the mutate request, setting partialFailure=true.\n return service.mutateAdGroups(\n MutateAdGroupsRequest.newBuilder()\n .setCustomerId(String.valueOf(customerId))\n .setCustomerId(Long.toString(customerId))\n .addAllOperations(Arrays.asList(op1, op2, op3))\n .setPartialFailure(true)\n .build());\n }\n}HandlePartialFailure.java\n```\n\nExample:\n```text\nprivate static MutateAdGroupsResponse CreateAdGroups(GoogleAdsClient client,\n long customerId, long campaignId)\n{\n // Get the AdGroupServiceClient.\n AdGroupServiceClient adGroupService = client.GetService(Services.V25.AdGroupService);\n\n string validAdGroupName = \"Valid AdGroup: \" + ExampleUtilities.GetRandomString();\n\n AdGroupOperation[] operations = new AdGroupOperation[]\n {\n // This operation will be successful, assuming the campaign specified in\n // campaignId parameter is correct.\n new AdGroupOperation()\n {\n Create = new AdGroup()\n {\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n Name = validAdGroupName\n }\n },\n // This operation will fail since we are using campaign ID = 0, which results\n // in an invalid resource name.\n new AdGroupOperation()\n {\n Create = new AdGroup()\n {\n Campaign = ResourceNames.Campaign(customerId, 0),\n Name = \"Broken AdGroup: \" + ExampleUtilities.GetRandomString()\n },\n },\n // This operation will fail since the ad group is using the same name as the ad\n // group from the first operation. Duplicate ad group names are not allowed.\n new AdGroupOperation()\n {\n Create = new AdGroup()\n {\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n Name = validAdGroupName\n }\n }\n };\n\n // Add the ad groups.\n MutateAdGroupsResponse response =\n adGroupService.MutateAdGroups(new MutateAdGroupsRequest()\n {\n CustomerId = customerId.ToString(),\n Operations = { operations },\n PartialFailure = true,\n ValidateOnly = false\n });\n return response;\n}HandlePartialFailure.cs\n```\n\nExample:\n```text\nprivate static function createAdGroups(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n) {\n $campaignResourceName = ResourceNames::forCampaign($customerId, $campaignId);\n\n // This ad group should be created successfully - assuming the campaign in the params\n // exists.\n $adGroup1 = new AdGroup([\n 'name' => 'Valid AdGroup #' . Helper::getPrintableDatetime(),\n 'campaign' => $campaignResourceName\n ]);\n\n // This ad group will always fail - campaign ID 0 in the resource name is never valid.\n $adGroup2 = new AdGroup([\n 'name' => 'Broken AdGroup #' . Helper::getPrintableDatetime(),\n 'campaign' => ResourceNames::forCampaign($customerId, 0)\n ]);\n\n // This ad group will always fail - duplicate ad group names are not allowed.\n $adGroup3 = new AdGroup([\n 'name' => $adGroup1->getName(),\n 'campaign' => $campaignResourceName\n ]);\n\n $operations = [];\n\n $adGroupOperation1 = new AdGroupOperation();\n $adGroupOperation1->setCreate($adGroup1);\n $operations[] = $adGroupOperation1;\n\n $adGroupOperation2 = new AdGroupOperation();\n $adGroupOperation2->setCreate($adGroup2);\n $operations[] = $adGroupOperation2;\n\n $adGroupOperation3 = new AdGroupOperation();\n $adGroupOperation3->setCreate($adGroup3);\n $operations[] = $adGroupOperation3;\n\n // Issues the mutate request, enabling partial failure mode.\n $adGroupServiceClient = $googleAdsClient->getAdGroupServiceClient();\n return $adGroupServiceClient->mutateAdGroups(\n MutateAdGroupsRequest::build($customerId, $operations)->setPartialFailure(true)\n );\n}HandlePartialFailure.php\n```\n\nExample:\n```text\ndef create_ad_groups(\n client: GoogleAdsClient, customer_id: str, campaign_id: str\n) -> MutateAdGroupsResponse:\n \"\"\"Creates three Ad Groups, two of which intentionally generate errors.\n\n Args:\n client: An initialized GoogleAdsClient instance.\n customer_id: A valid customer account ID.\n campaign_id: The ID for a campaign to create Ad Groups under.\n\n Returns: A MutateAdGroupsResponse message instance.\n \"\"\"\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n resource_name: str = campaign_service.campaign_path(\n customer_id, campaign_id\n )\n\n invalid_resource_name: str = campaign_service.campaign_path(customer_id, 0)\n ad_group_operations: List[AdGroupOperation] = []\n\n # This AdGroup should be created successfully - assuming the campaign in\n # the params exists.\n ad_group_op1: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group_op1.create.name = f\"Valid AdGroup: {uuid.uuid4()}\"\n ad_group_op1.create.campaign = resource_name\n ad_group_operations.append(ad_group_op1)\n\n # This AdGroup will always fail - campaign ID 0 in resource names is\n # never valid.\n ad_group_op2: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group_op2.create.name = f\"Broken AdGroup: {uuid.uuid4()}\"\n ad_group_op2.create.campaign = invalid_resource_name\n ad_group_operations.append(ad_group_op2)\n\n # This AdGroup will always fail - duplicate ad group names are not allowed.\n ad_group_op3: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group_op3.create.name = ad_group_op1.create.name\n ad_group_op3.create.campaign = resource_name\n ad_group_operations.append(ad_group_op3)\n\n # Issue a mutate request, setting partial_failure=True.\n request: MutateAdGroupsRequest = client.get_type(\"MutateAdGroupsRequest\")\n request.customer_id = customer_id\n request.operations = ad_group_operations\n request.partial_failure = True\n return ad_group_service.mutate_ad_groups(request=request)handle_partial_failure.py\n```\n\nExample:\n```text\ndef add_ad_groups(customer_id, campaign_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n ad_groups = []\n # This ad group should be created successfully.\n ad_groups << client.resource.ad_group do |ag|\n ag.campaign = client.path.campaign(customer_id, campaign_id)\n ag.name = \"Valid ad group: #{(Time.new.to_f * 1000).to_i}\"\n end\n # This ad group should fail to create because it references an invalid campaign.\n ad_groups << client.resource.ad_group do |ag|\n ag.campaign = client.path.campaign(customer_id, 0)\n ag.name = \"Invalid ad group: #{(Time.new.to_f * 1000).to_i}\"\n end\n # This ad group should fail to create because it duplicates the name from the first one.\n ad_groups << client.resource.ad_group do |ag|\n ag.campaign = client.path.campaign(customer_id, campaign_id)\n ag.name = ad_groups.first.name\n end\n\n operations = ad_groups.map do |ag|\n client.operation.create_resource.ad_group(ag)\n end\n\n response = client.service.ad_group.mutate_ad_groups(\n customer_id: customer_id,\n operations: operations,\n partial_failure: true,\n )\n\n response.results.each_with_index do |ad_group, i|\n if ad_group.resource_name != \"\"\n puts(\"operations[#{i}] succeeded: Created ad group with id #{ad_group.resource_name}\")\n end\n end\n\n failures = client.decode_partial_failure_error(response.partial_failure_error)\n failures.each do |failure|\n failure.errors.each do |error|\n human_readable_error_path = error\n .location\n .field_path_elements\n .map { |location_info|\n if location_info.index\n \"#{location_info.field_name}[#{location_info.index}]\"\n else\n \"#{location_info.field_name}\"\n end\n }.join(\" > \")\n\n errmsg = \"error occured creating ad group #{human_readable_error_path}\" \\\n \" with value: #{error.trigger.string_value}\" \\\n \" because #{error.message.downcase}\"\n puts errmsg\n end\n end\nendhandle_partial_failure.rb\n```\n\nExample:\n```text\nsub create_ad_groups {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n my $campaign_resource_name =\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign($customer_id,\n $campaign_id);\n\n # This ad group should be created successfully - assuming the campaign in the\n # params exists.\n my $ad_group1 = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Valid AdGroup: \" . uniqid(),\n campaign => $campaign_resource_name\n });\n\n # This ad group will always fail - campaign ID 0 in the resource name is never\n # valid.\n my $ad_group2 = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Broken AdGroup: \" . uniqid(),\n campaign => Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, 0\n )});\n\n # This ad group will always fail - duplicate ad group names are not allowed.\n my $ad_group3 = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => $ad_group1->{name},\n campaign => $campaign_resource_name\n });\n\n # Create ad group operations.\n my $ad_group_operation1 =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group1});\n my $ad_group_operation2 =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group2});\n my $ad_group_operation3 =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group3});\n\n # Issue the mutate request, enabling partial failure mode.\n my $ad_groups_response = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations =>\n [$ad_group_operation1, $ad_group_operation2, $ad_group_operation3],\n partialFailure => \"true\"\n });\n\n return $ad_groups_response;\n}handle_partial_failure.pl\n```\n\nExample:\n```text\nprivate boolean checkIfPartialFailureErrorExists(MutateAdGroupsResponse response) {\n return response.hasPartialFailureError();\n}HandlePartialFailure.java\n```\n\nExample:\n```text\nprivate static bool CheckIfPartialFailureErrorExists(MutateAdGroupsResponse response)\n{\n return response.PartialFailureError != null;\n}HandlePartialFailure.cs\n```\n\nExample:\n```text\nprivate static function checkIfPartialFailureErrorExists(MutateAdGroupsResponse $response)\n{\n if ($response->hasPartialFailureError()) {\n printf(\"Partial failures occurred. Details will be shown below.%s\", PHP_EOL);\n } else {\n printf(\n \"All operations completed successfully. No partial failures to show.%s\",\n PHP_EOL\n );\n }\n}HandlePartialFailure.php\n```\n\nExample:\n```text\ndef is_partial_failure_error_present(response: MutateAdGroupsResponse) -> bool:\n \"\"\"Checks whether a response message has a partial failure error.\n\n In Python the partial_failure_error attr is always present on a response\n message and is represented by a google.rpc.Status message. So we can't\n simply check whether the field is present, we must check that the code is\n non-zero. Error codes are represented by the google.rpc.Code proto Enum:\n https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto\n\n Args:\n response: A MutateAdGroupsResponse message instance.\n\n Returns: A boolean, whether or not the response message has a partial\n failure error.\n \"\"\"\n partial_failure: Any = getattr(response, \"partial_failure_error\", None)\n code: int = int(getattr(partial_failure, \"code\", 0)) # Default to 0 if None\n return code != 0handle_partial_failure.py\n```\n\nExample:\n```text\nfailures = client.decode_partial_failure_error(response.partial_failure_error)\nfailures.each do |failure|\n failure.errors.each do |error|\n human_readable_error_path = error\n .location\n .field_path_elements\n .map { |location_info|\n if location_info.index\n \"#{location_info.field_name}[#{location_info.index}]\"\n else\n \"#{location_info.field_name}\"\n end\n }.join(\" > \")\n\n errmsg = \"error occured creating ad group #{human_readable_error_path}\" \\\n \" with value: #{error.trigger.string_value}\" \\\n \" because #{error.message.downcase}\"\n puts errmsg\n end\nendhandle_partial_failure.rb\n```\n\nExample:\n```text\nsub check_if_partial_failure_error_exists {\n my $ad_groups_response = shift;\n\n if ($ad_groups_response->{partialFailureError}) {\n print \"Partial failures occurred. Details will be shown below.\\n\";\n } else {\n print\n \"All operations completed successfully. No partial failures to show.\\n\";\n }\n}handle_partial_failure.pl\n```\n\nExample:\n```text\nprivate void printResults(MutateAdGroupsResponse response) {\n int operationIndex = 0;\n for (MutateAdGroupResult result : response.getResultsList()) {\n if (ErrorUtils.getInstance().isPartialFailureResult(result)) {\n // May throw on this line. Most likely this means the wrong version of the ErrorUtils\n // class has been used.\n GoogleAdsFailure googleAdsFailure = ErrorUtils.getInstance()\n .getGoogleAdsFailure(response.getPartialFailureError());\n\n for (GoogleAdsError error :\n ErrorUtils.getInstance()\n .getGoogleAdsErrors(operationIndex, googleAdsFailure)) {\n System.out.printf(\"Operation %d failed with error: %s%n\", operationIndex, error);\n }\n } else {\n System.out.printf(\"Operation %d succeeded.%n\", operationIndex);\n }\n ++operationIndex;\n }\n}HandlePartialFailure.java\n```\n\nExample:\n```text\nprivate void PrintResults(MutateAdGroupsResponse response)\n{\n // Finds the failed operations by looping through the results.\n int operationIndex = 0;\n foreach (MutateAdGroupResult result in response.Results)\n {\n // This represents the result of a failed operation.\n if (result.IsEmpty())\n {\n List<GoogleAdsError> errors =\n response.PartialFailure.GetErrorsByOperationIndex(operationIndex);\n foreach (GoogleAdsError error in errors)\n {\n Console.WriteLine($\"Operation {operationIndex} failed with \" +\n $\"error: {error}.\");\n }\n }\n else\n {\n Console.WriteLine($\"Operation {operationIndex} succeeded.\",\n operationIndex);\n }\n operationIndex++;\n }\n}HandlePartialFailure.cs\n```\n\nExample:\n```text\nprivate static function printResults(MutateAdGroupsResponse $response)\n{\n // Finds the failed operations by looping through the results.\n $operationIndex = 0;\n foreach ($response->getResults() as $result) {\n /** @var AdGroup $result */\n if (PartialFailures::isPartialFailure($result)) {\n $errors = GoogleAdsErrors::fromStatus(\n $operationIndex,\n $response->getPartialFailureError()\n );\n foreach ($errors as $error) {\n printf(\n \"Operation %d failed with error: %s%s\",\n $operationIndex,\n $error->getMessage(),\n PHP_EOL\n );\n }\n } else {\n printf(\n \"Operation %d succeeded: ad group with resource name '%s'.%s\",\n $operationIndex,\n $result->getResourceName(),\n PHP_EOL\n );\n }\n $operationIndex++;\n }\n}HandlePartialFailure.php\n```\n\nExample:\n```text\ndef print_results(\n client: GoogleAdsClient, response: MutateAdGroupsResponse\n) -> None:\n \"\"\"Prints partial failure errors and success messages from a response.\n\n This function shows how to retrieve partial_failure errors from a response\n message (in the case of this example the message will be of type\n MutateAdGroupsResponse) and how to unpack those errors to GoogleAdsFailure\n instances. It also shows that a response with partial failures may still\n contain successful requests, and that those messages should be parsed\n separately. As an example, a GoogleAdsFailure object from this example will\n be structured similar to:\n\n error_code {\n range_error: TOO_LOW\n }\n message: \"Too low.\"\n trigger {\n string_value: \"\"\n }\n location {\n field_path_elements {\n field_name: \"operations\"\n index {\n value: 1\n }\n }\n field_path_elements {\n field_name: \"create\"\n }\n field_path_elements {\n field_name: \"campaign\"\n }\n }\n\n Args:\n client: an initialized GoogleAdsClient.\n response: a MutateAdGroupsResponse instance.\n \"\"\"\n # Check for existence of any partial failures in the response.\n if is_partial_failure_error_present(response):\n print(\"Partial failures occurred. Details will be shown below.\\n\")\n # Prints the details of the partial failure errors.\n partial_failure: Any = getattr(response, \"partial_failure_error\", None)\n # partial_failure_error.details is a repeated field and iterable\n error_details: List[Any] = getattr(partial_failure, \"details\", [])\n\n for error_detail in error_details:\n # Retrieve an instance of the GoogleAdsFailure class from the client\n failure_message: Any = client.get_type(\"GoogleAdsFailure\")\n # Parse the string into a GoogleAdsFailure message instance.\n # To access class-only methods on the message we retrieve its type.\n GoogleAdsFailure: Any = type(failure_message)\n failure_object: Any = GoogleAdsFailure.deserialize(\n error_detail.value\n )\n\n for error in failure_object.errors:\n # Construct and print a string that details which element in\n # the above ad_group_operations list failed (by index number)\n # as well as the error message and error code.\n print(\n \"A partial failure at index \"\n f\"{error.location.field_path_elements[0].index} occurred \"\n f\"\\nError message: {error.message}\\nError code: \"\n f\"{error.error_code}\"\n )\n else:\n print(\n \"All operations completed successfully. No partial failure \"\n \"to show.\"\n )\n\n # In the list of results, operations from the ad_group_operation list\n # that failed will be represented as empty messages. This loop detects\n # such empty messages and ignores them, while printing information about\n # successful operations.\n for message in response.results:\n if not message:\n continue\n\n print(f\"Created ad group with resource_name: {message.resource_name}.\")handle_partial_failure.py\n```\n\nExample:\n```text\nsub print_results {\n my $ad_groups_response = shift;\n\n # Find the failed operations by looping through the results.\n while (my ($operation_index, $result) =\n each @{$ad_groups_response->{results}})\n {\n if (is_partial_failure_result($result)) {\n my $google_ads_errors = get_google_ads_errors($operation_index,\n $ad_groups_response->{partialFailureError});\n\n foreach my $google_ads_error (@$google_ads_errors) {\n printf \"Operation %d failed with error: %s\\n\", $operation_index,\n $google_ads_error->{message};\n }\n } else {\n printf \"Operation %d succeeded: ad group with resource name '%s'.\\n\",\n $operation_index, $result->{resourceName};\n }\n }\n}handle_partial_failure.pl\n```\n\nExample:\n```text\n// Copyright 2019 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.errorhandling;\n\nimport static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.errors.GoogleAdsFailure;\nimport com.google.ads.googleads.v25.resources.AdGroup;\nimport com.google.ads.googleads.v25.services.AdGroupOperation;\nimport com.google.ads.googleads.v25.services.AdGroupServiceClient;\nimport com.google.ads.googleads.v25.services.MutateAdGroupResult;\nimport com.google.ads.googleads.v25.services.MutateAdGroupsRequest;\nimport com.google.ads.googleads.v25.services.MutateAdGroupsResponse;\nimport com.google.ads.googleads.v25.utils.ErrorUtils;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.Arrays;\n\n/**\n * Shows how to handle partial failures. There are several ways of detecting partial failures. This\n * highlights the top main detection options: empty results and error instances.\n *\n * <p>Access to the detailed error (<code>GoogleAdsFailure</code>) for each error is via a Any\n * proto. Deserializing these to retrieve the error details is may not be immediately obvious at\n * first, this example shows how to convert Any into <code>GoogleAdsFailure</code>.\n *\n * <p>Additionally, this example shows how to produce an error message for a specific failed\n * operation by looking up the failure details in the <code>GoogleAdsFailure</code> object.\n */\npublic class HandlePartialFailure {\n\n private static class HandlePartialFailureParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(names = ArgumentNames.CAMPAIGN_ID, required = true)\n private Long campaignId;\n }\n\n public static void main(String[] args) {\n HandlePartialFailureParams params = new HandlePartialFailureParams();\n if (!params.parseArguments(args)) {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID\");\n params.campaignId = Long.parseLong(\"INSERT_CAMPAIGN_ID\");\n }\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new HandlePartialFailure().runExample(googleAdsClient, params.customerId, params.campaignId);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /** Runs the example. */\n public void runExample(GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n MutateAdGroupsResponse response = createAdGroups(googleAdsClient, customerId, campaignId);\n\n // Checks for existence of any partial failures in the response.\n if (checkIfPartialFailureErrorExists(response)) {\n System.out.println(\"Partial failures occurred.\");\n } else {\n System.out.println(\"All operations completed successfully. No partial failures to show.\");\n return;\n }\n\n // Finds the failed operations by looping through the results.\n printResults(response);\n }\n\n /**\n * Attempts to create 3 ad groups with partial failure enabled. One of the ad groups will succeed,\n * while the other will fail.\n */\n private MutateAdGroupsResponse createAdGroups(\n GoogleAdsClient googleAdsClient, long customerId, long campaignId) {\n // This AdGroup should be created successfully - assuming the campaign in the params exists.\n AdGroup group1 =\n AdGroup.newBuilder()\n .setCampaign(ResourceNames.campaign(customerId, campaignId))\n .setName(\"Valid AdGroup: \" + getPrintableDateTime())\n .build();\n // This AdGroup will always fail - campaign ID 0 in resource names is never valid.\n AdGroup group2 =\n AdGroup.newBuilder()\n .setCampaign(ResourceNames.campaign(customerId, 0L))\n .setName(\"Broken AdGroup: \" + getPrintableDateTime())\n .build();\n // This AdGroup will always fail - duplicate ad group names are not allowed.\n AdGroup group3 =\n AdGroup.newBuilder()\n .setCampaign(ResourceNames.campaign(customerId, campaignId))\n .setName(group1.getName())\n .build();\n\n AdGroupOperation op1 = AdGroupOperation.newBuilder().setCreate(group1).build();\n AdGroupOperation op2 = AdGroupOperation.newBuilder().setCreate(group2).build();\n AdGroupOperation op3 = AdGroupOperation.newBuilder().setCreate(group3).build();\n\n try (AdGroupServiceClient service =\n googleAdsClient.getLatestVersion().createAdGroupServiceClient()) {\n // Issues the mutate request, setting partialFailure=true.\n return service.mutateAdGroups(\n MutateAdGroupsRequest.newBuilder()\n .setCustomerId(String.valueOf(customerId))\n .setCustomerId(Long.toString(customerId))\n .addAllOperations(Arrays.asList(op1, op2, op3))\n .setPartialFailure(true)\n .build());\n }\n }\n\n /** Inspects a response to check for presence of partial failure errors. */\n private boolean checkIfPartialFailureErrorExists(MutateAdGroupsResponse response) {\n return response.hasPartialFailureError();\n }\n\n /** Displays the result from the mutate operation. */\n private void printResults(MutateAdGroupsResponse response) {\n int operationIndex = 0;\n for (MutateAdGroupResult result : response.getResultsList()) {\n if (ErrorUtils.getInstance().isPartialFailureResult(result)) {\n // May throw on this line. Most likely this means the wrong version of the ErrorUtils\n // class has been used.\n GoogleAdsFailure googleAdsFailure = ErrorUtils.getInstance()\n .getGoogleAdsFailure(response.getPartialFailureError());\n\n for (GoogleAdsError error :\n ErrorUtils.getInstance()\n .getGoogleAdsErrors(operationIndex, googleAdsFailure)) {\n System.out.printf(\"Operation %d failed with error: %s%n\", operationIndex, error);\n }\n } else {\n System.out.printf(\"Operation %d succeeded.%n\", operationIndex);\n }\n ++operationIndex;\n }\n }\n}\nHandlePartialFailure.java\n```\n\nExample:\n```text\n// Copyright 2019 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.Gax.Lib;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System;\nusing System.Collections.Generic;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example demonstrates how to handle partial failures.\n /// </summary>\n public class HandlePartialFailure : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"HandlePartialFailure\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// ID of the campaign to which ad groups are added.\n /// </summary>\n [Option(\"campaignId\", Required = true, HelpText =\n \"ID of the campaign to which ad groups are added.\")]\n public long CampaignId { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n HandlePartialFailure codeExample = new HandlePartialFailure();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.CampaignId);\n }\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example demonstrates how to handle partial failures.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The customer ID for which the call is made.</param>\n /// <param name=\"campaignId\">ID of the campaign to which ad groups are added.</param>\n public void Run(GoogleAdsClient client, long customerId, long campaignId)\n {\n try\n {\n MutateAdGroupsResponse response = CreateAdGroups(client, customerId, campaignId);\n\n // Checks for existence of any partial failures in the response.\n if (CheckIfPartialFailureErrorExists(response))\n {\n Console.WriteLine(\"Partial failures occurred. Details will be shown below.\");\n }\n else\n {\n Console.WriteLine(\"All operations completed successfully. No partial \" +\n \"failures to show.\");\n return;\n }\n\n // Finds the failed operations by looping through the results.\n PrintResults(response);\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Displays the result from the mutate operation.\n /// </summary>\n /// <param name=\"response\">The mutate response from the Google Ads API server..</param>\n private void PrintResults(MutateAdGroupsResponse response)\n {\n // Finds the failed operations by looping through the results.\n int operationIndex = 0;\n foreach (MutateAdGroupResult result in response.Results)\n {\n // This represents the result of a failed operation.\n if (result.IsEmpty())\n {\n List<GoogleAdsError> errors =\n response.PartialFailure.GetErrorsByOperationIndex(operationIndex);\n foreach (GoogleAdsError error in errors)\n {\n Console.WriteLine($\"Operation {operationIndex} failed with \" +\n $\"error: {error}.\");\n }\n }\n else\n {\n Console.WriteLine($\"Operation {operationIndex} succeeded.\",\n operationIndex);\n }\n operationIndex++;\n }\n }\n\n /// <summary>\n /// Inspects a response to check for presence of partial failure errors.\n /// </summary>\n /// <param name=\"response\">The response.</param>\n /// <returns>True if there are partial failures, false otherwise.</returns>\n private static bool CheckIfPartialFailureErrorExists(MutateAdGroupsResponse response)\n {\n return response.PartialFailureError != null;\n }\n\n /// <summary>\n /// Attempts to create 3 ad groups with partial failure enabled. One of the ad groups\n /// will succeed, while the other will fail.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The customer ID for which the call is made.</param>\n /// <param name=\"campaignId\">ID of the campaign to which ad groups are added.</param>\n /// <returns>The mutate response from the Google Ads server.</returns>\n private static MutateAdGroupsResponse CreateAdGroups(GoogleAdsClient client,\n long customerId, long campaignId)\n {\n // Get the AdGroupServiceClient.\n AdGroupServiceClient adGroupService = client.GetService(Services.V25.AdGroupService);\n\n string validAdGroupName = \"Valid AdGroup: \" + ExampleUtilities.GetRandomString();\n\n AdGroupOperation[] operations = new AdGroupOperation[]\n {\n // This operation will be successful, assuming the campaign specified in\n // campaignId parameter is correct.\n new AdGroupOperation()\n {\n Create = new AdGroup()\n {\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n Name = validAdGroupName\n }\n },\n // This operation will fail since we are using campaign ID = 0, which results\n // in an invalid resource name.\n new AdGroupOperation()\n {\n Create = new AdGroup()\n {\n Campaign = ResourceNames.Campaign(customerId, 0),\n Name = \"Broken AdGroup: \" + ExampleUtilities.GetRandomString()\n },\n },\n // This operation will fail since the ad group is using the same name as the ad\n // group from the first operation. Duplicate ad group names are not allowed.\n new AdGroupOperation()\n {\n Create = new AdGroup()\n {\n Campaign = ResourceNames.Campaign(customerId, campaignId),\n Name = validAdGroupName\n }\n }\n };\n\n // Add the ad groups.\n MutateAdGroupsResponse response =\n adGroupService.MutateAdGroups(new MutateAdGroupsRequest()\n {\n CustomerId = customerId.ToString(),\n Operations = { operations },\n PartialFailure = true,\n ValidateOnly = false\n });\n return response;\n }\n }\n}\nHandlePartialFailure.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2019 Google LLC\n *\n * 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.\n */\n\n namespace Google\\Ads\\GoogleAds\\Examples\\ErrorHandling;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\GoogleAdsErrors;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\PartialFailures;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AdGroup;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AdGroupOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAdGroupsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAdGroupsResponse;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * Shows how to handle partial failures. There are several ways of detecting partial failures. This\n * highlights the top main detection options: empty results and error instances.\n *\n * <p>Access to the detailed error (<code>GoogleAdsFailure</code>) for each error is via a Any\n * proto. Deserializing these to retrieve the error details is may not be immediately obvious at\n * first, this example shows how to convert Any into <code>GoogleAdsFailure</code>.\n *\n * <p>Additionally, this example shows how to produce an error message for a specific failed\n * operation by looking up the failure details in the <code>GoogleAdsFailure</code> object.\n */\nclass HandlePartialFailure\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const CAMPAIGN_ID = 'INSERT_CAMPAIGN_ID_HERE';\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::CAMPAIGN_ID => GetOpt::REQUIRED_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())\n ->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::CAMPAIGN_ID] ?: self::CAMPAIGN_ID\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $campaignId a campaign ID\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n ) {\n $response = self::createAdGroups($googleAdsClient, $customerId, $campaignId);\n self::checkIfPartialFailureErrorExists($response);\n self::printResults($response);\n }\n\n /**\n * Create ad groups by enabling partial failure mode.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $campaignId a campaign ID\n * @return MutateAdGroupsResponse\n */\n private static function createAdGroups(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $campaignId\n ) {\n $campaignResourceName = ResourceNames::forCampaign($customerId, $campaignId);\n\n // This ad group should be created successfully - assuming the campaign in the params\n // exists.\n $adGroup1 = new AdGroup([\n 'name' => 'Valid AdGroup #' . Helper::getPrintableDatetime(),\n 'campaign' => $campaignResourceName\n ]);\n\n // This ad group will always fail - campaign ID 0 in the resource name is never valid.\n $adGroup2 = new AdGroup([\n 'name' => 'Broken AdGroup #' . Helper::getPrintableDatetime(),\n 'campaign' => ResourceNames::forCampaign($customerId, 0)\n ]);\n\n // This ad group will always fail - duplicate ad group names are not allowed.\n $adGroup3 = new AdGroup([\n 'name' => $adGroup1->getName(),\n 'campaign' => $campaignResourceName\n ]);\n\n $operations = [];\n\n $adGroupOperation1 = new AdGroupOperation();\n $adGroupOperation1->setCreate($adGroup1);\n $operations[] = $adGroupOperation1;\n\n $adGroupOperation2 = new AdGroupOperation();\n $adGroupOperation2->setCreate($adGroup2);\n $operations[] = $adGroupOperation2;\n\n $adGroupOperation3 = new AdGroupOperation();\n $adGroupOperation3->setCreate($adGroup3);\n $operations[] = $adGroupOperation3;\n\n // Issues the mutate request, enabling partial failure mode.\n $adGroupServiceClient = $googleAdsClient->getAdGroupServiceClient();\n return $adGroupServiceClient->mutateAdGroups(\n MutateAdGroupsRequest::build($customerId, $operations)->setPartialFailure(true)\n );\n }\n\n /**\n * Check if there exists partial failure error in the given mutate ad group response.\n *\n * @param MutateAdGroupsResponse $response the mutate ad group response\n */\n private static function checkIfPartialFailureErrorExists(MutateAdGroupsResponse $response)\n {\n if ($response->hasPartialFailureError()) {\n printf(\"Partial failures occurred. Details will be shown below.%s\", PHP_EOL);\n } else {\n printf(\n \"All operations completed successfully. No partial failures to show.%s\",\n PHP_EOL\n );\n }\n }\n\n /**\n * Print results of the given mutate ad group response. For those that are partial failure,\n * print all their errors with corresponding operation indices. For those that succeeded, print\n * the resource names of created ad groups.\n *\n * @param MutateAdGroupsResponse $response the mutate ad group response\n */\n private static function printResults(MutateAdGroupsResponse $response)\n {\n // Finds the failed operations by looping through the results.\n $operationIndex = 0;\n foreach ($response->getResults() as $result) {\n /** @var AdGroup $result */\n if (PartialFailures::isPartialFailure($result)) {\n $errors = GoogleAdsErrors::fromStatus(\n $operationIndex,\n $response->getPartialFailureError()\n );\n foreach ($errors as $error) {\n printf(\n \"Operation %d failed with error: %s%s\",\n $operationIndex,\n $error->getMessage(),\n PHP_EOL\n );\n }\n } else {\n printf(\n \"Operation %d succeeded: ad group with resource name '%s'.%s\",\n $operationIndex,\n $result->getResourceName(),\n PHP_EOL\n );\n }\n $operationIndex++;\n }\n }\n}\n\nHandlePartialFailure::main();\nHandlePartialFailure.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2018 Google LLC\n#\n# 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.\n\"\"\"This shows how to handle responses that may include partial_failure errors.\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import Any, List\nimport uuid\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.services.services.ad_group_service import (\n AdGroupServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.campaign_service import (\n CampaignServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_service import (\n AdGroupOperation,\n MutateAdGroupsResponse,\n MutateAdGroupsRequest,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef main(client: GoogleAdsClient, customer_id: str, campaign_id: str) -> None:\n \"\"\"Runs the example code, which demonstrates how to handle partial failures.\n\n The example creates three Ad Groups, two of which intentionally fail in\n order to generate a partial failure error. It also demonstrates how to\n properly identify a partial error and how to log the error messages.\n\n Args:\n client: An initialized GoogleAdsClient instance.\n customer_id: A valid customer account ID.\n campaign_id: The ID for a campaign to create Ad Groups under.\n \"\"\"\n try:\n ad_group_response: MutateAdGroupsResponse = create_ad_groups(\n client, customer_id, campaign_id\n )\n except GoogleAdsException as ex:\n print(\n f'Request with ID \"{ex.request_id}\" failed with status '\n f'\"{ex.error.code().name}\" and includes the following errors:'\n )\n for error in ex.failure.errors:\n print(f'\\tError with message \"{error.message}\".')\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\n else:\n print_results(client, ad_group_response)\n\n\ndef create_ad_groups(\n client: GoogleAdsClient, customer_id: str, campaign_id: str\n) -> MutateAdGroupsResponse:\n \"\"\"Creates three Ad Groups, two of which intentionally generate errors.\n\n Args:\n client: An initialized GoogleAdsClient instance.\n customer_id: A valid customer account ID.\n campaign_id: The ID for a campaign to create Ad Groups under.\n\n Returns: A MutateAdGroupsResponse message instance.\n \"\"\"\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n campaign_service: CampaignServiceClient = client.get_service(\n \"CampaignService\"\n )\n resource_name: str = campaign_service.campaign_path(\n customer_id, campaign_id\n )\n\n invalid_resource_name: str = campaign_service.campaign_path(customer_id, 0)\n ad_group_operations: List[AdGroupOperation] = []\n\n # This AdGroup should be created successfully - assuming the campaign in\n # the params exists.\n ad_group_op1: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group_op1.create.name = f\"Valid AdGroup: {uuid.uuid4()}\"\n ad_group_op1.create.campaign = resource_name\n ad_group_operations.append(ad_group_op1)\n\n # This AdGroup will always fail - campaign ID 0 in resource names is\n # never valid.\n ad_group_op2: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group_op2.create.name = f\"Broken AdGroup: {uuid.uuid4()}\"\n ad_group_op2.create.campaign = invalid_resource_name\n ad_group_operations.append(ad_group_op2)\n\n # This AdGroup will always fail - duplicate ad group names are not allowed.\n ad_group_op3: AdGroupOperation = client.get_type(\"AdGroupOperation\")\n ad_group_op3.create.name = ad_group_op1.create.name\n ad_group_op3.create.campaign = resource_name\n ad_group_operations.append(ad_group_op3)\n\n # Issue a mutate request, setting partial_failure=True.\n request: MutateAdGroupsRequest = client.get_type(\"MutateAdGroupsRequest\")\n request.customer_id = customer_id\n request.operations = ad_group_operations\n request.partial_failure = True\n return ad_group_service.mutate_ad_groups(request=request)\n\n\ndef is_partial_failure_error_present(response: MutateAdGroupsResponse) -> bool:\n \"\"\"Checks whether a response message has a partial failure error.\n\n In Python the partial_failure_error attr is always present on a response\n message and is represented by a google.rpc.Status message. So we can't\n simply check whether the field is present, we must check that the code is\n non-zero. Error codes are represented by the google.rpc.Code proto Enum:\n https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto\n\n Args:\n response: A MutateAdGroupsResponse message instance.\n\n Returns: A boolean, whether or not the response message has a partial\n failure error.\n \"\"\"\n partial_failure: Any = getattr(response, \"partial_failure_error\", None)\n code: int = int(getattr(partial_failure, \"code\", 0)) # Default to 0 if None\n return code != 0\n\n\ndef print_results(\n client: GoogleAdsClient, response: MutateAdGroupsResponse\n) -> None:\n \"\"\"Prints partial failure errors and success messages from a response.\n\n This function shows how to retrieve partial_failure errors from a response\n message (in the case of this example the message will be of type\n MutateAdGroupsResponse) and how to unpack those errors to GoogleAdsFailure\n instances. It also shows that a response with partial failures may still\n contain successful requests, and that those messages should be parsed\n separately. As an example, a GoogleAdsFailure object from this example will\n be structured similar to:\n\n error_code {\n range_error: TOO_LOW\n }\n message: \"Too low.\"\n trigger {\n string_value: \"\"\n }\n location {\n field_path_elements {\n field_name: \"operations\"\n index {\n value: 1\n }\n }\n field_path_elements {\n field_name: \"create\"\n }\n field_path_elements {\n field_name: \"campaign\"\n }\n }\n\n Args:\n client: an initialized GoogleAdsClient.\n response: a MutateAdGroupsResponse instance.\n \"\"\"\n # Check for existence of any partial failures in the response.\n if is_partial_failure_error_present(response):\n print(\"Partial failures occurred. Details will be shown below.\\n\")\n # Prints the details of the partial failure errors.\n partial_failure: Any = getattr(response, \"partial_failure_error\", None)\n # partial_failure_error.details is a repeated field and iterable\n error_details: List[Any] = getattr(partial_failure, \"details\", [])\n\n for error_detail in error_details:\n # Retrieve an instance of the GoogleAdsFailure class from the client\n failure_message: Any = client.get_type(\"GoogleAdsFailure\")\n # Parse the string into a GoogleAdsFailure message instance.\n # To access class-only methods on the message we retrieve its type.\n GoogleAdsFailure: Any = type(failure_message)\n failure_object: Any = GoogleAdsFailure.deserialize(\n error_detail.value\n )\n\n for error in failure_object.errors:\n # Construct and print a string that details which element in\n # the above ad_group_operations list failed (by index number)\n # as well as the error message and error code.\n print(\n \"A partial failure at index \"\n f\"{error.location.field_path_elements[0].index} occurred \"\n f\"\\nError message: {error.message}\\nError code: \"\n f\"{error.error_code}\"\n )\n else:\n print(\n \"All operations completed successfully. No partial failure \"\n \"to show.\"\n )\n\n # In the list of results, operations from the ad_group_operation list\n # that failed will be represented as empty messages. This loop detects\n # such empty messages and ignores them, while printing information about\n # successful operations.\n for message in response.results:\n if not message:\n continue\n\n print(f\"Created ad group with resource_name: {message.resource_name}.\")\n\n\nif __name__ == \"__main__\":\n parser = argparse.ArgumentParser(\n description=\"Adds an ad group for specified customer and campaign id.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-i\", \"--campaign_id\", type=str, required=True, help=\"The campaign ID.\"\n )\n args = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n main(googleads_client, args.customer_id, args.campaign_id)\nhandle_partial_failure.py\n```\n\nExample:\n```text\n# Encoding: utf-8\n#\n# Copyright 2019 Google LLC\n#\n# 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.\n#\n# This code example shows how to deal with partial failures\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\nrequire 'date'\n\ndef add_ad_groups(customer_id, campaign_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n\n ad_groups = []\n # This ad group should be created successfully.\n ad_groups << client.resource.ad_group do |ag|\n ag.campaign = client.path.campaign(customer_id, campaign_id)\n ag.name = \"Valid ad group: #{(Time.new.to_f * 1000).to_i}\"\n end\n # This ad group should fail to create because it references an invalid campaign.\n ad_groups << client.resource.ad_group do |ag|\n ag.campaign = client.path.campaign(customer_id, 0)\n ag.name = \"Invalid ad group: #{(Time.new.to_f * 1000).to_i}\"\n end\n # This ad group should fail to create because it duplicates the name from the first one.\n ad_groups << client.resource.ad_group do |ag|\n ag.campaign = client.path.campaign(customer_id, campaign_id)\n ag.name = ad_groups.first.name\n end\n\n operations = ad_groups.map do |ag|\n client.operation.create_resource.ad_group(ag)\n end\n\n response = client.service.ad_group.mutate_ad_groups(\n customer_id: customer_id,\n operations: operations,\n partial_failure: true,\n )\n\n response.results.each_with_index do |ad_group, i|\n if ad_group.resource_name != \"\"\n puts(\"operations[#{i}] succeeded: Created ad group with id #{ad_group.resource_name}\")\n end\n end\n\n failures = client.decode_partial_failure_error(response.partial_failure_error)\n failures.each do |failure|\n failure.errors.each do |error|\n human_readable_error_path = error\n .location\n .field_path_elements\n .map { |location_info|\n if location_info.index\n \"#{location_info.field_name}[#{location_info.index}]\"\n else\n \"#{location_info.field_name}\"\n end\n }.join(\" > \")\n\n errmsg = \"error occured creating ad group #{human_readable_error_path}\" \\\n \" with value: #{error.trigger.string_value}\" \\\n \" because #{error.message.downcase}\"\n puts errmsg\n end\n end\nend\n\nif __FILE__ == $PROGRAM_NAME\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:campaign_id] = 'INSERT_CAMPAIGN_ID_HERE'\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: ruby %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-c', '--campaign-id CAMPAIGN-ID', String, 'Ad Group ID') do |v|\n options[:campaign_id] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n add_ad_groups(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options.fetch(:campaign_id),\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\nhandle_partial_failure.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2019, Google LLC\n#\n# 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# http://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.\n#\n# This example shows how to deal with partial failures. There are several ways\n# of detecting partial failures. This example highlights the top main detection\n# options: empty results and error instances.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::Utils::PartialFailureUtils;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroup;\nuse Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\nmy $campaign_id = \"INSERT_CAMPAIGN_ID_HERE\";\n\nsub handle_partial_failure {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n my $ad_groups_response =\n create_ad_groups($api_client, $customer_id, $campaign_id);\n check_if_partial_failure_error_exists($ad_groups_response);\n print_results($ad_groups_response);\n\n return 1;\n}\n\n# Creates ad groups by enabling partial failure mode.\nsub create_ad_groups {\n my ($api_client, $customer_id, $campaign_id) = @_;\n\n my $campaign_resource_name =\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign($customer_id,\n $campaign_id);\n\n # This ad group should be created successfully - assuming the campaign in the\n # params exists.\n my $ad_group1 = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Valid AdGroup: \" . uniqid(),\n campaign => $campaign_resource_name\n });\n\n # This ad group will always fail - campaign ID 0 in the resource name is never\n # valid.\n my $ad_group2 = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => \"Broken AdGroup: \" . uniqid(),\n campaign => Google::Ads::GoogleAds::V25::Utils::ResourceNames::campaign(\n $customer_id, 0\n )});\n\n # This ad group will always fail - duplicate ad group names are not allowed.\n my $ad_group3 = Google::Ads::GoogleAds::V25::Resources::AdGroup->new({\n name => $ad_group1->{name},\n campaign => $campaign_resource_name\n });\n\n # Create ad group operations.\n my $ad_group_operation1 =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group1});\n my $ad_group_operation2 =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group2});\n my $ad_group_operation3 =\n Google::Ads::GoogleAds::V25::Services::AdGroupService::AdGroupOperation->\n new({create => $ad_group3});\n\n # Issue the mutate request, enabling partial failure mode.\n my $ad_groups_response = $api_client->AdGroupService()->mutate({\n customerId => $customer_id,\n operations =>\n [$ad_group_operation1, $ad_group_operation2, $ad_group_operation3],\n partialFailure => \"true\"\n });\n\n return $ad_groups_response;\n}\n\n# Checks if partial failure error exists in the given mutate ad group response.\nsub check_if_partial_failure_error_exists {\n my $ad_groups_response = shift;\n\n if ($ad_groups_response->{partialFailureError}) {\n print \"Partial failures occurred. Details will be shown below.\\n\";\n } else {\n print\n \"All operations completed successfully. No partial failures to show.\\n\";\n }\n}\n\n# Prints results of the given mutate ad group response. For those that are partial\n# failure, prints all their errors with corresponding operation indices. For those\n# that succeeded, prints the resource names of created ad groups.\nsub print_results {\n my $ad_groups_response = shift;\n\n # Find the failed operations by looping through the results.\n while (my ($operation_index, $result) =\n each @{$ad_groups_response->{results}})\n {\n if (is_partial_failure_result($result)) {\n my $google_ads_errors = get_google_ads_errors($operation_index,\n $ad_groups_response->{partialFailureError});\n\n foreach my $google_ads_error (@$google_ads_errors) {\n printf \"Operation %d failed with error: %s\\n\", $operation_index,\n $google_ads_error->{message};\n }\n } else {\n printf \"Operation %d succeeded: ad group with resource name '%s'.\\n\",\n $operation_index, $result->{resourceName};\n }\n }\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\"customer_id=s\" => \\$customer_id, \"campaign_id=i\" => \\$campaign_id);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id, $campaign_id);\n\n# Call the example.\nhandle_partial_failure($api_client, $customer_id =~ s/-//gr, $campaign_id);\n\n=pod\n\n=head1 NAME\n\nhandle_partial_failure\n\n=head1 DESCRIPTION\n\nThis example shows how to deal with partial failures. There are several ways of\ndetecting partial failures. This example highlights the top main detection\noptions: empty results and error instances.\n\n=head1 SYNOPSIS\n\nhandle_partial_failure.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -campaign_id The campaign ID.\n\n=cut\nhandle_partial_failure.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.622Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":29,"totalLines":1878,"estimatedTokens":17151}}246{"id":"doc-requesting_exemption_for_ads_google_ads_api_goog-805b612d","source":"documentation","title":"Requesting Exemption for Ads | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/policy-exemption/ads","text":"Example:\n```text\nprivate List<String> fetchIgnorablePolicyTopics(GoogleAdsException gae) {\n System.out.println(\"Google Ads failure details:\");\n\n // Creates a list to store the result.\n List<String> ignorableTopics = new ArrayList<>();\n\n // Searches all errors for ignorable policy topics.\n for (GoogleAdsError error : gae.getGoogleAdsFailure().getErrorsList()) {\n // Supports sending exemption request for the policy finding error only.\n if (error.getErrorCode().getErrorCodeCase() != ErrorCodeCase.POLICY_FINDING_ERROR) {\n throw gae;\n }\n\n // Shows some information about the error encountered.\n System.out.printf(\"\\t%s: %s%n\", error.getErrorCode().getErrorCodeCase(), error.getMessage());\n\n // Checks policy finding details for ignorable policy topics.\n if (error.getDetails() != null) {\n PolicyFindingDetails policyFindingDetails = error.getDetails().getPolicyFindingDetails();\n if (policyFindingDetails != null) {\n System.out.println(\"\\tPolicy finding details:\");\n // Shows all the policy topics for the current error.\n for (PolicyTopicEntry policyTopicEntry :\n policyFindingDetails.getPolicyTopicEntriesList()) {\n // Adds this topic to the result.\n ignorableTopics.add(policyTopicEntry.getTopic());\n System.out.printf(\"\\t\\tPolicy topic name: '%s'%n\", policyTopicEntry.getTopic());\n System.out.printf(\"\\t\\tPolicy topic entry type: '%s'%n\", policyTopicEntry.getType());\n // For the sake of brevity, we exclude printing \"policy topic evidences\" and\n // \"policy topic constraints\" here. You can fetch those data by calling:\n // - policyTopicEntry.getEvidences()\n // - policyTopicEntry.getConstraints()\n }\n }\n }\n }\n return ignorableTopics;\n}HandleResponsiveSearchAdPolicyViolations.java\n```\n\nExample:\n```text\nprivate static string[] FetchIgnorablePolicyTopics(GoogleAdsException ex)\n{\n List<string> ignorablePolicyTopics = new List<string>(); ;\n\n Console.WriteLine(\"Google Ads failure details:\");\n foreach (GoogleAdsError error in ex.Failure.Errors)\n {\n if (error.ErrorCode.ErrorCodeCase != ErrorCode.ErrorCodeOneofCase.PolicyFindingError)\n {\n throw ex;\n }\n if (error.Details != null && error.Details.PolicyFindingDetails != null)\n {\n PolicyFindingDetails details = error.Details.PolicyFindingDetails;\n Console.WriteLine($\"- Policy finding details:\");\n\n foreach (PolicyTopicEntry entry in details.PolicyTopicEntries)\n {\n ignorablePolicyTopics.Add(entry.Topic);\n Console.WriteLine($\" - Policy topic name: '{entry.Topic}'\");\n Console.WriteLine($\" - Policy topic entry type: '{entry.Type}'\");\n // For the sake of brevity, we exclude printing \"policy topic evidences\"\n // and \"policy topic constraints\" here. You can fetch those data by\n // calling:\n // - entry.Evidences\n // - entry.Constraints\n }\n }\n }\n return ignorablePolicyTopics.ToArray();\n}HandleResponsiveSearchAdPolicyViolations.cs\n```\n\nExample:\n```text\nprivate static function fetchIgnorablePolicyTopics(GoogleAdsException $googleAdsException)\n{\n $ignorablePolicyTopics = [];\n\n printf(\"Google Ads failure details:%s\", PHP_EOL);\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n if ($error->getErrorCode()->getErrorCode() !== 'policy_finding_error') {\n // This example supports sending exemption request for the policy finding error\n // only.\n throw $googleAdsException;\n }\n\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n if (\n !is_null($error->getDetails())\n && !is_null($error->getDetails()->getPolicyFindingDetails())\n ) {\n $policyFindingDetails = $error->getDetails()->getPolicyFindingDetails();\n printf(\"\\tPolicy finding details:%s\", PHP_EOL);\n\n foreach ($policyFindingDetails->getPolicyTopicEntries() as $policyTopicEntry) {\n /** @var PolicyTopicEntry $policyTopicEntry */\n $ignorablePolicyTopics[] = $policyTopicEntry->getTopic();\n printf(\n \"\\t\\tPolicy topic name: '%s'%s\",\n $policyTopicEntry->getTopic(),\n PHP_EOL\n );\n printf(\n \"\\t\\tPolicy topic entry type: '%s'%s\",\n PolicyTopicEntryType::name($policyTopicEntry->getType()),\n PHP_EOL\n );\n // For the sake of brevity, we exclude printing \"policy topic evidences\" and\n // \"policy topic constraints\" here. You can fetch those data by calling:\n // - $policyTopicEntry->getEvidences()\n // - $policyTopicEntry->getConstraints()\n }\n }\n }\n return $ignorablePolicyTopics;\n}HandleResponsiveSearchAdPolicyViolations.php\n```\n\nExample:\n```text\ndef fetch_ignorable_policy_topics(\n client: GoogleAdsClient, googleads_exception: GoogleAdsException\n) -> List[str]:\n \"\"\"Collects all ignorable policy topics to be sent for exemption request.\n\n Args:\n client: The GoogleAds client instance.\n googleads_exception: The exception that contains the policy\n violation(s).\n\n Returns:\n A list of ignorable policy topics.\n \"\"\"\n ignorable_policy_topics: List[str] = []\n\n print(\"Google Ads failure details:\")\n for error in googleads_exception.failure.errors:\n if (\n error.error_code.policy_finding_error\n != client.enums.PolicyFindingErrorEnum.POLICY_FINDING\n ):\n print(\n \"This example supports sending exemption request for the \"\n \"policy finding error only.\"\n )\n raise googleads_exception\n\n print(f\"\\t{error.error_code.policy_finding_error}: {error.message}\")\n\n if (\n error.details is not None\n and error.details.policy_finding_details is not None\n ):\n policy_finding_details: PolicyFindingErrorEnum = (\n error.details.policy_finding_details\n )\n print(\"\\tPolicy finding details:\")\n\n for (\n policy_topic_entry\n ) in policy_finding_details.policy_topic_entries:\n ignorable_policy_topics.append(policy_topic_entry.topic)\n print(f\"\\t\\tPolicy topic name: '{policy_topic_entry.topic}'\")\n print(\n f\"\\t\\tPolicy topic entry type: '{policy_topic_entry.type_}'\"\n )\n # For the sake of brevity, we exclude printing \"policy topic\n # evidences\" and \"policy topic constraints\" here. You can fetch\n # those data by calling:\n # - policy_topic_entry.evidences\n # - policy_topic_entry.constraints\n\n return ignorable_policy_topicshandle_responsive_search_ad_policy_violations.py\n```\n\nExample:\n```text\ndef fetch_ignorable_policy_topics(exception)\n ignorable_policy_topics = []\n\n exception.failure.errors.each do |error|\n if error.error_code.policy_finding_error != :POLICY_FINDING\n puts \"Non-policy finding error found. Aborting.\"\n raise exception\n end\n puts \"#{error.error_code.policy_finding_error}: #{error.message}\"\n\n error&.details&.policy_finding_details&.policy_topic_entries.each do |entry|\n ignorable_policy_topics << entry.topic\n puts \"\\tPolicy topic name: #{entry.topic}\"\n puts \"\\tPolicy topic entry type: #{entry.type}\"\n end\n end\n\n ignorable_policy_topics\nendhandle_responsive_search_ad_policy_violations.rb\n```\n\nExample:\n```text\nsub fetch_ignorable_policy_topics {\n my $google_ads_exception = shift;\n\n my $ignorable_policy_topics = [];\n\n printf \"Google Ads failure details:\\n\";\n foreach\n my $error (@{$google_ads_exception->get_google_ads_failure()->{errors}})\n {\n if ([keys %{$error->{errorCode}}]->[0] ne \"policyFindingError\") {\n # This example supports sending exemption request for the policy finding\n # error only.\n die $google_ads_exception->get_message();\n }\n\n printf \"\\t%s: %s\\n\", [keys %{$error->{errorCode}}]->[0], $error->{message};\n\n if ($error->{details}{policyFindingDetails}) {\n my $policy_finding_details = $error->{details}{policyFindingDetails};\n printf \"\\tPolicy finding details:\\n\";\n\n foreach my $policy_topic_entry (\n @{$policy_finding_details->{policyTopicEntries}})\n {\n push @$ignorable_policy_topics, $policy_topic_entry->{topic};\n printf\n \"\\t\\tPolicy topic name: '%s'\\n\",\n $policy_topic_entry->{topic};\n printf \"\\t\\tPolicy topic entry type: '%s'\\n\",\n $policy_topic_entry->{type};\n # For the sake of brevity, we exclude printing \"policy topic evidences\" and\n # \"policy topic constraints\" here. You can fetch those data by calling:\n # - $policy_topic_entry->{evidences}\n # - $policy_topic_entry->{constraints}\n }\n }\n }\n\n return $ignorable_policy_topics;\n}handle_responsive_search_ad_policy_violations.pl\n```\n\nExample:\n```text\nprivate void requestExemption(\n List<String> ignorablePolicyTopics,\n AdGroupAdServiceClient client,\n AdGroupAdOperation operation,\n long customerID) {\n System.out.println(\n \"Trying to add a responsive search ad again by requesting exemption for its policy\"\n + \" violations.\");\n // Converts the operation back to a builder.\n AdGroupAdOperation.Builder operationBuilder = operation.toBuilder();\n\n // Adds the exemption request.\n operationBuilder\n .getPolicyValidationParameterBuilder()\n .addAllIgnorablePolicyTopics(ignorablePolicyTopics);\n\n // Sends the request back to the API.\n MutateAdGroupAdsResponse response =\n client.mutateAdGroupAds(\n String.valueOf(customerID), ImmutableList.of(operationBuilder.build()));\n\n // Shows the newly added ad resource name.\n System.out.printf(\n \"Successfully added a responsive search ad with resource name '%s' by requesting a policy\"\n + \" violation exemption.%n\",\n response.getResults(0).getResourceName());\n}HandleResponsiveSearchAdPolicyViolations.java\n```\n\nExample:\n```text\nprivate static void RequestExemption(long customerId, AdGroupAdServiceClient service,\n AdGroupAdOperation operation, string[] ignorablePolicyTopics)\n{\n Console.WriteLine(\"Try adding a responsive search ad again by requesting exemption for \" +\n \"its policy violations.\");\n PolicyValidationParameter validationParameter = new PolicyValidationParameter();\n validationParameter.IgnorablePolicyTopics.AddRange(ignorablePolicyTopics);\n operation.PolicyValidationParameter = validationParameter;\n\n MutateAdGroupAdsResponse response = service.MutateAdGroupAds(\n customerId.ToString(), new[] { operation });\n Console.WriteLine($\"Successfully added a responsive search ad with resource name \" +\n $\"'{response.Results[0].ResourceName}' by requesting for policy violation \" +\n $\"exemption.\");\n}HandleResponsiveSearchAdPolicyViolations.cs\n```\n\nExample:\n```text\nprivate static function requestExemption(\n int $customerId,\n AdGroupAdServiceClient $adGroupAdServiceClient,\n AdGroupAdOperation $adGroupAdOperation,\n array $ignorablePolicyTopics\n) {\n print \"Try adding a responsive search ad again by requesting exemption for its policy\"\n . \" violations.\" . PHP_EOL;\n $adGroupAdOperation->setPolicyValidationParameter(\n new PolicyValidationParameter(['ignorable_policy_topics' => $ignorablePolicyTopics])\n );\n $response = $adGroupAdServiceClient->mutateAdGroupAds(MutateAdGroupAdsRequest::build(\n $customerId,\n [$adGroupAdOperation]\n ));\n printf(\n \"Successfully added a responsive search ad with resource name '%s' by requesting\"\n . \" for policy violation exemption.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n}HandleResponsiveSearchAdPolicyViolations.php\n```\n\nExample:\n```text\ndef request_exemption(\n customer_id: str,\n ad_group_ad_service_client: AdGroupAdServiceClient,\n ad_group_ad_operation: AdGroupAdOperation,\n ignorable_policy_topics: List[str],\n) -> None:\n \"\"\"Sends exemption requests for creating a responsive search ad.\n\n Args:\n customer_id: The customer ID for which to add the responsive search ad.\n ad_group_ad_service_client: The AdGroupAdService client instance.\n ad_group_ad_operation: The AdGroupAdOperation that returned policy\n violation(s).\n ignorable_policy_topics: The extracted list of policy topic entries.\n \"\"\"\n print(\n \"Attempting to add a responsive search ad again by requesting \"\n \"exemption for its policy violations.\"\n )\n ad_group_ad_operation.policy_validation_parameter.ignorable_policy_topics.extend(\n ignorable_policy_topics\n )\n response: MutateAdGroupAdsResponse = (\n ad_group_ad_service_client.mutate_ad_group_ads(\n customer_id=customer_id, operations=[ad_group_ad_operation]\n )\n )\n print(\n \"Successfully added a responsive search ad with resource name \"\n f\"'{response.results[0].resource_name}' for policy violation \"\n \"exemption.\"\n )handle_responsive_search_ad_policy_violations.py\n```\n\nExample:\n```text\ndef request_exemption(\n client, customer_id, ad_group_ad_service, ad_group_ad_operation, ignorable_policy_topics)\n # Add all the found ignorable policy topics to the operation.\n ad_group_ad_operation.policy_validation_parameter =\n client.resource.policy_validation_parameter do |pvp|\n pvp.ignorable_policy_topics.push(\n *ignorable_policy_topics\n )\n end\n response = ad_group_ad_service.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [ad_group_ad_operation],\n )\n puts \"Successfully added a responsive search ad with resource name \" \\\n \"#{response.results.first.resource_name} for policy violation exception.\"\nendhandle_responsive_search_ad_policy_violations.rb\n```\n\nExample:\n```text\nsub request_exemption {\n my ($api_client, $customer_id, $ad_group_ad_operation,\n $ignorable_policy_topics)\n = @_;\n\n print\n \"Try adding a responsive search ad again by requesting exemption for its \"\n . \"policy violations.\\n\";\n\n $ad_group_ad_operation->{policyValidationParameter} =\n Google::Ads::GoogleAds::V25::Common::PolicyValidationParameter->new(\n {ignorablePolicyTopics => $ignorable_policy_topics});\n\n my $ad_group_ads_response = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]});\n\n printf\n \"Successfully added a responsive search ad with resource name '%s' by \" .\n \"requesting for policy violation exemption.\\n\",\n $ad_group_ads_response->{results}[0]{resourceName};\n}handle_responsive_search_ad_policy_violations.pl\n```\n\nExample:\n```text\n// Copyright 2020 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.errorhandling;\n\nimport static com.google.ads.googleads.examples.utils.CodeSampleHelper.getShortPrintableDateTime;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.AdTextAsset;\nimport com.google.ads.googleads.v25.common.PolicyTopicEntry;\nimport com.google.ads.googleads.v25.enums.AdGroupAdStatusEnum.AdGroupAdStatus;\nimport com.google.ads.googleads.v25.errors.ErrorCode.ErrorCodeCase;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.errors.PolicyFindingDetails;\nimport com.google.ads.googleads.v25.resources.AdGroupAd;\nimport com.google.ads.googleads.v25.services.AdGroupAdOperation;\nimport com.google.ads.googleads.v25.services.AdGroupAdServiceClient;\nimport com.google.ads.googleads.v25.services.MutateAdGroupAdsResponse;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Demonstrates how to request an exemption for policy violations of a responsive search ad. If the\n * request somehow fails with exceptions that are not policy finding errors, the example will stop\n * instead of trying sending an exemption request.\n */\npublic class HandleResponsiveSearchAdPolicyViolations {\n\n private static class HandleResponsiveSearchAdPolicyViolationsParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(names = ArgumentNames.AD_GROUP_ID, required = true)\n private Long adGroupId;\n }\n\n public static void main(String[] args) {\n HandleResponsiveSearchAdPolicyViolationsParams params =\n new HandleResponsiveSearchAdPolicyViolationsParams();\n if (!params.parseArguments(args)) {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID\");\n params.adGroupId = Long.parseLong(\"INSERT_AD_GROUP_ID\");\n }\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new HandleResponsiveSearchAdPolicyViolations()\n .runExample(googleAdsClient, params.customerId, params.adGroupId);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the client to use.\n * @param customerId the customer ID.\n * @param adGroupId the ad group ID.\n */\n public void runExample(GoogleAdsClient googleAdsClient, long customerId, long adGroupId) {\n // Creates an ad group ad for the specified ad group.\n AdGroupAd.Builder adGroupAdBuilder =\n AdGroupAd.newBuilder()\n .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))\n .setStatus(AdGroupAdStatus.PAUSED);\n\n adGroupAdBuilder\n .getAdBuilder()\n // Sets the final URLS.\n .addFinalUrls(\"https://www.example.com\")\n // Adds a responsive search ad.\n .getResponsiveSearchAdBuilder()\n .addAllHeadlines(\n ImmutableList.of(\n AdTextAsset.newBuilder()\n .setText(\"Cruise to Mars #\" + getShortPrintableDateTime())\n .build(),\n AdTextAsset.newBuilder().setText(\"Best Space Cruise Line\").build(),\n AdTextAsset.newBuilder().setText(\"Experience the Stars\").build()))\n .addAllDescriptions(\n ImmutableList.of(\n // Intentionally uses an ad text that violates policy - too many exclamation marks.\n AdTextAsset.newBuilder().setText(\"Buy your tickets now!!!!!!!\").build(),\n AdTextAsset.newBuilder().setText(\"Visit the Red Planet\").build()));\n\n // Constructs an operation to send to the API.\n AdGroupAdOperation operation =\n AdGroupAdOperation.newBuilder().setCreate(adGroupAdBuilder.build()).build();\n\n // Connects to the API. Note that we could use try-with-resources, however doing so would\n // require that we either (1) need to reconnect to the API for requesting the exemption, or (2)\n // introduce a doubly nested try-catch structure here.\n AdGroupAdServiceClient client =\n googleAdsClient.getLatestVersion().createAdGroupAdServiceClient();\n\n try {\n // Sends the request which we expect to fail with policy violations.\n client.mutateAdGroupAds(String.valueOf(customerId), ImmutableList.of(operation));\n } catch (GoogleAdsException ex) {\n // Retrieves the ignorable policy topics.\n List<String> ignorablePolicyTopics = fetchIgnorablePolicyTopics(ex);\n // Requests an exemption to add the creative with the known violations.\n requestExemption(ignorablePolicyTopics, client, operation, customerId);\n } finally {\n // Disconnects the API connection. Very important!\n client.close();\n }\n }\n\n /**\n * Collects all ignorable policy topics that will be sent for exemption request later.\n *\n * @param gae the Google Ads exception.\n * @return the ignorable policy topics.\n */\n private List<String> fetchIgnorablePolicyTopics(GoogleAdsException gae) {\n System.out.println(\"Google Ads failure details:\");\n\n // Creates a list to store the result.\n List<String> ignorableTopics = new ArrayList<>();\n\n // Searches all errors for ignorable policy topics.\n for (GoogleAdsError error : gae.getGoogleAdsFailure().getErrorsList()) {\n // Supports sending exemption request for the policy finding error only.\n if (error.getErrorCode().getErrorCodeCase() != ErrorCodeCase.POLICY_FINDING_ERROR) {\n throw gae;\n }\n\n // Shows some information about the error encountered.\n System.out.printf(\"\\t%s: %s%n\", error.getErrorCode().getErrorCodeCase(), error.getMessage());\n\n // Checks policy finding details for ignorable policy topics.\n if (error.getDetails() != null) {\n PolicyFindingDetails policyFindingDetails = error.getDetails().getPolicyFindingDetails();\n if (policyFindingDetails != null) {\n System.out.println(\"\\tPolicy finding details:\");\n // Shows all the policy topics for the current error.\n for (PolicyTopicEntry policyTopicEntry :\n policyFindingDetails.getPolicyTopicEntriesList()) {\n // Adds this topic to the result.\n ignorableTopics.add(policyTopicEntry.getTopic());\n System.out.printf(\"\\t\\tPolicy topic name: '%s'%n\", policyTopicEntry.getTopic());\n System.out.printf(\"\\t\\tPolicy topic entry type: '%s'%n\", policyTopicEntry.getType());\n // For the sake of brevity, we exclude printing \"policy topic evidences\" and\n // \"policy topic constraints\" here. You can fetch those data by calling:\n // - policyTopicEntry.getEvidences()\n // - policyTopicEntry.getConstraints()\n }\n }\n }\n }\n return ignorableTopics;\n }\n\n /**\n * Sends exemption requests for creating a responsive search ad.\n *\n * @param ignorablePolicyTopics topics to request exemption for.\n * @param client client to use for API access.\n * @param operation operation which generated original violations.\n * @param customerID the customer ID to operate on.\n */\n private void requestExemption(\n List<String> ignorablePolicyTopics,\n AdGroupAdServiceClient client,\n AdGroupAdOperation operation,\n long customerID) {\n System.out.println(\n \"Trying to add a responsive search ad again by requesting exemption for its policy\"\n + \" violations.\");\n // Converts the operation back to a builder.\n AdGroupAdOperation.Builder operationBuilder = operation.toBuilder();\n\n // Adds the exemption request.\n operationBuilder\n .getPolicyValidationParameterBuilder()\n .addAllIgnorablePolicyTopics(ignorablePolicyTopics);\n\n // Sends the request back to the API.\n MutateAdGroupAdsResponse response =\n client.mutateAdGroupAds(\n String.valueOf(customerID), ImmutableList.of(operationBuilder.build()));\n\n // Shows the newly added ad resource name.\n System.out.printf(\n \"Successfully added a responsive search ad with resource name '%s' by requesting a policy\"\n + \" violation exemption.%n\",\n response.getResults(0).getResourceName());\n }\n}\nHandleResponsiveSearchAdPolicyViolations.java\n```\n\nExample:\n```text\n// Copyright 2020 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System;\nusing System.Collections.Generic;\nusing static Google.Ads.GoogleAds.V25.Enums.AdGroupAdStatusEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example demonstrates how to request an exemption for policy violations of a\n /// responsive search ad. If the request somehow fails with exceptions that are not policy finding\n /// errors, the code example will stop instead of trying to send an exemption request.\n /// </summary>\n public class HandleResponsiveSearchAdPolicyViolations : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"HandleResponsiveSearchAdPolicyViolations\"/>\n /// example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// ID of the ad group to which ads are added.\n /// </summary>\n [Option(\"adGroupId\", Required = true, HelpText =\n \"ID of the ad group to which ads are added.\")]\n public long AdGroupId { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n HandleResponsiveSearchAdPolicyViolations codeExample =\n new HandleResponsiveSearchAdPolicyViolations();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.AdGroupId);\n }\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example demonstrates how to request an exemption for policy violations of \" +\n \"a responsive search ad. If the request somehow fails with exceptions that are not \" +\n \"policy finding errors, the code example will stop instead of trying to send an \" +\n \"exemption request.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The customer ID for which the call is made.</param>\n /// <param name=\"adGroupId\">ID of the ad group to which ads are added.</param>\n public void Run(GoogleAdsClient client, long customerId, long adGroupId)\n {\n // Get the AdGroupAdServiceClient.\n AdGroupAdServiceClient adGroupAdService = client.GetService(\n Services.V25.AdGroupAdService);\n\n string adGroupResourceName = ResourceNames.AdGroup(customerId, adGroupId);\n ResponsiveSearchAdInfo responsiveSearchAdInfo = new ResponsiveSearchAdInfo()\n {\n Headlines = {\n new AdTextAsset() { Text = $\"Cruise to Mars #{ExampleUtilities.GetShortRandomString()}\" },\n new AdTextAsset() { Text = \"Best Space Cruise Line\" },\n new AdTextAsset() { Text = \"Experience the Stars\" }\n },\n Descriptions = {\n // Intentionally use an ad text that violates policy -- having too many exclamation\n // marks.\n new AdTextAsset() { Text = \"Buy your tickets now!!!!!!!\" },\n new AdTextAsset() { Text = \"Visit the Red Planet\" }\n }\n };\n\n // Creates an ad group ad to hold the above ad.\n AdGroupAd adGroupAd = new AdGroupAd()\n {\n AdGroup = adGroupResourceName,\n // Set the ad group ad to PAUSED to prevent it from immediately serving.\n // Set to ENABLED once you've added targeting and the ad are ready to serve.\n Status = AdGroupAdStatus.Paused,\n // Sets the responsive search ad info on an Ad.\n Ad = new Ad()\n {\n ResponsiveSearchAd = responsiveSearchAdInfo,\n FinalUrls = { \"https://www.example.com\" }\n }\n };\n\n // Creates an ad group ad operation.\n AdGroupAdOperation operation = new AdGroupAdOperation()\n {\n Create = adGroupAd\n };\n\n try\n {\n try\n {\n // Try sending a mutate request to add the ad group ad.\n adGroupAdService.MutateAdGroupAds(customerId.ToString(), new[] { operation });\n }\n catch (GoogleAdsException ex)\n {\n // The request will always fail because of the policy violation in the\n // description of the ad.\n var ignorablePolicyTopics = FetchIgnorablePolicyTopics(ex);\n // Try sending exemption requests for creating a responsive search ad.\n RequestExemption(customerId, adGroupAdService, operation, ignorablePolicyTopics);\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Collects all ignorable policy topics that will be sent for exemption request later.\n /// </summary>\n /// <param name=\"ex\">The API exception from a previous call to add ad group ads.</param>\n /// <returns>The ignorable policy topics</returns>\n private static string[] FetchIgnorablePolicyTopics(GoogleAdsException ex)\n {\n List<string> ignorablePolicyTopics = new List<string>(); ;\n\n Console.WriteLine(\"Google Ads failure details:\");\n foreach (GoogleAdsError error in ex.Failure.Errors)\n {\n if (error.ErrorCode.ErrorCodeCase != ErrorCode.ErrorCodeOneofCase.PolicyFindingError)\n {\n throw ex;\n }\n if (error.Details != null && error.Details.PolicyFindingDetails != null)\n {\n PolicyFindingDetails details = error.Details.PolicyFindingDetails;\n Console.WriteLine($\"- Policy finding details:\");\n\n foreach (PolicyTopicEntry entry in details.PolicyTopicEntries)\n {\n ignorablePolicyTopics.Add(entry.Topic);\n Console.WriteLine($\" - Policy topic name: '{entry.Topic}'\");\n Console.WriteLine($\" - Policy topic entry type: '{entry.Type}'\");\n // For the sake of brevity, we exclude printing \"policy topic evidences\"\n // and \"policy topic constraints\" here. You can fetch those data by\n // calling:\n // - entry.Evidences\n // - entry.Constraints\n }\n }\n }\n return ignorablePolicyTopics.ToArray();\n }\n\n /// <summary>\n /// Sends exemption requests for creating a responsive search ad.\n /// </summary>\n /// <param name=\"customerId\">The customer ID for which the call is made.</param>\n /// <param name=\"service\">The ad group ad service.</param>\n /// <param name=\"operation\">The ad group ad operation to request exemption for.</param>\n /// <param name=\"ignorablePolicyTopics\">The ignorable policy topics.</param>\n private static void RequestExemption(long customerId, AdGroupAdServiceClient service,\n AdGroupAdOperation operation, string[] ignorablePolicyTopics)\n {\n Console.WriteLine(\"Try adding a responsive search ad again by requesting exemption for \" +\n \"its policy violations.\");\n PolicyValidationParameter validationParameter = new PolicyValidationParameter();\n validationParameter.IgnorablePolicyTopics.AddRange(ignorablePolicyTopics);\n operation.PolicyValidationParameter = validationParameter;\n\n MutateAdGroupAdsResponse response = service.MutateAdGroupAds(\n customerId.ToString(), new[] { operation });\n Console.WriteLine($\"Successfully added a responsive search ad with resource name \" +\n $\"'{response.Results[0].ResourceName}' by requesting for policy violation \" +\n $\"exemption.\");\n }\n }\n}\nHandleResponsiveSearchAdPolicyViolations.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2019 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\ErrorHandling;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\AdTextAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\PolicyTopicEntry;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\PolicyValidationParameter;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ResponsiveSearchAdInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdGroupAdStatusEnum\\AdGroupAdStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\PolicyTopicEntryTypeEnum\\PolicyTopicEntryType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Ad;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AdGroupAd;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AdGroupAdOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\Client\\AdGroupAdServiceClient;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAdGroupAdsRequest;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * This example demonstrates how to request an exemption for policy violations of a responsive\n * search ad. If the request somehow fails with exceptions that are not policy finding errors, the\n * example will stop instead of trying sending an exemption request.\n */\nclass HandleResponsiveSearchAdPolicyViolations\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const AD_GROUP_ID = 'INSERT_AD_GROUP_ID_HERE';\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::AD_GROUP_ID => GetOpt::REQUIRED_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::AD_GROUP_ID] ?: self::AD_GROUP_ID\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $adGroupId the ad group ID to add a responsive search ad to\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId\n ) {\n // Creates a responsive search ad info object.\n $responsiveSearchAdInfo = new ResponsiveSearchAdInfo([\n 'headlines' => [\n new AdTextAsset([\n 'text' => 'Cruise to Mars #' . Helper::getShortPrintableDatetime()\n ]),\n new AdTextAsset(['text' => 'Best Space Cruise Line']),\n new AdTextAsset(['text' => 'Experience the Stars'])\n ],\n // Intentionally use an ad text that violates policy -- having too many exclamation\n // marks.\n 'descriptions' => [\n new AdTextAsset(['text' => 'Buy your tickets now!!!!!!!']),\n new AdTextAsset(['text' => 'Visit the Red Planet'])\n ]\n ]);\n\n // Creates an ad group ad to hold the above ad.\n $adGroupAd = new AdGroupAd([\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n // Set the ad group ad to PAUSED to prevent it from immediately serving.\n // Set to ENABLED once you've added targeting and the ad are ready to serve.\n 'status' => AdGroupAdStatus::PAUSED,\n // Sets the responsive search ad info on an Ad.\n 'ad' => new Ad([\n 'responsive_search_ad' => $responsiveSearchAdInfo,\n 'final_urls' => ['https://www.example.com']\n ])\n ]);\n\n // Creates an ad group ad operation.\n $adGroupAdOperation = new AdGroupAdOperation();\n $adGroupAdOperation->setCreate($adGroupAd);\n $adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();\n\n $ignorablePolicyTopics = [];\n try {\n // Try sending a mutate request to add the ad group ad.\n $adGroupAdServiceClient->mutateAdGroupAds(\n MutateAdGroupAdsRequest::build($customerId, [$adGroupAdOperation])\n );\n } catch (GoogleAdsException $googleAdsException) {\n // The request will always fail because of the policy violation in the description of\n // the ad.\n $ignorablePolicyTopics = self::fetchIgnorablePolicyTopics($googleAdsException);\n }\n\n // Try sending exemption requests for creating a responsive search ad.\n self::requestExemption(\n $customerId,\n $adGroupAdServiceClient,\n $adGroupAdOperation,\n $ignorablePolicyTopics\n );\n }\n\n /**\n * Collects all ignorable policy topics that will be sent for exemption request later.\n *\n * @param GoogleAdsException $googleAdsException the Google Ads exception\n * @return string[] the ignorable policy topics\n */\n private static function fetchIgnorablePolicyTopics(GoogleAdsException $googleAdsException)\n {\n $ignorablePolicyTopics = [];\n\n printf(\"Google Ads failure details:%s\", PHP_EOL);\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n if ($error->getErrorCode()->getErrorCode() !== 'policy_finding_error') {\n // This example supports sending exemption request for the policy finding error\n // only.\n throw $googleAdsException;\n }\n\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n if (\n !is_null($error->getDetails())\n && !is_null($error->getDetails()->getPolicyFindingDetails())\n ) {\n $policyFindingDetails = $error->getDetails()->getPolicyFindingDetails();\n printf(\"\\tPolicy finding details:%s\", PHP_EOL);\n\n foreach ($policyFindingDetails->getPolicyTopicEntries() as $policyTopicEntry) {\n /** @var PolicyTopicEntry $policyTopicEntry */\n $ignorablePolicyTopics[] = $policyTopicEntry->getTopic();\n printf(\n \"\\t\\tPolicy topic name: '%s'%s\",\n $policyTopicEntry->getTopic(),\n PHP_EOL\n );\n printf(\n \"\\t\\tPolicy topic entry type: '%s'%s\",\n PolicyTopicEntryType::name($policyTopicEntry->getType()),\n PHP_EOL\n );\n // For the sake of brevity, we exclude printing \"policy topic evidences\" and\n // \"policy topic constraints\" here. You can fetch those data by calling:\n // - $policyTopicEntry->getEvidences()\n // - $policyTopicEntry->getConstraints()\n }\n }\n }\n return $ignorablePolicyTopics;\n }\n\n /**\n * Sends exemption requests for creating a responsive search ad.\n *\n * @param int $customerId\n * @param AdGroupAdServiceClient $adGroupAdServiceClient\n * @param AdGroupAdOperation $adGroupAdOperation\n * @param string[] $ignorablePolicyTopics\n */\n private static function requestExemption(\n int $customerId,\n AdGroupAdServiceClient $adGroupAdServiceClient,\n AdGroupAdOperation $adGroupAdOperation,\n array $ignorablePolicyTopics\n ) {\n print \"Try adding a responsive search ad again by requesting exemption for its policy\"\n . \" violations.\" . PHP_EOL;\n $adGroupAdOperation->setPolicyValidationParameter(\n new PolicyValidationParameter(['ignorable_policy_topics' => $ignorablePolicyTopics])\n );\n $response = $adGroupAdServiceClient->mutateAdGroupAds(MutateAdGroupAdsRequest::build(\n $customerId,\n [$adGroupAdOperation]\n ));\n printf(\n \"Successfully added a responsive search ad with resource name '%s' by requesting\"\n . \" for policy violation exemption.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n }\n}\n\nHandleResponsiveSearchAdPolicyViolations::main();\nHandleResponsiveSearchAdPolicyViolations.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2020 Google LLC\n#\n# 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.\n\"\"\"Requests an exemption for policy violations of a responsive search ad.\n\nIf the request somehow fails with exceptions that are not policy finding\nerrors, the example will stop instead of trying sending an exemption request.\n\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import List\nimport uuid\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.services.services.ad_group_ad_service import (\n AdGroupAdServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.ad_group_service import (\n AdGroupServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_ad_service import (\n AdGroupAdOperation,\n MutateAdGroupAdsResponse,\n)\nfrom google.ads.googleads.v24.resources.types.ad_group_ad import AdGroupAd\nfrom google.ads.googleads.v24.common.types.ad_type_infos import (\n ResponsiveSearchAdInfo,\n)\nfrom google.ads.googleads.v24.common.types.ad_asset import (\n AdTextAsset,\n)\nfrom google.ads.googleads.v24.errors.types.policy_finding_error import (\n PolicyFindingErrorEnum,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef main(client: GoogleAdsClient, customer_id: str, ad_group_id: str) -> None:\n \"\"\"Handles responsive search ad policy violations.\n\n Args:\n client: The Google Ads client.\n customer_id: The customer ID for which to add the responsive search ad.\n ad_group_id: The ad group ID to which to add a responsive search ad.\n \"\"\"\n ad_group_ad_service_client: AdGroupAdServiceClient = client.get_service(\n \"AdGroupAdService\"\n )\n ad_group_ad_operation: AdGroupAdOperation = create_responsive_search_ad(\n client, ad_group_ad_service_client, customer_id, ad_group_id\n )\n\n ignorable_policy_topics: List[str] = []\n try:\n # Try sending a mutate request to add the ad group ad.\n ad_group_ad_service_client.mutate_ad_group_ads(\n customer_id=customer_id, operations=[ad_group_ad_operation]\n )\n except GoogleAdsException as googleads_exception:\n # The request will always fail due to the policy violation in the\n # ad's description.\n ignorable_policy_topics = fetch_ignorable_policy_topics(\n client, googleads_exception\n )\n\n request_exemption(\n customer_id,\n ad_group_ad_service_client,\n ad_group_ad_operation,\n ignorable_policy_topics,\n )\n\n\ndef create_responsive_search_ad(\n client: GoogleAdsClient,\n ad_group_ad_service_client: AdGroupAdServiceClient,\n customer_id: str,\n ad_group_id: str,\n) -> AdGroupAdOperation:\n \"\"\"Create a responsive search ad that includes a policy violation.\n\n Args:\n client: The GoogleAds client instance.\n ad_group_ad_service_client: The AdGroupAdService client instance.\n customer_id: The customer ID for which to add the responsive search ad.\n ad_group_id: The ad group ID to which to add a responsive search ad.\n\n Returns:\n The attempted AdGroupAdOperation instance.\n \"\"\"\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n ad_group_resource_name: str = ad_group_service.ad_group_path(\n customer_id, ad_group_id\n )\n\n # Creates an operation and ad group ad to create and hold the above ad.\n ad_group_ad_operation: AdGroupAdOperation = client.get_type(\n \"AdGroupAdOperation\"\n )\n ad_group_ad: AdGroupAd = ad_group_ad_operation.create\n ad_group_ad.ad_group = ad_group_resource_name\n # Set the ad group ad to PAUSED to prevent it from immediately serving.\n # Set to ENABLED once you've added targeting and the ad are ready to serve.\n ad_group_ad.status = client.enums.AdGroupAdStatusEnum.PAUSED\n # Sets the responsive search ad info on an ad.\n responsive_search_ad_info: ResponsiveSearchAdInfo = (\n ad_group_ad.ad.responsive_search_ad\n )\n\n headline_1: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline_1.text = f\"Cruise to Mars #{str(uuid.uuid4())[0:13]}\"\n headline_2: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline_2.text = \"Best Space Cruise Line\"\n headline_3: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline_3.text = \"Experience the Stars\"\n responsive_search_ad_info.headlines.extend(\n [headline_1, headline_2, headline_3]\n )\n\n # Intentionally use an ad text that violates policy by having too many\n # exclamation marks.\n description_1: AdTextAsset = client.get_type(\"AdTextAsset\")\n description_1.text = \"Buy your tickets now!!!!!!!\"\n description_2: AdTextAsset = client.get_type(\"AdTextAsset\")\n description_2.text = \"Visit the Red Planet\"\n responsive_search_ad_info.descriptions.extend(\n [description_1, description_2]\n )\n\n ad_group_ad.ad.final_urls.append(\"https://www.example.com\")\n\n return ad_group_ad_operation\n\n\ndef fetch_ignorable_policy_topics(\n client: GoogleAdsClient, googleads_exception: GoogleAdsException\n) -> List[str]:\n \"\"\"Collects all ignorable policy topics to be sent for exemption request.\n\n Args:\n client: The GoogleAds client instance.\n googleads_exception: The exception that contains the policy\n violation(s).\n\n Returns:\n A list of ignorable policy topics.\n \"\"\"\n ignorable_policy_topics: List[str] = []\n\n print(\"Google Ads failure details:\")\n for error in googleads_exception.failure.errors:\n if (\n error.error_code.policy_finding_error\n != client.enums.PolicyFindingErrorEnum.POLICY_FINDING\n ):\n print(\n \"This example supports sending exemption request for the \"\n \"policy finding error only.\"\n )\n raise googleads_exception\n\n print(f\"\\t{error.error_code.policy_finding_error}: {error.message}\")\n\n if (\n error.details is not None\n and error.details.policy_finding_details is not None\n ):\n policy_finding_details: PolicyFindingErrorEnum = (\n error.details.policy_finding_details\n )\n print(\"\\tPolicy finding details:\")\n\n for (\n policy_topic_entry\n ) in policy_finding_details.policy_topic_entries:\n ignorable_policy_topics.append(policy_topic_entry.topic)\n print(f\"\\t\\tPolicy topic name: '{policy_topic_entry.topic}'\")\n print(\n f\"\\t\\tPolicy topic entry type: '{policy_topic_entry.type_}'\"\n )\n # For the sake of brevity, we exclude printing \"policy topic\n # evidences\" and \"policy topic constraints\" here. You can fetch\n # those data by calling:\n # - policy_topic_entry.evidences\n # - policy_topic_entry.constraints\n\n return ignorable_policy_topics\n\n\ndef request_exemption(\n customer_id: str,\n ad_group_ad_service_client: AdGroupAdServiceClient,\n ad_group_ad_operation: AdGroupAdOperation,\n ignorable_policy_topics: List[str],\n) -> None:\n \"\"\"Sends exemption requests for creating a responsive search ad.\n\n Args:\n customer_id: The customer ID for which to add the responsive search ad.\n ad_group_ad_service_client: The AdGroupAdService client instance.\n ad_group_ad_operation: The AdGroupAdOperation that returned policy\n violation(s).\n ignorable_policy_topics: The extracted list of policy topic entries.\n \"\"\"\n print(\n \"Attempting to add a responsive search ad again by requesting \"\n \"exemption for its policy violations.\"\n )\n ad_group_ad_operation.policy_validation_parameter.ignorable_policy_topics.extend(\n ignorable_policy_topics\n )\n response: MutateAdGroupAdsResponse = (\n ad_group_ad_service_client.mutate_ad_group_ads(\n customer_id=customer_id, operations=[ad_group_ad_operation]\n )\n )\n print(\n \"Successfully added a responsive search ad with resource name \"\n f\"'{response.results[0].resource_name}' for policy violation \"\n \"exemption.\"\n )\n\n\nif __name__ == \"__main__\":\n parser: argparse.ArgumentParser = argparse.ArgumentParser(\n description=\"Requests an exemption for responsive search ad policy \"\n \"violations.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-a\",\n \"--ad_group_id\",\n type=str,\n required=True,\n help=\"The ad group ID to which to add a responsive search ad.\",\n )\n args = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(googleads_client, args.customer_id, args.ad_group_id)\n except GoogleAdsException as ex:\n print(\n f\"Request with ID '{ex.request_id}' failed with status \"\n f\"'{ex.error.code().name}' and includes the following errors:\"\n )\n for error in ex.failure.errors:\n print(f\"\\tError with message '{error.message}'.\")\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nhandle_responsive_search_ad_policy_violations.py\n```\n\nExample:\n```text\n# Encoding: utf-8\n#\n# Copyright 2020 Google LLC\n#\n# 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.\n#\n# Requests an exemption for policy violations of a responsive search ad.\n#\n# If the request somehow fails with exceptions that are not policy finding\n# errors, the example will stop instead of trying to send an exemption request.\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\nrequire 'date'\n\ndef handle_responsive_search_ad_policy_violations(customer_id, ad_group_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n ad_group_ad_service = client.service.ad_group_ad\n\n ad_group_ad_operation, ignorable_policy_topics = create_responsive_search_ad(\n client,\n ad_group_ad_service,\n customer_id,\n ad_group_id,\n )\n\n request_exemption(\n client,\n customer_id,\n ad_group_ad_service,\n ad_group_ad_operation,\n ignorable_policy_topics,\n )\nend\n\ndef create_responsive_search_ad(client, ad_group_ad_service, customer_id, ad_group_id)\n ad_group_ad_operation = client.operation.create_resource.ad_group_ad do |aga|\n aga.ad_group = client.path.ad_group(customer_id, ad_group_id)\n aga.status = :PAUSED\n\n aga.ad = client.resource.ad do |ad|\n ad.final_urls << \"http://www.example.com\"\n ad.responsive_search_ad = client.resource.responsive_search_ad_info do |rsa|\n rsa.headlines << client.resource.ad_text_asset do |ta|\n ta.text = \"Cruise to Mars ##{(Time.new.to_f * 1000).to_i}\"\n end\n rsa.headlines << client.resource.ad_text_asset do |ta|\n ta.text = \"Best space cruise line\"\n end\n rsa.headlines << client.resource.ad_text_asset do |ta|\n ta.text = \"Experience the stars\"\n end\n rsa.descriptions << client.resource.ad_text_asset do |ta|\n # Intentionally use an ad text that violates policy -- having too\n # many exclamation marks.\n ta.text = \"Buy your tickets now!!!!!!!\"\n end\n rsa.descriptions << client.resource.ad_text_asset do |ta|\n ta.text = \"Visit the Red Planet\"\n end\n end\n end\n end\n\n ignorable_policy_topics = []\n begin\n ad_group_ad_service.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [ad_group_ad_operation],\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n ignorable_policy_topics = fetch_ignorable_policy_topics(e)\n end\n\n return ad_group_ad_operation, ignorable_policy_topics\nend\n\ndef fetch_ignorable_policy_topics(exception)\n ignorable_policy_topics = []\n\n exception.failure.errors.each do |error|\n if error.error_code.policy_finding_error != :POLICY_FINDING\n puts \"Non-policy finding error found. Aborting.\"\n raise exception\n end\n puts \"#{error.error_code.policy_finding_error}: #{error.message}\"\n\n error&.details&.policy_finding_details&.policy_topic_entries.each do |entry|\n ignorable_policy_topics << entry.topic\n puts \"\\tPolicy topic name: #{entry.topic}\"\n puts \"\\tPolicy topic entry type: #{entry.type}\"\n end\n end\n\n ignorable_policy_topics\nend\n\ndef request_exemption(\n client, customer_id, ad_group_ad_service, ad_group_ad_operation, ignorable_policy_topics)\n # Add all the found ignorable policy topics to the operation.\n ad_group_ad_operation.policy_validation_parameter =\n client.resource.policy_validation_parameter do |pvp|\n pvp.ignorable_policy_topics.push(\n *ignorable_policy_topics\n )\n end\n response = ad_group_ad_service.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [ad_group_ad_operation],\n )\n puts \"Successfully added a responsive search ad with resource name \" \\\n \"#{response.results.first.resource_name} for policy violation exception.\"\nend\n\nif __FILE__ == $PROGRAM_NAME\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:ad_group_id] = 'INSERT_AD_GROUP_ID_HERE'\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: ruby %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-A', '--ad-group-id AD-GROUP-ID', String, 'Ad Group ID') do |v|\n options[:ad_group_id] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n handle_responsive_search_ad_policy_violations(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options.fetch(:ad_group_id),\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\nhandle_responsive_search_ad_policy_violations.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2019, Google LLC\n#\n# 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# http://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.\n#\n# This example demonstrates how to request an exemption for policy violations\n# of a responsive search ad. If the request somehow fails with exceptions that are\n# not policy finding errors, the example will stop instead of trying sending an\n# exemption request.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroupAd;\nuse Google::Ads::GoogleAds::V25::Resources::Ad;\nuse Google::Ads::GoogleAds::V25::Common::AdTextAsset;\nuse Google::Ads::GoogleAds::V25::Common::ResponsiveSearchAdInfo;\nuse Google::Ads::GoogleAds::V25::Common::PolicyValidationParameter;\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupStatusEnum qw(PAUSED);\nuse Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\n\nsub handle_responsive_search_ad_policy_violations {\n my ($api_client, $customer_id, $ad_group_id) = @_;\n\n my $ad_group_resource_name =\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group($customer_id,\n $ad_group_id);\n\n # Create a responsive search ad info object.\n my $responsive_search_ad_info =\n Google::Ads::GoogleAds::V25::Common::ResponsiveSearchAdInfo->new({\n headlines => [\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Cruise to Mars #\" . uniqid()}\n ),\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Best Space Cruise Line\"\n }\n ),\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Experience the Stars\"\n })\n ],\n descriptions => [\n # Intentionally use an ad text that violates policy -- having too many\n # exclamation marks.\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Buy your tickets now!!!!!!!\"\n }\n ),\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Visit the Red Planet\"\n })]});\n\n # Create an ad group ad to hold the above ad.\n my $ad_group_ad = Google::Ads::GoogleAds::V25::Resources::AdGroupAd->new({\n adGroup => $ad_group_resource_name,\n # Set the ad group ad to PAUSED to prevent it from immediately serving.\n # Set to ENABLED once you've added targeting and the ad are ready to serve.\n status => PAUSED,\n # Set the responsive search ad info on an ad.\n ad => Google::Ads::GoogleAds::V25::Resources::Ad->new({\n responsiveSearchAd => $responsive_search_ad_info,\n finalUrls => [\"https://www.example.com\"]})});\n\n # Create an ad group ad operation.\n my $ad_group_ad_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation\n ->new({\n create => $ad_group_ad\n });\n\n # Try sending a mutate request to add the ad group ad.\n my $response = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]});\n\n my $ignorable_policy_topics = [];\n if ($response->isa(\"Google::Ads::GoogleAds::GoogleAdsException\")) {\n # The request will always fail because of the policy violation in the\n # description of the ad.\n $ignorable_policy_topics = fetch_ignorable_policy_topics($response);\n }\n\n # Try sending exemption requests for creating a responsive search ad.\n request_exemption($api_client, $customer_id, $ad_group_ad_operation,\n $ignorable_policy_topics);\n\n return 1;\n}\n\n# Collects all ignorable policy topics that will be sent for exemption request\n# later.\nsub fetch_ignorable_policy_topics {\n my $google_ads_exception = shift;\n\n my $ignorable_policy_topics = [];\n\n printf \"Google Ads failure details:\\n\";\n foreach\n my $error (@{$google_ads_exception->get_google_ads_failure()->{errors}})\n {\n if ([keys %{$error->{errorCode}}]->[0] ne \"policyFindingError\") {\n # This example supports sending exemption request for the policy finding\n # error only.\n die $google_ads_exception->get_message();\n }\n\n printf \"\\t%s: %s\\n\", [keys %{$error->{errorCode}}]->[0], $error->{message};\n\n if ($error->{details}{policyFindingDetails}) {\n my $policy_finding_details = $error->{details}{policyFindingDetails};\n printf \"\\tPolicy finding details:\\n\";\n\n foreach my $policy_topic_entry (\n @{$policy_finding_details->{policyTopicEntries}})\n {\n push @$ignorable_policy_topics, $policy_topic_entry->{topic};\n printf\n \"\\t\\tPolicy topic name: '%s'\\n\",\n $policy_topic_entry->{topic};\n printf \"\\t\\tPolicy topic entry type: '%s'\\n\",\n $policy_topic_entry->{type};\n # For the sake of brevity, we exclude printing \"policy topic evidences\" and\n # \"policy topic constraints\" here. You can fetch those data by calling:\n # - $policy_topic_entry->{evidences}\n # - $policy_topic_entry->{constraints}\n }\n }\n }\n\n return $ignorable_policy_topics;\n}\n\n# Sends exemption requests for creating a responsive search ad.\nsub request_exemption {\n my ($api_client, $customer_id, $ad_group_ad_operation,\n $ignorable_policy_topics)\n = @_;\n\n print\n \"Try adding a responsive search ad again by requesting exemption for its \"\n . \"policy violations.\\n\";\n\n $ad_group_ad_operation->{policyValidationParameter} =\n Google::Ads::GoogleAds::V25::Common::PolicyValidationParameter->new(\n {ignorablePolicyTopics => $ignorable_policy_topics});\n\n my $ad_group_ads_response = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]});\n\n printf\n \"Successfully added a responsive search ad with resource name '%s' by \" .\n \"requesting for policy violation exemption.\\n\",\n $ad_group_ads_response->{results}[0]{resourceName};\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(0);\n\nmy $customer_id = undef;\nmy $ad_group_id = undef;\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"ad_group_id=i\" => \\$ad_group_id\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id, $ad_group_id);\n\n# Call the example.\nhandle_responsive_search_ad_policy_violations($api_client,\n $customer_id =~ s/-//gr, $ad_group_id);\n\n=pod\n\n=head1 NAME\n\nhandle_responsive_search_ad_policy_violations\n\n=head1 DESCRIPTION\n\nThis example demonstrates how to request an exemption for policy violations of a\nresponsive search ad. If the request somehow fails with exceptions that are not policy\nfinding errors, the example will stop instead of trying sending an exemption request.\n\n=head1 SYNOPSIS\n\nhandle_responsive_search_ad_policy_violations.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -ad_group_id The ad group ID.\n\n=cut\nhandle_responsive_search_ad_policy_violations.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.628Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":1846,"estimatedTokens":17778}}247{"id":"doc-request_exemption_for_keywords_google_ads_api_go-c7aea06d","source":"documentation","title":"Request Exemption for Keywords | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/policy-exemption/keywords","text":"Example:\n```text\nprivate List<PolicyViolationKey> extractExemptiblePolicyViolationKeys(\n GoogleAdsException googleAdsException) {\n List<PolicyViolationKey> exemptibleKeys = new ArrayList<>();\n System.out.println(\"Google Ads failure details:\");\n for (GoogleAdsError googleAdsError : googleAdsException.getGoogleAdsFailure().getErrorsList()) {\n System.out.printf(\"\\t%s: %s%n\", googleAdsError.getErrorCode(), googleAdsError.getMessage());\n if (googleAdsError.hasDetails() && googleAdsError.getDetails().hasPolicyViolationDetails()) {\n PolicyViolationDetails policyViolationDetails =\n googleAdsError.getDetails().getPolicyViolationDetails();\n System.out.println(\"\\tPolicy violation details:\");\n System.out.printf(\n \"\\t\\tExternal policy name: '%s'%n\", policyViolationDetails.getExternalPolicyName());\n System.out.printf(\n \"\\t\\tExternal policy description: '%s'%n\",\n policyViolationDetails.getExternalPolicyDescription());\n System.out.printf(\"\\t\\tIs exemptible? '%s'%n\", policyViolationDetails.getIsExemptible());\n if (policyViolationDetails.getIsExemptible() && policyViolationDetails.hasKey()) {\n PolicyViolationKey policyViolationKey = policyViolationDetails.getKey();\n exemptibleKeys.add(policyViolationKey);\n System.out.println(\"\\t\\tPolicy violation key:\");\n System.out.printf(\"\\t\\t\\tName: '%s'%n\", policyViolationKey.getPolicyName());\n System.out.printf(\"\\t\\t\\tViolating text: '%s'%n\", policyViolationKey.getViolatingText());\n }\n }\n }\n return exemptibleKeys;\n}HandleKeywordPolicyViolations.java\n```\n\nExample:\n```text\nprivate static PolicyViolationKey[] FetchExemptPolicyViolationKeys(GoogleAdsException ex)\n{\n bool isFullyExemptable = true;\n List<PolicyViolationKey> exemptPolicyViolationKeys = new List<PolicyViolationKey>();\n\n Console.WriteLine(\"Google Ads failure details:\");\n foreach (GoogleAdsError error in ex.Failure.Errors)\n {\n if (error.ErrorCode.ErrorCodeCase !=\n ErrorCode.ErrorCodeOneofCase.PolicyViolationError)\n {\n Console.WriteLine(\"No exemption request is sent because there are other \" +\n \"non-policy related errors thrown.\");\n throw ex;\n }\n if (error.Details != null && error.Details.PolicyViolationDetails != null)\n {\n PolicyViolationDetails details = error.Details.PolicyViolationDetails;\n Console.WriteLine($\"- Policy violation details:\");\n\n Console.WriteLine(\" - Policy violation details:\");\n Console.WriteLine($\" - External policy name: '{details.ExternalPolicyName}'\");\n Console.WriteLine($\" - External policy description: \" +\n $\"'{details.ExternalPolicyDescription}'\");\n Console.WriteLine($\" - Is exemptable: '{details.IsExemptible}'\");\n\n if (details.IsExemptible && details.Key != null)\n {\n PolicyViolationKey key = details.Key;\n Console.WriteLine($\" - Policy violation key:\");\n Console.WriteLine($\" - Name: {key.PolicyName}\");\n Console.WriteLine($\" - Violating Text: {key.ViolatingText}\");\n exemptPolicyViolationKeys.Add(key);\n }\n else\n {\n isFullyExemptable = false;\n }\n }\n }\n\n if (!isFullyExemptable)\n {\n Console.WriteLine(\"No exemption request is sent because your keyword \" +\n \"contained some non-exemptible policy violations.\");\n throw ex;\n }\n return exemptPolicyViolationKeys.ToArray();\n}HandleKeywordPolicyViolations.cs\n```\n\nExample:\n```text\nprivate static function fetchExemptPolicyViolationKeys(GoogleAdsException $googleAdsException)\n{\n $exemptPolicyViolationKeys = [];\n\n printf(\"Google Ads failure details:%s\", PHP_EOL);\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n if (\n !is_null($error->getDetails())\n && !is_null($error->getDetails()->getPolicyViolationDetails())\n ) {\n $policyViolationDetails = $error->getDetails()->getPolicyViolationDetails();\n printf(\"\\tPolicy violation details:%s\", PHP_EOL);\n printf(\n \"\\t\\tExternal policy name: '%s'%s\",\n $policyViolationDetails->getExternalPolicyName(),\n PHP_EOL\n );\n printf(\n \"\\t\\tExternal policy description: '%s'%s\",\n $policyViolationDetails->getExternalPolicyDescription(),\n PHP_EOL\n );\n printf(\n \"\\t\\tIs exemptible? '%s'%s\",\n $policyViolationDetails->getIsExemptible() ? 'yes' : 'no',\n PHP_EOL\n );\n\n if (\n $policyViolationDetails->getIsExemptible() &&\n !is_null($policyViolationDetails->getKey())\n ) {\n $policyViolationDetailsKey = $policyViolationDetails->getKey();\n $exemptPolicyViolationKeys[] = $policyViolationDetailsKey;\n printf(\"\\t\\tPolicy violation key:%s\", PHP_EOL);\n printf(\n \"\\t\\t\\tName: '%s'%s\",\n $policyViolationDetailsKey->getPolicyName(),\n PHP_EOL\n );\n printf(\n \"\\t\\t\\tViolating text: '%s'%s\",\n $policyViolationDetailsKey->getViolatingText(),\n PHP_EOL\n );\n } else {\n print \"No exemption request is sent because your keyword contained some \"\n . \"non-exemptible policy violations.\" . PHP_EOL;\n throw $googleAdsException;\n }\n } else {\n print \"No exemption request is sent because there are other non-policy related \"\n . \"errors thrown.\" . PHP_EOL;\n throw $googleAdsException;\n }\n }\n return $exemptPolicyViolationKeys;\n}HandleKeywordPolicyViolations.php\n```\n\nExample:\n```text\ndef fetch_exempt_policy_violation_keys(\n googleads_exception: GoogleAdsException,\n) -> List[PolicyViolationKey]:\n \"\"\"Collects all policy violation keys that can be exempted.\n\n Args:\n googleads_exception: The exception to check for policy violation(s).\n\n Returns:\n A list of policy violation keys.\n \"\"\"\n exempt_policy_violation_keys: List[PolicyViolationKey] = []\n\n print(\"Google Ads failure details:\")\n for error in googleads_exception.failure.errors:\n print(f\"\\t{error.error_code}: {error.message}\")\n\n if (\n error.details is not None\n and error.details.policy_violation_details is not None\n ):\n policy_violation_details = error.details.policy_violation_details\n print(\n \"\\tPolicy violation details:\\n\"\n f\"\\t\\tExternal policy name: '{policy_violation_details}'\\n\"\n \"\\t\\tExternal policy description: \"\n f\"'{policy_violation_details.external_policy_description}'\\n\"\n f\"\\t\\tIs exemptible? '{policy_violation_details.is_exemptible}'\"\n )\n\n if (\n policy_violation_details.is_exemptible\n and policy_violation_details.key is not None\n ):\n exempt_policy_violation_keys.append(\n policy_violation_details.key\n )\n print(\n f\"\\t\\tPolicy violation key: {policy_violation_details.key}\"\n )\n print(\n f\"\\t\\t\\tName: '{policy_violation_details.key.policy_name}'\"\n \"\\t\\t\\tViolating text: \"\n f\"'{policy_violation_details.key.violating_text}'\"\n )\n else:\n print(\n \"No exemption request is sent because your keyword \"\n \"contained some non-exemptible policy violations.\"\n )\n raise googleads_exception\n else:\n print(\n \"No exemption request is sent because there are non-policy \"\n \"related errors thrown.\"\n )\n raise googleads_exception\n\n return exempt_policy_violation_keyshandle_keyword_policy_violations.py\n```\n\nExample:\n```text\ndef fetch_exempt_policy_violation_keys(exception)\n exempt_policy_violation_keys = []\n\n exception.failure.errors.each do |error|\n details = error.details.policy_violation_details\n puts \"Policy violation details:\"\n puts \"\\tExternal policy name: #{details.external_policy_name}\"\n puts \"\\tExternal policy description:\\n#{details.external_policy_description}\"\n puts \"\\tIs exemptible: #{details.is_exemptible}\"\n\n if details.is_exemptible && !details.key.nil?\n exempt_policy_violation_keys << details.key\n puts \"Policy violation key:\"\n puts \"\\tPolicy Name: #{details.key.policy_name}\"\n puts \"\\tViolating Text: #{details.key.violating_text}\"\n else\n puts \"No exemption request will be sent because your keyword contained \"\\\n \"some non-exemptible policy violations.\"\n end\n end\n\n exempt_policy_violation_keys\nendhandle_keyword_policy_violations.rb\n```\n\nExample:\n```text\nsub fetch_exempt_policy_violation_keys {\n my $google_ads_exception = shift;\n\n my $exempt_policy_violation_keys = [];\n\n print \"Google Ads failure details:\\n\";\n foreach\n my $error (@{$google_ads_exception->get_google_ads_failure()->{errors}})\n {\n printf \"\\t%s: %s\\n\", [keys %{$error->{errorCode}}]->[0], $error->{message};\n\n if ($error->{details}{policyViolationDetails}) {\n my $policy_violation_details = $error->{details}{policyViolationDetails};\n printf \"\\tPolicy violation details:\\n\";\n printf \"\\t\\tExternal policy name: '%s'\\n\",\n $policy_violation_details->{externalPolicyName};\n printf\n \"\\t\\tExternal policy description: '%s'\\n\",\n $policy_violation_details->{externalPolicyDescription};\n printf\n \"\\t\\tIs exemptible? '%s'\\n\",\n $policy_violation_details->{isExemptible} ? \"yes\" : \"no\";\n\n if ( $policy_violation_details->{isExemptible}\n and $policy_violation_details->{key})\n {\n my $policy_violation_details_key = $policy_violation_details->{key};\n push @$exempt_policy_violation_keys, $policy_violation_details_key;\n\n printf \"\\t\\tPolicy violation key:\\n\";\n printf \"\\t\\t\\tName: '%s'\\n\",\n $policy_violation_details_key->{policyName};\n printf\n \"\\t\\t\\tViolating text: '%s'\\n\",\n $policy_violation_details_key->{violatingText};\n }\n }\n }\n\n return $exempt_policy_violation_keys;\n}handle_keyword_policy_violations.pl\n```\n\nExample:\n```text\n// Tries sending exemption requests for creating the keyword. However, if your keyword\n// contains many policy violations, but not all of them are exemptible, the request will not\n// be sent.\nif (exemptibleKeys.size() == errorCount) {\n System.out.println(\n \"Attempting to add the keyword again by requesting exemption for its policy\"\n + \" violations.\");\n // Creates a modified version of the operation with the exempt policy violation keys.\n operation = operation.toBuilder().addAllExemptPolicyViolationKeys(exemptibleKeys).build();\n\n // Tries sending the mutate request again.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n String.valueOf(customerId), ImmutableList.of(operation));\n System.out.printf(\n \"Successfully added a keyword with resource name '%s' by requesting \"\n + \"policy violation exemptions.%n\",\n response.getResults(0).getResourceName());\n} else {HandleKeywordPolicyViolations.java\n```\n\nExample:\n```text\nprivate static void RequestExemption(\n long customerId, AdGroupCriterionServiceClient service,\n AdGroupCriterionOperation operation, PolicyViolationKey[] exemptPolicyViolationKeys)\n{\n Console.WriteLine(\"Try adding a keyword again by requesting exemption for its policy \"\n + \"violations.\");\n PolicyValidationParameter validationParameter = new PolicyValidationParameter();\n validationParameter.ExemptPolicyViolationKeys.AddRange(exemptPolicyViolationKeys);\n operation.ExemptPolicyViolationKeys.AddRange(exemptPolicyViolationKeys);\n\n MutateAdGroupCriteriaResponse response = service.MutateAdGroupCriteria(\n customerId.ToString(), new[] { operation });\n Console.WriteLine($\"Successfully added a keyword with resource name \" +\n $\"'{response.Results[0].ResourceName}' by requesting for policy violation \" +\n $\"exemption.\");\n}HandleKeywordPolicyViolations.cs\n```\n\nExample:\n```text\nprivate static function requestExemption(\n int $customerId,\n AdGroupCriterionServiceClient $adGroupCriterionServiceClient,\n AdGroupCriterionOperation $adGroupCriterionOperation,\n array $exemptPolicyViolationKeys\n) {\n print \"Try adding a keyword again by requesting exemption for its policy\"\n . \" violations.\" . PHP_EOL;\n $adGroupCriterionOperation->setExemptPolicyViolationKeys($exemptPolicyViolationKeys);\n $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$adGroupCriterionOperation])\n );\n printf(\n \"Successfully added a keyword with resource name '%s' by requesting for\"\n . \" policy violation exemption.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n}HandleKeywordPolicyViolations.php\n```\n\nExample:\n```text\ndef request_exemption(\n customer_id: str,\n ad_group_criterion_service: AdGroupCriterionServiceClient,\n ad_group_criterion_operation: AdGroupCriterionOperation,\n exempt_policy_violation_keys: List[PolicyViolationKey],\n) -> None:\n \"\"\"Sends exemption requests for creating a keyword.\n\n Args:\n customer_id: The customer ID for which to add the expanded text ad.\n ad_group_criterion_service: The AdGroupCriterionService client instance.\n ad_group_criterion_operation: The AdGroupCriterionOperation for which\n to request exemption.\n exempt_policy_violation_keys: The exemptible policy violation keys.\n \"\"\"\n print(\n \"Attempting to add a keyword again by requesting exemption for its \"\n \"policy violations.\"\n )\n ad_group_criterion_operation.exempt_policy_violation_keys.extend(\n exempt_policy_violation_keys\n )\n response: Any = ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n print(\n \"Successfully added a keyword with resource name \"\n f\"'{response.results[0].resource_name}' by requesting a policy \"\n \"violation exemption.\"\n )handle_keyword_policy_violations.py\n```\n\nExample:\n```text\ndef request_exemption(\n client,\n customer_id,\n ad_group_criterion_service,\n ad_group_criterion_operation,\n exempt_policy_violation_keys\n)\n # Add all the found ignorable policy topics to the operation.\n ad_group_criterion_operation.exempt_policy_violation_keys.push(\n *exempt_policy_violation_keys\n )\n response = ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [ad_group_criterion_operation],\n )\n puts \"Successfully added a keyword with resource name \" \\\n \"#{response.results.first.resource_name} for policy violation exception.\"\nendhandle_keyword_policy_violations.rb\n```\n\nExample:\n```text\nsub request_exemption {\n my ($api_client, $customer_id, $ad_group_criterion_operation,\n $exempt_policy_violation_keys)\n = @_;\n\n print \"Try adding a keyword again by requesting exemption for its \" .\n \"policy violations.\\n\";\n\n $ad_group_criterion_operation->{exemptPolicyViolationKeys} =\n $exempt_policy_violation_keys;\n\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n printf \"Successfully added a keyword with resource name '%s' by requesting \" .\n \"for policy violation exemption.\\n\",\n $ad_group_criteria_response->{results}[0]{resourceName};\n}handle_keyword_policy_violations.pl\n```\n\nExample:\n```text\n// Copyright 2020 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.errorhandling;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.KeywordInfo;\nimport com.google.ads.googleads.v25.common.PolicyViolationKey;\nimport com.google.ads.googleads.v25.enums.AdGroupCriterionStatusEnum.AdGroupCriterionStatus;\nimport com.google.ads.googleads.v25.enums.KeywordMatchTypeEnum.KeywordMatchType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.errors.PolicyViolationDetails;\nimport com.google.ads.googleads.v25.resources.AdGroupCriterion;\nimport com.google.ads.googleads.v25.services.AdGroupCriterionOperation;\nimport com.google.ads.googleads.v25.services.AdGroupCriterionServiceClient;\nimport com.google.ads.googleads.v25.services.MutateAdGroupCriteriaResponse;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Demonstrates how to request an exemption for policy violations of a keyword.\n *\n * <p>The example uses an exemptible policy-violating keyword by default. If you use a keyword that\n * contains non-exemptible policy violations, it will not be sent with exemptions requested and you\n * will still fail to create a keyword.\n *\n * <p>If you specify a keyword that doesn't violate any policies, this example will just add the\n * keyword as usual, similar to what the AddKeywords example does.\n *\n * <p>When you send a request to add a keyword after requesting a policy exemption for that keyword,\n * the request will pass as if you were adding a non-violating keyword.\n */\npublic class HandleKeywordPolicyViolations {\n\n private static class HandleKeywordPolicyViolationsParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(names = ArgumentNames.AD_GROUP_ID, required = true)\n private Long adGroupId;\n\n @Parameter(names = ArgumentNames.KEYWORD_TEXT)\n private String keywordText = \"medication\";\n }\n\n public static void main(String[] args) throws IOException {\n HandleKeywordPolicyViolationsParams params = new HandleKeywordPolicyViolationsParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.adGroupId = Long.parseLong(\"INSERT_AD_GROUP_ID_HERE\");\n\n // Optional: Specify a keywordText here, or the default specified above will be used.\n // params.keywordText = \"INSERT_KEYWORD_TEXT_HERE\";\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new HandleKeywordPolicyViolations()\n .runExample(googleAdsClient, params.customerId, params.adGroupId, params.keywordText);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param adGroupId the ID of the ad group to add a keyword to.\n * @param keywordText the keyword text to add.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void runExample(\n GoogleAdsClient googleAdsClient, long customerId, Long adGroupId, String keywordText) {\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Configures the keyword text and match type settings.\n KeywordInfo keywordInfo =\n KeywordInfo.newBuilder()\n .setText(keywordText)\n .setMatchType(KeywordMatchType.EXACT)\n .build();\n\n // Constructs an ad group criterion using the keyword text info above.\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))\n .setStatus(AdGroupCriterionStatus.ENABLED)\n .setKeyword(keywordInfo)\n .build();\n\n // Constructs an operation to create the ad group criterion.\n AdGroupCriterionOperation operation =\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterion).build();\n\n // Tries sending a mutate request to add the keyword.\n List<PolicyViolationKey> exemptibleKeys = new ArrayList<>();\n int errorCount = 0;\n try {\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n String.valueOf(customerId), ImmutableList.of(operation));\n // If the request succeeded, then either the keyword does not require a policy exemption or\n // a policy exemption was previously submitted for the keyword. In either case, returns and\n // skips the remaining portion of this example that resubmits with an exemption request.\n System.out.printf(\n \"Successfully added a keyword with resource name '%s'. No exemptions needed.%n\",\n response.getResults(0).getResourceName());\n return;\n } catch (GoogleAdsException e) {\n exemptibleKeys = extractExemptiblePolicyViolationKeys(e);\n errorCount = e.getGoogleAdsFailure().getErrorsCount();\n }\n\n // Tries sending exemption requests for creating the keyword. However, if your keyword\n // contains many policy violations, but not all of them are exemptible, the request will not\n // be sent.\n if (exemptibleKeys.size() == errorCount) {\n System.out.println(\n \"Attempting to add the keyword again by requesting exemption for its policy\"\n + \" violations.\");\n // Creates a modified version of the operation with the exempt policy violation keys.\n operation = operation.toBuilder().addAllExemptPolicyViolationKeys(exemptibleKeys).build();\n\n // Tries sending the mutate request again.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n String.valueOf(customerId), ImmutableList.of(operation));\n System.out.printf(\n \"Successfully added a keyword with resource name '%s' by requesting \"\n + \"policy violation exemptions.%n\",\n response.getResults(0).getResourceName());\n } else {\n System.out.println(\"No exemption request was sent because either:\");\n System.out.println(\"1) your keyword contained some non-exemptible policy violations, or\");\n System.out.println(\"2) other non-policy related errors were thrown\");\n }\n }\n }\n\n /**\n * Collects all policy violation keys that can be exempted for sending an exemption request later.\n *\n * @param googleAdsException the exception to extract the keys from.\n * @return the list of extracted exemptible keys.\n */\n private List<PolicyViolationKey> extractExemptiblePolicyViolationKeys(\n GoogleAdsException googleAdsException) {\n List<PolicyViolationKey> exemptibleKeys = new ArrayList<>();\n System.out.println(\"Google Ads failure details:\");\n for (GoogleAdsError googleAdsError : googleAdsException.getGoogleAdsFailure().getErrorsList()) {\n System.out.printf(\"\\t%s: %s%n\", googleAdsError.getErrorCode(), googleAdsError.getMessage());\n if (googleAdsError.hasDetails() && googleAdsError.getDetails().hasPolicyViolationDetails()) {\n PolicyViolationDetails policyViolationDetails =\n googleAdsError.getDetails().getPolicyViolationDetails();\n System.out.println(\"\\tPolicy violation details:\");\n System.out.printf(\n \"\\t\\tExternal policy name: '%s'%n\", policyViolationDetails.getExternalPolicyName());\n System.out.printf(\n \"\\t\\tExternal policy description: '%s'%n\",\n policyViolationDetails.getExternalPolicyDescription());\n System.out.printf(\"\\t\\tIs exemptible? '%s'%n\", policyViolationDetails.getIsExemptible());\n if (policyViolationDetails.getIsExemptible() && policyViolationDetails.hasKey()) {\n PolicyViolationKey policyViolationKey = policyViolationDetails.getKey();\n exemptibleKeys.add(policyViolationKey);\n System.out.println(\"\\t\\tPolicy violation key:\");\n System.out.printf(\"\\t\\t\\tName: '%s'%n\", policyViolationKey.getPolicyName());\n System.out.printf(\"\\t\\t\\tViolating text: '%s'%n\", policyViolationKey.getViolatingText());\n }\n }\n }\n return exemptibleKeys;\n }\n}\nHandleKeywordPolicyViolations.java\n```\n\nExample:\n```text\n// Copyright 2020 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System;\nusing System.Collections.Generic;\nusing static Google.Ads.GoogleAds.V25.Enums.AdGroupCriterionStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.KeywordMatchTypeEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example demonstrates how to request an exemption for policy violations of a\n /// keyword. Note that the example uses an exemptible policy-violating keyword by default.\n /// If you use a keyword that contains non-exemptible policy violations, they will not be\n /// sent for exemption request and you will still fail to create a keyword.\n /// If you specify a keyword that doesn't violate any policies, this example will just add the\n /// keyword as usual, similar to what the AddKeywords example does.\n /// Note that once you've requested policy exemption for a keyword, when you send a request for\n /// adding it again, the request will pass like when you add a non-violating keyword.\n /// </summary>\n public class HandleKeywordPolicyViolations : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"HandleKeywordPolicyViolations\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// ID of the ad group to which keywords are added.\n /// </summary>\n [Option(\"adGroupId\", Required = true, HelpText =\n \"ID of the ad group to which keywords are added.\")]\n public long AdGroupId { get; set; }\n\n /// <summary>\n /// The keyword text to add to the ad group.\n /// </summary>\n [Option(\"keywordText\", Required = false, HelpText =\n \"The keyword text to add to the ad group.\")]\n public string KeywordText { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n HandleKeywordPolicyViolations codeExample = new HandleKeywordPolicyViolations();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.AdGroupId,\n options.KeywordText);\n }\n\n /// <summary>\n /// The default keyword to be used if keyword is not provided.\n /// </summary>\n private const string DEFAULT_KEYWORD = \"medication\";\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example demonstrates how to request an exemption for policy violations of \" +\n \"a keyword. Note that the example uses an exemptible policy-violating keyword by \" +\n \"default. If you use a keyword that contains non-exemptible policy violations, they \" +\n \"will not be sent for exemption request and you will still fail to create a keyword. \" +\n \"If you specify a keyword that doesn't violate any policies, this example will just \" +\n \"add the keyword as usual, similar to what the AddKeywords example does. Note that \" +\n \"once you've requested policy exemption for a keyword, when you send a request for \" +\n \"adding it again, the request will pass like when you add a non-violating keyword.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The customer ID for which the call is made.</param>\n /// <param name=\"adGroupId\">ID of the ad group to which keywords are added.</param>\n /// <param name=\"keywordText\">The keyword text to add to the ad group.</param>\n public void Run(GoogleAdsClient client, long customerId, long adGroupId,\n string keywordText)\n {\n // Get the AdGroupCriterionServiceClient.\n AdGroupCriterionServiceClient service = client.GetService(\n Services.V25.AdGroupCriterionService);\n\n if (string.IsNullOrEmpty(keywordText))\n {\n keywordText = DEFAULT_KEYWORD;\n }\n // Configures the keyword text and match type settings.\n KeywordInfo keywordInfo = new KeywordInfo()\n {\n Text = keywordText,\n MatchType = KeywordMatchType.Exact\n };\n\n // Constructs an ad group criterion using the keyword text info above.\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion()\n {\n AdGroup = ResourceNames.AdGroup(customerId, adGroupId),\n Status = AdGroupCriterionStatus.Paused,\n Keyword = keywordInfo\n };\n\n AdGroupCriterionOperation operation = new AdGroupCriterionOperation()\n {\n Create = adGroupCriterion\n };\n\n try\n {\n try\n {\n // Try sending a mutate request to add the keyword.\n MutateAdGroupCriteriaResponse response = service.MutateAdGroupCriteria(\n customerId.ToString(), new[] { operation });\n Console.WriteLine($\"Added a keyword with resource name \" +\n $\"'{response.Results[0].ResourceName}'.\");\n }\n catch (GoogleAdsException ex)\n {\n PolicyViolationKey[] exemptPolicyViolationKeys =\n FetchExemptPolicyViolationKeys(ex);\n\n // Try sending exemption requests for creating a keyword. However, if your\n // keyword contains many policy violations, but not all of them are exemptible,\n // the request will not be sent.\n RequestExemption(customerId, service, operation, exemptPolicyViolationKeys);\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Collects all policy violation keys that can be exempted for sending a exemption\n /// request later.\n /// </summary>\n /// <param name=\"ex\">The Google Ads exception.</param>\n /// <returns>The exemptible policy violation keys.</returns>\n private static PolicyViolationKey[] FetchExemptPolicyViolationKeys(GoogleAdsException ex)\n {\n bool isFullyExemptable = true;\n List<PolicyViolationKey> exemptPolicyViolationKeys = new List<PolicyViolationKey>();\n\n Console.WriteLine(\"Google Ads failure details:\");\n foreach (GoogleAdsError error in ex.Failure.Errors)\n {\n if (error.ErrorCode.ErrorCodeCase !=\n ErrorCode.ErrorCodeOneofCase.PolicyViolationError)\n {\n Console.WriteLine(\"No exemption request is sent because there are other \" +\n \"non-policy related errors thrown.\");\n throw ex;\n }\n if (error.Details != null && error.Details.PolicyViolationDetails != null)\n {\n PolicyViolationDetails details = error.Details.PolicyViolationDetails;\n Console.WriteLine($\"- Policy violation details:\");\n\n Console.WriteLine(\" - Policy violation details:\");\n Console.WriteLine($\" - External policy name: '{details.ExternalPolicyName}'\");\n Console.WriteLine($\" - External policy description: \" +\n $\"'{details.ExternalPolicyDescription}'\");\n Console.WriteLine($\" - Is exemptable: '{details.IsExemptible}'\");\n\n if (details.IsExemptible && details.Key != null)\n {\n PolicyViolationKey key = details.Key;\n Console.WriteLine($\" - Policy violation key:\");\n Console.WriteLine($\" - Name: {key.PolicyName}\");\n Console.WriteLine($\" - Violating Text: {key.ViolatingText}\");\n exemptPolicyViolationKeys.Add(key);\n }\n else\n {\n isFullyExemptable = false;\n }\n }\n }\n\n if (!isFullyExemptable)\n {\n Console.WriteLine(\"No exemption request is sent because your keyword \" +\n \"contained some non-exemptible policy violations.\");\n throw ex;\n }\n return exemptPolicyViolationKeys.ToArray();\n }\n\n /// <summary>\n /// Sends exemption requests for creating a keyword.\n /// </summary>\n /// <param name=\"customerId\">The customer ID for which the call is made.</param>\n /// <param name=\"service\">The ad group criterion service.</param>\n /// <param name=\"operation\">The ad group criterion operation to request exemption for.\n /// </param>\n /// <param name=\"exemptPolicyViolationKeys\">The exemptable policy violation keys.</param>\n private static void RequestExemption(\n long customerId, AdGroupCriterionServiceClient service,\n AdGroupCriterionOperation operation, PolicyViolationKey[] exemptPolicyViolationKeys)\n {\n Console.WriteLine(\"Try adding a keyword again by requesting exemption for its policy \"\n + \"violations.\");\n PolicyValidationParameter validationParameter = new PolicyValidationParameter();\n validationParameter.ExemptPolicyViolationKeys.AddRange(exemptPolicyViolationKeys);\n operation.ExemptPolicyViolationKeys.AddRange(exemptPolicyViolationKeys);\n\n MutateAdGroupCriteriaResponse response = service.MutateAdGroupCriteria(\n customerId.ToString(), new[] { operation });\n Console.WriteLine($\"Successfully added a keyword with resource name \" +\n $\"'{response.Results[0].ResourceName}' by requesting for policy violation \" +\n $\"exemption.\");\n }\n }\n}\nHandleKeywordPolicyViolations.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2019 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\ErrorHandling;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\KeywordInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\PolicyViolationKey;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdGroupCriterionStatusEnum\\AdGroupCriterionStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\KeywordMatchTypeEnum\\KeywordMatchType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AdGroupCriterion;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AdGroupCriterionOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\Client\\AdGroupCriterionServiceClient;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAdGroupCriteriaRequest;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * This example demonstrates how to request an exemption for policy violations of a keyword.\n * Note that the example uses an exemptible policy-violating keyword by default. If you use a\n * keyword that contains non-exemptible policy violations, they will not be sent for exemption\n * request and you will still fail to create a keyword.\n * If you specify a keyword that doesn't violate any policies, this example will just add the\n * keyword as usual, similar to what the AddKeywords example does.\n *\n * Note that once you've requested policy exemption for a keyword, when you send a request for\n * adding it again, the request will pass like when you add a non-violating keyword.\n */\nclass HandleKeywordPolicyViolations\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const AD_GROUP_ID = 'INSERT_AD_GROUP_ID_HERE';\n // Specify the keyword text here or the default specified below will be used.\n private const KEYWORD_TEXT = 'medication';\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::AD_GROUP_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::KEYWORD_TEXT => GetOpt::OPTIONAL_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::AD_GROUP_ID] ?: self::AD_GROUP_ID,\n $options[ArgumentNames::KEYWORD_TEXT] ?: self::KEYWORD_TEXT\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $adGroupId the ad group ID to add a keyword to\n * @param string $keywordText the keyword text to add\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n string $keywordText\n ) {\n // Configures the keyword text and match type settings.\n $keywordInfo = new KeywordInfo([\n 'text' => $keywordText,\n 'match_type' => KeywordMatchType::EXACT\n ]);\n\n // Constructs an ad group criterion using the keyword text info above.\n $adGroupCriterion = new AdGroupCriterion([\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'status' => AdGroupCriterionStatus::ENABLED,\n 'keyword' => $keywordInfo\n ]);\n\n $adGroupCriterionOperation = new AdGroupCriterionOperation();\n $adGroupCriterionOperation->setCreate($adGroupCriterion);\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n\n try {\n // Try sending a mutate request to add the keyword.\n $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$adGroupCriterionOperation])\n );\n printf(\n \"Added a keyword with resource name '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n } catch (GoogleAdsException $googleAdsException) {\n // Try sending exemption requests for creating a keyword. However, if your keyword\n // contains many policy violations, but not all of them are exemptible, the request\n // will not be sent.\n $exemptPolicyViolationKeys = self::fetchExemptPolicyViolationKeys($googleAdsException);\n self::requestExemption(\n $customerId,\n $adGroupCriterionServiceClient,\n $adGroupCriterionOperation,\n $exemptPolicyViolationKeys\n );\n }\n }\n\n /**\n * Collects all policy violation keys that can be exempted for sending a exemption request\n * later.\n *\n * @param GoogleAdsException $googleAdsException the Google Ads exception\n * @return PolicyViolationKey[] the exemptible policy violation keys\n */\n private static function fetchExemptPolicyViolationKeys(GoogleAdsException $googleAdsException)\n {\n $exemptPolicyViolationKeys = [];\n\n printf(\"Google Ads failure details:%s\", PHP_EOL);\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n if (\n !is_null($error->getDetails())\n && !is_null($error->getDetails()->getPolicyViolationDetails())\n ) {\n $policyViolationDetails = $error->getDetails()->getPolicyViolationDetails();\n printf(\"\\tPolicy violation details:%s\", PHP_EOL);\n printf(\n \"\\t\\tExternal policy name: '%s'%s\",\n $policyViolationDetails->getExternalPolicyName(),\n PHP_EOL\n );\n printf(\n \"\\t\\tExternal policy description: '%s'%s\",\n $policyViolationDetails->getExternalPolicyDescription(),\n PHP_EOL\n );\n printf(\n \"\\t\\tIs exemptible? '%s'%s\",\n $policyViolationDetails->getIsExemptible() ? 'yes' : 'no',\n PHP_EOL\n );\n\n if (\n $policyViolationDetails->getIsExemptible() &&\n !is_null($policyViolationDetails->getKey())\n ) {\n $policyViolationDetailsKey = $policyViolationDetails->getKey();\n $exemptPolicyViolationKeys[] = $policyViolationDetailsKey;\n printf(\"\\t\\tPolicy violation key:%s\", PHP_EOL);\n printf(\n \"\\t\\t\\tName: '%s'%s\",\n $policyViolationDetailsKey->getPolicyName(),\n PHP_EOL\n );\n printf(\n \"\\t\\t\\tViolating text: '%s'%s\",\n $policyViolationDetailsKey->getViolatingText(),\n PHP_EOL\n );\n } else {\n print \"No exemption request is sent because your keyword contained some \"\n . \"non-exemptible policy violations.\" . PHP_EOL;\n throw $googleAdsException;\n }\n } else {\n print \"No exemption request is sent because there are other non-policy related \"\n . \"errors thrown.\" . PHP_EOL;\n throw $googleAdsException;\n }\n }\n return $exemptPolicyViolationKeys;\n }\n\n /**\n * Sends exemption requests for creating a keyword.\n *\n * @param int $customerId the customer ID\n * @param AdGroupCriterionServiceClient $adGroupCriterionServiceClient the ad group criterion\n * service API client\n * @param AdGroupCriterionOperation $adGroupCriterionOperation the ad group criterion operation\n * to request exemption for\n * @param PolicyViolationKey[] $exemptPolicyViolationKeys the exemptible policy violation keys\n */\n private static function requestExemption(\n int $customerId,\n AdGroupCriterionServiceClient $adGroupCriterionServiceClient,\n AdGroupCriterionOperation $adGroupCriterionOperation,\n array $exemptPolicyViolationKeys\n ) {\n print \"Try adding a keyword again by requesting exemption for its policy\"\n . \" violations.\" . PHP_EOL;\n $adGroupCriterionOperation->setExemptPolicyViolationKeys($exemptPolicyViolationKeys);\n $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$adGroupCriterionOperation])\n );\n printf(\n \"Successfully added a keyword with resource name '%s' by requesting for\"\n . \" policy violation exemption.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n }\n}\n\nHandleKeywordPolicyViolations::main();\nHandleKeywordPolicyViolations.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2020 Google LLC\n#\n# 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.\n\"\"\"Demonstrates how to request an exemption for policy violations of a keyword.\n\nNote that the example uses an exemptible policy-violating keyword by default.\nIf you use a keyword that contains non-exemptible policy violations, they will\nnot be sent for exemption request, and you will still fail to create a keyword.\nIf you specify a keyword that doesn't violate any policies, this example will\njust add the keyword as usual, similar to what the AddKeywords example does.\n\nNote that once you've requested policy exemption for a keyword, when you send\na request for adding it again, the request will pass like when you add a\nnon-violating keyword.\n\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import Any, List, Optional, Tuple\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.services.services.ad_group_criterion_service import (\n AdGroupCriterionServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_criterion_service import (\n AdGroupCriterionOperation,\n)\nfrom google.ads.googleads.v24.common.types.policy import PolicyViolationKey\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n keyword_text: str,\n) -> None:\n \"\"\"Demonstrates how to request an exemption for keyword policy violations.\n\n Args:\n client: The Google Ads client.\n customer_id: The customer ID for which to add the keyword.\n ad_group_id: The ad group ID to which to add keyword.\n keyword_text: The keyword text to add.\n \"\"\"\n\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n\n googleads_exception: Optional[GoogleAdsException]\n ad_group_criterion_operation: AdGroupCriterionOperation\n (\n googleads_exception,\n ad_group_criterion_operation,\n ) = create_keyword_criterion(\n client,\n ad_group_criterion_service,\n customer_id,\n ad_group_id,\n keyword_text,\n )\n\n try:\n # Try sending exemption requests for creating a keyword. However, if\n # your keyword contains many policy violations, but not all of them are\n # exemptible, the request will not be sent.\n if googleads_exception is not None:\n exempt_policy_violation_keys: List[PolicyViolationKey] = (\n fetch_exempt_policy_violation_keys(googleads_exception)\n )\n request_exemption(\n customer_id,\n ad_group_criterion_service,\n ad_group_criterion_operation,\n exempt_policy_violation_keys,\n )\n except GoogleAdsException as ex:\n print(\n f\"Request with ID '{ex.request_id}' failed with status \"\n f\"'{ex.error.code().name}' and includes the following errors:\"\n )\n for error in ex.failure.errors:\n print(f\"\\tError with message '{error.message}'.\")\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\n\n\ndef create_keyword_criterion(\n client: GoogleAdsClient,\n ad_group_criterion_service: AdGroupCriterionServiceClient,\n customer_id: str,\n ad_group_id: str,\n keyword_text: str,\n) -> Tuple[Optional[GoogleAdsException], AdGroupCriterionOperation]:\n \"\"\"Attempts to add a keyword criterion to an ad group.\n\n Args:\n client: The GoogleAds client instance.\n ad_group_criterion_service: The AdGroupCriterionService client instance.\n customer_id: The customer ID for which to add the expanded text ad.\n ad_group_id: The ad group ID to which to add an expanded text ad.\n keyword_text: The keyword text to add.\n\n Returns:\n The GoogleAdsException that occurred (or None if the operation was\n successful) and the modified operation.\n \"\"\"\n # Constructs an ad group criterion using the keyword text provided.\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n ad_group_criterion: Any = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = client.get_service(\n \"AdGroupService\"\n ).ad_group_path(customer_id, ad_group_id)\n ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED\n ad_group_criterion.keyword.text = keyword_text\n ad_group_criterion.keyword.match_type = (\n client.enums.KeywordMatchTypeEnum.EXACT\n )\n\n try:\n # Try sending a mutate request to add the keyword.\n response: Any = ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n except GoogleAdsException as googleads_exception:\n # Return the exception in order to extract keyword violation details.\n return googleads_exception, ad_group_criterion_operation\n\n # Report that the mutate request was completed successfully.\n print(\n \"Added a keyword with resource name \"\n f\"'{response.results[0].resource_name}'.\"\n )\n\n return None, ad_group_criterion_operation\n\n\ndef fetch_exempt_policy_violation_keys(\n googleads_exception: GoogleAdsException,\n) -> List[PolicyViolationKey]:\n \"\"\"Collects all policy violation keys that can be exempted.\n\n Args:\n googleads_exception: The exception to check for policy violation(s).\n\n Returns:\n A list of policy violation keys.\n \"\"\"\n exempt_policy_violation_keys: List[PolicyViolationKey] = []\n\n print(\"Google Ads failure details:\")\n for error in googleads_exception.failure.errors:\n print(f\"\\t{error.error_code}: {error.message}\")\n\n if (\n error.details is not None\n and error.details.policy_violation_details is not None\n ):\n policy_violation_details = error.details.policy_violation_details\n print(\n \"\\tPolicy violation details:\\n\"\n f\"\\t\\tExternal policy name: '{policy_violation_details}'\\n\"\n \"\\t\\tExternal policy description: \"\n f\"'{policy_violation_details.external_policy_description}'\\n\"\n f\"\\t\\tIs exemptible? '{policy_violation_details.is_exemptible}'\"\n )\n\n if (\n policy_violation_details.is_exemptible\n and policy_violation_details.key is not None\n ):\n exempt_policy_violation_keys.append(\n policy_violation_details.key\n )\n print(\n f\"\\t\\tPolicy violation key: {policy_violation_details.key}\"\n )\n print(\n f\"\\t\\t\\tName: '{policy_violation_details.key.policy_name}'\"\n \"\\t\\t\\tViolating text: \"\n f\"'{policy_violation_details.key.violating_text}'\"\n )\n else:\n print(\n \"No exemption request is sent because your keyword \"\n \"contained some non-exemptible policy violations.\"\n )\n raise googleads_exception\n else:\n print(\n \"No exemption request is sent because there are non-policy \"\n \"related errors thrown.\"\n )\n raise googleads_exception\n\n return exempt_policy_violation_keys\n\n\ndef request_exemption(\n customer_id: str,\n ad_group_criterion_service: AdGroupCriterionServiceClient,\n ad_group_criterion_operation: AdGroupCriterionOperation,\n exempt_policy_violation_keys: List[PolicyViolationKey],\n) -> None:\n \"\"\"Sends exemption requests for creating a keyword.\n\n Args:\n customer_id: The customer ID for which to add the expanded text ad.\n ad_group_criterion_service: The AdGroupCriterionService client instance.\n ad_group_criterion_operation: The AdGroupCriterionOperation for which\n to request exemption.\n exempt_policy_violation_keys: The exemptible policy violation keys.\n \"\"\"\n print(\n \"Attempting to add a keyword again by requesting exemption for its \"\n \"policy violations.\"\n )\n ad_group_criterion_operation.exempt_policy_violation_keys.extend(\n exempt_policy_violation_keys\n )\n response: Any = ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n print(\n \"Successfully added a keyword with resource name \"\n f\"'{response.results[0].resource_name}' by requesting a policy \"\n \"violation exemption.\"\n )\n\n\nif __name__ == \"__main__\":\n parser = argparse.ArgumentParser(\n description=\"Demonstrates how to request an exemption for policy \"\n \"violations of a keyword.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-a\",\n \"--ad_group_id\",\n type=str,\n required=True,\n help=\"The ad group ID to which to add an expanded text ad.\",\n )\n parser.add_argument(\n \"-k\",\n \"--keyword_text\",\n type=str,\n required=False,\n default=\"medication\",\n help=\"Specify the keyword text here or use the default keyword \"\n \"'medication'.\",\n )\n args = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n main(\n googleads_client, args.customer_id, args.ad_group_id, args.keyword_text\n )\nhandle_keyword_policy_violations.py\n```\n\nExample:\n```text\n# Encoding: utf-8\n#\n# Copyright 2020 Google LLC\n#\n# 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.\n#\n# Demonstrates how to request an exemption for policy violations of a keyword.\n#\n# Note that the example uses an exemptible policy-violating keyword by default.\n# If you use a keyword that contains non-exemptible policy violations, they\n# will not be sent for exemption request and you will still fail to create a\n# keyword. If you specify a keyword that doesn't violate any policies, this\n# example will just add the keyword as usual, similar to what the AddKeywords\n# example does.\n#\n# Note that once you've requested policy exemption for a keyword, when you send\n# a request for adding it again, the request will pass like when you add a\n# non-violating keyword.\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\nrequire 'date'\n\ndef handle_keyword_policy_violations(customer_id, ad_group_id, keyword_text)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n ad_group_criterion_service = client.service.ad_group_criterion\n\n exception, ad_group_criterion_operation = create_keyword_criterion(\n client,\n ad_group_criterion_service,\n customer_id,\n ad_group_id,\n keyword_text,\n )\n\n unless exception.nil?\n exempt_policy_violation_keys = fetch_exempt_policy_violation_keys(exception)\n request_exemption(\n client,\n customer_id,\n ad_group_criterion_service,\n ad_group_criterion_operation,\n exempt_policy_violation_keys,\n )\n end\nend\n\ndef create_keyword_criterion(\n client, ad_group_criterion_service, customer_id, ad_group_id, keyword_text)\n ad_group_criterion_operation = client.operation.create_resource.ad_group_criterion do |agc|\n agc.ad_group = client.path.ad_group(customer_id, ad_group_id)\n agc.status = :ENABLED\n agc.keyword = client.resource.keyword_info do |ki|\n ki.match_type = :EXACT\n ki.text = keyword_text\n end\n end\n\n ignorable_policy_topics = []\n begin\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [ad_group_criterion_operation],\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n return e, ad_group_criterion_operation\n end\n\n return nil, ad_group_criterion_operation\nend\n\ndef fetch_exempt_policy_violation_keys(exception)\n exempt_policy_violation_keys = []\n\n exception.failure.errors.each do |error|\n details = error.details.policy_violation_details\n puts \"Policy violation details:\"\n puts \"\\tExternal policy name: #{details.external_policy_name}\"\n puts \"\\tExternal policy description:\\n#{details.external_policy_description}\"\n puts \"\\tIs exemptible: #{details.is_exemptible}\"\n\n if details.is_exemptible && !details.key.nil?\n exempt_policy_violation_keys << details.key\n puts \"Policy violation key:\"\n puts \"\\tPolicy Name: #{details.key.policy_name}\"\n puts \"\\tViolating Text: #{details.key.violating_text}\"\n else\n puts \"No exemption request will be sent because your keyword contained \"\\\n \"some non-exemptible policy violations.\"\n end\n end\n\n exempt_policy_violation_keys\nend\n\ndef request_exemption(\n client,\n customer_id,\n ad_group_criterion_service,\n ad_group_criterion_operation,\n exempt_policy_violation_keys\n)\n # Add all the found ignorable policy topics to the operation.\n ad_group_criterion_operation.exempt_policy_violation_keys.push(\n *exempt_policy_violation_keys\n )\n response = ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [ad_group_criterion_operation],\n )\n puts \"Successfully added a keyword with resource name \" \\\n \"#{response.results.first.resource_name} for policy violation exception.\"\nend\n\nif __FILE__ == $PROGRAM_NAME\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:ad_group_id] = 'INSERT_AD_GROUP_ID_HERE'\n options[:keyword_text] = 'INSERT_KEYWORD_TEXT_HERE'\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: ruby %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-A', '--ad-group-id AD-GROUP-ID', String, 'Ad Group ID') do |v|\n options[:ad_group_id] = v\n end\n\n opts.on('-k', '--keyword-text KEYWORD-TEXT', String, 'Keyword') do |v|\n options[:keyword_text] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n handle_keyword_policy_violations(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options.fetch(:ad_group_id),\n options.fetch(:keyword_text),\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\nhandle_keyword_policy_violations.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2019, Google LLC\n#\n# 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# http://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.\n#\n# This example demonstrates how to request an exemption for policy violations\n# of a keyword. Note that the example uses an exemptible policy-violating\n# keyword by default. If you use a keyword that contains non-exemptible policy\n# violations, they will not be sent for exemption request and you will still\n# fail to create a keyword.\n# If you specify a keyword that doesn't violate any policies, this example will\n# just add the keyword as usual, similar to what the add_keywords.pl example does.\n#\n# Note that once you've requested policy exemption for a keyword, when you send\n# a request for adding it again, the request will pass like when you add a\n# non-violating keyword.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion;\nuse Google::Ads::GoogleAds::V25::Common::KeywordInfo;\nuse Google::Ads::GoogleAds::V25::Enums::KeywordMatchTypeEnum qw(EXACT);\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupCriterionStatusEnum qw(ENABLED);\nuse\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\nmy $ad_group_id = \"INSERT_AD_GROUP_ID_HERE\";\nmy $keyword_text = \"medication\";\n\nsub handle_keyword_policy_violations {\n my ($api_client, $customer_id, $ad_group_id, $keyword_text) = @_;\n\n # Configure the keyword text and match type settings.\n my $keyword_info = Google::Ads::GoogleAds::V25::Common::KeywordInfo->new({\n text => $keyword_text,\n matchType => EXACT\n });\n\n # Construct an ad group criterion using the keyword info above.\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n status => ENABLED,\n keyword => $keyword_info\n });\n\n # Create an ad group criterion operation.\n my $ad_group_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({create => $ad_group_criterion});\n\n # Try sending a mutate request to add the keyword.\n my $response = $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n if ($response->isa(\"Google::Ads::GoogleAds::GoogleAdsException\")) {\n my $exempt_policy_violation_keys =\n fetch_exempt_policy_violation_keys($response);\n\n # Try sending exemption requests for creating a keyword. However, if your\n # keyword contains many policy violations, but not all of them are exemptible,\n # the request will not be sent.\n if (@$exempt_policy_violation_keys ==\n @{$response->get_google_ads_failure()->{errors}})\n {\n request_exemption($api_client, $customer_id,\n $ad_group_criterion_operation, $exempt_policy_violation_keys);\n } else {\n print \"No exemption request is sent because 1) your keyword contained \" .\n \"some non-exemptible policy violations or 2) there are other \" .\n \"non-policy related errors thrown.\\n\";\n }\n } else {\n printf \"Added a keyword with resource name '%s'.\\n\",\n $response->{results}[0]{resourceName};\n }\n\n return 1;\n}\n\n# Collects all policy violation keys that can be exempted for sending a exemption\n# request later.\nsub fetch_exempt_policy_violation_keys {\n my $google_ads_exception = shift;\n\n my $exempt_policy_violation_keys = [];\n\n print \"Google Ads failure details:\\n\";\n foreach\n my $error (@{$google_ads_exception->get_google_ads_failure()->{errors}})\n {\n printf \"\\t%s: %s\\n\", [keys %{$error->{errorCode}}]->[0], $error->{message};\n\n if ($error->{details}{policyViolationDetails}) {\n my $policy_violation_details = $error->{details}{policyViolationDetails};\n printf \"\\tPolicy violation details:\\n\";\n printf \"\\t\\tExternal policy name: '%s'\\n\",\n $policy_violation_details->{externalPolicyName};\n printf\n \"\\t\\tExternal policy description: '%s'\\n\",\n $policy_violation_details->{externalPolicyDescription};\n printf\n \"\\t\\tIs exemptible? '%s'\\n\",\n $policy_violation_details->{isExemptible} ? \"yes\" : \"no\";\n\n if ( $policy_violation_details->{isExemptible}\n and $policy_violation_details->{key})\n {\n my $policy_violation_details_key = $policy_violation_details->{key};\n push @$exempt_policy_violation_keys, $policy_violation_details_key;\n\n printf \"\\t\\tPolicy violation key:\\n\";\n printf \"\\t\\t\\tName: '%s'\\n\",\n $policy_violation_details_key->{policyName};\n printf\n \"\\t\\t\\tViolating text: '%s'\\n\",\n $policy_violation_details_key->{violatingText};\n }\n }\n }\n\n return $exempt_policy_violation_keys;\n}\n\n# Sends exemption requests for creating a keyword.\nsub request_exemption {\n my ($api_client, $customer_id, $ad_group_criterion_operation,\n $exempt_policy_violation_keys)\n = @_;\n\n print \"Try adding a keyword again by requesting exemption for its \" .\n \"policy violations.\\n\";\n\n $ad_group_criterion_operation->{exemptPolicyViolationKeys} =\n $exempt_policy_violation_keys;\n\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n printf \"Successfully added a keyword with resource name '%s' by requesting \" .\n \"for policy violation exemption.\\n\",\n $ad_group_criteria_response->{results}[0]{resourceName};\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(0);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"ad_group_id=i\" => \\$ad_group_id,\n \"keyword_text=s\" => \\$keyword_text\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id, $ad_group_id, $keyword_text);\n\n# Call the example.\nhandle_keyword_policy_violations($api_client, $customer_id =~ s/-//gr,\n $ad_group_id, $keyword_text);\n\n=pod\n\n=head1 NAME\n\nhandle_keyword_policy_violations\n\n=head1 DESCRIPTION\n\nThis example demonstrates how to request an exemption for policy violations of a keyword.\nNote that the example uses an exemptible policy-violating keyword by default. If you use\na keyword that contains non-exemptible policy violations, they will not be sent for\nexemption request and you will still fail to create a keyword.\nIf you specify a keyword that doesn't violate any policies, this example will just add the\nkeyword as usual, similar to what the add_keywords.pl example does.\n\nNote that once you've requested policy exemption for a keyword, when you send a request for\nadding it again, the request will pass like when you add a non-violating keyword.\n\n=head1 SYNOPSIS\n\nhandle_keyword_policy_violations.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -ad_group_id The ad group ID.\n -keyword_text [optional] The keyword to be added to the ad group.\n\n=cut\nhandle_keyword_policy_violations.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.633Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":1920,"estimatedTokens":19102}}248{"id":"doc-policy_exemption_requests_google_ads_api_google_-0ee791e5","source":"documentation","title":"Policy Exemption Requests | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/policy-exemption/overview","text":"Example:\n```text\n// Copyright 2020 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.errorhandling;\n\nimport static com.google.ads.googleads.examples.utils.CodeSampleHelper.getShortPrintableDateTime;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.AdTextAsset;\nimport com.google.ads.googleads.v25.common.PolicyTopicEntry;\nimport com.google.ads.googleads.v25.enums.AdGroupAdStatusEnum.AdGroupAdStatus;\nimport com.google.ads.googleads.v25.errors.ErrorCode.ErrorCodeCase;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.errors.PolicyFindingDetails;\nimport com.google.ads.googleads.v25.resources.AdGroupAd;\nimport com.google.ads.googleads.v25.services.AdGroupAdOperation;\nimport com.google.ads.googleads.v25.services.AdGroupAdServiceClient;\nimport com.google.ads.googleads.v25.services.MutateAdGroupAdsResponse;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Demonstrates how to request an exemption for policy violations of a responsive search ad. If the\n * request somehow fails with exceptions that are not policy finding errors, the example will stop\n * instead of trying sending an exemption request.\n */\npublic class HandleResponsiveSearchAdPolicyViolations {\n\n private static class HandleResponsiveSearchAdPolicyViolationsParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(names = ArgumentNames.AD_GROUP_ID, required = true)\n private Long adGroupId;\n }\n\n public static void main(String[] args) {\n HandleResponsiveSearchAdPolicyViolationsParams params =\n new HandleResponsiveSearchAdPolicyViolationsParams();\n if (!params.parseArguments(args)) {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID\");\n params.adGroupId = Long.parseLong(\"INSERT_AD_GROUP_ID\");\n }\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new HandleResponsiveSearchAdPolicyViolations()\n .runExample(googleAdsClient, params.customerId, params.adGroupId);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the client to use.\n * @param customerId the customer ID.\n * @param adGroupId the ad group ID.\n */\n public void runExample(GoogleAdsClient googleAdsClient, long customerId, long adGroupId) {\n // Creates an ad group ad for the specified ad group.\n AdGroupAd.Builder adGroupAdBuilder =\n AdGroupAd.newBuilder()\n .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))\n .setStatus(AdGroupAdStatus.PAUSED);\n\n adGroupAdBuilder\n .getAdBuilder()\n // Sets the final URLS.\n .addFinalUrls(\"https://www.example.com\")\n // Adds a responsive search ad.\n .getResponsiveSearchAdBuilder()\n .addAllHeadlines(\n ImmutableList.of(\n AdTextAsset.newBuilder()\n .setText(\"Cruise to Mars #\" + getShortPrintableDateTime())\n .build(),\n AdTextAsset.newBuilder().setText(\"Best Space Cruise Line\").build(),\n AdTextAsset.newBuilder().setText(\"Experience the Stars\").build()))\n .addAllDescriptions(\n ImmutableList.of(\n // Intentionally uses an ad text that violates policy - too many exclamation marks.\n AdTextAsset.newBuilder().setText(\"Buy your tickets now!!!!!!!\").build(),\n AdTextAsset.newBuilder().setText(\"Visit the Red Planet\").build()));\n\n // Constructs an operation to send to the API.\n AdGroupAdOperation operation =\n AdGroupAdOperation.newBuilder().setCreate(adGroupAdBuilder.build()).build();\n\n // Connects to the API. Note that we could use try-with-resources, however doing so would\n // require that we either (1) need to reconnect to the API for requesting the exemption, or (2)\n // introduce a doubly nested try-catch structure here.\n AdGroupAdServiceClient client =\n googleAdsClient.getLatestVersion().createAdGroupAdServiceClient();\n\n try {\n // Sends the request which we expect to fail with policy violations.\n client.mutateAdGroupAds(String.valueOf(customerId), ImmutableList.of(operation));\n } catch (GoogleAdsException ex) {\n // Retrieves the ignorable policy topics.\n List<String> ignorablePolicyTopics = fetchIgnorablePolicyTopics(ex);\n // Requests an exemption to add the creative with the known violations.\n requestExemption(ignorablePolicyTopics, client, operation, customerId);\n } finally {\n // Disconnects the API connection. Very important!\n client.close();\n }\n }\n\n /**\n * Collects all ignorable policy topics that will be sent for exemption request later.\n *\n * @param gae the Google Ads exception.\n * @return the ignorable policy topics.\n */\n private List<String> fetchIgnorablePolicyTopics(GoogleAdsException gae) {\n System.out.println(\"Google Ads failure details:\");\n\n // Creates a list to store the result.\n List<String> ignorableTopics = new ArrayList<>();\n\n // Searches all errors for ignorable policy topics.\n for (GoogleAdsError error : gae.getGoogleAdsFailure().getErrorsList()) {\n // Supports sending exemption request for the policy finding error only.\n if (error.getErrorCode().getErrorCodeCase() != ErrorCodeCase.POLICY_FINDING_ERROR) {\n throw gae;\n }\n\n // Shows some information about the error encountered.\n System.out.printf(\"\\t%s: %s%n\", error.getErrorCode().getErrorCodeCase(), error.getMessage());\n\n // Checks policy finding details for ignorable policy topics.\n if (error.getDetails() != null) {\n PolicyFindingDetails policyFindingDetails = error.getDetails().getPolicyFindingDetails();\n if (policyFindingDetails != null) {\n System.out.println(\"\\tPolicy finding details:\");\n // Shows all the policy topics for the current error.\n for (PolicyTopicEntry policyTopicEntry :\n policyFindingDetails.getPolicyTopicEntriesList()) {\n // Adds this topic to the result.\n ignorableTopics.add(policyTopicEntry.getTopic());\n System.out.printf(\"\\t\\tPolicy topic name: '%s'%n\", policyTopicEntry.getTopic());\n System.out.printf(\"\\t\\tPolicy topic entry type: '%s'%n\", policyTopicEntry.getType());\n // For the sake of brevity, we exclude printing \"policy topic evidences\" and\n // \"policy topic constraints\" here. You can fetch those data by calling:\n // - policyTopicEntry.getEvidences()\n // - policyTopicEntry.getConstraints()\n }\n }\n }\n }\n return ignorableTopics;\n }\n\n /**\n * Sends exemption requests for creating a responsive search ad.\n *\n * @param ignorablePolicyTopics topics to request exemption for.\n * @param client client to use for API access.\n * @param operation operation which generated original violations.\n * @param customerID the customer ID to operate on.\n */\n private void requestExemption(\n List<String> ignorablePolicyTopics,\n AdGroupAdServiceClient client,\n AdGroupAdOperation operation,\n long customerID) {\n System.out.println(\n \"Trying to add a responsive search ad again by requesting exemption for its policy\"\n + \" violations.\");\n // Converts the operation back to a builder.\n AdGroupAdOperation.Builder operationBuilder = operation.toBuilder();\n\n // Adds the exemption request.\n operationBuilder\n .getPolicyValidationParameterBuilder()\n .addAllIgnorablePolicyTopics(ignorablePolicyTopics);\n\n // Sends the request back to the API.\n MutateAdGroupAdsResponse response =\n client.mutateAdGroupAds(\n String.valueOf(customerID), ImmutableList.of(operationBuilder.build()));\n\n // Shows the newly added ad resource name.\n System.out.printf(\n \"Successfully added a responsive search ad with resource name '%s' by requesting a policy\"\n + \" violation exemption.%n\",\n response.getResults(0).getResourceName());\n }\n}\nHandleResponsiveSearchAdPolicyViolations.java\n```\n\nExample:\n```text\n// Copyright 2020 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System;\nusing System.Collections.Generic;\nusing static Google.Ads.GoogleAds.V25.Enums.AdGroupAdStatusEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example demonstrates how to request an exemption for policy violations of a\n /// responsive search ad. If the request somehow fails with exceptions that are not policy finding\n /// errors, the code example will stop instead of trying to send an exemption request.\n /// </summary>\n public class HandleResponsiveSearchAdPolicyViolations : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"HandleResponsiveSearchAdPolicyViolations\"/>\n /// example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// ID of the ad group to which ads are added.\n /// </summary>\n [Option(\"adGroupId\", Required = true, HelpText =\n \"ID of the ad group to which ads are added.\")]\n public long AdGroupId { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n HandleResponsiveSearchAdPolicyViolations codeExample =\n new HandleResponsiveSearchAdPolicyViolations();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.AdGroupId);\n }\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example demonstrates how to request an exemption for policy violations of \" +\n \"a responsive search ad. If the request somehow fails with exceptions that are not \" +\n \"policy finding errors, the code example will stop instead of trying to send an \" +\n \"exemption request.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The customer ID for which the call is made.</param>\n /// <param name=\"adGroupId\">ID of the ad group to which ads are added.</param>\n public void Run(GoogleAdsClient client, long customerId, long adGroupId)\n {\n // Get the AdGroupAdServiceClient.\n AdGroupAdServiceClient adGroupAdService = client.GetService(\n Services.V25.AdGroupAdService);\n\n string adGroupResourceName = ResourceNames.AdGroup(customerId, adGroupId);\n ResponsiveSearchAdInfo responsiveSearchAdInfo = new ResponsiveSearchAdInfo()\n {\n Headlines = {\n new AdTextAsset() { Text = $\"Cruise to Mars #{ExampleUtilities.GetShortRandomString()}\" },\n new AdTextAsset() { Text = \"Best Space Cruise Line\" },\n new AdTextAsset() { Text = \"Experience the Stars\" }\n },\n Descriptions = {\n // Intentionally use an ad text that violates policy -- having too many exclamation\n // marks.\n new AdTextAsset() { Text = \"Buy your tickets now!!!!!!!\" },\n new AdTextAsset() { Text = \"Visit the Red Planet\" }\n }\n };\n\n // Creates an ad group ad to hold the above ad.\n AdGroupAd adGroupAd = new AdGroupAd()\n {\n AdGroup = adGroupResourceName,\n // Set the ad group ad to PAUSED to prevent it from immediately serving.\n // Set to ENABLED once you've added targeting and the ad are ready to serve.\n Status = AdGroupAdStatus.Paused,\n // Sets the responsive search ad info on an Ad.\n Ad = new Ad()\n {\n ResponsiveSearchAd = responsiveSearchAdInfo,\n FinalUrls = { \"https://www.example.com\" }\n }\n };\n\n // Creates an ad group ad operation.\n AdGroupAdOperation operation = new AdGroupAdOperation()\n {\n Create = adGroupAd\n };\n\n try\n {\n try\n {\n // Try sending a mutate request to add the ad group ad.\n adGroupAdService.MutateAdGroupAds(customerId.ToString(), new[] { operation });\n }\n catch (GoogleAdsException ex)\n {\n // The request will always fail because of the policy violation in the\n // description of the ad.\n var ignorablePolicyTopics = FetchIgnorablePolicyTopics(ex);\n // Try sending exemption requests for creating a responsive search ad.\n RequestExemption(customerId, adGroupAdService, operation, ignorablePolicyTopics);\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Collects all ignorable policy topics that will be sent for exemption request later.\n /// </summary>\n /// <param name=\"ex\">The API exception from a previous call to add ad group ads.</param>\n /// <returns>The ignorable policy topics</returns>\n private static string[] FetchIgnorablePolicyTopics(GoogleAdsException ex)\n {\n List<string> ignorablePolicyTopics = new List<string>(); ;\n\n Console.WriteLine(\"Google Ads failure details:\");\n foreach (GoogleAdsError error in ex.Failure.Errors)\n {\n if (error.ErrorCode.ErrorCodeCase != ErrorCode.ErrorCodeOneofCase.PolicyFindingError)\n {\n throw ex;\n }\n if (error.Details != null && error.Details.PolicyFindingDetails != null)\n {\n PolicyFindingDetails details = error.Details.PolicyFindingDetails;\n Console.WriteLine($\"- Policy finding details:\");\n\n foreach (PolicyTopicEntry entry in details.PolicyTopicEntries)\n {\n ignorablePolicyTopics.Add(entry.Topic);\n Console.WriteLine($\" - Policy topic name: '{entry.Topic}'\");\n Console.WriteLine($\" - Policy topic entry type: '{entry.Type}'\");\n // For the sake of brevity, we exclude printing \"policy topic evidences\"\n // and \"policy topic constraints\" here. You can fetch those data by\n // calling:\n // - entry.Evidences\n // - entry.Constraints\n }\n }\n }\n return ignorablePolicyTopics.ToArray();\n }\n\n /// <summary>\n /// Sends exemption requests for creating a responsive search ad.\n /// </summary>\n /// <param name=\"customerId\">The customer ID for which the call is made.</param>\n /// <param name=\"service\">The ad group ad service.</param>\n /// <param name=\"operation\">The ad group ad operation to request exemption for.</param>\n /// <param name=\"ignorablePolicyTopics\">The ignorable policy topics.</param>\n private static void RequestExemption(long customerId, AdGroupAdServiceClient service,\n AdGroupAdOperation operation, string[] ignorablePolicyTopics)\n {\n Console.WriteLine(\"Try adding a responsive search ad again by requesting exemption for \" +\n \"its policy violations.\");\n PolicyValidationParameter validationParameter = new PolicyValidationParameter();\n validationParameter.IgnorablePolicyTopics.AddRange(ignorablePolicyTopics);\n operation.PolicyValidationParameter = validationParameter;\n\n MutateAdGroupAdsResponse response = service.MutateAdGroupAds(\n customerId.ToString(), new[] { operation });\n Console.WriteLine($\"Successfully added a responsive search ad with resource name \" +\n $\"'{response.Results[0].ResourceName}' by requesting for policy violation \" +\n $\"exemption.\");\n }\n }\n}\nHandleResponsiveSearchAdPolicyViolations.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2019 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\ErrorHandling;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\AdTextAsset;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\PolicyTopicEntry;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\PolicyValidationParameter;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ResponsiveSearchAdInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdGroupAdStatusEnum\\AdGroupAdStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\PolicyTopicEntryTypeEnum\\PolicyTopicEntryType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\Ad;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AdGroupAd;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AdGroupAdOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\Client\\AdGroupAdServiceClient;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAdGroupAdsRequest;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * This example demonstrates how to request an exemption for policy violations of a responsive\n * search ad. If the request somehow fails with exceptions that are not policy finding errors, the\n * example will stop instead of trying sending an exemption request.\n */\nclass HandleResponsiveSearchAdPolicyViolations\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const AD_GROUP_ID = 'INSERT_AD_GROUP_ID_HERE';\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::AD_GROUP_ID => GetOpt::REQUIRED_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::AD_GROUP_ID] ?: self::AD_GROUP_ID\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $adGroupId the ad group ID to add a responsive search ad to\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId\n ) {\n // Creates a responsive search ad info object.\n $responsiveSearchAdInfo = new ResponsiveSearchAdInfo([\n 'headlines' => [\n new AdTextAsset([\n 'text' => 'Cruise to Mars #' . Helper::getShortPrintableDatetime()\n ]),\n new AdTextAsset(['text' => 'Best Space Cruise Line']),\n new AdTextAsset(['text' => 'Experience the Stars'])\n ],\n // Intentionally use an ad text that violates policy -- having too many exclamation\n // marks.\n 'descriptions' => [\n new AdTextAsset(['text' => 'Buy your tickets now!!!!!!!']),\n new AdTextAsset(['text' => 'Visit the Red Planet'])\n ]\n ]);\n\n // Creates an ad group ad to hold the above ad.\n $adGroupAd = new AdGroupAd([\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n // Set the ad group ad to PAUSED to prevent it from immediately serving.\n // Set to ENABLED once you've added targeting and the ad are ready to serve.\n 'status' => AdGroupAdStatus::PAUSED,\n // Sets the responsive search ad info on an Ad.\n 'ad' => new Ad([\n 'responsive_search_ad' => $responsiveSearchAdInfo,\n 'final_urls' => ['https://www.example.com']\n ])\n ]);\n\n // Creates an ad group ad operation.\n $adGroupAdOperation = new AdGroupAdOperation();\n $adGroupAdOperation->setCreate($adGroupAd);\n $adGroupAdServiceClient = $googleAdsClient->getAdGroupAdServiceClient();\n\n $ignorablePolicyTopics = [];\n try {\n // Try sending a mutate request to add the ad group ad.\n $adGroupAdServiceClient->mutateAdGroupAds(\n MutateAdGroupAdsRequest::build($customerId, [$adGroupAdOperation])\n );\n } catch (GoogleAdsException $googleAdsException) {\n // The request will always fail because of the policy violation in the description of\n // the ad.\n $ignorablePolicyTopics = self::fetchIgnorablePolicyTopics($googleAdsException);\n }\n\n // Try sending exemption requests for creating a responsive search ad.\n self::requestExemption(\n $customerId,\n $adGroupAdServiceClient,\n $adGroupAdOperation,\n $ignorablePolicyTopics\n );\n }\n\n /**\n * Collects all ignorable policy topics that will be sent for exemption request later.\n *\n * @param GoogleAdsException $googleAdsException the Google Ads exception\n * @return string[] the ignorable policy topics\n */\n private static function fetchIgnorablePolicyTopics(GoogleAdsException $googleAdsException)\n {\n $ignorablePolicyTopics = [];\n\n printf(\"Google Ads failure details:%s\", PHP_EOL);\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n if ($error->getErrorCode()->getErrorCode() !== 'policy_finding_error') {\n // This example supports sending exemption request for the policy finding error\n // only.\n throw $googleAdsException;\n }\n\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n if (\n !is_null($error->getDetails())\n && !is_null($error->getDetails()->getPolicyFindingDetails())\n ) {\n $policyFindingDetails = $error->getDetails()->getPolicyFindingDetails();\n printf(\"\\tPolicy finding details:%s\", PHP_EOL);\n\n foreach ($policyFindingDetails->getPolicyTopicEntries() as $policyTopicEntry) {\n /** @var PolicyTopicEntry $policyTopicEntry */\n $ignorablePolicyTopics[] = $policyTopicEntry->getTopic();\n printf(\n \"\\t\\tPolicy topic name: '%s'%s\",\n $policyTopicEntry->getTopic(),\n PHP_EOL\n );\n printf(\n \"\\t\\tPolicy topic entry type: '%s'%s\",\n PolicyTopicEntryType::name($policyTopicEntry->getType()),\n PHP_EOL\n );\n // For the sake of brevity, we exclude printing \"policy topic evidences\" and\n // \"policy topic constraints\" here. You can fetch those data by calling:\n // - $policyTopicEntry->getEvidences()\n // - $policyTopicEntry->getConstraints()\n }\n }\n }\n return $ignorablePolicyTopics;\n }\n\n /**\n * Sends exemption requests for creating a responsive search ad.\n *\n * @param int $customerId\n * @param AdGroupAdServiceClient $adGroupAdServiceClient\n * @param AdGroupAdOperation $adGroupAdOperation\n * @param string[] $ignorablePolicyTopics\n */\n private static function requestExemption(\n int $customerId,\n AdGroupAdServiceClient $adGroupAdServiceClient,\n AdGroupAdOperation $adGroupAdOperation,\n array $ignorablePolicyTopics\n ) {\n print \"Try adding a responsive search ad again by requesting exemption for its policy\"\n . \" violations.\" . PHP_EOL;\n $adGroupAdOperation->setPolicyValidationParameter(\n new PolicyValidationParameter(['ignorable_policy_topics' => $ignorablePolicyTopics])\n );\n $response = $adGroupAdServiceClient->mutateAdGroupAds(MutateAdGroupAdsRequest::build(\n $customerId,\n [$adGroupAdOperation]\n ));\n printf(\n \"Successfully added a responsive search ad with resource name '%s' by requesting\"\n . \" for policy violation exemption.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n }\n}\n\nHandleResponsiveSearchAdPolicyViolations::main();\nHandleResponsiveSearchAdPolicyViolations.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2020 Google LLC\n#\n# 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.\n\"\"\"Requests an exemption for policy violations of a responsive search ad.\n\nIf the request somehow fails with exceptions that are not policy finding\nerrors, the example will stop instead of trying sending an exemption request.\n\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import List\nimport uuid\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.services.services.ad_group_ad_service import (\n AdGroupAdServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.ad_group_service import (\n AdGroupServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_ad_service import (\n AdGroupAdOperation,\n MutateAdGroupAdsResponse,\n)\nfrom google.ads.googleads.v24.resources.types.ad_group_ad import AdGroupAd\nfrom google.ads.googleads.v24.common.types.ad_type_infos import (\n ResponsiveSearchAdInfo,\n)\nfrom google.ads.googleads.v24.common.types.ad_asset import (\n AdTextAsset,\n)\nfrom google.ads.googleads.v24.errors.types.policy_finding_error import (\n PolicyFindingErrorEnum,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef main(client: GoogleAdsClient, customer_id: str, ad_group_id: str) -> None:\n \"\"\"Handles responsive search ad policy violations.\n\n Args:\n client: The Google Ads client.\n customer_id: The customer ID for which to add the responsive search ad.\n ad_group_id: The ad group ID to which to add a responsive search ad.\n \"\"\"\n ad_group_ad_service_client: AdGroupAdServiceClient = client.get_service(\n \"AdGroupAdService\"\n )\n ad_group_ad_operation: AdGroupAdOperation = create_responsive_search_ad(\n client, ad_group_ad_service_client, customer_id, ad_group_id\n )\n\n ignorable_policy_topics: List[str] = []\n try:\n # Try sending a mutate request to add the ad group ad.\n ad_group_ad_service_client.mutate_ad_group_ads(\n customer_id=customer_id, operations=[ad_group_ad_operation]\n )\n except GoogleAdsException as googleads_exception:\n # The request will always fail due to the policy violation in the\n # ad's description.\n ignorable_policy_topics = fetch_ignorable_policy_topics(\n client, googleads_exception\n )\n\n request_exemption(\n customer_id,\n ad_group_ad_service_client,\n ad_group_ad_operation,\n ignorable_policy_topics,\n )\n\n\ndef create_responsive_search_ad(\n client: GoogleAdsClient,\n ad_group_ad_service_client: AdGroupAdServiceClient,\n customer_id: str,\n ad_group_id: str,\n) -> AdGroupAdOperation:\n \"\"\"Create a responsive search ad that includes a policy violation.\n\n Args:\n client: The GoogleAds client instance.\n ad_group_ad_service_client: The AdGroupAdService client instance.\n customer_id: The customer ID for which to add the responsive search ad.\n ad_group_id: The ad group ID to which to add a responsive search ad.\n\n Returns:\n The attempted AdGroupAdOperation instance.\n \"\"\"\n ad_group_service: AdGroupServiceClient = client.get_service(\n \"AdGroupService\"\n )\n ad_group_resource_name: str = ad_group_service.ad_group_path(\n customer_id, ad_group_id\n )\n\n # Creates an operation and ad group ad to create and hold the above ad.\n ad_group_ad_operation: AdGroupAdOperation = client.get_type(\n \"AdGroupAdOperation\"\n )\n ad_group_ad: AdGroupAd = ad_group_ad_operation.create\n ad_group_ad.ad_group = ad_group_resource_name\n # Set the ad group ad to PAUSED to prevent it from immediately serving.\n # Set to ENABLED once you've added targeting and the ad are ready to serve.\n ad_group_ad.status = client.enums.AdGroupAdStatusEnum.PAUSED\n # Sets the responsive search ad info on an ad.\n responsive_search_ad_info: ResponsiveSearchAdInfo = (\n ad_group_ad.ad.responsive_search_ad\n )\n\n headline_1: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline_1.text = f\"Cruise to Mars #{str(uuid.uuid4())[0:13]}\"\n headline_2: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline_2.text = \"Best Space Cruise Line\"\n headline_3: AdTextAsset = client.get_type(\"AdTextAsset\")\n headline_3.text = \"Experience the Stars\"\n responsive_search_ad_info.headlines.extend(\n [headline_1, headline_2, headline_3]\n )\n\n # Intentionally use an ad text that violates policy by having too many\n # exclamation marks.\n description_1: AdTextAsset = client.get_type(\"AdTextAsset\")\n description_1.text = \"Buy your tickets now!!!!!!!\"\n description_2: AdTextAsset = client.get_type(\"AdTextAsset\")\n description_2.text = \"Visit the Red Planet\"\n responsive_search_ad_info.descriptions.extend(\n [description_1, description_2]\n )\n\n ad_group_ad.ad.final_urls.append(\"https://www.example.com\")\n\n return ad_group_ad_operation\n\n\ndef fetch_ignorable_policy_topics(\n client: GoogleAdsClient, googleads_exception: GoogleAdsException\n) -> List[str]:\n \"\"\"Collects all ignorable policy topics to be sent for exemption request.\n\n Args:\n client: The GoogleAds client instance.\n googleads_exception: The exception that contains the policy\n violation(s).\n\n Returns:\n A list of ignorable policy topics.\n \"\"\"\n ignorable_policy_topics: List[str] = []\n\n print(\"Google Ads failure details:\")\n for error in googleads_exception.failure.errors:\n if (\n error.error_code.policy_finding_error\n != client.enums.PolicyFindingErrorEnum.POLICY_FINDING\n ):\n print(\n \"This example supports sending exemption request for the \"\n \"policy finding error only.\"\n )\n raise googleads_exception\n\n print(f\"\\t{error.error_code.policy_finding_error}: {error.message}\")\n\n if (\n error.details is not None\n and error.details.policy_finding_details is not None\n ):\n policy_finding_details: PolicyFindingErrorEnum = (\n error.details.policy_finding_details\n )\n print(\"\\tPolicy finding details:\")\n\n for (\n policy_topic_entry\n ) in policy_finding_details.policy_topic_entries:\n ignorable_policy_topics.append(policy_topic_entry.topic)\n print(f\"\\t\\tPolicy topic name: '{policy_topic_entry.topic}'\")\n print(\n f\"\\t\\tPolicy topic entry type: '{policy_topic_entry.type_}'\"\n )\n # For the sake of brevity, we exclude printing \"policy topic\n # evidences\" and \"policy topic constraints\" here. You can fetch\n # those data by calling:\n # - policy_topic_entry.evidences\n # - policy_topic_entry.constraints\n\n return ignorable_policy_topics\n\n\ndef request_exemption(\n customer_id: str,\n ad_group_ad_service_client: AdGroupAdServiceClient,\n ad_group_ad_operation: AdGroupAdOperation,\n ignorable_policy_topics: List[str],\n) -> None:\n \"\"\"Sends exemption requests for creating a responsive search ad.\n\n Args:\n customer_id: The customer ID for which to add the responsive search ad.\n ad_group_ad_service_client: The AdGroupAdService client instance.\n ad_group_ad_operation: The AdGroupAdOperation that returned policy\n violation(s).\n ignorable_policy_topics: The extracted list of policy topic entries.\n \"\"\"\n print(\n \"Attempting to add a responsive search ad again by requesting \"\n \"exemption for its policy violations.\"\n )\n ad_group_ad_operation.policy_validation_parameter.ignorable_policy_topics.extend(\n ignorable_policy_topics\n )\n response: MutateAdGroupAdsResponse = (\n ad_group_ad_service_client.mutate_ad_group_ads(\n customer_id=customer_id, operations=[ad_group_ad_operation]\n )\n )\n print(\n \"Successfully added a responsive search ad with resource name \"\n f\"'{response.results[0].resource_name}' for policy violation \"\n \"exemption.\"\n )\n\n\nif __name__ == \"__main__\":\n parser: argparse.ArgumentParser = argparse.ArgumentParser(\n description=\"Requests an exemption for responsive search ad policy \"\n \"violations.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-a\",\n \"--ad_group_id\",\n type=str,\n required=True,\n help=\"The ad group ID to which to add a responsive search ad.\",\n )\n args = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n try:\n main(googleads_client, args.customer_id, args.ad_group_id)\n except GoogleAdsException as ex:\n print(\n f\"Request with ID '{ex.request_id}' failed with status \"\n f\"'{ex.error.code().name}' and includes the following errors:\"\n )\n for error in ex.failure.errors:\n print(f\"\\tError with message '{error.message}'.\")\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nhandle_responsive_search_ad_policy_violations.py\n```\n\nExample:\n```text\n# Encoding: utf-8\n#\n# Copyright 2020 Google LLC\n#\n# 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.\n#\n# Requests an exemption for policy violations of a responsive search ad.\n#\n# If the request somehow fails with exceptions that are not policy finding\n# errors, the example will stop instead of trying to send an exemption request.\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\nrequire 'date'\n\ndef handle_responsive_search_ad_policy_violations(customer_id, ad_group_id)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n ad_group_ad_service = client.service.ad_group_ad\n\n ad_group_ad_operation, ignorable_policy_topics = create_responsive_search_ad(\n client,\n ad_group_ad_service,\n customer_id,\n ad_group_id,\n )\n\n request_exemption(\n client,\n customer_id,\n ad_group_ad_service,\n ad_group_ad_operation,\n ignorable_policy_topics,\n )\nend\n\ndef create_responsive_search_ad(client, ad_group_ad_service, customer_id, ad_group_id)\n ad_group_ad_operation = client.operation.create_resource.ad_group_ad do |aga|\n aga.ad_group = client.path.ad_group(customer_id, ad_group_id)\n aga.status = :PAUSED\n\n aga.ad = client.resource.ad do |ad|\n ad.final_urls << \"http://www.example.com\"\n ad.responsive_search_ad = client.resource.responsive_search_ad_info do |rsa|\n rsa.headlines << client.resource.ad_text_asset do |ta|\n ta.text = \"Cruise to Mars ##{(Time.new.to_f * 1000).to_i}\"\n end\n rsa.headlines << client.resource.ad_text_asset do |ta|\n ta.text = \"Best space cruise line\"\n end\n rsa.headlines << client.resource.ad_text_asset do |ta|\n ta.text = \"Experience the stars\"\n end\n rsa.descriptions << client.resource.ad_text_asset do |ta|\n # Intentionally use an ad text that violates policy -- having too\n # many exclamation marks.\n ta.text = \"Buy your tickets now!!!!!!!\"\n end\n rsa.descriptions << client.resource.ad_text_asset do |ta|\n ta.text = \"Visit the Red Planet\"\n end\n end\n end\n end\n\n ignorable_policy_topics = []\n begin\n ad_group_ad_service.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [ad_group_ad_operation],\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n ignorable_policy_topics = fetch_ignorable_policy_topics(e)\n end\n\n return ad_group_ad_operation, ignorable_policy_topics\nend\n\ndef fetch_ignorable_policy_topics(exception)\n ignorable_policy_topics = []\n\n exception.failure.errors.each do |error|\n if error.error_code.policy_finding_error != :POLICY_FINDING\n puts \"Non-policy finding error found. Aborting.\"\n raise exception\n end\n puts \"#{error.error_code.policy_finding_error}: #{error.message}\"\n\n error&.details&.policy_finding_details&.policy_topic_entries.each do |entry|\n ignorable_policy_topics << entry.topic\n puts \"\\tPolicy topic name: #{entry.topic}\"\n puts \"\\tPolicy topic entry type: #{entry.type}\"\n end\n end\n\n ignorable_policy_topics\nend\n\ndef request_exemption(\n client, customer_id, ad_group_ad_service, ad_group_ad_operation, ignorable_policy_topics)\n # Add all the found ignorable policy topics to the operation.\n ad_group_ad_operation.policy_validation_parameter =\n client.resource.policy_validation_parameter do |pvp|\n pvp.ignorable_policy_topics.push(\n *ignorable_policy_topics\n )\n end\n response = ad_group_ad_service.mutate_ad_group_ads(\n customer_id: customer_id,\n operations: [ad_group_ad_operation],\n )\n puts \"Successfully added a responsive search ad with resource name \" \\\n \"#{response.results.first.resource_name} for policy violation exception.\"\nend\n\nif __FILE__ == $PROGRAM_NAME\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:ad_group_id] = 'INSERT_AD_GROUP_ID_HERE'\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: ruby %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-A', '--ad-group-id AD-GROUP-ID', String, 'Ad Group ID') do |v|\n options[:ad_group_id] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n handle_responsive_search_ad_policy_violations(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options.fetch(:ad_group_id),\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\nhandle_responsive_search_ad_policy_violations.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2019, Google LLC\n#\n# 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# http://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.\n#\n# This example demonstrates how to request an exemption for policy violations\n# of a responsive search ad. If the request somehow fails with exceptions that are\n# not policy finding errors, the example will stop instead of trying sending an\n# exemption request.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroupAd;\nuse Google::Ads::GoogleAds::V25::Resources::Ad;\nuse Google::Ads::GoogleAds::V25::Common::AdTextAsset;\nuse Google::Ads::GoogleAds::V25::Common::ResponsiveSearchAdInfo;\nuse Google::Ads::GoogleAds::V25::Common::PolicyValidationParameter;\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupStatusEnum qw(PAUSED);\nuse Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Data::Uniqid qw(uniqid);\n\nsub handle_responsive_search_ad_policy_violations {\n my ($api_client, $customer_id, $ad_group_id) = @_;\n\n my $ad_group_resource_name =\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group($customer_id,\n $ad_group_id);\n\n # Create a responsive search ad info object.\n my $responsive_search_ad_info =\n Google::Ads::GoogleAds::V25::Common::ResponsiveSearchAdInfo->new({\n headlines => [\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Cruise to Mars #\" . uniqid()}\n ),\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Best Space Cruise Line\"\n }\n ),\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Experience the Stars\"\n })\n ],\n descriptions => [\n # Intentionally use an ad text that violates policy -- having too many\n # exclamation marks.\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Buy your tickets now!!!!!!!\"\n }\n ),\n Google::Ads::GoogleAds::V25::Common::AdTextAsset->new({\n text => \"Visit the Red Planet\"\n })]});\n\n # Create an ad group ad to hold the above ad.\n my $ad_group_ad = Google::Ads::GoogleAds::V25::Resources::AdGroupAd->new({\n adGroup => $ad_group_resource_name,\n # Set the ad group ad to PAUSED to prevent it from immediately serving.\n # Set to ENABLED once you've added targeting and the ad are ready to serve.\n status => PAUSED,\n # Set the responsive search ad info on an ad.\n ad => Google::Ads::GoogleAds::V25::Resources::Ad->new({\n responsiveSearchAd => $responsive_search_ad_info,\n finalUrls => [\"https://www.example.com\"]})});\n\n # Create an ad group ad operation.\n my $ad_group_ad_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupAdService::AdGroupAdOperation\n ->new({\n create => $ad_group_ad\n });\n\n # Try sending a mutate request to add the ad group ad.\n my $response = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]});\n\n my $ignorable_policy_topics = [];\n if ($response->isa(\"Google::Ads::GoogleAds::GoogleAdsException\")) {\n # The request will always fail because of the policy violation in the\n # description of the ad.\n $ignorable_policy_topics = fetch_ignorable_policy_topics($response);\n }\n\n # Try sending exemption requests for creating a responsive search ad.\n request_exemption($api_client, $customer_id, $ad_group_ad_operation,\n $ignorable_policy_topics);\n\n return 1;\n}\n\n# Collects all ignorable policy topics that will be sent for exemption request\n# later.\nsub fetch_ignorable_policy_topics {\n my $google_ads_exception = shift;\n\n my $ignorable_policy_topics = [];\n\n printf \"Google Ads failure details:\\n\";\n foreach\n my $error (@{$google_ads_exception->get_google_ads_failure()->{errors}})\n {\n if ([keys %{$error->{errorCode}}]->[0] ne \"policyFindingError\") {\n # This example supports sending exemption request for the policy finding\n # error only.\n die $google_ads_exception->get_message();\n }\n\n printf \"\\t%s: %s\\n\", [keys %{$error->{errorCode}}]->[0], $error->{message};\n\n if ($error->{details}{policyFindingDetails}) {\n my $policy_finding_details = $error->{details}{policyFindingDetails};\n printf \"\\tPolicy finding details:\\n\";\n\n foreach my $policy_topic_entry (\n @{$policy_finding_details->{policyTopicEntries}})\n {\n push @$ignorable_policy_topics, $policy_topic_entry->{topic};\n printf\n \"\\t\\tPolicy topic name: '%s'\\n\",\n $policy_topic_entry->{topic};\n printf \"\\t\\tPolicy topic entry type: '%s'\\n\",\n $policy_topic_entry->{type};\n # For the sake of brevity, we exclude printing \"policy topic evidences\" and\n # \"policy topic constraints\" here. You can fetch those data by calling:\n # - $policy_topic_entry->{evidences}\n # - $policy_topic_entry->{constraints}\n }\n }\n }\n\n return $ignorable_policy_topics;\n}\n\n# Sends exemption requests for creating a responsive search ad.\nsub request_exemption {\n my ($api_client, $customer_id, $ad_group_ad_operation,\n $ignorable_policy_topics)\n = @_;\n\n print\n \"Try adding a responsive search ad again by requesting exemption for its \"\n . \"policy violations.\\n\";\n\n $ad_group_ad_operation->{policyValidationParameter} =\n Google::Ads::GoogleAds::V25::Common::PolicyValidationParameter->new(\n {ignorablePolicyTopics => $ignorable_policy_topics});\n\n my $ad_group_ads_response = $api_client->AdGroupAdService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_ad_operation]});\n\n printf\n \"Successfully added a responsive search ad with resource name '%s' by \" .\n \"requesting for policy violation exemption.\\n\",\n $ad_group_ads_response->{results}[0]{resourceName};\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(0);\n\nmy $customer_id = undef;\nmy $ad_group_id = undef;\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"ad_group_id=i\" => \\$ad_group_id\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id, $ad_group_id);\n\n# Call the example.\nhandle_responsive_search_ad_policy_violations($api_client,\n $customer_id =~ s/-//gr, $ad_group_id);\n\n=pod\n\n=head1 NAME\n\nhandle_responsive_search_ad_policy_violations\n\n=head1 DESCRIPTION\n\nThis example demonstrates how to request an exemption for policy violations of a\nresponsive search ad. If the request somehow fails with exceptions that are not policy\nfinding errors, the example will stop instead of trying sending an exemption request.\n\n=head1 SYNOPSIS\n\nhandle_responsive_search_ad_policy_violations.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -ad_group_id The ad group ID.\n\n=cut\nhandle_responsive_search_ad_policy_violations.pl\n```\n\nExample:\n```text\n// Copyright 2020 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.errorhandling;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.KeywordInfo;\nimport com.google.ads.googleads.v25.common.PolicyViolationKey;\nimport com.google.ads.googleads.v25.enums.AdGroupCriterionStatusEnum.AdGroupCriterionStatus;\nimport com.google.ads.googleads.v25.enums.KeywordMatchTypeEnum.KeywordMatchType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.errors.PolicyViolationDetails;\nimport com.google.ads.googleads.v25.resources.AdGroupCriterion;\nimport com.google.ads.googleads.v25.services.AdGroupCriterionOperation;\nimport com.google.ads.googleads.v25.services.AdGroupCriterionServiceClient;\nimport com.google.ads.googleads.v25.services.MutateAdGroupCriteriaResponse;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Demonstrates how to request an exemption for policy violations of a keyword.\n *\n * <p>The example uses an exemptible policy-violating keyword by default. If you use a keyword that\n * contains non-exemptible policy violations, it will not be sent with exemptions requested and you\n * will still fail to create a keyword.\n *\n * <p>If you specify a keyword that doesn't violate any policies, this example will just add the\n * keyword as usual, similar to what the AddKeywords example does.\n *\n * <p>When you send a request to add a keyword after requesting a policy exemption for that keyword,\n * the request will pass as if you were adding a non-violating keyword.\n */\npublic class HandleKeywordPolicyViolations {\n\n private static class HandleKeywordPolicyViolationsParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(names = ArgumentNames.AD_GROUP_ID, required = true)\n private Long adGroupId;\n\n @Parameter(names = ArgumentNames.KEYWORD_TEXT)\n private String keywordText = \"medication\";\n }\n\n public static void main(String[] args) throws IOException {\n HandleKeywordPolicyViolationsParams params = new HandleKeywordPolicyViolationsParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.adGroupId = Long.parseLong(\"INSERT_AD_GROUP_ID_HERE\");\n\n // Optional: Specify a keywordText here, or the default specified above will be used.\n // params.keywordText = \"INSERT_KEYWORD_TEXT_HERE\";\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new HandleKeywordPolicyViolations()\n .runExample(googleAdsClient, params.customerId, params.adGroupId, params.keywordText);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param adGroupId the ID of the ad group to add a keyword to.\n * @param keywordText the keyword text to add.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void runExample(\n GoogleAdsClient googleAdsClient, long customerId, Long adGroupId, String keywordText) {\n try (AdGroupCriterionServiceClient adGroupCriterionServiceClient =\n googleAdsClient.getLatestVersion().createAdGroupCriterionServiceClient()) {\n // Configures the keyword text and match type settings.\n KeywordInfo keywordInfo =\n KeywordInfo.newBuilder()\n .setText(keywordText)\n .setMatchType(KeywordMatchType.EXACT)\n .build();\n\n // Constructs an ad group criterion using the keyword text info above.\n AdGroupCriterion adGroupCriterion =\n AdGroupCriterion.newBuilder()\n .setAdGroup(ResourceNames.adGroup(customerId, adGroupId))\n .setStatus(AdGroupCriterionStatus.ENABLED)\n .setKeyword(keywordInfo)\n .build();\n\n // Constructs an operation to create the ad group criterion.\n AdGroupCriterionOperation operation =\n AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterion).build();\n\n // Tries sending a mutate request to add the keyword.\n List<PolicyViolationKey> exemptibleKeys = new ArrayList<>();\n int errorCount = 0;\n try {\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n String.valueOf(customerId), ImmutableList.of(operation));\n // If the request succeeded, then either the keyword does not require a policy exemption or\n // a policy exemption was previously submitted for the keyword. In either case, returns and\n // skips the remaining portion of this example that resubmits with an exemption request.\n System.out.printf(\n \"Successfully added a keyword with resource name '%s'. No exemptions needed.%n\",\n response.getResults(0).getResourceName());\n return;\n } catch (GoogleAdsException e) {\n exemptibleKeys = extractExemptiblePolicyViolationKeys(e);\n errorCount = e.getGoogleAdsFailure().getErrorsCount();\n }\n\n // Tries sending exemption requests for creating the keyword. However, if your keyword\n // contains many policy violations, but not all of them are exemptible, the request will not\n // be sent.\n if (exemptibleKeys.size() == errorCount) {\n System.out.println(\n \"Attempting to add the keyword again by requesting exemption for its policy\"\n + \" violations.\");\n // Creates a modified version of the operation with the exempt policy violation keys.\n operation = operation.toBuilder().addAllExemptPolicyViolationKeys(exemptibleKeys).build();\n\n // Tries sending the mutate request again.\n MutateAdGroupCriteriaResponse response =\n adGroupCriterionServiceClient.mutateAdGroupCriteria(\n String.valueOf(customerId), ImmutableList.of(operation));\n System.out.printf(\n \"Successfully added a keyword with resource name '%s' by requesting \"\n + \"policy violation exemptions.%n\",\n response.getResults(0).getResourceName());\n } else {\n System.out.println(\"No exemption request was sent because either:\");\n System.out.println(\"1) your keyword contained some non-exemptible policy violations, or\");\n System.out.println(\"2) other non-policy related errors were thrown\");\n }\n }\n }\n\n /**\n * Collects all policy violation keys that can be exempted for sending an exemption request later.\n *\n * @param googleAdsException the exception to extract the keys from.\n * @return the list of extracted exemptible keys.\n */\n private List<PolicyViolationKey> extractExemptiblePolicyViolationKeys(\n GoogleAdsException googleAdsException) {\n List<PolicyViolationKey> exemptibleKeys = new ArrayList<>();\n System.out.println(\"Google Ads failure details:\");\n for (GoogleAdsError googleAdsError : googleAdsException.getGoogleAdsFailure().getErrorsList()) {\n System.out.printf(\"\\t%s: %s%n\", googleAdsError.getErrorCode(), googleAdsError.getMessage());\n if (googleAdsError.hasDetails() && googleAdsError.getDetails().hasPolicyViolationDetails()) {\n PolicyViolationDetails policyViolationDetails =\n googleAdsError.getDetails().getPolicyViolationDetails();\n System.out.println(\"\\tPolicy violation details:\");\n System.out.printf(\n \"\\t\\tExternal policy name: '%s'%n\", policyViolationDetails.getExternalPolicyName());\n System.out.printf(\n \"\\t\\tExternal policy description: '%s'%n\",\n policyViolationDetails.getExternalPolicyDescription());\n System.out.printf(\"\\t\\tIs exemptible? '%s'%n\", policyViolationDetails.getIsExemptible());\n if (policyViolationDetails.getIsExemptible() && policyViolationDetails.hasKey()) {\n PolicyViolationKey policyViolationKey = policyViolationDetails.getKey();\n exemptibleKeys.add(policyViolationKey);\n System.out.println(\"\\t\\tPolicy violation key:\");\n System.out.printf(\"\\t\\t\\tName: '%s'%n\", policyViolationKey.getPolicyName());\n System.out.printf(\"\\t\\t\\tViolating text: '%s'%n\", policyViolationKey.getViolatingText());\n }\n }\n }\n return exemptibleKeys;\n }\n}\nHandleKeywordPolicyViolations.java\n```\n\nExample:\n```text\n// Copyright 2020 Google LLC\n//\n// 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// http://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.\n\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing System;\nusing System.Collections.Generic;\nusing static Google.Ads.GoogleAds.V25.Enums.AdGroupCriterionStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.KeywordMatchTypeEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example demonstrates how to request an exemption for policy violations of a\n /// keyword. Note that the example uses an exemptible policy-violating keyword by default.\n /// If you use a keyword that contains non-exemptible policy violations, they will not be\n /// sent for exemption request and you will still fail to create a keyword.\n /// If you specify a keyword that doesn't violate any policies, this example will just add the\n /// keyword as usual, similar to what the AddKeywords example does.\n /// Note that once you've requested policy exemption for a keyword, when you send a request for\n /// adding it again, the request will pass like when you add a non-violating keyword.\n /// </summary>\n public class HandleKeywordPolicyViolations : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"HandleKeywordPolicyViolations\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// ID of the ad group to which keywords are added.\n /// </summary>\n [Option(\"adGroupId\", Required = true, HelpText =\n \"ID of the ad group to which keywords are added.\")]\n public long AdGroupId { get; set; }\n\n /// <summary>\n /// The keyword text to add to the ad group.\n /// </summary>\n [Option(\"keywordText\", Required = false, HelpText =\n \"The keyword text to add to the ad group.\")]\n public string KeywordText { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n HandleKeywordPolicyViolations codeExample = new HandleKeywordPolicyViolations();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.AdGroupId,\n options.KeywordText);\n }\n\n /// <summary>\n /// The default keyword to be used if keyword is not provided.\n /// </summary>\n private const string DEFAULT_KEYWORD = \"medication\";\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example demonstrates how to request an exemption for policy violations of \" +\n \"a keyword. Note that the example uses an exemptible policy-violating keyword by \" +\n \"default. If you use a keyword that contains non-exemptible policy violations, they \" +\n \"will not be sent for exemption request and you will still fail to create a keyword. \" +\n \"If you specify a keyword that doesn't violate any policies, this example will just \" +\n \"add the keyword as usual, similar to what the AddKeywords example does. Note that \" +\n \"once you've requested policy exemption for a keyword, when you send a request for \" +\n \"adding it again, the request will pass like when you add a non-violating keyword.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The customer ID for which the call is made.</param>\n /// <param name=\"adGroupId\">ID of the ad group to which keywords are added.</param>\n /// <param name=\"keywordText\">The keyword text to add to the ad group.</param>\n public void Run(GoogleAdsClient client, long customerId, long adGroupId,\n string keywordText)\n {\n // Get the AdGroupCriterionServiceClient.\n AdGroupCriterionServiceClient service = client.GetService(\n Services.V25.AdGroupCriterionService);\n\n if (string.IsNullOrEmpty(keywordText))\n {\n keywordText = DEFAULT_KEYWORD;\n }\n // Configures the keyword text and match type settings.\n KeywordInfo keywordInfo = new KeywordInfo()\n {\n Text = keywordText,\n MatchType = KeywordMatchType.Exact\n };\n\n // Constructs an ad group criterion using the keyword text info above.\n AdGroupCriterion adGroupCriterion = new AdGroupCriterion()\n {\n AdGroup = ResourceNames.AdGroup(customerId, adGroupId),\n Status = AdGroupCriterionStatus.Paused,\n Keyword = keywordInfo\n };\n\n AdGroupCriterionOperation operation = new AdGroupCriterionOperation()\n {\n Create = adGroupCriterion\n };\n\n try\n {\n try\n {\n // Try sending a mutate request to add the keyword.\n MutateAdGroupCriteriaResponse response = service.MutateAdGroupCriteria(\n customerId.ToString(), new[] { operation });\n Console.WriteLine($\"Added a keyword with resource name \" +\n $\"'{response.Results[0].ResourceName}'.\");\n }\n catch (GoogleAdsException ex)\n {\n PolicyViolationKey[] exemptPolicyViolationKeys =\n FetchExemptPolicyViolationKeys(ex);\n\n // Try sending exemption requests for creating a keyword. However, if your\n // keyword contains many policy violations, but not all of them are exemptible,\n // the request will not be sent.\n RequestExemption(customerId, service, operation, exemptPolicyViolationKeys);\n }\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Collects all policy violation keys that can be exempted for sending a exemption\n /// request later.\n /// </summary>\n /// <param name=\"ex\">The Google Ads exception.</param>\n /// <returns>The exemptible policy violation keys.</returns>\n private static PolicyViolationKey[] FetchExemptPolicyViolationKeys(GoogleAdsException ex)\n {\n bool isFullyExemptable = true;\n List<PolicyViolationKey> exemptPolicyViolationKeys = new List<PolicyViolationKey>();\n\n Console.WriteLine(\"Google Ads failure details:\");\n foreach (GoogleAdsError error in ex.Failure.Errors)\n {\n if (error.ErrorCode.ErrorCodeCase !=\n ErrorCode.ErrorCodeOneofCase.PolicyViolationError)\n {\n Console.WriteLine(\"No exemption request is sent because there are other \" +\n \"non-policy related errors thrown.\");\n throw ex;\n }\n if (error.Details != null && error.Details.PolicyViolationDetails != null)\n {\n PolicyViolationDetails details = error.Details.PolicyViolationDetails;\n Console.WriteLine($\"- Policy violation details:\");\n\n Console.WriteLine(\" - Policy violation details:\");\n Console.WriteLine($\" - External policy name: '{details.ExternalPolicyName}'\");\n Console.WriteLine($\" - External policy description: \" +\n $\"'{details.ExternalPolicyDescription}'\");\n Console.WriteLine($\" - Is exemptable: '{details.IsExemptible}'\");\n\n if (details.IsExemptible && details.Key != null)\n {\n PolicyViolationKey key = details.Key;\n Console.WriteLine($\" - Policy violation key:\");\n Console.WriteLine($\" - Name: {key.PolicyName}\");\n Console.WriteLine($\" - Violating Text: {key.ViolatingText}\");\n exemptPolicyViolationKeys.Add(key);\n }\n else\n {\n isFullyExemptable = false;\n }\n }\n }\n\n if (!isFullyExemptable)\n {\n Console.WriteLine(\"No exemption request is sent because your keyword \" +\n \"contained some non-exemptible policy violations.\");\n throw ex;\n }\n return exemptPolicyViolationKeys.ToArray();\n }\n\n /// <summary>\n /// Sends exemption requests for creating a keyword.\n /// </summary>\n /// <param name=\"customerId\">The customer ID for which the call is made.</param>\n /// <param name=\"service\">The ad group criterion service.</param>\n /// <param name=\"operation\">The ad group criterion operation to request exemption for.\n /// </param>\n /// <param name=\"exemptPolicyViolationKeys\">The exemptable policy violation keys.</param>\n private static void RequestExemption(\n long customerId, AdGroupCriterionServiceClient service,\n AdGroupCriterionOperation operation, PolicyViolationKey[] exemptPolicyViolationKeys)\n {\n Console.WriteLine(\"Try adding a keyword again by requesting exemption for its policy \"\n + \"violations.\");\n PolicyValidationParameter validationParameter = new PolicyValidationParameter();\n validationParameter.ExemptPolicyViolationKeys.AddRange(exemptPolicyViolationKeys);\n operation.ExemptPolicyViolationKeys.AddRange(exemptPolicyViolationKeys);\n\n MutateAdGroupCriteriaResponse response = service.MutateAdGroupCriteria(\n customerId.ToString(), new[] { operation });\n Console.WriteLine($\"Successfully added a keyword with resource name \" +\n $\"'{response.Results[0].ResourceName}' by requesting for policy violation \" +\n $\"exemption.\");\n }\n }\n}\nHandleKeywordPolicyViolations.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2019 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\ErrorHandling;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\KeywordInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\PolicyViolationKey;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\AdGroupCriterionStatusEnum\\AdGroupCriterionStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\KeywordMatchTypeEnum\\KeywordMatchType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\AdGroupCriterion;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AdGroupCriterionOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\Client\\AdGroupCriterionServiceClient;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\MutateAdGroupCriteriaRequest;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * This example demonstrates how to request an exemption for policy violations of a keyword.\n * Note that the example uses an exemptible policy-violating keyword by default. If you use a\n * keyword that contains non-exemptible policy violations, they will not be sent for exemption\n * request and you will still fail to create a keyword.\n * If you specify a keyword that doesn't violate any policies, this example will just add the\n * keyword as usual, similar to what the AddKeywords example does.\n *\n * Note that once you've requested policy exemption for a keyword, when you send a request for\n * adding it again, the request will pass like when you add a non-violating keyword.\n */\nclass HandleKeywordPolicyViolations\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n private const AD_GROUP_ID = 'INSERT_AD_GROUP_ID_HERE';\n // Specify the keyword text here or the default specified below will be used.\n private const KEYWORD_TEXT = 'medication';\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::AD_GROUP_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::KEYWORD_TEXT => GetOpt::OPTIONAL_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::AD_GROUP_ID] ?: self::AD_GROUP_ID,\n $options[ArgumentNames::KEYWORD_TEXT] ?: self::KEYWORD_TEXT\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param int $adGroupId the ad group ID to add a keyword to\n * @param string $keywordText the keyword text to add\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n int $adGroupId,\n string $keywordText\n ) {\n // Configures the keyword text and match type settings.\n $keywordInfo = new KeywordInfo([\n 'text' => $keywordText,\n 'match_type' => KeywordMatchType::EXACT\n ]);\n\n // Constructs an ad group criterion using the keyword text info above.\n $adGroupCriterion = new AdGroupCriterion([\n 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId),\n 'status' => AdGroupCriterionStatus::ENABLED,\n 'keyword' => $keywordInfo\n ]);\n\n $adGroupCriterionOperation = new AdGroupCriterionOperation();\n $adGroupCriterionOperation->setCreate($adGroupCriterion);\n $adGroupCriterionServiceClient = $googleAdsClient->getAdGroupCriterionServiceClient();\n\n try {\n // Try sending a mutate request to add the keyword.\n $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$adGroupCriterionOperation])\n );\n printf(\n \"Added a keyword with resource name '%s'.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n } catch (GoogleAdsException $googleAdsException) {\n // Try sending exemption requests for creating a keyword. However, if your keyword\n // contains many policy violations, but not all of them are exemptible, the request\n // will not be sent.\n $exemptPolicyViolationKeys = self::fetchExemptPolicyViolationKeys($googleAdsException);\n self::requestExemption(\n $customerId,\n $adGroupCriterionServiceClient,\n $adGroupCriterionOperation,\n $exemptPolicyViolationKeys\n );\n }\n }\n\n /**\n * Collects all policy violation keys that can be exempted for sending a exemption request\n * later.\n *\n * @param GoogleAdsException $googleAdsException the Google Ads exception\n * @return PolicyViolationKey[] the exemptible policy violation keys\n */\n private static function fetchExemptPolicyViolationKeys(GoogleAdsException $googleAdsException)\n {\n $exemptPolicyViolationKeys = [];\n\n printf(\"Google Ads failure details:%s\", PHP_EOL);\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n if (\n !is_null($error->getDetails())\n && !is_null($error->getDetails()->getPolicyViolationDetails())\n ) {\n $policyViolationDetails = $error->getDetails()->getPolicyViolationDetails();\n printf(\"\\tPolicy violation details:%s\", PHP_EOL);\n printf(\n \"\\t\\tExternal policy name: '%s'%s\",\n $policyViolationDetails->getExternalPolicyName(),\n PHP_EOL\n );\n printf(\n \"\\t\\tExternal policy description: '%s'%s\",\n $policyViolationDetails->getExternalPolicyDescription(),\n PHP_EOL\n );\n printf(\n \"\\t\\tIs exemptible? '%s'%s\",\n $policyViolationDetails->getIsExemptible() ? 'yes' : 'no',\n PHP_EOL\n );\n\n if (\n $policyViolationDetails->getIsExemptible() &&\n !is_null($policyViolationDetails->getKey())\n ) {\n $policyViolationDetailsKey = $policyViolationDetails->getKey();\n $exemptPolicyViolationKeys[] = $policyViolationDetailsKey;\n printf(\"\\t\\tPolicy violation key:%s\", PHP_EOL);\n printf(\n \"\\t\\t\\tName: '%s'%s\",\n $policyViolationDetailsKey->getPolicyName(),\n PHP_EOL\n );\n printf(\n \"\\t\\t\\tViolating text: '%s'%s\",\n $policyViolationDetailsKey->getViolatingText(),\n PHP_EOL\n );\n } else {\n print \"No exemption request is sent because your keyword contained some \"\n . \"non-exemptible policy violations.\" . PHP_EOL;\n throw $googleAdsException;\n }\n } else {\n print \"No exemption request is sent because there are other non-policy related \"\n . \"errors thrown.\" . PHP_EOL;\n throw $googleAdsException;\n }\n }\n return $exemptPolicyViolationKeys;\n }\n\n /**\n * Sends exemption requests for creating a keyword.\n *\n * @param int $customerId the customer ID\n * @param AdGroupCriterionServiceClient $adGroupCriterionServiceClient the ad group criterion\n * service API client\n * @param AdGroupCriterionOperation $adGroupCriterionOperation the ad group criterion operation\n * to request exemption for\n * @param PolicyViolationKey[] $exemptPolicyViolationKeys the exemptible policy violation keys\n */\n private static function requestExemption(\n int $customerId,\n AdGroupCriterionServiceClient $adGroupCriterionServiceClient,\n AdGroupCriterionOperation $adGroupCriterionOperation,\n array $exemptPolicyViolationKeys\n ) {\n print \"Try adding a keyword again by requesting exemption for its policy\"\n . \" violations.\" . PHP_EOL;\n $adGroupCriterionOperation->setExemptPolicyViolationKeys($exemptPolicyViolationKeys);\n $response = $adGroupCriterionServiceClient->mutateAdGroupCriteria(\n MutateAdGroupCriteriaRequest::build($customerId, [$adGroupCriterionOperation])\n );\n printf(\n \"Successfully added a keyword with resource name '%s' by requesting for\"\n . \" policy violation exemption.%s\",\n $response->getResults()[0]->getResourceName(),\n PHP_EOL\n );\n }\n}\n\nHandleKeywordPolicyViolations::main();\nHandleKeywordPolicyViolations.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2020 Google LLC\n#\n# 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.\n\"\"\"Demonstrates how to request an exemption for policy violations of a keyword.\n\nNote that the example uses an exemptible policy-violating keyword by default.\nIf you use a keyword that contains non-exemptible policy violations, they will\nnot be sent for exemption request, and you will still fail to create a keyword.\nIf you specify a keyword that doesn't violate any policies, this example will\njust add the keyword as usual, similar to what the AddKeywords example does.\n\nNote that once you've requested policy exemption for a keyword, when you send\na request for adding it again, the request will pass like when you add a\nnon-violating keyword.\n\"\"\"\n\nimport argparse\nimport logging\nimport sys\nfrom typing import Any, List, Optional, Tuple\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.services.services.ad_group_criterion_service import (\n AdGroupCriterionServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.ad_group_criterion_service import (\n AdGroupCriterionOperation,\n)\nfrom google.ads.googleads.v24.common.types.policy import PolicyViolationKey\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n ad_group_id: str,\n keyword_text: str,\n) -> None:\n \"\"\"Demonstrates how to request an exemption for keyword policy violations.\n\n Args:\n client: The Google Ads client.\n customer_id: The customer ID for which to add the keyword.\n ad_group_id: The ad group ID to which to add keyword.\n keyword_text: The keyword text to add.\n \"\"\"\n\n ad_group_criterion_service: AdGroupCriterionServiceClient = (\n client.get_service(\"AdGroupCriterionService\")\n )\n\n googleads_exception: Optional[GoogleAdsException]\n ad_group_criterion_operation: AdGroupCriterionOperation\n (\n googleads_exception,\n ad_group_criterion_operation,\n ) = create_keyword_criterion(\n client,\n ad_group_criterion_service,\n customer_id,\n ad_group_id,\n keyword_text,\n )\n\n try:\n # Try sending exemption requests for creating a keyword. However, if\n # your keyword contains many policy violations, but not all of them are\n # exemptible, the request will not be sent.\n if googleads_exception is not None:\n exempt_policy_violation_keys: List[PolicyViolationKey] = (\n fetch_exempt_policy_violation_keys(googleads_exception)\n )\n request_exemption(\n customer_id,\n ad_group_criterion_service,\n ad_group_criterion_operation,\n exempt_policy_violation_keys,\n )\n except GoogleAdsException as ex:\n print(\n f\"Request with ID '{ex.request_id}' failed with status \"\n f\"'{ex.error.code().name}' and includes the following errors:\"\n )\n for error in ex.failure.errors:\n print(f\"\\tError with message '{error.message}'.\")\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\n\n\ndef create_keyword_criterion(\n client: GoogleAdsClient,\n ad_group_criterion_service: AdGroupCriterionServiceClient,\n customer_id: str,\n ad_group_id: str,\n keyword_text: str,\n) -> Tuple[Optional[GoogleAdsException], AdGroupCriterionOperation]:\n \"\"\"Attempts to add a keyword criterion to an ad group.\n\n Args:\n client: The GoogleAds client instance.\n ad_group_criterion_service: The AdGroupCriterionService client instance.\n customer_id: The customer ID for which to add the expanded text ad.\n ad_group_id: The ad group ID to which to add an expanded text ad.\n keyword_text: The keyword text to add.\n\n Returns:\n The GoogleAdsException that occurred (or None if the operation was\n successful) and the modified operation.\n \"\"\"\n # Constructs an ad group criterion using the keyword text provided.\n ad_group_criterion_operation: AdGroupCriterionOperation = client.get_type(\n \"AdGroupCriterionOperation\"\n )\n ad_group_criterion: Any = ad_group_criterion_operation.create\n ad_group_criterion.ad_group = client.get_service(\n \"AdGroupService\"\n ).ad_group_path(customer_id, ad_group_id)\n ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED\n ad_group_criterion.keyword.text = keyword_text\n ad_group_criterion.keyword.match_type = (\n client.enums.KeywordMatchTypeEnum.EXACT\n )\n\n try:\n # Try sending a mutate request to add the keyword.\n response: Any = ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n except GoogleAdsException as googleads_exception:\n # Return the exception in order to extract keyword violation details.\n return googleads_exception, ad_group_criterion_operation\n\n # Report that the mutate request was completed successfully.\n print(\n \"Added a keyword with resource name \"\n f\"'{response.results[0].resource_name}'.\"\n )\n\n return None, ad_group_criterion_operation\n\n\ndef fetch_exempt_policy_violation_keys(\n googleads_exception: GoogleAdsException,\n) -> List[PolicyViolationKey]:\n \"\"\"Collects all policy violation keys that can be exempted.\n\n Args:\n googleads_exception: The exception to check for policy violation(s).\n\n Returns:\n A list of policy violation keys.\n \"\"\"\n exempt_policy_violation_keys: List[PolicyViolationKey] = []\n\n print(\"Google Ads failure details:\")\n for error in googleads_exception.failure.errors:\n print(f\"\\t{error.error_code}: {error.message}\")\n\n if (\n error.details is not None\n and error.details.policy_violation_details is not None\n ):\n policy_violation_details = error.details.policy_violation_details\n print(\n \"\\tPolicy violation details:\\n\"\n f\"\\t\\tExternal policy name: '{policy_violation_details}'\\n\"\n \"\\t\\tExternal policy description: \"\n f\"'{policy_violation_details.external_policy_description}'\\n\"\n f\"\\t\\tIs exemptible? '{policy_violation_details.is_exemptible}'\"\n )\n\n if (\n policy_violation_details.is_exemptible\n and policy_violation_details.key is not None\n ):\n exempt_policy_violation_keys.append(\n policy_violation_details.key\n )\n print(\n f\"\\t\\tPolicy violation key: {policy_violation_details.key}\"\n )\n print(\n f\"\\t\\t\\tName: '{policy_violation_details.key.policy_name}'\"\n \"\\t\\t\\tViolating text: \"\n f\"'{policy_violation_details.key.violating_text}'\"\n )\n else:\n print(\n \"No exemption request is sent because your keyword \"\n \"contained some non-exemptible policy violations.\"\n )\n raise googleads_exception\n else:\n print(\n \"No exemption request is sent because there are non-policy \"\n \"related errors thrown.\"\n )\n raise googleads_exception\n\n return exempt_policy_violation_keys\n\n\ndef request_exemption(\n customer_id: str,\n ad_group_criterion_service: AdGroupCriterionServiceClient,\n ad_group_criterion_operation: AdGroupCriterionOperation,\n exempt_policy_violation_keys: List[PolicyViolationKey],\n) -> None:\n \"\"\"Sends exemption requests for creating a keyword.\n\n Args:\n customer_id: The customer ID for which to add the expanded text ad.\n ad_group_criterion_service: The AdGroupCriterionService client instance.\n ad_group_criterion_operation: The AdGroupCriterionOperation for which\n to request exemption.\n exempt_policy_violation_keys: The exemptible policy violation keys.\n \"\"\"\n print(\n \"Attempting to add a keyword again by requesting exemption for its \"\n \"policy violations.\"\n )\n ad_group_criterion_operation.exempt_policy_violation_keys.extend(\n exempt_policy_violation_keys\n )\n response: Any = ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id=customer_id, operations=[ad_group_criterion_operation]\n )\n print(\n \"Successfully added a keyword with resource name \"\n f\"'{response.results[0].resource_name}' by requesting a policy \"\n \"violation exemption.\"\n )\n\n\nif __name__ == \"__main__\":\n parser = argparse.ArgumentParser(\n description=\"Demonstrates how to request an exemption for policy \"\n \"violations of a keyword.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-a\",\n \"--ad_group_id\",\n type=str,\n required=True,\n help=\"The ad group ID to which to add an expanded text ad.\",\n )\n parser.add_argument(\n \"-k\",\n \"--keyword_text\",\n type=str,\n required=False,\n default=\"medication\",\n help=\"Specify the keyword text here or use the default keyword \"\n \"'medication'.\",\n )\n args = parser.parse_args()\n\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n main(\n googleads_client, args.customer_id, args.ad_group_id, args.keyword_text\n )\nhandle_keyword_policy_violations.py\n```\n\nExample:\n```text\n# Encoding: utf-8\n#\n# Copyright 2020 Google LLC\n#\n# 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.\n#\n# Demonstrates how to request an exemption for policy violations of a keyword.\n#\n# Note that the example uses an exemptible policy-violating keyword by default.\n# If you use a keyword that contains non-exemptible policy violations, they\n# will not be sent for exemption request and you will still fail to create a\n# keyword. If you specify a keyword that doesn't violate any policies, this\n# example will just add the keyword as usual, similar to what the AddKeywords\n# example does.\n#\n# Note that once you've requested policy exemption for a keyword, when you send\n# a request for adding it again, the request will pass like when you add a\n# non-violating keyword.\n\nrequire 'optparse'\nrequire 'google/ads/google_ads'\nrequire 'date'\n\ndef handle_keyword_policy_violations(customer_id, ad_group_id, keyword_text)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n ad_group_criterion_service = client.service.ad_group_criterion\n\n exception, ad_group_criterion_operation = create_keyword_criterion(\n client,\n ad_group_criterion_service,\n customer_id,\n ad_group_id,\n keyword_text,\n )\n\n unless exception.nil?\n exempt_policy_violation_keys = fetch_exempt_policy_violation_keys(exception)\n request_exemption(\n client,\n customer_id,\n ad_group_criterion_service,\n ad_group_criterion_operation,\n exempt_policy_violation_keys,\n )\n end\nend\n\ndef create_keyword_criterion(\n client, ad_group_criterion_service, customer_id, ad_group_id, keyword_text)\n ad_group_criterion_operation = client.operation.create_resource.ad_group_criterion do |agc|\n agc.ad_group = client.path.ad_group(customer_id, ad_group_id)\n agc.status = :ENABLED\n agc.keyword = client.resource.keyword_info do |ki|\n ki.match_type = :EXACT\n ki.text = keyword_text\n end\n end\n\n ignorable_policy_topics = []\n begin\n ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [ad_group_criterion_operation],\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n return e, ad_group_criterion_operation\n end\n\n return nil, ad_group_criterion_operation\nend\n\ndef fetch_exempt_policy_violation_keys(exception)\n exempt_policy_violation_keys = []\n\n exception.failure.errors.each do |error|\n details = error.details.policy_violation_details\n puts \"Policy violation details:\"\n puts \"\\tExternal policy name: #{details.external_policy_name}\"\n puts \"\\tExternal policy description:\\n#{details.external_policy_description}\"\n puts \"\\tIs exemptible: #{details.is_exemptible}\"\n\n if details.is_exemptible && !details.key.nil?\n exempt_policy_violation_keys << details.key\n puts \"Policy violation key:\"\n puts \"\\tPolicy Name: #{details.key.policy_name}\"\n puts \"\\tViolating Text: #{details.key.violating_text}\"\n else\n puts \"No exemption request will be sent because your keyword contained \"\\\n \"some non-exemptible policy violations.\"\n end\n end\n\n exempt_policy_violation_keys\nend\n\ndef request_exemption(\n client,\n customer_id,\n ad_group_criterion_service,\n ad_group_criterion_operation,\n exempt_policy_violation_keys\n)\n # Add all the found ignorable policy topics to the operation.\n ad_group_criterion_operation.exempt_policy_violation_keys.push(\n *exempt_policy_violation_keys\n )\n response = ad_group_criterion_service.mutate_ad_group_criteria(\n customer_id: customer_id,\n operations: [ad_group_criterion_operation],\n )\n puts \"Successfully added a keyword with resource name \" \\\n \"#{response.results.first.resource_name} for policy violation exception.\"\nend\n\nif __FILE__ == $PROGRAM_NAME\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:ad_group_id] = 'INSERT_AD_GROUP_ID_HERE'\n options[:keyword_text] = 'INSERT_KEYWORD_TEXT_HERE'\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: ruby %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-A', '--ad-group-id AD-GROUP-ID', String, 'Ad Group ID') do |v|\n options[:ad_group_id] = v\n end\n\n opts.on('-k', '--keyword-text KEYWORD-TEXT', String, 'Keyword') do |v|\n options[:keyword_text] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n handle_keyword_policy_violations(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options.fetch(:ad_group_id),\n options.fetch(:keyword_text),\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n raise\n end\nend\nhandle_keyword_policy_violations.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2019, Google LLC\n#\n# 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# http://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.\n#\n# This example demonstrates how to request an exemption for policy violations\n# of a keyword. Note that the example uses an exemptible policy-violating\n# keyword by default. If you use a keyword that contains non-exemptible policy\n# violations, they will not be sent for exemption request and you will still\n# fail to create a keyword.\n# If you specify a keyword that doesn't violate any policies, this example will\n# just add the keyword as usual, similar to what the add_keywords.pl example does.\n#\n# Note that once you've requested policy exemption for a keyword, when you send\n# a request for adding it again, the request will pass like when you add a\n# non-violating keyword.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion;\nuse Google::Ads::GoogleAds::V25::Common::KeywordInfo;\nuse Google::Ads::GoogleAds::V25::Enums::KeywordMatchTypeEnum qw(EXACT);\nuse Google::Ads::GoogleAds::V25::Enums::AdGroupCriterionStatusEnum qw(ENABLED);\nuse\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\nmy $ad_group_id = \"INSERT_AD_GROUP_ID_HERE\";\nmy $keyword_text = \"medication\";\n\nsub handle_keyword_policy_violations {\n my ($api_client, $customer_id, $ad_group_id, $keyword_text) = @_;\n\n # Configure the keyword text and match type settings.\n my $keyword_info = Google::Ads::GoogleAds::V25::Common::KeywordInfo->new({\n text => $keyword_text,\n matchType => EXACT\n });\n\n # Construct an ad group criterion using the keyword info above.\n my $ad_group_criterion =\n Google::Ads::GoogleAds::V25::Resources::AdGroupCriterion->new({\n adGroup => Google::Ads::GoogleAds::V25::Utils::ResourceNames::ad_group(\n $customer_id, $ad_group_id\n ),\n status => ENABLED,\n keyword => $keyword_info\n });\n\n # Create an ad group criterion operation.\n my $ad_group_criterion_operation =\n Google::Ads::GoogleAds::V25::Services::AdGroupCriterionService::AdGroupCriterionOperation\n ->new({create => $ad_group_criterion});\n\n # Try sending a mutate request to add the keyword.\n my $response = $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n if ($response->isa(\"Google::Ads::GoogleAds::GoogleAdsException\")) {\n my $exempt_policy_violation_keys =\n fetch_exempt_policy_violation_keys($response);\n\n # Try sending exemption requests for creating a keyword. However, if your\n # keyword contains many policy violations, but not all of them are exemptible,\n # the request will not be sent.\n if (@$exempt_policy_violation_keys ==\n @{$response->get_google_ads_failure()->{errors}})\n {\n request_exemption($api_client, $customer_id,\n $ad_group_criterion_operation, $exempt_policy_violation_keys);\n } else {\n print \"No exemption request is sent because 1) your keyword contained \" .\n \"some non-exemptible policy violations or 2) there are other \" .\n \"non-policy related errors thrown.\\n\";\n }\n } else {\n printf \"Added a keyword with resource name '%s'.\\n\",\n $response->{results}[0]{resourceName};\n }\n\n return 1;\n}\n\n# Collects all policy violation keys that can be exempted for sending a exemption\n# request later.\nsub fetch_exempt_policy_violation_keys {\n my $google_ads_exception = shift;\n\n my $exempt_policy_violation_keys = [];\n\n print \"Google Ads failure details:\\n\";\n foreach\n my $error (@{$google_ads_exception->get_google_ads_failure()->{errors}})\n {\n printf \"\\t%s: %s\\n\", [keys %{$error->{errorCode}}]->[0], $error->{message};\n\n if ($error->{details}{policyViolationDetails}) {\n my $policy_violation_details = $error->{details}{policyViolationDetails};\n printf \"\\tPolicy violation details:\\n\";\n printf \"\\t\\tExternal policy name: '%s'\\n\",\n $policy_violation_details->{externalPolicyName};\n printf\n \"\\t\\tExternal policy description: '%s'\\n\",\n $policy_violation_details->{externalPolicyDescription};\n printf\n \"\\t\\tIs exemptible? '%s'\\n\",\n $policy_violation_details->{isExemptible} ? \"yes\" : \"no\";\n\n if ( $policy_violation_details->{isExemptible}\n and $policy_violation_details->{key})\n {\n my $policy_violation_details_key = $policy_violation_details->{key};\n push @$exempt_policy_violation_keys, $policy_violation_details_key;\n\n printf \"\\t\\tPolicy violation key:\\n\";\n printf \"\\t\\t\\tName: '%s'\\n\",\n $policy_violation_details_key->{policyName};\n printf\n \"\\t\\t\\tViolating text: '%s'\\n\",\n $policy_violation_details_key->{violatingText};\n }\n }\n }\n\n return $exempt_policy_violation_keys;\n}\n\n# Sends exemption requests for creating a keyword.\nsub request_exemption {\n my ($api_client, $customer_id, $ad_group_criterion_operation,\n $exempt_policy_violation_keys)\n = @_;\n\n print \"Try adding a keyword again by requesting exemption for its \" .\n \"policy violations.\\n\";\n\n $ad_group_criterion_operation->{exemptPolicyViolationKeys} =\n $exempt_policy_violation_keys;\n\n my $ad_group_criteria_response =\n $api_client->AdGroupCriterionService()->mutate({\n customerId => $customer_id,\n operations => [$ad_group_criterion_operation]});\n\n printf \"Successfully added a keyword with resource name '%s' by requesting \" .\n \"for policy violation exemption.\\n\",\n $ad_group_criteria_response->{results}[0]{resourceName};\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(0);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"ad_group_id=i\" => \\$ad_group_id,\n \"keyword_text=s\" => \\$keyword_text\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2) if not check_params($customer_id, $ad_group_id, $keyword_text);\n\n# Call the example.\nhandle_keyword_policy_violations($api_client, $customer_id =~ s/-//gr,\n $ad_group_id, $keyword_text);\n\n=pod\n\n=head1 NAME\n\nhandle_keyword_policy_violations\n\n=head1 DESCRIPTION\n\nThis example demonstrates how to request an exemption for policy violations of a keyword.\nNote that the example uses an exemptible policy-violating keyword by default. If you use\na keyword that contains non-exemptible policy violations, they will not be sent for\nexemption request and you will still fail to create a keyword.\nIf you specify a keyword that doesn't violate any policies, this example will just add the\nkeyword as usual, similar to what the add_keywords.pl example does.\n\nNote that once you've requested policy exemption for a keyword, when you send a request for\nadding it again, the request will pass like when you add a non-violating keyword.\n\n=head1 SYNOPSIS\n\nhandle_keyword_policy_violations.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -ad_group_id The ad group ID.\n -keyword_text [optional] The keyword to be added to the ad group.\n\n=cut\nhandle_keyword_policy_violations.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.639Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":2920,"estimatedTokens":28933}}249{"id":"doc-free_listings_content_api_for_shopping_google_fo-87fff98d","source":"documentation","title":"Free listings | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/review-free-listings","text":"Example:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/merchantId/freelistingprogram\n```\n\nExample:\n```text\n{\n \"globalState\": \"NOT_ENABLED\",\n \"regionStatuses\": [\n {\n \"regionCodes\": [\n \"US\"\n ],\n \"eligibilityStatus\": \"DISAPPROVED\",\n \"reviewIssues\": [\n \"editorial_and_professional_standards_destination_url_down_policy\"\n ],\n \"onboardingIssues\": [\n \"home_page_issue\"\n ],\n \"disapprovalDate\": \"2013-02-25\",\n \"reviewEligibilityStatus\": \"INELIGIBLE\",\n \"reviewIneligibilityReason\": \"IN_COOLDOWN_PERIOD\",\n \"reviewIneligibilityReasonDescription\": \"Cool down applies: Wait for one-week cool-down period to end before you can request a review from Google. End of cool down is 2013-02-30\",\n \"reviewIneligibilityReasonDetails\": {\n \"cooldownTime\": \"2013-02-30\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/merchantId/freelistingsprogram/requestreview\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.641Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":39,"estimatedTokens":254}}250{"id":"doc-issue_severity_and_merchant_center_diagnostics_g-7ee2bd23","source":"documentation","title":"Issue severity and Merchant Center Diagnostics | Google for Developers","url":"https://developers.google.com/shopping-content/guides/how-tos/severity-mapping","text":"Example:\n```text\n{\n \"kind\": \"content#accountStatus\",\n \"accountId\": \"...\",\n \"accountLevelIssues\": [\n {\n \"id\": \"editorial_and_professional_standards_destination_url_down_policy\",\n \"title\": \"Account suspended due to policy violation: landing page not working\",\n \"country\": \"US\",\n \"severity\": \"critical\",\n \"documentation\": \"https://support.google.com/merchants/answer/6150244#wycd-usefulness\"\n },\n {\n \"id\": \"missing_ad_words_link\",\n \"title\": \"No Google Ads account linked\",\n \"severity\": \"error\",\n \"documentation\": \"https://support.google.com/merchants/answer/6159060\"\n }\n ],\n \"products\": [\n {\n \"channel\": \"online\",\n \"destination\": \"Shopping\",\n \"country\": \"US\",\n \"statistics\": {\n \"active\": \"0\",\n \"pending\": \"0\",\n \"disapproved\": \"5\",\n \"expiring\": \"0\"\n },\n \"itemLevelIssues\": [\n {\n \"code\": \"image_link_broken\",\n \"servability\": \"disapproved\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"image link\",\n \"description\": \"Invalid image [image link]\",\n \"detail\": \"Ensure the image is accessible and uses an accepted image format (JPEG, PNG, GIF)\",\n \"documentation\": \"https://support.google.com/merchants/answer/6098289\",\n \"numItems\": \"2\"\n },\n {\n \"code\": \"landing_page_error\",\n \"servability\": \"disapproved\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"link\",\n \"description\": \"Unavailable desktop landing page\",\n \"detail\": \"Update your website or landing page URL to enable access from desktop devices\",\n \"documentation\": \"https://support.google.com/merchants/answer/6098155\",\n \"numItems\": \"5\"\n }\n ]\n },\n ...\n}\n```\n\nExample:\n```text\n{\n \"kind\": \"content#productstatusesListResponse\",\n ...\n \"resources\": [\n {\n \"kind\": \"content#productStatus\",\n \"productId\": \"online:en:US:online-en-US-GGL614\",\n ...\n \"itemLevelIssues\": [\n {\n \"code\": \"mobile_landing_page_crawling_not_allowed\",\n \"servability\": \"disapproved\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"link\",\n \"destination\": \"Shopping\",\n \"description\": \"Mobile page not crawlable due to robots.txt\",\n \"detail\": \"Update your robots.txt file to allow user-agents \\\"Googlebot\\\" and \\\"Googlebot-Image\\\" to crawl your site\",\n \"documentation\": \"https://support.google.com/merchants/answer/6098296\"\n },\n {\n \"code\": \"pending_initial_policy_review\",\n \"servability\": \"disapproved\",\n \"resolution\": \"pending_processing\",\n \"destination\": \"Shopping\",\n \"description\": \"Pending initial review\",\n \"documentation\": \"https://support.google.com/merchants/answer/2948694\"\n },\n {\n \"code\": \"ambiguous_gtin\",\n \"servability\": \"unaffected\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"gtin\",\n \"destination\": \"Shopping\",\n \"description\": \"Ambiguous value [gtin]\",\n \"detail\": \"Use the full GTIN. Include leading zeroes, and use the full UPC, EAN, JAN, ISBN-13, or ITF-14.\",\n \"documentation\": \"https://support.google.com/merchants/answer/7000891\"\n }\n ],\n ...\n },\n ...\n ]\n }\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.642Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":106,"estimatedTokens":841}}251{"id":"doc-make_requests_content_api_for_shopping_google_fo-bf3dd59e","source":"documentation","title":"Make requests | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/making-requests","text":"Example:\n```text\nPOST /content/v2.1/YOUR_MERCHANT_ID/products\n\n{\n \"offerId\": \"book123\",\n \"title\": \"A Tale of Two Cities\",\n \"description\": \"A classic novel about the French Revolution\",\n \"link\": \"http://my-book-shop.com/tale-of-two-cities.html\",\n \"imageLink\": \"http://my-book-shop.com/tale-of-two-cities.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"GB\",\n \"feedLabel\": \"GB\",\n \"channel\": \"online\",\n \"availability\": \"in stock\",\n \"condition\": \"new\",\n \"googleProductCategory\": \"Media > Books\",\n \"gtin\": \"9780007350896\",\n \"price\": {\n \"value\": \"2.50\",\n \"currency\": \"GBP\"\n },\n \"shipping\": [{\n \"country\": \"GB\",\n \"service\": \"Standard shipping\",\n \"price\": {\n \"value\": \"0.99\",\n \"currency\": \"GBP\"\n }\n }],\n \"shippingWeight\": {\n \"value\": \"200\",\n \"unit\": \"grams\"\n }\n}\n```\n\nExample:\n```text\nProduct product = new Product();\n\nproduct.setOfferId(\"book123\");\nproduct.setTitle(\"A Tale of Two Cities\");\nproduct.setDescription(\"A classic novel about the French Revolution\");\nproduct.setLink(\"http://my-book-shop.com/tale-of-two-cities.html\");\nproduct.setImageLink(\"http://my-book-shop.com/tale-of-two-cities.jpg\");\nproduct.setContentLanguage(\"en\");\nproduct.setTargetCountry(\"GB\");\nproduct.setChannel(\"online\");\nproduct.setAvailability(\"in stock\");\nproduct.setCondition(\"new\");\nproduct.setGoogleProductCategory(\"Media > Books\");\nproduct.setGtin(\"9780007350896\");\n\nPrice price = new Price();\nprice.setValue(\"2.50\");\nprice.setCurrency(\"GBP\");\nproduct.setPrice(price);\n\nPrice shippingPrice = new Price();\nshippingPrice.setValue(\"0.99\");\nshippingPrice.setCurrency(\"GBP\");\n\nProductShipping shipping = new ProductShipping();\nshipping.setPrice(shippingPrice);\nshipping.setCountry(\"GB\");\nshipping.setService(\"Standard shipping\");\n\nArrayList shippingList = new ArrayList();\nshippingList.add(shipping);\nproduct.setShipping(shippingList);\n\nProduct result = service.products().insert(merchantId, product).execute();\n```\n\nExample:\n```text\n$product = new Google_Service_ShoppingContent_Product();\n$product->setOfferId('book123');\n$product->setTitle('A Tale of Two Cities');\n$product->setDescription('A classic novel about the French Revolution');\n$product->setLink('http://my-book-shop.com/tale-of-two-cities.html');\n$product->setImageLink('http://my-book-shop.com/tale-of-two-cities.jpg');\n$product->setContentLanguage('en');\n$product->setTargetCountry('GB');\n$product->setChannel('online');\n$product->setAvailability('in stock');\n$product->setCondition('new');\n$product->setGoogleProductCategory('Media > Books');\n$product->setGtin('9780007350896');\n\n$price = new Google_Service_ShoppingContent_Price();\n$price->setValue('2.50');\n$price->setCurrency('GBP');\n\n$shipping_price = new Google_Service_ShoppingContent_Price();\n$shipping_price->setValue('0.99');\n$shipping_price->setCurrency('GBP');\n\n$shipping = new Google_Service_ShoppingContent_ProductShipping();\n$shipping->setPrice($shipping_price);\n$shipping->setCountry('GB');\n$shipping->setService('Standard shipping');\n\n$shipping_weight = new Google_Service_ShoppingContent_ProductShippingWeight();\n$shipping_weight->setValue(200);\n$shipping_weight->setUnit('grams');\n\n$product->setPrice($price);\n$product->setShipping(array($shipping));\n$product->setShippingWeight($shipping_weight);\n\n$result = $service->products->insert($merchant_id, $product);\n```\n\nExample:\n```text\nGET /content/v2.1/YOUR_MERCHANT_ID/products\n```\n\nExample:\n```text\nList productsList = service.products().list(merchantId);\n\nProductsListResponse page = productsList.execute();\nwhile ((page.getResources() != null) && !page.getResources().isEmpty()) {\n for (Product product : page.getResources()) {\n System.out.printf(\"%s %s%n\", product.getId(), product.getTitle());\n }\n\n if (page.getNextPageToken() == null) {\n break;\n }\n\n productsList.setPageToken(page.getNextPageToken());\n page = productsList.execute();\n}\n```\n\nExample:\n```text\n$products = $service->products->listProducts($merchantId);\n$parameters = array();\nwhile (!empty($products->getResources()) {\n foreach ($products->getResources() as $product) {\n printf(\"%s %s\\n\", $product->getId(), $product->getTitle());\n }\n if (!empty($products->getNextPageToken()) {\n break;\n }\n $parameters['pageToken'] = $products->nextPageToken;\n $products = $service->products->listProducts($merchantId, $parameters);\n}\n```\n\nExample:\n```text\nGET /content/v2.1/YOUR_MERCHANT_ID/products/online:en:GB:book123\n```\n\nExample:\n```text\nProduct product = service.products()\n .get(merchantId, \"online:en:GB:book123\")\n .execute();\nSystem.out.printf(\"%s %s\\n\", product.getId(), product.getTitle());\n```\n\nExample:\n```text\n$product = $service->products->get($merchant_id, 'online:en:GB:book123');\nprintf(\"%s %s\\n\", $product->getId(), $product->getTitle());\n```\n\nExample:\n```text\nPOST /content/v2.1/YOUR_MERCHANT_ID/products?YOUR_SUPPLEMENTAL_FEED_ID\n\n{\n \"offerId\": \"book123\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"GB\",\n \"feedLabel\": \"GB\",\n \"channel\": \"online\",\n \"availability\": \"out of stock\"\n}\n```\n\nExample:\n```text\nProduct product = new Product();\n// Mandatory Fields\nproduct.setOfferId(\"book123\");\nproduct.setContentLanguage(\"en\");\nproduct.setTargetCountry(\"GB\");\nproduct.setChannel(\"online\");\n\n// Optional Fields to Update\nproduct.setAvailability(\"out of stock\");\n\n// Your unique supplemental feedId\nfeedId=123456789\n\nProduct result = service.products().insert(merchantId, product, feedId).execute();\n```\n\nExample:\n```text\n$product = new Google_Service_ShoppingContent_Product();\n// Mandatory Fields\n$product->setOfferId('book123');\n$product->setContentLanguage('en');\n$product->setTargetCountry('GB');\n$product->setChannel('online');\n\n// Optional Fields to Update\n$product->setAvailability('out of stock');\n\n// Your unique supplemental feedId\n$feedId=123456789\n\n$result = $service->products->insert($merchant_id, $product, $feedId);\n```\n\nExample:\n```text\nPOST /content/v2.1/YOUR_MERCHANT_ID/localinventory/online/products/online:en:GB:book123\n\n{\n \"availability\": \"out of stock\"\n}\n```\n\nExample:\n```text\nProduct product = new Product();\n// Mandatory Fields\nproduct.setOfferId(\"book123\");\nproduct.setContentLanguage(\"en\");\nproduct.setTargetCountry(\"GB\");\nproduct.setChannel(\"online\");\n\n// Optional Fields to Update\nproduct.setAvailability(\"out of stock\");\n\nProduct result = service.localinventory().insert(merchantId, product).execute();\n```\n\nExample:\n```text\n$product = new Google_Service_ShoppingContent_Product();\n// Mandatory Fields\n$product->setOfferId('book123');\n$product->setContentLanguage('en');\n$product->setTargetCountry('GB');\n$product->setChannel('online');\n\n// Optional Fields to Update\n$product->setAvailability('out of stock');\n\n$result = $service->localinventory->insert($merchant_id, $product);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.643Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":264,"estimatedTokens":1686}}252{"id":"doc-performance_tips_content_api_for_shopping_google-3cb3a20a","source":"documentation","title":"Performance tips | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/how-tos/performance","text":"Example:\n```text\nAccept-Encoding: gzip\nUser-Agent: my program (gzip)\n```\n\nExample:\n```text\nhttps://www.googleapis.com/demo/v1\n```\n\nExample:\n```text\n{\n \"kind\": \"demo\",\n ...\n \"items\": [\n {\n \"title\": \"First title\",\n \"comment\": \"First comment.\",\n \"characteristics\": {\n \"length\": \"short\",\n \"accuracy\": \"high\",\n \"followers\": [\"Jo\", \"Will\"],\n },\n \"status\": \"active\",\n ...\n },\n {\n \"title\": \"Second title\",\n \"comment\": \"Second comment.\",\n \"characteristics\": {\n \"length\": \"long\",\n \"accuracy\": \"medium\"\n \"followers\": [ ],\n },\n \"status\": \"pending\",\n ...\n },\n ...\n ]\n}\n```\n\nExample:\n```text\nhttps://www.googleapis.com/demo/v1?fields=kind,items(title,characteristics/length)\n```\n\nExample:\n```text\n200 OK\n```\n\nExample:\n```text\n{\n \"kind\": \"demo\",\n \"items\": [{\n \"title\": \"First title\",\n \"characteristics\": {\n \"length\": \"short\"\n }\n }, {\n \"title\": \"Second title\",\n \"characteristics\": {\n \"length\": \"long\"\n }\n },\n ...\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.643Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":75,"estimatedTokens":257}}253{"id":"doc-add_a_checkout_link_to_your_free_listings_conten-3fb2965f","source":"documentation","title":"Add a checkout link to your free listings | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/add-checkout-link","text":"Example:\n```text\nhttps://mystore.com/path-to-product/{id}\n```\n\nExample:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/freelistingsprogram/checkoutsettings\n```\n\nExample:\n```text\n{\n uri_settings: {\n checkout_uri_template: \"https://domain_name.com/custom_path/{id}\"\n }\n}\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/freelistingsprogram/checkoutsettings\n```\n\nExample:\n```text\nDELETE https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/freelistingsprogram/checkoutsettings\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.644Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":30,"estimatedTokens":146}}254{"id":"doc-shopping_ads_content_api_for_shopping_google_for-8c19041d","source":"documentation","title":"Shopping ads | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/review-shopping-ads","text":"Example:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/merchantId/shoppingadsprogram\n```\n\nExample:\n```text\n{\n \"globalState\": \"NOT_ENABLED\",\n \"regionStatuses\": [\n {\n \"regionCodes\": [\n \"US\"\n ],\n \"eligibilityStatus\": \"DISAPPROVED\",\n \"reviewIssues\": [\n \"editorial_and_professional_standards_destination_url_down_policy\"\n ],\n \"onboardingIssues\": [\n \"home_page_issue\"\n ],\n \"disapprovalDate\": \"2013-02-25\",\n \"reviewEligibilityStatus\": \"INELIGIBLE\",\n \"reviewIneligibilityReason\": \"IN_COOLDOWN_PERIOD\",\n \"reviewIneligibilityReasonDescription\": \"Cool down applies: Wait for one-week cool-down period to end before you can request a review from Google. End of cool down is 2013-02-30\",\n \"reviewIneligibilityReasonDetails\": {\n \"cooldownTime\": \"2013-02-30\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/merchantId/shoppingadsprogram/requestreview\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.645Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":39,"estimatedTokens":254}}255{"id":"doc-logging_google_ads_api_google_for_developers-488b2b65","source":"documentation","title":"Logging | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/productionize/logging","text":"Example:\n```text\nGoogleAds.SummaryRequestLogs Warning: 1 : [2023-09-15 19:58:39Z] -\nRequest made: Host: , Method: /google.ads.googleads.v24.services.GoogleAdsService/SearchStream,\nClientCustomerID: 5951878031, RequestID: hELhBPNlEDd8mWYcZu7b8g,\nIsFault: True, FaultMessage: Status(StatusCode=\"InvalidArgument\",\nDetail=\"Request contains an invalid argument.\")\n```\n\nExample:\n```text\nGoogleAds.DetailedRequestLogs Verbose: 1 : [2023-11-02 21:09:36Z] -\n---------------BEGIN API CALL---------------\n\nRequest\n-------\n\nMethod Name: /google.ads.googleads.v24.services.GoogleAdsService/SearchStream\nHost:\nHeaders: {\n \"x-goog-api-client\": \"gl-dotnet/5.0.0 gapic/17.0.1 gax/4.2.0 grpc/2.46.3 gccl/3.0.1 pb/3.21.5\",\n \"developer-token\": \"REDACTED\",\n \"login-customer-id\": \"1234567890\",\n \"x-goog-request-params\": \"customer_id=4567890123\"\n}\n\n{ \"customerId\": \"4567890123\", \"query\": \"SELECT ad_group_criterion.type FROM\n ad_group_criterion WHERE ad_group.status IN(ENABLED, PAUSED) AND\n campaign.status IN(ENABLED, PAUSED) \", \"summaryRowSetting\": \"NO_SUMMARY_ROW\" }\n\nResponse\n--------\nHeaders: {\n \"date\": \"Thu, 02 Nov 2023 21:09:35 GMT\",\n \"alt-svc\": \"h3-29=\\\":443\\\"; ma=2592000\"\n}\n\n{\n \"results\": [ {\n \"adGroupCriterion\": {\n \"resourceName\": \"customers/4567890123/adGroupCriteria/456789456789~123456123467\",\n \"type\": \"KEYWORD\"\n } }, {\n \"adGroupCriterion\": {\n \"resourceName\": \"customers/4567890123/adGroupCriteria/456789456789~56789056788\",\n \"type\": \"KEYWORD\"\n } } ],\n \"fieldMask\": \"adGroupCriterion.type\", \"requestId\": \"VsJ4F00ew6s9heHvAJ-abw\"\n}\n----------------END API CALL----------------\n```\n\nExample:\n```text\n# Copyright 2022 Google LLC\n#\n# 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.\n\"\"\"A custom gRPC Interceptor that logs requests and responses to Cloud Logging.\n\nThe custom interceptor object is passed into the get_service method of the\nGoogleAdsClient. It intercepts requests and responses, parses them into a\nhuman readable structure and logs them using the logging service instantiated\nwithin the class (in this case, a Cloud Logging client).\n\"\"\"\n\nimport logging\nimport sys\nimport time\nfrom typing import Any, Callable, Dict, Optional\n\nfrom google.cloud import logging as google_cloud_logging\nfrom grpc._interceptor import _ClientCallDetails\n\nfrom google.ads.googleads.interceptors import LoggingInterceptor\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\nclass CloudLoggingInterceptor(LoggingInterceptor):\n \"\"\"An interceptor that logs rpc request and response details to Google Cloud Logging.\n\n This class inherits logic from the LoggingInterceptor, which simplifies the\n implementation here. Some logic is required here in order to make the\n underlying logic work -- comments make note of this where applicable.\n NOTE: Inheriting from the LoggingInterceptor class could yield unexpected side\n effects. For example, if the LoggingInterceptor class is updated, this class would\n inherit the updated logic, which could affect its functionality. One option to avoid\n this is to inherit from the Interceptor class instead, and selectively copy whatever\n logic is needed from the LoggingInterceptor class.\"\"\"\n\n def __init__(self, api_version: str):\n \"\"\"Initializer for the CloudLoggingInterceptor.\n\n Args:\n api_version: a str of the API version of the request.\n \"\"\"\n super().__init__(logger=None, api_version=api_version)\n # Instantiate the Cloud Logging client.\n logging_client: google_cloud_logging.Client = google_cloud_logging.Client()\n self.logger: google_cloud_logging.Logger = logging_client.logger(\"cloud_logging\")\n self.rpc_start: float\n self.rpc_end: float\n\n def log_successful_request(\n self,\n method: str,\n customer_id: Optional[str],\n metadata_json: str,\n request_id: str,\n request: Any, # google.ads.googleads.vX.services.types.SearchGoogleAdsRequest or SearchGoogleAdsStreamRequest\n trailing_metadata_json: str,\n response: Any, # grpc.Call or grpc.Future\n ) -> None:\n \"\"\"Handles logging of a successful request.\n\n Args:\n method: The method of the request.\n customer_id: The customer ID associated with the request.\n metadata_json: A JSON str of initial_metadata.\n request_id: A unique ID for the request provided in the response.\n request: An instance of a request proto message.\n trailing_metadata_json: A JSON str of trailing_metadata.\n response: A grpc.Call/grpc.Future instance.\n \"\"\"\n # Retrieve and mask the RPC result from the response future.\n # This method is available from the LoggingInterceptor class.\n # Ensure self._cache is set in order for this to work.\n # The response result could contain up to 10,000 rows of data,\n # so consider truncating this value before logging it, to save\n # on data storage costs and maintain readability.\n result: Any = self.retrieve_and_mask_result(response)\n\n # elapsed_ms is the approximate elapsed time of the RPC, in milliseconds.\n # There are different ways to define and measure elapsed time, so use\n # whatever approach makes sense for your monitoring purposes.\n # rpc_start and rpc_end are set in the intercept_unary_* methods below.\n elapsed_ms: float = (self.rpc_end - self.rpc_start) * 1000\n\n debug_log: Dict[str, Any] = {\n \"method\": method,\n \"host\": metadata_json,\n \"request_id\": request_id,\n \"request\": str(request),\n \"headers\": trailing_metadata_json,\n \"response\": str(result),\n \"is_fault\": False,\n \"elapsed_ms\": elapsed_ms,\n }\n self.logger.log_struct(debug_log, severity=\"DEBUG\")\n\n info_log: Dict[str, Any] = {\n \"customer_id\": customer_id,\n \"method\": method,\n \"request_id\": request_id,\n \"is_fault\": False,\n # Available from the Interceptor class.\n \"api_version\": self._api_version,\n }\n self.logger.log_struct(info_log, severity=\"INFO\")\n\n def log_failed_request(\n self,\n method: str,\n customer_id: Optional[str],\n metadata_json: str,\n request_id: str,\n request: Any, # google.ads.googleads.vX.services.types.SearchGoogleAdsRequest or SearchGoogleAdsStreamRequest\n trailing_metadata_json: str,\n response: Any, # grpc.Call or grpc.Future\n ) -> None:\n \"\"\"Handles logging of a failed request.\n\n Args:\n method: The method of the request.\n customer_id: The customer ID associated with the request.\n metadata_json: A JSON str of initial_metadata.\n request_id: A unique ID for the request provided in the response.\n request: An instance of a request proto message.\n trailing_metadata_json: A JSON str of trailing_metadata.\n response: A JSON str of the response message.\n \"\"\"\n exception: Any = self._get_error_from_response(response)\n exception_str: str = self._parse_exception_to_str(exception)\n fault_message: str = self._get_fault_message(exception)\n\n info_log: Dict[str, Any] = {\n \"method\": method,\n \"endpoint\": self.endpoint,\n \"host\": metadata_json,\n \"request_id\": request_id,\n \"request\": str(request),\n \"headers\": trailing_metadata_json,\n \"exception\": exception_str,\n \"is_fault\": True,\n }\n self.logger.log_struct(info_log, severity=\"INFO\")\n\n error_log: Dict[str, Any] = {\n \"method\": method,\n \"endpoint\": self.endpoint,\n \"request_id\": request_id,\n \"customer_id\": customer_id,\n \"is_fault\": True,\n \"fault_message\": fault_message,\n }\n self.logger.log_struct(error_log, severity=\"ERROR\")\n\n def intercept_unary_unary(\n self,\n continuation: Callable[[_ClientCallDetails, Any], Any], # Any is request type\n client_call_details: _ClientCallDetails,\n request: Any, # google.ads.googleads.vX.services.types.SearchGoogleAdsRequest\n ) -> Any: # grpc.Call or grpc.Future\n \"\"\"Intercepts and logs API interactions.\n\n Overrides abstract method defined in grpc.UnaryUnaryClientInterceptor.\n\n Args:\n continuation: a function to continue the request process.\n client_call_details: a grpc._interceptor._ClientCallDetails\n instance containing request metadata.\n request: a SearchGoogleAdsRequest or SearchGoogleAdsStreamRequest\n message class instance.\n\n Returns:\n A grpc.Call/grpc.Future instance representing a service response.\n \"\"\"\n # Set the rpc_end value to current time when RPC completes.\n def update_rpc_end(response_future: Any) -> None: # response_future is grpc.Future\n self.rpc_end = time.perf_counter()\n\n # Capture precise clock time to later calculate approximate elapsed\n # time of the RPC.\n self.rpc_start = time.perf_counter()\n\n # The below call is REQUIRED.\n response: Any = continuation(client_call_details, request) # response is grpc.Call or grpc.Future\n\n response.add_done_callback(update_rpc_end)\n\n self.log_request(client_call_details, request, response)\n\n # The below return is REQUIRED.\n return response\n\n def intercept_unary_stream(\n self,\n continuation: Callable[[_ClientCallDetails, Any], Any], # Any is request type\n client_call_details: _ClientCallDetails,\n request: Any, # google.ads.googleads.vX.services.types.SearchGoogleAdsStreamRequest\n ) -> Any: # grpc.Call or grpc.Future\n \"\"\"Intercepts and logs API interactions for Unary-Stream requests.\n\n Overrides abstract method defined in grpc.UnaryStreamClientInterceptor.\n\n Args:\n continuation: a function to continue the request process.\n client_call_details: a grpc._interceptor._ClientCallDetails\n instance containing request metadata.\n request: a SearchGoogleAdsRequest or SearchGoogleAdsStreamRequest\n message class instance.\n\n Returns:\n A grpc.Call/grpc.Future instance representing a service response.\n \"\"\"\n\n def on_rpc_complete(response_future: Any) -> None: # response_future is grpc.Future\n self.rpc_end = time.perf_counter()\n self.log_request(client_call_details, request, response_future)\n\n # Capture precise clock time to later calculate approximate elapsed\n # time of the RPC.\n self.rpc_start = time.perf_counter()\n\n # The below call is REQUIRED.\n response: Any = continuation(client_call_details, request) # response is grpc.Call or grpc.Future\n\n # Set self._cache to the cache on the response wrapper in order to\n # access the streaming logs. This is REQUIRED in order to log streaming\n # requests.\n self._cache = response.get_cache()\n\n response.add_done_callback(on_rpc_complete)\n\n # The below return is REQUIRED.\n return response\ncloud_logging_interceptor.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.647Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":301,"estimatedTokens":2984}}256{"id":"doc-display_issues_and_solutions_to_merchants_conten-bce68988","source":"documentation","title":"Display issues and solutions to merchants | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/merchant-support","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/merchantsupport/renderaccountissues?timeZone=America/Los_Angeles&languageCode=en-GB {}\n```\n\nExample:\n```text\n{\n \"issues\": [\n {\n \"title\": \"Misrepresentation\",\n \"impact\": {\n \"message\": \"Prevents all products from showing in all countries\",\n \"severity\": \"ERROR\",\n \"breakdowns\": [\n {\n \"regions\": [\n {\n \"code\": \"001\",\n \"name\": \"All countries\"\n }\n ],\n \"details\": [\n \"Products not showing organically\"\n ]\n }\n ]\n },\n \"prerenderedContent\": \"\\u003cdiv class=\\\"issue-detail\\\"\\u003e\\u003cdiv class=\\\"issue-content\\\"\\u003e\\u003cp class=\\\"content-element\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eBased on the information available about your business, there is reason to believe that customers are being misled on Google. Review the Misrepresentation policy and make changes to your Merchant Center and/or online store.u003c/span\\u003e\\u003c/p\\u003e\\u003cp class=\\\"content-element root-causes-intro\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eMake sure your Merchant Center and online store follow the following best practices / guidelines\\u003c/span\\u003e\\u003c/p\\u003e\\u003cul class=\\\"content-element root-causes\\\"\\u003e\\u003cli\\u003e\\u003cp\\u003e\\u003cspan class=\\\"segment\\\"\\u003eProvide transparency about your business identity, business model, policies and how your customers can interact with you\\u003c/span\\u003e\\u003c/p\\u003e\\u003c/li\\u003e\\u003cli\\u003e\\u003cp\\u003e\\u003cspan class=\\\"segment\\\"\\u003ePromote your online reputation by showing reviews or highlighting any badges or seals of approval\\u003c/span\\u003e\\u003c/p\\u003e\\u003c/li\\u003e\\u003cli\\u003e\\u003cp class=\\\"tooltip tooltip-style-info\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eUse a professional design for your online store that includes an SSL certificate\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-icon\\\"\\u003e\\u003cbr\\u003e\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-text\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eMake sure it's accessible for all users without any redirects and doesn't have any placeholders for text and images.u003c/span\\u003e\\u003c/span\\u003e\\u003c/p\\u003e\\u003c/li\\u003e\\u003cli\\u003e\\u003cp class=\\\"tooltip tooltip-style-info\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eProvide information in the business information settings in your Merchant Center\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-icon\\\"\\u003e\\u003cbr\\u003e\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-text\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eLink any relevant third-party platforms to your Merchant Center and create a Google Business Profile.u003c/span\\u003e\\u003c/span\\u003e\\u003c/p\\u003e\\u003c/li\\u003e\\u003cli\\u003e\\u003cp\\u003e\\u003cspan class=\\\"segment\\\"\\u003eFollow SEO guidelines, improve your eligibility for seller ratings and match your product data in your Merchant Center with your online store\\u003c/span\\u003e\\u003c/p\\u003e\\u003c/li\\u003e\\u003c/ul\\u003e\\u003ca href=\\\"https://support.google.com/merchants/answer/6150127?hl=en-US\\\" class=\\\"content-element\\\"\\u003eLearn more about the Misrepresentation policy\\u003c/a\\u003e\\u003c/div\\u003e\\u003c/div\\u003e\",\n \"actions\": [\n {\n \"externalAction\": {\n \"type\": \"REVIEW_ACCOUNT_ISSUE_IN_MERCHANT_CENTER\",\n \"uri\": \"https://merchants.google.com/mc/products/diagnostics/accountissues?a=672911686&hl=en-US\"\n },\n \"buttonLabel\": \"Request review\",\n \"isAvailable\": true\n }\n ],\n \"prerenderedOutOfCourtDisputeSettlement\": \"\\u003cdetails class=\\\"ods-section\\\"\\u003e\\u003csummary\\u003eShow additional options available to you\\u003c/summary\\u003e\\u003cp class=\\\"ods-description\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eYou may have the option to request an external appeal. You'll also be asked to provide your routing and reference IDs.\\u003c/span\\u003e \\u003ca href=\\\"https://support.google.com/european-union-digital-services-act-redress-options?hl=en-GB\\\" target=\\\"_blank\\\" class=\\\"segment\\\"\\u003eLearn more about external appeals\\u003c/a\\u003e\\u003c/p\\u003e\\u003cp class=\\\"ods-param ods-routing-id\\\"\\u003e\\u003cspan class=\\\"segment ods-param-header\\\"\\u003eRouting ID:\\u003c/span\\u003e \\u003cspan class=\\\"segment ods-param-value\\\"\\u003eRDAX\\u003c/span\\u003e\\u003c/p\\u003e\\u003cp class=\\\"ods-param ods-reference-id\\\"\\u003e\\u003cspan class=\\\"segment ods-param-header\\\"\\u003eReference ID:\\u003c/span\\u003e \\u003cspan class=\\\"segment ods-param-value\\\"\\u003e672911686\\u003c/span\\u003e\\u003c/p\\u003e\\u003c/details\\u003e\"\n },\n {\n \"title\": \"Adult-oriented content\",\n \"impact\": {\n \"message\": \"Prevents all products from showing in all countries\",\n \"severity\": \"ERROR\",\n \"breakdowns\": [\n {\n \"regions\": [\n {\n \"code\": \"001\",\n \"name\": \"All countries\"\n }\n ],\n \"details\": [\n \"Products not showing organically\"\n ]\n }\n ]\n },\n \"prerenderedContent\": \"\\u003cdiv class=\\\"issue-detail\\\"\\u003e\\u003cdiv class=\\\"callout-banners\\\"\\u003e\\u003cdiv class=\\\"callout-banner callout-banner-info\\\"\\u003e\\u003cp\\u003e\\u003cspan class=\\\"segment\\\"\\u003eReview requested on Aug 9, 2023. It can take a few days to complete.u003c/span\\u003e\\u003c/p\\u003e\\u003c/div\\u003e\\u003c/div\\u003e\\u003cdiv class=\\\"issue-content\\\"\\u003e\\u003cp class=\\\"content-element\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eThere was a problem identified with the sale of prohibited adult products on your online store. In the case that you are intentionally selling adult items, enable Adult content in Settings in your Merchant Center. In your product file, use the \\u003c/span\\u003e\\u003cspan class=\\\"segment segment-attribute\\\"\\u003eadult\\u003c/span\\u003e\\u003cspan class=\\\"segment\\\"\\u003e attribute for specific products.u003c/span\\u003e\\u003c/p\\u003e\\u003cp class=\\\"content-element root-causes-intro\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eMake sure the products meet the policy requirements\\u003c/span\\u003e\\u003c/p\\u003e\\u003cul class=\\\"content-element root-causes\\\"\\u003e\\u003cli\\u003e\\u003cp class=\\\"tooltip tooltip-style-info\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eAdult oriented content may be prohibited or restricted depending on the product sold and the country it is sold\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-icon\\\"\\u003e\\u003cbr\\u003e\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-text\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eSee a full list of countries in the HelpCenter\\u003c/span\\u003e\\u003c/span\\u003e\\u003c/p\\u003e\\u003c/li\\u003e\\u003cli\\u003e\\u003cp class=\\\"tooltip tooltip-style-info\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eDon't list sexually explicit content that is intended to arouse or includes content such as text, image, audio, or video of graphic sexual acts intended to arouse\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-icon\\\"\\u003e\\u003cbr\\u003e\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-text\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eExamples: Graphic depictions of sexual acts in progress, including hardcore pornography, any type of genital, anal, or oral sexual activity; graphic depictions of masturbation or genital arousal and language explicitly referencing arousal, masturbation, cartoon porn, or hentai\\u003c/span\\u003e\\u003c/span\\u003e\\u003c/p\\u003e\\u003c/li\\u003e\\u003c/ul\\u003e\\u003ca href=\\\"https://support.google.com/merchants/answer/6150138?hl=en-US#wycd-restricted-adult-content\\\" class=\\\"content-element\\\"\\u003eLearn more about the Adult-oriented content policy\\u003c/a\\u003e\\u003c/div\\u003e\\u003c/div\\u003e\"\n },\n {\n \"title\": \"Missing return and refund policy\",\n \"impact\": {\n \"message\": \"Limits visibility of all products in all countries\",\n \"severity\": \"ERROR\",\n \"breakdowns\": [\n {\n \"regions\": [\n {\n \"code\": \"001\",\n \"name\": \"All countries\"\n }\n ],\n \"details\": [\n \"Limited visibility for products showing organically\"\n ]\n }\n ]\n },\n \"prerenderedContent\": \"\\u003cdiv class=\\\"issue-detail\\\"\\u003e\\u003cdiv class=\\\"issue-content\\\"\\u003e\\u003cp class=\\\"content-element\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eThere was a problem identified with the return and/or refund policy of your online store. Update your return or refund policy to provide customers a transparent shopping experience.u003c/span\\u003e\\u003c/p\\u003e\\u003cp class=\\\"content-element root-causes-intro\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eMake sure your products meet the Shopping policy requirements\\u003c/span\\u003e\\u003c/p\\u003e\\u003cul class=\\\"content-element root-causes\\\"\\u003e\\u003cli\\u003e\\u003cp class=\\\"tooltip tooltip-style-info\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eIt's available on your online store\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-icon\\\"\\u003e\\u003cbr\\u003e\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-text\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eWe recommend that you have a separate landing page for your policy and link to it from the other pages on your online store, so that it's easy to find.u003c/span\\u003e\\u003c/span\\u003e\\u003c/p\\u003e\\u003c/li\\u003e\\u003cli\\u003e\\u003cp class=\\\"tooltip tooltip-style-info\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eIt's available in the language of the country you're selling in or in English\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-icon\\\"\\u003e\\u003cbr\\u003e\\u003c/span\\u003e\\u003cspan class=\\\"tooltip-text\\\"\\u003e\\u003cspan class=\\\"segment\\\"\\u003eMake sure that the return and/or refund policy is available in the target language or in English. Ideally, users should be given the option to select the return and/or refund policy in their own language.u003c/span\\u003e\\u003c/span\\u003e\\u003c/p\\u003e\\u003c/li\\u003e\\u003cli\\u003e\\u003cp\\u003e\\u003cspan class=\\\"segment\\\"\\u003eIt's accessible to everyone visiting your online store, without having to log in, sign up or enter any personal information\\u003c/span\\u003e\\u003c/p\\u003e\\u003c/li\\u003e\\u003c/ul\\u003e\\u003ca href=\\\"https://support.google.com/merchants/answer/9158778?hl=en-US\\\" class=\\\"content-element\\\"\\u003eLearn more about Missing return and refund policy\\u003c/a\\u003e\\u003c/div\\u003e\\u003c/div\\u003e\",\n \"actions\": [\n {\n \"externalAction\": {\n \"type\": \"REVIEW_ACCOUNT_ISSUE_IN_MERCHANT_CENTER\",\n \"uri\": \"https://merchants.google.com/mc/products/diagnostics/accountissues?a=672911686&hl=en-US\"\n },\n \"buttonLabel\": \"Request review\",\n \"isAvailable\": true\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n<div class=\"issue-detail\">\n <div class=\"callout-banners\">\n <div class=\"callout-banner callout-banner-info\"><p><span class=\"segment\">Review requested on Aug 9, 2023. It can take a few days to complete.</span>\n </p></div>\n </div>\n <div class=\"issue-content\"><p class=\"content-element\"><span class=\"segment\">There was a problem identified with the sale of prohibited adult products on your online store. In the case that you are intentionally selling adult items, enable Adult content in Settings in your Merchant Center. In your product file, use the </span><span\n class=\"segment segment-attribute\">adult</span><span class=\"segment\"> attribute for specific products.</span>\n </p>\n <p class=\"content-element root-causes-intro\"><span class=\"segment\">Make sure the products meet the policy requirements</span>\n </p>\n <ul class=\"content-element root-causes\">\n <li><p class=\"tooltip tooltip-style-info\"><span class=\"segment\">Adult oriented content may be prohibited or restricted depending on the product sold and the country it is sold</span><span\n class=\"tooltip-icon\"><br></span><span class=\"tooltip-text\"><span class=\"segment\">See a full list of countries in the HelpCenter</span></span>\n </p></li>\n <li><p class=\"tooltip tooltip-style-info\"><span class=\"segment\">Don't list sexually explicit content that is intended to arouse or includes content such as text, image, audio, or video of graphic sexual acts intended to arouse</span><span\n class=\"tooltip-icon\"><br></span><span class=\"tooltip-text\"><span class=\"segment\">Examples: Graphic depictions of sexual acts in progress, including hardcore pornography, any type of genital, anal, or oral sexual activity; graphic depictions of masturbation or genital arousal and language explicitly referencing arousal, masturbation, cartoon porn, or hentai</span></span>\n </p></li>\n </ul>\n <a href=\"https://support.google.com/merchants/answer/6150138?hl=en-US#wycd-restricted-adult-content\"\n class=\"content-element\">Learn more about the Adult-oriented content policy</a></div>\n</div>\n```\n\nExample:\n```text\nissue-detail {\n text-align: left;\n width: 700px;\n border-radius: 8px;\n background: white;\n margin: 16px;\n padding: 16px;\n}\n\n.content-element {\n margin: 8px 0 8px 0;\n display: block;\n}\n\n/* callout banners */\n.callout-banners {\n margin: 0 0 16px 0;\n}\n\n.callout-banner {\n display: block;\n padding: 16px 16px 6px 16px;\n margin: 0 0 8px 0;\n border-radius: 8px;\n}\n\n.callout-banner-info {\n background: #e8f0fe;\n}\n\n.callout-banner-warning {\n background: #fef7e0;\n}\n\n.callout-banner-error {\n background: #fce8e6;\n}\n\n/* add an icon to the callout banner */\n.callout-banner p {\n background-repeat: no-repeat;\n padding-left: 32px;\n}\n\n.callout-banner-error p {\n background-image: url(\"https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/error/default/20px.svg\");\n}\n\n.callout-banner-warning p {\n background-image: url(\"https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/warning/default/20px.svg\");\n}\n\n.callout-banner-info p {\n background-image: url(\"https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/search/default/20px.svg\");\n}\n\n/* segments with style */\n.segment-attribute {\n color: #198639;\n font-family: monospace, monospace;\n}\n\n.segment-bold {\n font-weight: bold;\n}\n\n.segment-italic {\n font-style: italic;\n}\n\n/* tooltip */\n.tooltip {\n position: relative;\n}\n\n.tooltip-style-info .tooltip-icon:before {\n content: '(i)';\n font-style: normal;\n font-weight: normal;\n text-decoration: inherit;\n margin-left: 5px;\n}\n\n.tooltip-style-question .tooltip-icon:before {\n content: '(?)';\n font-style: normal;\n font-weight: normal;\n text-decoration: inherit;\n margin-left: 5px;\n}\n\n.tooltip .tooltip-text {\n visibility: hidden;\n text-align: left;\n background: white;\n border-radius: 8px;\n padding: 5px 0;\n border: 1px solid;\n padding: 10px;\n box-shadow: 3px 7px 12px #c1c1c1;\n position: absolute;\n z-index: 1;\n}\n\n.tooltip:hover .tooltip-text {\n visibility: visible;\n}\n\n/* table */\ntable.content-element {\n margin: 16px 0 16px 0;\n border: 1px solid #ccc;\n border-collapse: collapse;\n margin: 1em 0;\n}\n\ntable.content-element th {\n background-color: #eee;\n}\n\ntable.content-element th, table td {\n border: 1px solid #ddd;\n font-size: 0.9em;\n padding: 0.3em 1em;\n}\n\n/* hidde elements added in future, until they are supported in your application */\n.new-element {\n visibility: hidden;\n}\n```\n\nExample:\n```text\n<details class=\"ods-section\" open=\"\">\n <summary>Show additional options available to you</summary>\n <p class=\"ods-description\">\n <span class=\"segment\">You may have the option to request an external appeal. You'll also be asked to provide your routing and reference IDs.</span>\n <a href=\"https://support.google.com/european-union-digital-services-act-redress-options?hl=en-US\"\n target=\"_blank\" class=\"segment\">Learn more about external appeals</a>\n </p>\n <p class=\"ods-param ods-routing-id\">\n <span class=\"segment ods-param-header\">Routing ID:</span>\n <span class=\"segment ods-param-value\">RDAX</span>\n </p>\n <p class=\"ods-param ods-reference-id\">\n <span class=\"segment ods-param-header\">Reference ID:</span>\n <span class=\"segment ods-param-value\">672911686</span>\n </p>\n</details>\n```\n\nExample:\n```text\n.ods-param-value {\n background: #dee1e37d;\n font-family: monospace, monospace;\n padding: 3px;\n}\n\n.ods-param-header {\n font-size: .75rem;\n}\n\n.ods-section summary {\n font-size: .75rem;\n}\n```\n\nExample:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/merchantsupport/renderaccountissues\n\n{\n \"user_input_action_option\": \"BUILT_IN_USER_INPUT_ACTIONS\"\n}\n```\n\nExample:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/merchantsupport/triggeraction\n\n{\n actionContext: \"ActionContextValue=\",\n actionInput: { actionFlowId: \"flow1\",\n inputValues: [\n { input_field_id: \"input1\", checkbox_input_value: { value: true } }\n ]\n}\n```\n\nExample:\n```text\n{\n \"error\":\n {\n \"code\": 400,\n \"message\": \"[actionInput.inputValues] Invalid user input\",\n \"status\": \"INVALID_ARGUMENT\",\n \"details\": [\n {\n \"@type\": \"type.googleapis.com/google.rpc.ErrorInfo\",\n \"reason\": \"invalid\",\n \"domain\": \"global\"\n },\n {\n \"@type\": \"type.googleapis.com/google.rpc.BadRequest\",\n \"fieldViolations\": [\n {\n \"field\": \"actionInput.inputValues.input\",\n \"description\": \"The field is required\"\n }\n ]\n }\n ]\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.651Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":344,"estimatedTokens":4354}}257{"id":"doc-css_label_management_content_api_for_shopping_go-7d73b59a","source":"documentation","title":"CSS label management | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/css-label-management","text":"Example:\n```text\nhttps://shoppingcontent.googleapis.com/content/v2.1/accounts/accountId/labels\n```\n\nExample:\n```text\n{\n \"name\": \"key-accounts\",\n \"description\": \"All accounts with over a million products\"\n}\n```\n\nExample:\n```text\nhttps://shoppingcontent.googleapis.com/content/v2.1/cssId/accounts/subaccountId/updatelabels/\n```\n\nExample:\n```text\n{\n \"labelIds\": [‘123’] // ‘key-accounts’\n}\n```\n\nExample:\n```text\nhttps://shoppingcontent.googleapis.com/content/v2.1/cssId/accounts?view=CSS&label=123\n```\n\nExample:\n```text\nhttps://shoppingcontent.googleapis.com/content/v2.1/cssGroupId/csses/cssDomainId/updatelabels/\n```\n\nExample:\n```text\n{\n \"labelIds\": [‘456’] // ‘key-domains’\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.652Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":43,"estimatedTokens":174}}258{"id":"doc-request_links_content_api_for_shopping_google_fo-a9acc0a5","source":"documentation","title":"Request links | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/flagging/request","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/123456789/accounts/123456789/link\n{\n \"linkedAccountId\": \"98765\",\n \"linkType\": \"eCommercePlatform\",\n \"services\": [\"shoppingAdsProductManagement\", \"shoppingActionsOrderManagement\"],\n \"action\": \"request\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.652Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":12,"estimatedTokens":75}}259{"id":"doc-phone_verification_content_api_for_shopping_goog-22b461e4","source":"documentation","title":"Phone verification | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/phoneverification","text":"Example:\n```text\nPOST https://www.googleapis.com/content/v2.1/merchantId/accounts/accountId/requestphoneverification\n```\n\nExample:\n```text\n{\n \"phoneRegionCode\": \"US\",\n \"phoneNumber\": \"phoneNumber\",\n \"phoneVerificationMethod\": \"SMS\",\n \"languageCode\": \"en-US\"\n}\n```\n\nExample:\n```text\n{\n \"verificationId\": \"2-47b7ef80ff494daf8079f4808e750dcb-1626331725036\"\n}\n```\n\nExample:\n```text\nPOST https://www.googleapis.com/content/v2.1/merchantId/accounts/accountId/verifyphonenumber\n```\n\nExample:\n```text\n{\n \"verificationId\": \"verificationId\",\n \"verificationCode\": \"verificationCode\",\n \"phoneVerificationMethod\": \"SMS\"\n}\n```\n\nExample:\n```text\n{\n \"verifiedPhoneNumber\": \"(123) 456-7890\"\n}\n```\n\nExample:\n```text\nGET https://www.googleapis.com/content/v2.1/v2.1/merchantId/accounts/accountId\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.652Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":49,"estimatedTokens":201}}260{"id":"doc-manage_conversion_sources_content_api_for_shoppi-1010fffe","source":"documentation","title":"Manage conversion sources | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/manage-conversion-sources","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/conversionsources/\n```\n\nExample:\n```text\n{\n \"googleAnalyticsLink\": {\n \"propertyId\": {propertyId}\n }\n}\n```\n\nExample:\n```text\n{\n \"conversionSourceId\": \"galk:{propertyId}\",\n \"googleAnalyticsLink\": {\n \"propertyId\": \"{propertyId}\",\n \"attributionSettings\": {\n \"attributionLookbackWindowInDays\": 90,\n \"atributionModel\": \"CROSS_CHANNEL_DATA_DRIVEN\",\n \"conversionType\": [\n {\n \"name\": \"purchase\",\n \"includeInReporting\": true\n }\n ]\n },\n \"propertyName\": \"My Property Name\"\n },\n \"state\": \"ACTIVE\",\n \"controller\": \"MERCHANT\"\n}\n```\n\nExample:\n```text\n{\n \"merchantCenterDestination\": {\n \"displayName\": \"My tag destination\",\n \"attributionSettings\": {\n \"attributionLookbackWindowInDays\": 60,\n \"attributionModel\": \"CROSS_CHANNEL_LAST_CLICK\"\n },\n \"currencyCode\": \"CHF\"\n }\n}\n```\n\nExample:\n```text\n{\n \"conversionSourceId\": \"mcdn:12341241234\",\n \"merchantCenterDestination\": {\n \"destinationId\": \"MC-ABCD1234\",\n \"attributionSettings\": {\n \"attributionLookbackWindowInDays\": 60,\n \"attributionModel\": \"CROSS_CHANNEL_LAST_CLICK\"\n },\n \"displayName\": \"My tag destination\",\n \"currencyCode\": \"CHF\"\n },\n \"state\": \"ACTIVE\",\n \"controller\": \"MERCHANT\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.653Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":70,"estimatedTokens":337}}261{"id":"doc-remove_links_content_api_for_shopping_google_for-ead9bcdd","source":"documentation","title":"Remove links | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/flagging/remove","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/98765/accounts/98765/link\n{\n \"linkedAccountId\": \"123456789\",\n \"linkType\": \"eCommercePlatform\",\n \"services\": [\"shoppingAdsProductManagement\"],\n \"action\": \"remove\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.653Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":12,"estimatedTokens":65}}262{"id":"doc-enable_automatic_improvements_content_api_for_sh-e7484852","source":"documentation","title":"Enable automatic improvements | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/automatic-improvements","text":"Example:\n```text\n{\n \"accountItemUpdatesSettings\": {\n \"allowPriceUpdates\": true,\n \"allowAvailabilityUpdates\": false,\n \"allowStrictAvailabilityUpdates\": false\n \"allowConditionUpdates\": true\n}\n,\n \"effectiveAllowPriceUpdates\": true,\n \"effectiveAllowAvailabilityUpdates\": false,\n \"effectiveAllowStrictAvailabilityUpdates\": false\n \"effectiveConditionUpdates\": true\n}\n```\n\nExample:\n```text\n{\n \"accountImageImprovementsSettings\": {\n \"allowAutomaticImageImprovements\": true\n}\n\n \"effectiveAllowAutomaticImageImprovements\": true\n}\n```\n\nExample:\n```text\n{\n \"allowShippingImprovements\": true\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.653Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":36,"estimatedTokens":156}}263{"id":"doc-list_links_content_api_for_shopping_google_for_d-5fa428e3","source":"documentation","title":"List links | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/flagging/list","text":"Example:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/123456789/accounts/123456789/listlinks\n```\n\nExample:\n```text\n{\n \"linkedAccountId\": \"98765\",\n \"services\": [\n {\n \"service\": \"shoppingAdsProductManagement\",\n \"status\": \"pending\"\n },\n {\n \"service\": \"shoppingActionsOrderManagement\",\n \"status\": \"pending\"\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.654Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":23,"estimatedTokens":107}}264{"id":"doc-approve_links_content_api_for_shopping_google_fo-e1fe41d4","source":"documentation","title":"Approve links | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/flagging/approve","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/98765/accounts/98765/link\n{\n \"linkedAccountId\": \"123456789\",\n \"linkType\": \"eCommercePlatform\",\n \"services\": [\"shoppingAdsProductManagement\"],\n \"action\": \"approve\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.654Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":12,"estimatedTokens":65}}265{"id":"doc-shopping_ads_and_free_listings_return_settings_c-b1738c63","source":"documentation","title":"Shopping Ads and free listings return settings | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/free-listings-return-settings","text":"Example:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/merchantId/returnpolicyonline\n```\n\nExample:\n```text\n{\n \"returnPolicies\": [\n {\n \"returnPolicyId\": \"transactions:US:default\",\n \"label\": \"default\",\n \"countries\": [\n \"GB\"\n ],\n \"policy\": {\n \"type\": \"NUMBER_OF_DAYS_AFTER_DELIVERY\",\n \"days\": \"90\"\n },\n \"restockingFee\": {\n \"fixedFee\": {\n \"value\": \"5.99\",\n \"currency\": \"GBP\"\n }\n },\n \"returnMethods\": [\n \"IN_STORE\",\n \"BY_MAIL\"\n ],\n \"itemConditions\": [\n \"NEW\",\n \"USED\"\n ],\n \"returnReasonCategoryInfo\": [\n {\n \"returnReasonCategory\": \"ITEM_DEFECT\",\n \"returnLabelSource\": \"DOWNLOAD_AND_PRINT\",\n \"returnShippingFee\": {\n \"type\": \"FIXED\",\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"GBP\"\n }\n }\n },\n {\n \"returnReasonCategory\": \"BUYER_REMORSE\",\n \"returnLabelSource\": \"DOWNLOAD_AND_PRINT\",\n \"returnShippingFee\": {\n \"type\": \"FIXED\",\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"GBP\"\n }\n }\n }\n ],\n \"returnPolicyUri\": \"https://www.example.com/return-policy\"\n },\n {\n \"returnPolicyId\": \"transactions:US:default\",\n \"label\": \"default120days\",\n \"countries\": [\n \"US\",\n \"FR\"\n ],\n \"name\": \"returnpolicy120days\",\n \"policy\": {\n \"type\": \"NUMBER_OF_DAYS_AFTER_DELIVERY\",\n \"days\": \"90\"\n },\n \"restockingFee\": {\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n },\n \"returnMethods\": [\n \"BY_MAIL\"\n ],\n \"itemConditions\": [\n \"NEW\",\n \"USED\"\n ],\n \"returnReasonCategoryInfo\": [\n {\n \"returnReasonCategory\": \"ITEM_DEFECT\",\n \"returnLabelSource\": \"DOWNLOAD_AND_PRINT\",\n \"returnShippingFee\": {\n \"type\": \"FIXED\",\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n }\n },\n {\n \"returnReasonCategory\": \"BUYER_REMORSE\",\n \"returnLabelSource\": \"DOWNLOAD_AND_PRINT\",\n \"returnShippingFee\": {\n \"type\": \"FIXED\",\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n }\n }\n ],\n \"returnPolicyUri\": \"https://www.example.com/return-policy\"\n }\n ]\n}\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/merchantId/returnpolicyonline/returnPolicyId\n```\n\nExample:\n```text\n{\n \"returnPolicyId\": \"transactions:US:default\",\n \"label\": \"default\",\n \"countries\": [\n \"US\"\n ],\n \"policy\": {\n \"type\": \"NUMBER_OF_DAYS_AFTER_DELIVERY\",\n \"days\": \"90\"\n },\n \"restockingFee\": {\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n },\n \"returnMethods\": [\n \"BY_MAIL\"\n ],\n \"itemConditions\": [\n \"NEW\",\n \"USED\"\n ],\n \"returnReasonCategoryInfo\": [\n {\n \"returnReasonCategory\": \"ITEM_DEFECT\",\n \"returnLabelSource\": \"DOWNLOAD_AND_PRINT\",\n \"returnShippingFee\": {\n \"type\": \"FIXED\",\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n }\n },\n {\n \"returnReasonCategory\": \"BUYER_REMORSE\",\n \"returnLabelSource\": \"DOWNLOAD_AND_PRINT\",\n \"returnShippingFee\": {\n \"type\": \"FIXED\",\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n }\n }\n ],\n \"returnPolicyUri\": \"https://www.example.com/return-policy\"\n}\n```\n\nExample:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/merchantId/returnpolicyonline\n```\n\nExample:\n```text\n{\n \"returnPolicyId\": \"12345678\",\n \"label\": \"default90days\",\n \"name\": \"returnpolicy90days\",\n \"policy\": {\n \"type\": \"NUMBER_OF_DAYS_AFTER_DELIVERY\",\n \"days\": \"90\"\n },\n \"countries\": [\n \"US\"\n ],\n \"itemConditions\": [\n \"NEW\",\n \"USED\"\n ],\n \"restockingFee\": {\n \"fixedFee\": {\n \"currency\": \"USD\",\n \"value\": \"0.00\"\n }\n },\n \"returnMethods\": [\n \"BY_MAIL\"\n ],\n \"returnReasonCategoryInfo\": [\n {\n \"returnReasonCategory\": \"ITEM_DEFECT\",\n \"returnLabelSource\": \"DOWNLOAD_AND_PRINT\",\n \"returnShippingFee\": {\n \"type\": \"FIXED\",\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n }\n }\n ],\n \"returnPolicyUri\": \"https://www.example.com/return-policy\"\n}\n```\n\nExample:\n```text\n{\n \"returnPolicyId\": \"12345678\",\n \"label\": \"default90days\",\n \"countries\": [\n \"US\"\n ],\n \"name\": \"returnpolicy90days\",\n \"policy\": {\n \"type\": \"NUMBER_OF_DAYS_AFTER_DELIVERY\",\n \"days\": \"90\"\n },\n \"restockingFee\": {\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n },\n \"returnMethods\": [\n \"BY_MAIL\"\n ],\n \"itemConditions\": [\n \"NEW\",\n \"USED\"\n ],\n \"returnReasonCategoryInfo\": [\n {\n \"returnReasonCategory\": \"ITEM_DEFECT\",\n \"returnLabelSource\": \"DOWNLOAD_AND_PRINT\",\n \"returnShippingFee\": {\n \"type\": \"FIXED\",\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n }\n }\n ],\n \"returnPolicyUri\": \"https://www.example.com/return-policy\"\n}\n```\n\nExample:\n```text\nPATCH https://shoppingcontent.googleapis.com/content/v2.1/merchantId/returnpolicyonline/returnPolicyId\n```\n\nExample:\n```text\n{\n \"returnPolicyId\": \"12345678\",\n \"label\": \"default90days\",\n \"countries\": [\n \"US\",\n \"FR\"\n ],\n \"name\": \"returnpolicy90days\",\n \"policy\": {\n \"type\": \"NUMBER_OF_DAYS_AFTER_DELIVERY\",\n \"days\": \"90\"\n },\n \"restockingFee\": {\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n },\n \"returnMethods\": [\n \"BY_MAIL\"\n ],\n \"itemConditions\": [\n \"NEW\",\n \"USED\"\n ],\n \"returnReasonCategoryInfo\": [\n {\n \"returnReasonCategory\": \"ITEM_DEFECT\",\n \"returnLabelSource\": \"DOWNLOAD_AND_PRINT\",\n \"returnShippingFee\": {\n \"type\": \"FIXED\",\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n }\n },\n {\n \"returnReasonCategory\": \"BUYER_REMORSE\",\n \"returnLabelSource\": \"DOWNLOAD_AND_PRINT\",\n \"returnShippingFee\": {\n \"type\": \"FIXED\",\n \"fixedFee\": {\n \"value\": \"0.00\",\n \"currency\": \"USD\"\n }\n }\n }\n ],\n \"returnPolicyUri\": \"https://www.example.com/return-policy\"\n}\n```\n\nExample:\n```text\nDELETE https://shoppingcontent.googleapis.com/content/v2.1/merchantId/returnpolicyonline/returnPolicyId\n```\n\nExample:\n```text\n{\n \"error\": {\n \"code\": \"404\",\n \"message\": \"Return policy not found.\",\n \"status\": \"NOT_FOUND\",\n \"details\": [\n {\n \"type\": \"type.googleapis.com/google.rpc.ErrorInfo\",\n \"reason\": \"notFound\",\n \"domain\": \"global\"\n }\n ]\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.655Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":347,"estimatedTokens":1714}}266{"id":"doc-promotions_content_api_for_shopping_google_for_d-2ea40fbf","source":"documentation","title":"Promotions | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/promotions","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/promotions\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/promotions/{id}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.657Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":56}}267{"id":"doc-local_inventory_service_content_api_for_shopping-359ae282","source":"documentation","title":"Local inventory service | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/products/local-inventory","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/merchantId/products/productId/localinventory\n```\n\nExample:\n```text\n{\n \"storeCode\": “1235”,\n \"salePrice\": {\n \"value\": “100.00”,\n \"currency\": “USD”\n },\n \"salePriceEffectiveDate\": “2021-02-24T13:00-0800/2021-02-28T15:30-0800”,\n \"quantity\": 200,\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.658Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":19,"estimatedTokens":88}}268{"id":"doc-set_product_delivery_time_content_api_for_shoppi-9c6cc414","source":"documentation","title":"Set product delivery time | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/products/product-delivery-time","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/merchantId/productdeliverytime\n```\n\nExample:\n```text\n{\n \"productId\": {\n \"productId\": \"online:en:US:offer-id\"\n }\n \"areaDeliveryTimes\": [\n {\n \"deliveryArea\": {\n \"countryCode\": \"US\"\n \"postalCodeRange\": {\n \"firstPostalCode\": \"123*\"\n \"lastPostalCode\": \"456*\"\n }\n }\n \"deliveryTime\": {\n \"minHandlingTimeDays\": \"0\"\n \"maxHandlingTimeDays\": \"1\"\n \"minTransitTimeDays\": \"2\"\n \"maxTransitTimeDays\": \"4\"\n }\n },\n {\n \"deliveryArea\": {\n \"countryCode\": \"US\"\n \"administrativeAreaCode\": \"NY\"\n }\n \"deliveryTime\": {\n \"minHandlingTimeDays\": \"0\"\n \"maxHandlingTimeDays\": \"1\"\n \"minTransitTimeDays\": \"5\"\n \"maxTransitTimeDays\": \"7\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/merchantId/productdeliverytime/productId\n```\n\nExample:\n```text\nDELETE https://shoppingcontent.googleapis.com/content/v2.1/merchantId/productdeliverytime/productId\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.658Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":54,"estimatedTokens":279}}269{"id":"doc-shipping_settings_content_api_for_shopping_googl-d2ed2042","source":"documentation","title":"Shipping Settings | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/shippingsettings","text":"Example:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/merchantId/shippingsettings\n```\n\nExample:\n```text\n{\n \"kind\": \"content#shippingsettingsListResponse\",\n \"resources\": [\n {\n \"accountId\": \"1111\",\n \"services\": [\n {\n \"name\": \"Standard Shipping\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 1,\n \"transitTimeTable\": {\n \"postalCodeGroupNames\": [\n \"Region1\",\n \"Region2\",\n \"all other locations\"\n ],\n \"transitTimeLabels\": [\n \"all other labels\"\n ],\n \"rows\": [\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 1,\n \"maxTransitTimeInDays\": 2\n }\n ]\n },\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 2,\n \"maxTransitTimeInDays\": 3\n }\n ]\n },\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 3,\n \"maxTransitTimeInDays\": 5\n }\n ]\n }\n ]\n }\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"0\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"Standard Shipping\"\n }\n ],\n \"eligibility\": \"All scenarios\"\n },\n {\n \"name\": \"Expedited\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 2,\n \"maxTransitTimeInDays\": 2,\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 1\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"9.99\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"Expedited\"\n }\n ],\n \"eligibility\": \"All scenarios\"\n }\n ],\n \"postalCodeGroups\": [\n {\n \"name\": \"Region1\",\n \"country\": \"US\",\n \"postalCodeRanges\": [\n {\n \"postalCodeRangeBegin\": \"94035\",\n \"postalCodeRangeEnd\": \"94070\"\n }\n ]\n },\n {\n \"name\": \"Region2\",\n \"country\": \"US\",\n \"postalCodeRanges\": [\n {\n \"postalCodeRangeBegin\": \"94071\",\n \"postalCodeRangeEnd\": \"94082\"\n }\n ]\n }\n ]\n },\n {\n \"accountId\": \"2222\",\n \"services\": [\n {\n \"name\": \"FedEx\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 4,\n \"maxTransitTimeInDays\": 6,\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 0\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"5.99\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"All products\"\n }\n ],\n \"eligibility\": \"All scenarios except Shopping Actions\"\n },\n {\n \"name\": \"GSA Shipping - Free Ship Over $49.99\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 3,\n \"maxTransitTimeInDays\": 7,\n \"minHandlingTimeInDays\": 1,\n \"maxHandlingTimeInDays\": 2\n },\n \"rateGroups\": [\n {\n \"mainTable\": {\n \"rowHeaders\": {\n \"prices\": [\n {\n \"value\": \"49.99\",\n \"currency\": \"USD\"\n },\n {\n \"value\": \"infinity\",\n \"currency\": \"USD\"\n }\n ]\n },\n \"rows\": [\n {\n \"cells\": [\n {\n \"flatRate\": {\n \"value\": \"6.99\",\n \"currency\": \"USD\"\n }\n }\n ]\n },\n {\n \"cells\": [\n {\n \"flatRate\": {\n \"value\": \"0\",\n \"currency\": \"USD\"\n }\n }\n ]\n }\n ]\n },\n \"name\": \"Free Ship Over $49.99\"\n }\n ],\n \"eligibility\": \"Shopping Actions\"\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/merchantId/shippingsettings/accountId\n```\n\nExample:\n```text\n{\n \"accountId\": \"1111\",\n \"services\": [\n {\n \"name\": \"Standard Shipping\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 1,\n \"transitTimeTable\": {\n \"postalCodeGroupNames\": [\n \"Region1\",\n \"Region2\",\n \"all other locations\"\n ],\n \"transitTimeLabels\": [\n \"all other labels\"\n ],\n \"rows\": [\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 1,\n \"maxTransitTimeInDays\": 2\n }\n ]\n },\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 2,\n \"maxTransitTimeInDays\": 3\n }\n ]\n },\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 3,\n \"maxTransitTimeInDays\": 5\n }\n ]\n }\n ]\n }\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"0\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"Standard Shipping\"\n }\n ],\n \"eligibility\": \"All scenarios\"\n },\n {\n \"name\": \"Expedited\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 2,\n \"maxTransitTimeInDays\": 2,\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 1\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"9.99\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"Expedited\"\n }\n ],\n \"eligibility\": \"All scenarios\"\n }\n ],\n \"postalCodeGroups\": [\n {\n \"name\": \"Region1\",\n \"country\": \"US\",\n \"postalCodeRanges\": [\n {\n \"postalCodeRangeBegin\": \"94035\",\n \"postalCodeRangeEnd\": \"94070\"\n }\n ]\n },\n {\n \"name\": \"Region2\",\n \"country\": \"US\",\n \"postalCodeRanges\": [\n {\n \"postalCodeRangeBegin\": \"94071\",\n \"postalCodeRangeEnd\": \"94082\"\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\nPUT https://shoppingcontent.googleapis.com/content/v2.1/merchantId/shippingsettings/accountId\n```\n\nExample:\n```text\n{\n...\n \"services\": [\n {\n \"name\": \"FedEx\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 4,\n \"maxTransitTimeInDays\": 6,\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 0\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"5.99\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"All products\"\n }\n ],\n \"eligibility\": \"All scenarios except Shopping Actions\"\n },\n {\n \"name\": \"GSA Shipping - Free Ship Over $49.99\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 3,\n \"maxTransitTimeInDays\": 7,\n \"minHandlingTimeInDays\": 1,\n \"maxHandlingTimeInDays\": 2\n },\n \"rateGroups\": [\n {\n \"mainTable\": {\n \"rowHeaders\": {\n \"prices\": [\n {\n \"value\": \"49.99\",\n \"currency\": \"USD\"\n },\n {\n \"value\": \"infinity\",\n \"currency\": \"USD\"\n }\n ]\n },\n \"rows\": [\n {\n \"cells\": [\n {\n \"flatRate\": {\n \"value\": \"6.99\",\n \"currency\": \"USD\"\n }\n }\n ]\n },\n {\n \"cells\": [\n {\n \"flatRate\": {\n \"value\": \"0\",\n \"currency\": \"USD\"\n }\n }\n ]\n }\n ]\n },\n \"name\": \"Free Ship Over $49.99\"\n }\n ],\n \"eligibility\": \"Shopping Actions\"\n }\n ]\n }\n}\n```\n\nExample:\n```text\n{\n \"accountId\": \"2222\",\n \"services\": [\n {\n \"name\": \"FedEx\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 4,\n \"maxTransitTimeInDays\": 6,\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 0\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"5.99\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"All products\"\n }\n ],\n \"eligibility\": \"All scenarios except Shopping Actions\"\n },\n {\n \"name\": \"GSA Shipping - Free Ship Over $49.99\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 3,\n \"maxTransitTimeInDays\": 7,\n \"minHandlingTimeInDays\": 1,\n \"maxHandlingTimeInDays\": 2\n },\n \"rateGroups\": [\n {\n \"mainTable\": {\n \"rowHeaders\": {\n \"prices\": [\n {\n \"value\": \"49.99\",\n \"currency\": \"USD\"\n },\n {\n \"value\": \"infinity\",\n \"currency\": \"USD\"\n }\n ]\n },\n \"rows\": [\n {\n \"cells\": [\n {\n \"flatRate\": {\n \"value\": \"6.99\",\n \"currency\": \"USD\"\n }\n }\n ]\n },\n {\n \"cells\": [\n {\n \"flatRate\": {\n \"value\": \"0\",\n \"currency\": \"USD\"\n }\n }\n ]\n }\n ]\n },\n \"name\": \"Free Ship Over $49.99\"\n }\n ],\n \"eligibility\": \"Shopping Actions\"\n }\n ]\n}\n```\n\nExample:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/shippingsettings/batch\n```\n\nExample:\n```text\n{\n \"entries\": [\n {\n \"accountId\": 1111,\n \"merchantId\": 10,\n \"method\": \"get\",\n \"batchId\": 1\n },\n {\n \"accountId\": 2222,\n \"merchantId\": 10,\n \"method\": \"update\",\n \"batchId\": 2,\n \"shippingSettings\": {\n \"services\": [\n {\n \"name\": \"FedEx\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 4,\n \"maxTransitTimeInDays\": 5,\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 0\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"5.99\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"All products\"\n }\n ],\n \"eligibility\": \"All scenarios except Shopping Actions\"\n },\n {\n \"name\": \"GSA Shipping - Free Ship Over $49.99\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 3,\n \"maxTransitTimeInDays\": 8,\n \"minHandlingTimeInDays\": 1,\n \"maxHandlingTimeInDays\": 2\n },\n \"rateGroups\": [\n {\n \"mainTable\": {\n \"rowHeaders\": {\n \"prices\": [\n {\n \"value\": \"49.99\",\n \"currency\": \"USD\"\n },\n {\n \"value\": \"infinity\",\n \"currency\": \"USD\"\n }\n ]\n },\n \"rows\": [\n {\n \"cells\": [\n {\n \"flatRate\": {\n \"value\": \"6.99\",\n \"currency\": \"USD\"\n }\n }\n ]\n },\n {\n \"cells\": [\n {\n \"flatRate\": {\n \"value\": \"0\",\n \"currency\": \"USD\"\n }\n }\n ]\n }\n ]\n },\n \"name\": \"Free Ship Over $49.99\"\n }\n ],\n \"eligibility\": \"Shopping Actions\"\n }\n ]\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"kind\": \"content#shippingsettingsCustomBatchResponse\",\n \"entries\": [\n {\n \"kind\": \"content#shippingsettingsCustomBatchResponseEntry\",\n \"batchId\": 1,\n \"shippingSettings\": {\n \"accountId\": \"1111\",\n \"services\": [\n {\n \"name\": \"Standard Shipping\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 1,\n \"transitTimeTable\": {\n \"postalCodeGroupNames\": [\n \"Region1\",\n \"Region2\",\n \"all other locations\"\n ],\n \"transitTimeLabels\": [\n \"all other labels\"\n ],\n \"rows\": [\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 1,\n \"maxTransitTimeInDays\": 2\n }\n ]\n },\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 2,\n \"maxTransitTimeInDays\": 3\n }\n ]\n },\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 3,\n \"maxTransitTimeInDays\": 5\n }\n ]\n }\n ]\n }\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"0\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"Standard Shipping\"\n }\n ],\n \"eligibility\": \"All scenarios\"\n },\n {\n \"name\": \"Expedited\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 2,\n \"maxTransitTimeInDays\": 2,\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 1\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"9.99\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"Expedited\"\n }\n ],\n \"eligibility\": \"All scenarios\"\n }\n ],\n \"postalCodeGroups\": [\n {\n \"name\": \"Region1\",\n \"country\": \"US\",\n \"postalCodeRanges\": [\n {\n \"postalCodeRangeBegin\": \"94035\",\n \"postalCodeRangeEnd\": \"94070\"\n }\n ]\n },\n {\n \"name\": \"Region2\",\n \"country\": \"US\",\n \"postalCodeRanges\": [\n {\n \"postalCodeRangeBegin\": \"94071\",\n \"postalCodeRangeEnd\": \"94082\"\n }\n ]\n }\n ]\n }\n },\n {\n \"kind\": \"content#shippingsettingsCustomBatchResponseEntry\",\n \"batchId\": 2,\n \"shippingSettings\": {\n \"accountId\": \"2222\",\n \"services\": [\n {\n \"name\": \"FedEx\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 4,\n \"maxTransitTimeInDays\": 5,\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 0\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"5.99\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"All products\"\n }\n ],\n \"eligibility\": \"All scenarios except Shopping Actions\"\n },\n {\n \"name\": \"GSA Shipping - Free Ship Over $49.99\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 3,\n \"maxTransitTimeInDays\": 8,\n \"minHandlingTimeInDays\": 1,\n \"maxHandlingTimeInDays\": 2\n },\n \"rateGroups\": [\n {\n \"mainTable\": {\n \"rowHeaders\": {\n \"prices\": [\n {\n \"value\": \"49.99\",\n \"currency\": \"USD\"\n },\n {\n \"value\": \"infinity\",\n \"currency\": \"USD\"\n }\n ]\n },\n \"rows\": [\n {\n \"cells\": [\n {\n \"flatRate\": {\n \"value\": \"6.99\",\n \"currency\": \"USD\"\n }\n }\n ]\n },\n {\n \"cells\": [\n {\n \"flatRate\": {\n \"value\": \"0\",\n \"currency\": \"USD\"\n }\n }\n ]\n }\n ]\n },\n \"name\": \"Free Ship Over $49.99\"\n }\n ],\n \"eligibility\": \"Shopping Actions\"\n }\n ]\n }\n }\n ]\n}\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/merchantId/supportedCarriers\n```\n\nExample:\n```text\n{\n \"kind\": \"content#shippingsettingsGetSupportedCarriersResponse\",\n \"carriers\": [\n {\n \"name\": \"FedEx\",\n \"country\": \"US\",\n \"services\": [\n \"Ground\",\n \"Home Delivery\",\n \"Express Saver\",\n \"First Overnight\",\n \"Priority Overnight\",\n \"Standard Overnight\",\n \"2Day\"\n ]\n },\n {\n \"name\": \"UPS\",\n \"country\": \"US\",\n \"services\": [\n \"2nd Day Air\",\n \"2nd Day Air AM\",\n \"3 Day Select\",\n \"Ground\",\n \"Next Day Air\",\n \"Next Day Air Early AM\",\n \"Next Day Air Saver\"\n ]\n },\n {\n \"name\": \"USPS\",\n \"country\": \"US\",\n \"services\": [\n \"Priority Mail Express\",\n \"Media Mail\",\n \"Retail Ground\",\n \"Priority Mail\",\n \"First Class Package Service Retail\",\n \"First Class Package Service Commercial Base\"\n ]\n },\n {\n \"name\": \"Australia Post\",\n \"country\": \"AU\",\n \"services\": [\n \"Regular Parcel\",\n \"Express Post\"\n ]\n },\n {\n \"name\": \"TNT\",\n \"country\": \"AU\",\n \"services\": [\n \"Road Express\",\n \"Overnight Express\"\n ]\n },\n {\n \"name\": \"TOLL\",\n \"country\": \"AU\",\n \"services\": [\n \"Road Delivery\",\n \"Overnight Priority\"\n ]\n },\n {\n \"name\": \"DHL\",\n \"country\": \"DE\",\n \"services\": [\n \"Paket\",\n \"Päckchen\"\n ]\n },\n {\n \"name\": \"DPD\",\n \"country\": \"DE\",\n \"services\": [\n \"Express 12\",\n \"Express\",\n \"Classic Parcel\"\n ]\n },\n {\n \"name\": \"Hermes\",\n \"country\": \"DE\",\n \"services\": [\n \"Päckchen\",\n \"Paketklasse S\",\n \"Paketklasse M\",\n \"Paketklasse L\"\n ]\n },\n {\n \"name\": \"UPS\",\n \"country\": \"DE\",\n \"services\": [\n \"Express\",\n \"Express Saver\",\n \"Standard\"\n ]\n },\n {\n \"name\": \"DHL UK\",\n \"country\": \"GB\",\n \"services\": [\n \"Express\",\n \"Express 12\"\n ]\n },\n {\n \"name\": \"DPD UK\",\n \"country\": \"GB\",\n \"services\": [\n \"Express 12\",\n \"Express Next Day\",\n \"Standard Parcel 12\",\n \"Standard Parcel Next Day\",\n \"Standard Parcel Two Day\"\n ]\n },\n {\n \"name\": \"RMG\",\n \"country\": \"GB\",\n \"services\": [\n \"1st Class Small Parcel\",\n \"1st Class Medium Parcel\",\n \"2nd Class Small Parcel\",\n \"2nd Class Medium Parcel\"\n ]\n },\n {\n \"name\": \"TNT UK\",\n \"country\": \"GB\",\n \"services\": [\n \"Express\",\n \"Express 10\",\n \"Express 12\"\n ]\n },\n {\n \"name\": \"UPS UK\",\n \"country\": \"GB\",\n \"services\": [\n \"Express\",\n \"Express Saver\",\n \"Standard\"\n ]\n },\n {\n \"name\": \"Yodel\",\n \"country\": \"GB\",\n \"services\": [\n \"B2C 48HR\",\n \"B2C 72HR\",\n \"B2C Packet\"\n ]\n }\n ]\n}\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/merchantId/supportedHolidays\n```\n\nExample:\n```text\n{\n \"kind\": \"content#shippingsettingsGetSupportedHolidaysResponse\",\n \"holidays\": [\n {\n \"id\": \"FR_Christmas_2019-12-25\",\n \"countryCode\": \"FR\",\n \"type\": \"Christmas\",\n \"date\": \"2019-12-25\",\n \"deliveryGuaranteeDate\": \"2019-12-24\",\n \"deliveryGuaranteeHour\": \"18\"\n },\n {\n \"id\": \"US_Easter_2019-04-21\",\n \"countryCode\": \"US\",\n \"type\": \"Easter\",\n \"date\": \"2019-04-21\",\n \"deliveryGuaranteeDate\": \"2019-04-20\",\n \"deliveryGuaranteeHour\": \"18\"\n },\n {\n \"id\": \"US_Mother's Day_2019-05-12\",\n \"countryCode\": \"US\",\n \"type\": \"Mother's Day\",\n \"date\": \"2019-05-12\",\n \"deliveryGuaranteeDate\": \"2019-05-11\",\n \"deliveryGuaranteeHour\": \"18\"\n },\n {\n \"id\": \"US_Father's Day_2019-06-16\",\n \"countryCode\": \"US\",\n \"type\": \"Father's Day\",\n \"date\": \"2019-06-16\",\n \"deliveryGuaranteeDate\": \"2019-06-15\",\n \"deliveryGuaranteeHour\": \"18\"\n },\n {\n \"id\": \"US_Independence Day (USA)_2019-07-04\",\n \"countryCode\": \"US\",\n \"type\": \"Independence Day (USA)\",\n \"date\": \"2019-07-04\",\n \"deliveryGuaranteeDate\": \"2019-07-03\",\n \"deliveryGuaranteeHour\": \"18\"\n },\n {\n \"id\": \"US_Halloween_2019-10-31\",\n \"countryCode\": \"US\",\n \"type\": \"Halloween\",\n \"date\": \"2019-10-31\",\n \"deliveryGuaranteeDate\": \"2019-10-30\",\n \"deliveryGuaranteeHour\": \"18\"\n },\n {\n \"id\": \"US_Thanksgiving_2019-11-28\",\n \"countryCode\": \"US\",\n \"type\": \"Thanksgiving\",\n \"date\": \"2019-11-28\",\n \"deliveryGuaranteeDate\": \"2019-11-27\",\n \"deliveryGuaranteeHour\": \"18\"\n },\n {\n \"id\": \"US_Christmas_2019-12-25\",\n \"countryCode\": \"US\",\n \"type\": \"Christmas\",\n \"date\": \"2019-12-25\",\n \"deliveryGuaranteeDate\": \"2019-12-24\",\n \"deliveryGuaranteeHour\": \"18\"\n }\n ]\n}\n```\n\nExample:\n```text\n\"postalCodeGroups\": [\n {\n \"name\": \"string,\n \"country\": string,\n \"postalCodeRanges\": [\n {\n \"postalCodeRangeBegin\": string,\n \"postalCodeRangeEnd\": string\n }\n ]\n }\n]\n```\n\nExample:\n```text\n\"transitTimeLabels\": [\n \"all other labels\"\n],\n```\n\nExample:\n```text\n{\n \"services\": [\n {\n \"name\": \"Standard Shipping\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 1,\n \"transitTimeTable\": {\n \"postalCodeGroupNames\": [\n \"Region1\",\n \"Region2\",\n \"all other locations\"\n ],\n \"rows\": [\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 1,\n \"maxTransitTimeInDays\": 2\n }\n ]\n },\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 2,\n \"maxTransitTimeInDays\": 3\n }\n ]\n },\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 3,\n \"maxTransitTimeInDays\": 5\n }\n ]\n }\n ]\n }\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"0\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"Standard Shipping\"\n }\n ],\n \"eligibility\": \"All scenarios\"\n },\n {\n \"name\": \"Expedited\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 2,\n \"maxTransitTimeInDays\": 2,\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 1\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"9.99\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"Expedited\"\n }\n ],\n \"eligibility\": \"All scenarios\"\n }\n ],\n \"postalCodeGroups\": [\n {\n \"name\": \"Region1\",\n \"country\": \"US\",\n \"postalCodeRanges\": [\n {\n \"postalCodeRangeBegin\": \"94036\",\n \"postalCodeRangeEnd\": \"94070\"\n }\n ]\n },\n {\n \"name\": \"Region2\",\n \"country\": \"US\",\n \"postalCodeRanges\": [\n {\n \"postalCodeRangeBegin\": \"94071\",\n \"postalCodeRangeEnd\": \"94082\"\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"accountId\": \"1111\",\n \"services\": [\n {\n \"name\": \"Standard Shipping\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 1,\n \"transitTimeTable\": {\n \"postalCodeGroupNames\": [\n \"Region1\",\n \"Region2\",\n \"all other locations\"\n ],\n \"rows\": [\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 1,\n \"maxTransitTimeInDays\": 2\n }\n ]\n },\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 2,\n \"maxTransitTimeInDays\": 3\n }\n ]\n },\n {\n \"values\": [\n {\n \"minTransitTimeInDays\": 3,\n \"maxTransitTimeInDays\": 5\n }\n ]\n }\n ]\n }\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"0\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"Standard Shipping\"\n }\n ],\n \"eligibility\": \"All scenarios\"\n },\n {\n \"name\": \"Expedited\",\n \"active\": true,\n \"deliveryCountry\": \"US\",\n \"currency\": \"USD\",\n \"deliveryTime\": {\n \"minTransitTimeInDays\": 2,\n \"maxTransitTimeInDays\": 2,\n \"minHandlingTimeInDays\": 0,\n \"maxHandlingTimeInDays\": 1\n },\n \"rateGroups\": [\n {\n \"singleValue\": {\n \"flatRate\": {\n \"value\": \"9.99\",\n \"currency\": \"USD\"\n }\n },\n \"name\": \"Expedited\"\n }\n ],\n \"eligibility\": \"All scenarios\"\n }\n ],\n \"postalCodeGroups\": [\n {\n \"name\": \"Region1\",\n \"country\": \"US\",\n \"postalCodeRanges\": [\n {\n \"postalCodeRangeBegin\": \"94036\",\n \"postalCodeRangeEnd\": \"94070\"\n }\n ]\n },\n {\n \"name\": \"Region2\",\n \"country\": \"US\",\n \"postalCodeRanges\": [\n {\n \"postalCodeRangeBegin\": \"94071\",\n \"postalCodeRangeEnd\": \"94082\"\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"accountId\":\"accountId\",\n \"services\":[\n {\n \"name\": \"Local Delivery\",\n \"active\":true,\n \"shipmentType\":\"local_delivery\",\n \"deliveryCountry\":\"US\",\n \"currency\":\"USD\",\n \"rateGroups\":[\n {\n \"singleValue\":{\n \"flatRate\":{\n \"value\":\"0\",\n \"currency\":\"USD\"\n }\n }\n }\n ],\n \"eligibility\":\"All scenarios\",\n \"storeConfig\":{\n \"storeServiceType\":\"all stores\",\n \"storeCodes\":[\n \n ],\n \"cutoffConfig\":{\n \"storeCloseOffsetHours\":2,\n \"no_delivery_post_cutoff\":true\n },\n \"serviceRadius\":{\n \"value\":4,\n \"unit\":\"Miles\"\n }\n }\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.661Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":19,"totalLines":1350,"estimatedTokens":6652}}270{"id":"doc-warnings_google_ads_api_google_for_developers-75776a2f","source":"documentation","title":"Warnings | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/best-practices/warnings","text":"Example:\n```text\n// Issues a request to add the operations to the offline user data job.\nAddOfflineUserDataJobOperationsResponse response =\n offlineUserDataJobServiceClient.addOfflineUserDataJobOperations(\n AddOfflineUserDataJobOperationsRequest.newBuilder()\n .setResourceName(offlineUserDataJobResourceName)\n .setEnablePartialFailure(true)\n // Enables warnings (optional).\n .setEnableWarnings(true)\n .addAllOperations(userDataJobOperations)\n .build());UploadStoreSalesTransactions.java\n```\n\nExample:\n```text\n// Constructs a request with partial failure enabled to add the operations to the\n// offline user data job, and enable_warnings set to true to retrieve warnings.\nAddOfflineUserDataJobOperationsRequest request =\n new AddOfflineUserDataJobOperationsRequest()\n{\n EnablePartialFailure = true,\n ResourceName = offlineUserDataJobResourceName,\n Operations = { userDataJobOperations },\n EnableWarnings = true,\n};\n\nAddOfflineUserDataJobOperationsResponse response = offlineUserDataJobServiceClient\n .AddOfflineUserDataJobOperations(request);UploadStoreSalesTransactions.cs\n```\n\nExample:\n```text\n// Issues a request to add the operations to the offline user data job.\n/** @var AddOfflineUserDataJobOperationsResponse $operationResponse */\n$request = AddOfflineUserDataJobOperationsRequest::build(\n $offlineUserDataJobResourceName,\n $userDataJobOperations\n);\n// (Optional) Enables partial failure and warnings.\n$request->setEnablePartialFailure(true)->setEnableWarnings(true);\n$response = $offlineUserDataJobServiceClient->addOfflineUserDataJobOperations($request);UploadStoreSalesTransactions.php\n```\n\nExample:\n```text\n# Constructs a request with partial failure enabled to add the operations\n# to the offline user data job, and enable_warnings set to true to retrieve\n# warnings.\nrequest: AddOfflineUserDataJobOperationsRequest = client.get_type(\n \"AddOfflineUserDataJobOperationsRequest\"\n)\nrequest.resource_name = offline_user_data_job_resource_name\nrequest.enable_partial_failure = True\nrequest.enable_warnings = True\nrequest.operations = operations\n\nresponse: AddOfflineUserDataJobOperationsResponse = (\n offline_user_data_job_service.add_offline_user_data_job_operations(\n request=request,\n )\n)upload_store_sales_transactions.py\n```\n\nExample:\n```text\n# Issues a request to add the operations to the offline user data job.\nresponse = offline_user_data_job_service.add_offline_user_data_job_operations(\n resource_name: offline_user_data_job_resource_name,\n operations: user_data_job_operations,\n enable_partial_failure: true,\n enable_warnings: true,\n)upload_store_sales_transactions.rb\n```\n\nExample:\n```text\n# Issue a request to add the operations to the offline user data job.\nmy $response = $offline_user_data_job_service->add_operations({\n resourceName => $offline_user_data_job_resource_name,\n enablePartialFailure => \"true\",\n # Enable warnings (optional).\n enableWarnings => \"true\",\n operations => $user_data_job_operations\n});upload_store_sales_transactions.pl\n```\n\nExample:\n```text\n// Checks if any warnings occurred and displays details.\nif (response.hasWarning()) {\n // Converts the Any in response back to a GoogleAdsFailure object.\n GoogleAdsFailure warningsFailure =\n ErrorUtils.getInstance().getGoogleAdsFailure(response.getWarning());\n // Prints some information about the warnings encountered.\n System.out.println(\n System.out.printf(\"Encountered %d warning(s).%n\", warningsFailure.getErrorsCount()));\n}UploadStoreSalesTransactions.java\n```\n\nExample:\n```text\n// Prints the number of warnings if any warnings are returned. You can access\n// details of each warning using the same approach you'd use for partial failure\n// errors.\nif (request.EnableWarnings && response.Warnings != null)\n{\n // Extracts the warnings from the response.\n GoogleAdsFailure warnings = response.Warnings;\n Console.WriteLine($\"{warnings.Errors.Count} warning(s) occurred\");\n}UploadStoreSalesTransactions.cs\n```\n\nExample:\n```text\n// Prints the number of warnings if any warnings are returned. You can access\n// details of each warning using the same approach you'd use for partial failure\n// errors.\nif ($response->hasWarning()) {\n // Extracts all the warning errors from the response details into a single\n // GoogleAdsFailure object.\n $warningFailure = GoogleAdsFailures::fromAnys($response->getWarning()->getDetails());\n // Prints some information about the warnings encountered.\n printf(\n \"Encountered %d warning(s).%s\",\n count($warningFailure->getErrors()),\n PHP_EOL\n );\n}UploadStoreSalesTransactions.php\n```\n\nExample:\n```text\ndef print_google_ads_failures(\n client: GoogleAdsClient, status: status_pb2.Status\n) -> None:\n \"\"\"Prints the details for partial failure errors and warnings.\n\n Both partial failure errors and warnings are returned as Status instances,\n which include serialized GoogleAdsFailure objects. Here we deserialize\n each GoogleAdsFailure and print the error details it includes.\n\n Args:\n client: An initialized Google Ads API client.\n status: a google.rpc.Status instance.\n \"\"\"\n detail: Any\n for detail in status.details:\n google_ads_failure: GoogleAdsFailure = client.get_type(\n \"GoogleAdsFailure\"\n )\n # Retrieve the class definition of the GoogleAdsFailure instance\n # with type() in order to use the \"deserialize\" class method to parse\n # the detail string into a protobuf message instance.\n failure_instance: GoogleAdsFailure = type(\n google_ads_failure\n ).deserialize(detail.value)\n error: GoogleAdsError\n for error in failure_instance.errors:\n print(\n \"A partial failure or warning at index \"\n f\"{error.location.field_path_elements[0].index} occurred.\\n\"\n f\"Message: {error.message}\\n\"\n f\"Code: {error.error_code}\"\n )upload_store_sales_transactions.py\n```\n\nExample:\n```text\nif response.warning\n # Convert to a GoogleAdsFailure.\n warnings = client.decode_warning(response.warning)\n puts \"Encountered #{warnings.errors.size} warning(s).\"\nendupload_store_sales_transactions.rb\n```\n\nExample:\n```text\n# Print the number of warnings if any warnings are returned. You can access\n# details of each warning using the same approach you'd use for partial failure\n# errors.\nif ($response->{warning}) {\n # Extract the warnings from the response status.\n my $warnings_failure = $response->{warning}{details}[0];\n printf \"Encountered %d warning(s).\\n\",\n scalar @{$warnings_failure->{errors}};\n}upload_store_sales_transactions.pl\n```\n\nExample:\n```text\n// Copyright 2020 Google LLC\n//\n// 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.\n\npackage com.google.ads.googleads.examples.remarketing;\n\nimport com.beust.jcommander.Parameter;\nimport com.google.ads.googleads.examples.utils.ArgumentNames;\nimport com.google.ads.googleads.examples.utils.CodeSampleParams;\nimport com.google.ads.googleads.lib.GoogleAdsClient;\nimport com.google.ads.googleads.v25.common.Consent;\nimport com.google.ads.googleads.v25.common.OfflineUserAddressInfo;\nimport com.google.ads.googleads.v25.common.StoreSalesMetadata;\nimport com.google.ads.googleads.v25.common.StoreSalesThirdPartyMetadata;\nimport com.google.ads.googleads.v25.common.TransactionAttribute;\nimport com.google.ads.googleads.v25.common.UserData;\nimport com.google.ads.googleads.v25.common.UserIdentifier;\nimport com.google.ads.googleads.v25.enums.ConsentStatusEnum.ConsentStatus;\nimport com.google.ads.googleads.v25.enums.OfflineUserDataJobStatusEnum.OfflineUserDataJobStatus;\nimport com.google.ads.googleads.v25.enums.OfflineUserDataJobTypeEnum.OfflineUserDataJobType;\nimport com.google.ads.googleads.v25.errors.GoogleAdsError;\nimport com.google.ads.googleads.v25.errors.GoogleAdsException;\nimport com.google.ads.googleads.v25.errors.GoogleAdsFailure;\nimport com.google.ads.googleads.v25.resources.OfflineUserDataJob;\nimport com.google.ads.googleads.v25.services.AddOfflineUserDataJobOperationsRequest;\nimport com.google.ads.googleads.v25.services.AddOfflineUserDataJobOperationsResponse;\nimport com.google.ads.googleads.v25.services.CreateOfflineUserDataJobResponse;\nimport com.google.ads.googleads.v25.services.GoogleAdsRow;\nimport com.google.ads.googleads.v25.services.GoogleAdsServiceClient;\nimport com.google.ads.googleads.v25.services.OfflineUserDataJobOperation;\nimport com.google.ads.googleads.v25.services.OfflineUserDataJobServiceClient;\nimport com.google.ads.googleads.v25.utils.ErrorUtils;\nimport com.google.ads.googleads.v25.utils.ResourceNames;\nimport com.google.common.collect.ImmutableList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeoutException;\n\n/**\n * Uploads offline data for store sales transactions.\n *\n * <p>This feature is only available to allowlisted accounts. See\n * https://support.google.com/google-ads/answer/7620302 for more details.\n */\npublic class UploadStoreSalesTransactions {\n\n private static class UploadStoreSalesTransactionsParams extends CodeSampleParams {\n\n @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)\n private Long customerId;\n\n @Parameter(\n names = ArgumentNames.OFFLINE_USER_DATA_JOB_TYPE,\n required = false,\n description =\n \"The type of user data in the job (first or third party). If you have an official\"\n + \" store sales partnership with Google, use STORE_SALES_UPLOAD_THIRD_PARTY.\"\n + \" Otherwise, use STORE_SALES_UPLOAD_FIRST_PARTY or omit this parameter.\")\n private OfflineUserDataJobType offlineUserDataJobType =\n OfflineUserDataJobType.STORE_SALES_UPLOAD_FIRST_PARTY;\n\n @Parameter(\n names = ArgumentNames.EXTERNAL_ID,\n description =\n \"Optional (but recommended) external ID to identify the offline user data job\")\n private Long externalId;\n\n @Parameter(\n names = ArgumentNames.CONVERSION_ACTION_ID,\n required = true,\n description = \"The ID of a store sales conversion action\")\n private Long conversionActionId;\n\n @Parameter(\n names = ArgumentNames.CUSTOM_KEY,\n required = false,\n description =\n \"Only required after creating a custom key and custom values in the account.\"\n + \" Custom key and values are used to segment store sales conversions.\"\n + \" This measurement can be used to provide more advanced insights.\")\n private String customKey;\n\n @Parameter(\n names = ArgumentNames.ADVERTISER_UPLOAD_DATE_TIME,\n description = \"Only required if uploading third party data\")\n private String advertiserUploadDateTime;\n\n @Parameter(\n names = ArgumentNames.BRIDGE_MAP_VERSION_ID,\n description = \"Only required if uploading third party data\")\n private String bridgeMapVersionId;\n\n @Parameter(\n names = ArgumentNames.PARTNER_ID,\n description = \"Only required if uploading third party data\")\n private Long partnerId;\n\n @Parameter(\n names = ArgumentNames.ITEM_ID,\n description =\n \"Specify a unique identifier of a product, either the Merchant Center Item ID or\"\n + \" Global Trade Item Number (GTIN). Only required if uploading with item\"\n + \" attributes.\")\n private String itemId;\n\n @Parameter(\n names = ArgumentNames.MERCHANT_CENTER_ACCOUNT_ID,\n description =\n \"A Merchant Center Account ID. Only required if uploading with item attributes.\")\n private Long merchantCenterAccountId;\n\n @Parameter(\n names = ArgumentNames.COUNTRY_CODE,\n description =\n \"A two-letter country code of the location associated with the feed where your items\"\n + \" are uploaded. Only required if uploading with item attributes. For a list of\"\n + \" country codes see the country codes here:\"\n + \" https://developers.google.com/google-ads/api/reference/data/codes-formats#country-codes\")\n private String countryCode;\n\n @Parameter(\n names = ArgumentNames.LANGUAGE_CODE,\n description =\n \"A two-letter language code of the language associated with the feed where your items\"\n + \" are uploaded. Only required if uploading with item attributes. For a list of\"\n + \" language codes see:\"\n + \" https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\")\n private String languageCode;\n\n @Parameter(\n names = ArgumentNames.QUANTITY,\n description =\n \"The number of items sold. Can only be set when at least one other item attribute has\"\n + \" been provided. Only required if uploading with item attributes.\")\n private int quantity;\n\n @Parameter(names = ArgumentNames.AD_PERSONALIZATION_CONSENT, required = false)\n private ConsentStatus adPersonalizationConsent;\n\n @Parameter(names = ArgumentNames.AD_USER_DATA_CONSENT, required = false)\n private ConsentStatus adUserDataConsent;\n }\n\n /** Specifies the value to use if uploading data with custom key and values. */\n private static final String CUSTOM_VALUE = null;\n\n public static void main(String[] args)\n throws InterruptedException,\n ExecutionException,\n TimeoutException,\n UnsupportedEncodingException {\n UploadStoreSalesTransactionsParams params = new UploadStoreSalesTransactionsParams();\n if (!params.parseArguments(args)) {\n\n // Either pass the required parameters for this example on the command line, or insert them\n // into the code here. See the parameter class definition above for descriptions.\n params.customerId = Long.parseLong(\"INSERT_CUSTOMER_ID_HERE\");\n params.offlineUserDataJobType =\n OfflineUserDataJobType.valueOf(\"INSERT_OFFLINE_USER_DATA_JOB_TYPE_HERE\");\n params.conversionActionId = Long.parseLong(\"INSERT_CONVERSION_ACTION_ID_HERE\");\n // OPTIONAL (but recommended): Specify an external ID for the job.\n // params.externalId = Long.parseLong(\"INSERT_EXTERNAL_ID_HERE\");\n\n // OPTIONAL: specify the ad user data consent.\n // params.adUserDataConsent = ConsentStatus.valueOf(\"INSERT_AD_USER_DATA_CONSENT_HERE\");\n\n // OPTIONAL: If uploading data with custom key and values, also specify the following value:\n // params.customKey = \"INSERT_CUSTOM_KEY_HERE\";\n\n // OPTIONAL: If uploading third party data, also specify the following values:\n // params.advertiserUploadDateTime = \"INSERT_ADVERTISER_UPLOAD_DATE_TIME_HERE\";\n // params.bridgeMapVersionId = \"INSERT_BRIDGE_MAP_VERSION_ID_HERE\";\n // params.partnerId = Long.parseLong(\"INSERT_PARTNER_ID_HERE\");\n\n // OPTIONAL: Specify a unique identifier of a product, either the Merchant Center\n // Item ID or Global Trade Item Number (GTIN). Only required if uploading with\n // item attributes.\n // params.itemId = Long.parseLong(\"INSERT_ITEM_ID_HERE\");\n\n // OPTIONAL: Specify a Merchant Center Account ID. Only required if uploading\n // with item attributes.\n // params.merchantCenterAccountId = Long.parseLong(\"INSERT_MERCHANT_CENTER_ID_HERE\");\n\n // OPTIONAL: Specify a two-letter country code of the location associated with the\n // feed where your items are uploaded. Only required if uploading with item\n // attributes.\n // params.countryCode = \"INSERT_COUNTRY_CODE_HERE\";\n\n // OPTIONAL: Specify a two-letter language code of the language associated with\n // the feed where your items are uploaded. Only required if uploading with item\n // attributes.\n // params.languageCode = \"INSERT_LANGUAGE_CODE_HERE\";\n\n // OPTIONAL: Specify a number of items sold. Only required if uploading with item\n // attributes.\n // params.quantity = 1;\n }\n\n GoogleAdsClient googleAdsClient = null;\n try {\n googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();\n } catch (FileNotFoundException fnfe) {\n System.err.printf(\n \"Failed to load GoogleAdsClient configuration from file. Exception: %s%n\", fnfe);\n System.exit(1);\n } catch (IOException ioe) {\n System.err.printf(\"Failed to create GoogleAdsClient. Exception: %s%n\", ioe);\n System.exit(1);\n }\n\n try {\n new UploadStoreSalesTransactions()\n .runExample(\n googleAdsClient,\n params.customerId,\n params.offlineUserDataJobType,\n params.externalId,\n params.conversionActionId,\n params.adPersonalizationConsent,\n params.adUserDataConsent,\n params.customKey,\n params.advertiserUploadDateTime,\n params.bridgeMapVersionId,\n params.partnerId,\n params.itemId,\n params.merchantCenterAccountId,\n params.countryCode,\n params.languageCode,\n params.quantity);\n } catch (GoogleAdsException gae) {\n // GoogleAdsException is the base class for most exceptions thrown by an API request.\n // Instances of this exception have a message and a GoogleAdsFailure that contains a\n // collection of GoogleAdsErrors that indicate the underlying causes of the\n // GoogleAdsException.\n System.err.printf(\n \"Request ID %s failed due to GoogleAdsException. Underlying errors:%n\",\n gae.getRequestId());\n int i = 0;\n for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {\n System.err.printf(\" Error %d: %s%n\", i++, googleAdsError);\n }\n System.exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param googleAdsClient the Google Ads API client.\n * @param customerId the client customer ID.\n * @param offlineUserDataJobType the type of offline user data in the job (first party or third\n * party). If you have an official store sales partnership with Google, use {@code\n * STORE_SALES_UPLOAD_THIRD_PARTY}. Otherwise, use {@code STORE_SALES_UPLOAD_FIRST_PARTY}.\n * @param externalId optional (but recommended) external ID for the offline user data job.\n * @param conversionActionId the ID of a store sales conversion action.\n * @param adPersonalizationConsent the ad personalization consent status.\n * @param adUserDataConsent the ad user data consent status.\n * @param customKey to segment store sales conversions. Only required after creating a custom key\n * and custom values in the account.\n * @param advertiserUploadDateTime date and time the advertiser uploaded data to the partner. Only\n * required for third party uploads.\n * @param bridgeMapVersionId version of partner IDs to be used for uploads. Only required for\n * third party uploads.\n * @param partnerId ID of the third party partner. Only required for third party uploads.\n * @param itemId the ID of the item in merchant center (optional).\n * @param merchantCenterAccountId the ID of the merchant center account (optional).\n * @param countryCode the country code of the item for sale in merchant center.\n * @param languageCode the language of the item for sale in merchant center.\n * @param quantity the number of items that we sold.\n * @throws GoogleAdsException if an API request failed with one or more service errors.\n */\n private void runExample(\n GoogleAdsClient googleAdsClient,\n long customerId,\n OfflineUserDataJobType offlineUserDataJobType,\n Long externalId,\n long conversionActionId,\n ConsentStatus adPersonalizationConsent,\n ConsentStatus adUserDataConsent,\n String customKey,\n String advertiserUploadDateTime,\n String bridgeMapVersionId,\n Long partnerId,\n String itemId,\n Long merchantCenterAccountId,\n String countryCode,\n String languageCode,\n int quantity)\n throws InterruptedException,\n ExecutionException,\n TimeoutException,\n UnsupportedEncodingException {\n String offlineUserDataJobResourceName;\n try (OfflineUserDataJobServiceClient offlineUserDataJobServiceClient =\n googleAdsClient.getLatestVersion().createOfflineUserDataJobServiceClient()) {\n // Creates an offline user data job for uploading transactions.\n offlineUserDataJobResourceName =\n createOfflineUserDataJob(\n offlineUserDataJobServiceClient,\n customerId,\n offlineUserDataJobType,\n externalId,\n customKey,\n advertiserUploadDateTime,\n bridgeMapVersionId,\n partnerId);\n\n // Adds transactions to the job.\n addTransactionsToOfflineUserDataJob(\n offlineUserDataJobServiceClient,\n customerId,\n offlineUserDataJobResourceName,\n conversionActionId,\n adPersonalizationConsent,\n adUserDataConsent,\n customKey,\n itemId,\n merchantCenterAccountId,\n countryCode,\n languageCode,\n quantity);\n\n // Issues an asynchronous request to run the offline user data job.\n offlineUserDataJobServiceClient.runOfflineUserDataJobAsync(offlineUserDataJobResourceName);\n\n // BEWARE! The above call returns an OperationFuture. The execution of that future depends on\n // the thread pool which is owned by offlineUserDataJobServiceClient. If you use this future,\n // you *must* keep the service client in scope too.\n // See https://developers.google.com/google-ads/api/docs/client-libs/java/lro for more detail.\n\n System.out.printf(\n \"Sent request to asynchronously run offline user data job: %s%n\",\n offlineUserDataJobResourceName);\n }\n\n // Offline user data jobs may take up to 24 hours to complete, so instead of waiting for the job\n // to complete, retrieves and displays the job status once and then prints the query to use to\n // check the job again later.\n checkJobStatus(googleAdsClient, customerId, offlineUserDataJobResourceName);\n }\n\n /**\n * Creates an offline user data job for uploading store sales transactions.\n *\n * @return the resource name of the created job.\n */\n private String createOfflineUserDataJob(\n OfflineUserDataJobServiceClient offlineUserDataJobServiceClient,\n long customerId,\n OfflineUserDataJobType offlineUserDataJobType,\n Long externalId,\n String customKey,\n String advertiserUploadDateTime,\n String bridgeMapVersionId,\n Long partnerId) {\n // TIP: If you are migrating from the AdWords API, please note that Google Ads API uses the\n // term \"fraction\" instead of \"rate\". For example, loyaltyRate in the AdWords API is called\n // loyaltyFraction in the Google Ads API.\n StoreSalesMetadata.Builder storeSalesMetadataBuilder =\n // Please refer to https://support.google.com/google-ads/answer/7506124 for additional\n // details.\n StoreSalesMetadata.newBuilder()\n // Sets the fraction of your overall sales that you (or the advertiser, in the third\n // party case) can associate with a customer (email, phone number, address, etc.) in\n // your database or loyalty program.\n // For example, set this to 0.7 if you have 100 transactions over 30 days, and out of\n // those 100 transactions, you can identify 70 by an email address or phone number.\n .setLoyaltyFraction(0.7)\n // Sets the fraction of sales you're uploading out of the overall sales that you (or the\n // advertiser, in the third party case) can associate with a customer. In most cases,\n // you will set this to 1.0.\n // Continuing the example above for loyalty fraction, a value of 1.0 here indicates that\n // you are uploading all 70 of the transactions that can be identified by an email\n // address or phone number.\n .setTransactionUploadFraction(1.0);\n\n if (customKey != null && !customKey.isEmpty()) {\n storeSalesMetadataBuilder.setCustomKey(customKey);\n }\n\n if (OfflineUserDataJobType.STORE_SALES_UPLOAD_THIRD_PARTY == offlineUserDataJobType) {\n // Creates additional metadata required for uploading third party data.\n StoreSalesThirdPartyMetadata storeSalesThirdPartyMetadata =\n StoreSalesThirdPartyMetadata.newBuilder()\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n .setAdvertiserUploadDateTime(advertiserUploadDateTime)\n\n // Sets the fraction of transactions you received from the advertiser that have valid\n // formatting and values. This captures any transactions the advertiser provided to\n // you but which you are unable to upload to Google due to formatting errors or\n // missing data.\n // In most cases, you will set this to 1.0.\n .setValidTransactionFraction(1.0)\n // Sets the fraction of valid transactions (as defined above) you received from the\n // advertiser that you (the third party) have matched to an external user ID on your\n // side.\n // In most cases, you will set this to 1.0.\n .setPartnerMatchFraction(1.0)\n\n // Sets the fraction of transactions you (the third party) are uploading out of the\n // transactions you received from the advertiser that meet both of the following\n // criteria:\n // 1. Are valid in terms of formatting and values. See valid transaction fraction\n // above.\n // 2. You matched to an external user ID on your side. See partner match fraction\n // above.\n // In most cases, you will set this to 1.0.\n .setPartnerUploadFraction(1.0)\n\n // Please speak with your Google representative to get the values to use for the\n // bridge map version and partner IDs.\n\n // Sets the version of partner IDs to be used for uploads.\n .setBridgeMapVersionId(bridgeMapVersionId)\n // Sets the third party partner ID uploading the transactions.\n .setPartnerId(partnerId)\n .build();\n storeSalesMetadataBuilder.setThirdPartyMetadata(storeSalesThirdPartyMetadata);\n }\n\n // Creates a new offline user data job.\n OfflineUserDataJob.Builder offlineUserDataJobBuilder =\n OfflineUserDataJob.newBuilder()\n .setType(offlineUserDataJobType)\n .setStoreSalesMetadata(storeSalesMetadataBuilder);\n if (externalId != null) {\n offlineUserDataJobBuilder.setExternalId(externalId);\n }\n\n // Issues a request to create the offline user data job.\n CreateOfflineUserDataJobResponse createOfflineUserDataJobResponse =\n offlineUserDataJobServiceClient.createOfflineUserDataJob(\n Long.toString(customerId), offlineUserDataJobBuilder.build());\n String offlineUserDataJobResourceName = createOfflineUserDataJobResponse.getResourceName();\n System.out.printf(\n \"Created an offline user data job with resource name: %s.%n\",\n offlineUserDataJobResourceName);\n return offlineUserDataJobResourceName;\n }\n\n /** Adds operations to the job for a set of sample transactions. */\n private void addTransactionsToOfflineUserDataJob(\n OfflineUserDataJobServiceClient offlineUserDataJobServiceClient,\n long customerId,\n String offlineUserDataJobResourceName,\n long conversionActionId,\n ConsentStatus adPersonalizationConsent,\n ConsentStatus adUserDataConsent,\n String customKey,\n String itemId,\n Long merchantId,\n String countryCode,\n String languageCode,\n Integer quantity)\n throws InterruptedException,\n ExecutionException,\n TimeoutException,\n UnsupportedEncodingException {\n // Constructs the operation for each transaction.\n List<OfflineUserDataJobOperation> userDataJobOperations =\n buildOfflineUserDataJobOperations(\n customerId,\n conversionActionId,\n adPersonalizationConsent,\n adUserDataConsent,\n customKey,\n itemId,\n merchantId,\n countryCode,\n languageCode,\n quantity);\n\n // Issues a request to add the operations to the offline user data job.\n AddOfflineUserDataJobOperationsResponse response =\n offlineUserDataJobServiceClient.addOfflineUserDataJobOperations(\n AddOfflineUserDataJobOperationsRequest.newBuilder()\n .setResourceName(offlineUserDataJobResourceName)\n .setEnablePartialFailure(true)\n // Enables warnings (optional).\n .setEnableWarnings(true)\n .addAllOperations(userDataJobOperations)\n .build());\n\n // Prints the status message if any partial failure error is returned.\n // NOTE: The details of each partial failure error are not printed here, you can refer to\n // the example HandlePartialFailure.java to learn more.\n if (response.hasPartialFailureError()) {\n GoogleAdsFailure googleAdsFailure =\n ErrorUtils.getInstance().getGoogleAdsFailure(response.getPartialFailureError());\n googleAdsFailure\n .getErrorsList()\n .forEach(e -> System.out.println(\"Partial failure occurred: \" + e.getMessage()));\n System.out.printf(\n \"Encountered %d partial failure errors while adding %d operations to the offline user \"\n + \"data job: '%s'. Only the successfully added operations will be executed when \"\n + \"the job runs.%n\",\n ErrorUtils.getInstance().getFailedOperationIndices(googleAdsFailure).size(),\n userDataJobOperations.size(),\n response.getPartialFailureError().getMessage());\n\n // Checks if any warnings occurred and displays details.\n if (response.hasWarning()) {\n // Converts the Any in response back to a GoogleAdsFailure object.\n GoogleAdsFailure warningsFailure =\n ErrorUtils.getInstance().getGoogleAdsFailure(response.getWarning());\n // Prints some information about the warnings encountered.\n System.out.println(\n System.out.printf(\"Encountered %d warning(s).%n\", warningsFailure.getErrorsCount()));\n }\n } else {\n System.out.printf(\n \"Successfully added %d operations to the offline user data job.%n\",\n userDataJobOperations.size());\n }\n }\n\n /**\n * Creates a list of offline user data job operations for sample transactions.\n *\n * @return a list of operations.\n */\n private List<OfflineUserDataJobOperation> buildOfflineUserDataJobOperations(\n long customerId,\n long conversionActionId,\n ConsentStatus adPersonalizationConsent,\n ConsentStatus adUserDataConsent,\n String customKey,\n String itemId,\n Long merchantId,\n String countryCode,\n String languageCode,\n Integer quantity)\n throws UnsupportedEncodingException {\n MessageDigest sha256Digest;\n try {\n // Gets a digest for generating hashed values using SHA-256. You must normalize and hash the\n // the value for any field where the name begins with \"hashed\". See the normalizeAndHash()\n // method.\n sha256Digest = MessageDigest.getInstance(\"SHA-256\");\n } catch (NoSuchAlgorithmException e) {\n throw new RuntimeException(\"Missing SHA-256 algorithm implementation\", e);\n }\n\n // Create the first transaction for upload based on an email address and state.\n UserData.Builder userDataWithEmailAddress =\n UserData.newBuilder()\n .addAllUserIdentifiers(\n ImmutableList.of(\n UserIdentifier.newBuilder()\n .setHashedEmail(\n // Email addresses must be normalized and hashed.\n normalizeAndHash(sha256Digest, \"dana@example.com\"))\n .build(),\n UserIdentifier.newBuilder()\n .setAddressInfo(OfflineUserAddressInfo.newBuilder().setState(\"NY\"))\n .build()))\n .setTransactionAttribute(\n TransactionAttribute.newBuilder()\n .setConversionAction(\n ResourceNames.conversionAction(customerId, conversionActionId))\n .setCurrencyCode(\"USD\")\n // Converts the transaction amount from $200 USD to micros.\n .setTransactionAmountMicros(200L * 1_000_000L)\n // Specifies the date and time of the transaction. The format is\n // \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional\n // timezone offset from UTC. If the offset is absent, the API will\n // use the account's timezone as default. Examples: \"2018-03-05 09:15:00\"\n // or \"2018-02-01 14:34:30+03:00\".\n .setTransactionDateTime(\"2020-05-01 23:52:12\"));\n\n // Adds consent information if specified.\n if (adPersonalizationConsent != null || adUserDataConsent != null) {\n Consent.Builder consentBuilder = Consent.newBuilder();\n if (adPersonalizationConsent != null) {\n consentBuilder.setAdPersonalization(adPersonalizationConsent);\n }\n if (adUserDataConsent != null) {\n consentBuilder.setAdUserData(adUserDataConsent);\n }\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy for details.\n userDataWithEmailAddress.setConsent(consentBuilder);\n }\n\n // Optional: If uploading data with custom key and values, also assign the custom value.\n if (customKey != null) {\n userDataWithEmailAddress.getTransactionAttributeBuilder().setCustomValue(CUSTOM_VALUE);\n }\n\n // Creates the second transaction for upload based on a physical address.\n UserData.Builder userDataWithPhysicalAddress =\n UserData.newBuilder()\n .addUserIdentifiers(\n UserIdentifier.newBuilder()\n .setAddressInfo(\n OfflineUserAddressInfo.newBuilder()\n .setHashedFirstName(normalizeAndHash(sha256Digest, \"Dana\"))\n .setHashedLastName(normalizeAndHash(sha256Digest, \"Quinn\"))\n .setCountryCode(\"US\")\n .setPostalCode(\"10011\")))\n .setTransactionAttribute(\n TransactionAttribute.newBuilder()\n .setConversionAction(\n ResourceNames.conversionAction(customerId, conversionActionId))\n .setCurrencyCode(\"EUR\")\n // Converts the transaction amount from 450 EUR to micros.\n .setTransactionAmountMicros(450L * 1_000_000L)\n // Specifies the date and time of the transaction. This date and time will be\n // interpreted by the API using the Google Ads customer's time zone.\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n .setTransactionDateTime(\"2020-05-14 19:07:02\"));\n\n if (itemId != null) {\n userDataWithPhysicalAddress\n .getTransactionAttributeBuilder()\n .getItemAttributeBuilder()\n .setItemId(itemId)\n .setMerchantId(merchantId)\n .setCountryCode(countryCode)\n .setLanguageCode(languageCode)\n .setQuantity(quantity);\n }\n\n // Creates the operations to add the two transactions.\n List<OfflineUserDataJobOperation> operations = new ArrayList<>();\n for (UserData userData :\n Arrays.asList(userDataWithEmailAddress.build(), userDataWithPhysicalAddress.build())) {\n operations.add(OfflineUserDataJobOperation.newBuilder().setCreate(userData).build());\n }\n\n return operations;\n }\n\n /**\n * Returns the result of normalizing and then hashing the string using the provided digest.\n * Private customer data must be hashed during upload, as described at\n * https://support.google.com/google-ads/answer/7506124.\n *\n * @param digest the digest to use to hash the normalized string.\n * @param s the string to normalize and hash.\n */\n private String normalizeAndHash(MessageDigest digest, String s)\n throws UnsupportedEncodingException {\n // Normalizes by removing leading and trailing whitespace and converting all characters to\n // lower case.\n String normalized = s.trim().toLowerCase();\n // Hashes the normalized string using the hashing algorithm.\n byte[] hash = digest.digest(normalized.getBytes(\"UTF-8\"));\n StringBuilder result = new StringBuilder();\n for (byte b : hash) {\n result.append(String.format(\"%02x\", b));\n }\n\n return result.toString();\n }\n\n /** Retrieves, checks, and prints the status of the offline user data job. */\n private void checkJobStatus(\n GoogleAdsClient googleAdsClient, long customerId, String offlineUserDataJobResourceName) {\n try (GoogleAdsServiceClient googleAdsServiceClient =\n googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {\n String query =\n String.format(\n \"SELECT offline_user_data_job.resource_name, \"\n + \"offline_user_data_job.id, \"\n + \"offline_user_data_job.status, \"\n + \"offline_user_data_job.type, \"\n + \"offline_user_data_job.failure_reason \"\n + \"FROM offline_user_data_job \"\n + \"WHERE offline_user_data_job.resource_name = '%s'\",\n offlineUserDataJobResourceName);\n // Issues the query and gets the GoogleAdsRow containing the job from the response.\n GoogleAdsRow googleAdsRow =\n googleAdsServiceClient\n .search(Long.toString(customerId), query)\n .iterateAll()\n .iterator()\n .next();\n OfflineUserDataJob offlineUserDataJob = googleAdsRow.getOfflineUserDataJob();\n System.out.printf(\n \"Offline user data job ID %d with type '%s' has status: %s%n\",\n offlineUserDataJob.getId(), offlineUserDataJob.getType(), offlineUserDataJob.getStatus());\n OfflineUserDataJobStatus jobStatus = offlineUserDataJob.getStatus();\n if (OfflineUserDataJobStatus.FAILED == jobStatus) {\n System.out.printf(\" Failure reason: %s%n\", offlineUserDataJob.getFailureReason());\n } else if (OfflineUserDataJobStatus.PENDING == jobStatus\n || OfflineUserDataJobStatus.RUNNING == jobStatus) {\n System.out.println();\n System.out.printf(\n \"To check the status of the job periodically, use the following GAQL query with\"\n + \" GoogleAdsService.search:%n%s%n\",\n query);\n }\n }\n }\n}\nUploadStoreSalesTransactions.java\n```\n\nExample:\n```text\n// Copyright 2020 Google LLC\n//\n// 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// http://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.\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Security.Cryptography;\nusing System.Text;\nusing CommandLine;\nusing Google.Ads.Gax.Examples;\nusing Google.Ads.GoogleAds.Lib;\nusing Google.Ads.GoogleAds.V25.Common;\nusing Google.Ads.GoogleAds.V25.Errors;\nusing Google.Ads.GoogleAds.V25.Resources;\nusing Google.Ads.GoogleAds.V25.Services;\nusing static Google.Ads.GoogleAds.V25.Enums.ConsentStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.OfflineUserDataJobStatusEnum.Types;\nusing static Google.Ads.GoogleAds.V25.Enums.OfflineUserDataJobTypeEnum.Types;\n\nnamespace Google.Ads.GoogleAds.Examples.V25\n{\n /// <summary>\n /// This code example uploads offline data for store sales transactions.\n /// This feature is only available to allowlisted accounts. See\n /// https://support.google.com/google-ads/answer/7620302 for more details.\n /// </summary>\n public class UploadStoreSalesTransactions : ExampleBase\n {\n /// <summary>\n /// Command line options for running the <see cref=\"UploadStoreSalesTransactions\"/> example.\n /// </summary>\n public class Options : OptionsBase\n {\n /// <summary>\n /// The Google Ads customer ID for which the call is made.\n /// </summary>\n [Option(\"customerId\", Required = true, HelpText =\n \"The Google Ads customer ID for which the call is made.\")]\n public long CustomerId { get; set; }\n\n /// <summary>\n /// The ID of a store sales conversion action.\n /// </summary>\n [Option(\"conversionActionId\", Required = true, HelpText =\n \"The ID of a store sales conversion action.\")]\n public long ConversionActionId { get; set; }\n\n /// <summary>\n /// The type of user data in the job (first or third party). If you have an official\n /// store sales partnership with Google, use StoreSalesUploadThirdParty. Otherwise,\n /// use StoreSalesUploadFirstParty or omit this parameter.\n /// </summary>\n [Option(\"offlineUserDataJobType\", Required = false, HelpText =\n \"The type of user data in the job (first or third party). If you have an\" +\n \" official store sales partnership with Google, use \" +\n \"StoreSalesUploadThirdParty. Otherwise, use StoreSalesUploadFirstParty or \" +\n \"omit this parameter.\",\n Default = OfflineUserDataJobType.StoreSalesUploadFirstParty)]\n public OfflineUserDataJobType OfflineUserDataJobType { get; set; }\n\n /// <summary>\n /// Optional (but recommended) external ID to identify the offline user data job.\n /// </summary>\n [Option(\"externalId\", Required = false, HelpText =\n \"Optional (but recommended) external ID to identify the offline user data job.\",\n Default = null)]\n public long? ExternalId { get; set; }\n\n /// <summary>\n /// Date and time the advertiser uploaded data to the partner. Only required if\n /// uploading third party data.\n /// </summary>\n [Option(\"advertiserUploadDateTime\", Required = false, HelpText =\n \"Date and time the advertiser uploaded data to the partner. Only required if \" +\n \"uploading third party data.\", Default = null)]\n public string AdvertiserUploadDateTime { get; set; }\n\n /// <summary>\n /// Version of partner IDs to be used for uploads. Only required if uploading third\n /// party data.\n /// </summary>\n [Option(\"bridgeMapVersionId\", Required = false, HelpText =\n \"Version of partner IDs to be used for uploads. Only required if uploading \" +\n \"third party data.\", Default = null)]\n public string BridgeMapVersionId { get; set; }\n\n /// <summary>\n /// ID of the third party partner. Only required if uploading third party data.\n /// </summary>\n [Option(\"partnerId\", Required = false, HelpText =\n \"ID of the third party partner. Only required if uploading third party data.\",\n Default = null)]\n public long? PartnerId { get; set; }\n\n /// <summary>\n /// Optional custom key name. Only required if uploading data with custom key and\n /// values.\n /// </summary>\n [Option(\"customKey\", Required = false, HelpText =\n \"Optional custom key name. Only required if uploading data with custom key and\" +\n \" values.\", Default = null)]\n public string CustomKey { get; set; }\n\n /// <summary>\n /// A unique identifier of a product, either the Merchant Center Item ID or Global Trade\n /// Item Number (GTIN). Only required if uploading with item attributes.\n /// </summary>\n [Option(\"itemId\", Required = false, HelpText =\n \"A unique identifier of a product, either the Merchant Center Item ID or \" +\n \"Global Trade Item Number (GTIN). Only required if uploading with item \" +\n \"attributes.\",\n Default = null)]\n public string ItemId { get; set; }\n\n /// <summary>\n /// A Merchant Center Account ID. Only required if uploading with item attributes.\n /// </summary>\n [Option(\"merchantCenterAccountId\", Required = false, HelpText =\n \"A Merchant Center Account ID. Only required if uploading with item \" +\n \"attributes.\",\n Default = null)]\n public long? MerchantCenterAccountId { get; set; }\n\n /// <summary>\n /// A two-letter country code of the location associated with the feed where your items\n /// are uploaded. Only required if uploading with item attributes.\n /// For a list of country codes see:\n /// https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-16\n /// </summary>\n [Option(\"countryCode\", Required = false, HelpText =\n \"A two-letter country code of the location associated with the feed where your \" +\n \"items are uploaded. Only required if uploading with item attributes.\\nFor a \" +\n \"list of country codes see: \" +\n \"https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-16\",\n Default = null)]\n public string CountryCode { get; set; }\n\n /// <summary>\n /// A two-letter language code of the language associated with the feed where your items\n /// are uploaded. Only required if uploading with item attributes. For a list of\n /// language codes see:\n /// https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n /// </summary>\n [Option(\"languageCode\", Required = false, HelpText =\n \"A two-letter language code of the language associated with the feed where \" +\n \"your items are uploaded. Only required if uploading with item attributes.\\n\" +\n \"For a list of language codes see: \" +\n \"https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\",\n Default = null)]\n public string LanguageCode { get; set; }\n\n /// <summary>\n /// The number of items sold. Can only be set when at least one other item attribute has\n /// been provided. Only required if uploading with item attributes.\n /// </summary>\n [Option(\"quantity\", Required = false, HelpText =\n \"The number of items sold. Only required if uploading with item attributes.\",\n Default = 1)]\n public long Quantity { get; set; }\n\n /// <summary>\n /// The consent status for ad personalization.\n /// </summary>\n [Option(\"adPersonalizationConsent\", Required = false, HelpText =\n \"The consent status for ad user data.\")]\n public ConsentStatus? AdPersonalizationConsent { get; set; }\n\n /// <summary>\n /// The consent status for ad user data.\n /// </summary>\n [Option(\"adUserDataConsent\", Required = false, HelpText =\n \"The consent status for ad user data.\")]\n public ConsentStatus? AdUserDataConsent { get; set; }\n }\n\n /// <summary>\n /// Main method, to run this code example as a standalone application.\n /// </summary>\n /// <param name=\"args\">The command line arguments.</param>\n public static void Main(string[] args)\n {\n Options options = ExampleUtilities.ParseCommandLine<Options>(args);\n\n UploadStoreSalesTransactions codeExample = new UploadStoreSalesTransactions();\n Console.WriteLine(codeExample.Description);\n codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.ConversionActionId,\n options.OfflineUserDataJobType, options.ExternalId,\n options.AdvertiserUploadDateTime, options.BridgeMapVersionId, options.PartnerId,\n options.CustomKey, options.ItemId, options.MerchantCenterAccountId,\n options.CountryCode,\n options.LanguageCode, options.Quantity, options.AdPersonalizationConsent,\n options.AdUserDataConsent);\n }\n\n // Gets a digest for generating hashed values using SHA-256. You must normalize and hash the\n // the value for any field where the name begins with \"hashed\". See the normalizeAndHash()\n // method.\n private static readonly SHA256 _digest = SHA256.Create();\n\n // If uploading data with custom key and values, specify the value:\n private const string CUSTOM_VALUE = \"INSERT_CUSTOM_VALUE_HERE\";\n\n /// <summary>\n /// Returns a description about the code example.\n /// </summary>\n public override string Description =>\n \"This code example uploads offline data for store sales transactions. This feature \" +\n \"is only available to allowlisted accounts. See \" +\n \"https://support.google.com/google-ads/answer/7620302 for more details.\";\n\n /// <summary>\n /// Runs the code example.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"conversionActionId\">The ID of a store sales conversion action.</param>\n /// <param name=\"offlineUserDataJobType\">The type of user data in the job (first or third\n /// party). If you have an official store sales partnership with Google, use\n /// StoreSalesUploadThirdParty. Otherwise, use StoreSalesUploadFirstParty or\n /// omit this parameter.</param>\n /// <param name=\"externalId\">Optional (but recommended) external ID to identify the offline\n /// user data job.</param>\n /// <param name=\"advertiserUploadDateTime\">Date and time the advertiser uploaded data to the\n /// partner. Only required if uploading third party data.</param>\n /// <param name=\"bridgeMapVersionId\">Version of partner IDs to be used for uploads. Only\n /// required if uploading third party data.</param>\n /// <param name=\"partnerId\">ID of the third party partner. Only required if uploading third\n /// party data.</param>\n /// <param name=\"customKey\">Optional custom key name. Only required if uploading data\n /// with custom key and values.</param>\n /// <param name=\"itemId\">A unique identifier of a product, either the Merchant Center Item\n /// ID or Global Trade Item Number (GTIN). Only required if uploading with item\n /// attributes.</param>\n /// <param name=\"merchantCenterAccountId\">A Merchant Center Account ID. Only required if uploading with\n /// item attributes.</param>\n /// <param name=\"countryCode\">A two-letter country code of the location associated with the\n /// feed where your items are uploaded. Only required if uploading with item\n /// attributes.</param>\n /// <param name=\"languageCode\">A two-letter language code of the language associated with\n /// the feed where your items are uploaded. Only required if uploading with item\n /// attributes.</param>\n /// <param name=\"quantity\">The number of items sold. Only required if uploading with item\n /// attributes.</param>\n /// <param name=\"adPersonalizationConsent\">The consent status for ad personalization.\n /// </param>\n /// <param name=\"adUserDataConsent\">The consent status for ad user data.</param>\n public void Run(GoogleAdsClient client, long customerId, long conversionActionId,\n OfflineUserDataJobType offlineUserDataJobType, long? externalId,\n string advertiserUploadDateTime, string bridgeMapVersionId, long? partnerId,\n string customKey, string itemId, long? merchantCenterAccountId, string countryCode,\n string languageCode, long quantity, ConsentStatus? adPersonalizationConsent,\n ConsentStatus? adUserDataConsent)\n {\n // Get the OfflineUserDataJobServiceClient.\n OfflineUserDataJobServiceClient offlineUserDataJobServiceClient =\n client.GetService(Services.V25.OfflineUserDataJobService);\n\n // Ensure that a valid job type is provided.\n if (offlineUserDataJobType != OfflineUserDataJobType.StoreSalesUploadFirstParty &\n offlineUserDataJobType != OfflineUserDataJobType.StoreSalesUploadThirdParty)\n {\n Console.WriteLine(\"Invalid job type specified, defaulting to First Party.\");\n offlineUserDataJobType = OfflineUserDataJobType.StoreSalesUploadFirstParty;\n }\n\n try\n {\n // Creates an offline user data job for uploading transactions.\n string offlineUserDataJobResourceName =\n CreateOfflineUserDataJob(offlineUserDataJobServiceClient, customerId,\n offlineUserDataJobType, externalId, advertiserUploadDateTime,\n bridgeMapVersionId, partnerId, customKey);\n\n // Adds transactions to the job.\n AddTransactionsToOfflineUserDataJob(offlineUserDataJobServiceClient, customerId,\n offlineUserDataJobResourceName, conversionActionId, customKey, itemId,\n merchantCenterAccountId, countryCode, languageCode, quantity,\n adPersonalizationConsent, adUserDataConsent);\n\n // Issues an asynchronous request to run the offline user data job.\n offlineUserDataJobServiceClient.RunOfflineUserDataJobAsync(\n offlineUserDataJobResourceName);\n\n Console.WriteLine(\"Sent request to asynchronously run offline user data job \" +\n $\"{offlineUserDataJobResourceName}.\");\n\n // Offline user data jobs may take up to 24 hours to complete, so instead of waiting\n // for the job to complete, retrieves and displays the job status once and then\n // prints the query to use to check the job again later.\n CheckJobStatus(client, customerId, offlineUserDataJobResourceName);\n }\n catch (GoogleAdsException e)\n {\n Console.WriteLine(\"Failure:\");\n Console.WriteLine($\"Message: {e.Message}\");\n Console.WriteLine($\"Failure: {e.Failure}\");\n Console.WriteLine($\"Request ID: {e.RequestId}\");\n throw;\n }\n }\n\n /// <summary>\n /// Creates an offline user data job for uploading store sales transactions.\n /// </summary>\n /// <param name=\"offlineUserDataJobServiceClient\">The offline user data job service\n /// client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"offlineUserDataJobType\">The type of user data in the job (first or third\n /// party). If you have an official store sales partnership with Google, use\n /// StoreSalesUploadThirdParty. Otherwise, use StoreSalesUploadFirstParty or\n /// omit this parameter.</param>\n /// <param name=\"externalId\">Optional (but recommended) external ID to identify the offline\n /// user data job.</param>\n /// <param name=\"advertiserUploadDateTime\">Date and time the advertiser uploaded data to the\n /// partner. Only required if uploading third party data.</param>\n /// <param name=\"bridgeMapVersionId\">Version of partner IDs to be used for uploads. Only\n /// required if uploading third party data.</param>\n /// <param name=\"partnerId\">ID of the third party partner. Only required if uploading third\n /// party data.</param>\n /// <param name=\"customKey\">The custom key, or null if not uploading data with custom key\n /// and value.</param>\n /// <returns>The resource name of the created job.</returns>\n private string CreateOfflineUserDataJob(\n OfflineUserDataJobServiceClient offlineUserDataJobServiceClient, long customerId,\n OfflineUserDataJobType offlineUserDataJobType, long? externalId,\n string advertiserUploadDateTime, string bridgeMapVersionId, long? partnerId,\n string customKey)\n {\n // TIP: If you are migrating from the AdWords API, please note that Google Ads API uses\n // the term \"fraction\" instead of \"rate\". For example, loyaltyRate in the AdWords API is\n // called loyaltyFraction in the Google Ads API.\n\n // Please refer to https://support.google.com/google-ads/answer/7506124 for additional\n // details.\n StoreSalesMetadata storeSalesMetadata = new StoreSalesMetadata()\n {\n // Sets the fraction of your overall sales that you (or the advertiser, in the third\n // party case) can associate with a customer (email, phone number, address, etc.) in\n // your database or loyalty program.\n // For example, set this to 0.7 if you have 100 transactions over 30 days, and out\n // of those 100 transactions, you can identify 70 by an email address or phone\n // number.\n LoyaltyFraction = 0.7,\n // Sets the fraction of sales you're uploading out of the overall sales that you (or\n // the advertiser, in the third party case) can associate with a customer. In most\n // cases, you will set this to 1.0.\n // Continuing the example above for loyalty fraction, a value of 1.0 here indicates\n // that you are uploading all 70 of the transactions that can be identified by an\n // email address or phone number.\n TransactionUploadFraction = 1.0\n };\n\n // Apply the custom key if provided.\n if (!string.IsNullOrEmpty(customKey))\n {\n storeSalesMetadata.CustomKey = customKey;\n }\n\n // Creates additional metadata required for uploading third party data.\n if (offlineUserDataJobType == OfflineUserDataJobType.StoreSalesUploadThirdParty)\n {\n StoreSalesThirdPartyMetadata storeSalesThirdPartyMetadata =\n new StoreSalesThirdPartyMetadata()\n {\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n AdvertiserUploadDateTime = advertiserUploadDateTime,\n\n // Sets the fraction of transactions you received from the advertiser that\n // have valid formatting and values. This captures any transactions the\n // advertiser provided to you but which you are unable to upload to Google\n // due to formatting errors or missing data.\n // In most cases, you will set this to 1.0.\n ValidTransactionFraction = 1.0,\n\n // Sets the fraction of valid transactions (as defined above) you received\n // from the advertiser that you (the third party) have matched to an\n // external user ID on your side.\n // In most cases, you will set this to 1.0.\n PartnerMatchFraction = 1.0,\n\n // Sets the fraction of transactions you (the third party) are uploading out\n // of the transactions you received from the advertiser that meet both of\n // the following criteria:\n // 1. Are valid in terms of formatting and values. See valid transaction\n // fraction above.\n // 2. You matched to an external user ID on your side. See partner match\n // fraction above.\n // In most cases, you will set this to 1.0.\n PartnerUploadFraction = 1.0,\n\n // Sets the version of partner IDs to be used for uploads.\n // Please speak with your Google representative to get the values to use for\n // the bridge map version and partner IDs.\n BridgeMapVersionId = bridgeMapVersionId,\n };\n\n // Sets the third party partner ID uploading the transactions.\n if (partnerId.HasValue)\n {\n storeSalesThirdPartyMetadata.PartnerId = partnerId.Value;\n }\n\n storeSalesMetadata.ThirdPartyMetadata = storeSalesThirdPartyMetadata;\n }\n\n // Creates a new offline user data job.\n OfflineUserDataJob offlineUserDataJob = new OfflineUserDataJob()\n {\n Type = offlineUserDataJobType,\n StoreSalesMetadata = storeSalesMetadata\n };\n\n if (externalId.HasValue)\n {\n offlineUserDataJob.ExternalId = externalId.Value;\n }\n\n // Issues a request to create the offline user data job.\n CreateOfflineUserDataJobResponse createOfflineUserDataJobResponse =\n offlineUserDataJobServiceClient.CreateOfflineUserDataJob(\n customerId.ToString(), offlineUserDataJob);\n string offlineUserDataJobResourceName = createOfflineUserDataJobResponse.ResourceName;\n Console.WriteLine(\"Created an offline user data job with resource name: \" +\n $\"{offlineUserDataJobResourceName}.\");\n return offlineUserDataJobResourceName;\n }\n\n /// <summary>\n /// Adds operations to a job for a set of sample transactions.\n /// </summary>\n /// <param name=\"offlineUserDataJobServiceClient\">The offline user data job service\n /// client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"offlineUserDataJobResourceName\">The resource name of the job to which to\n /// add transactions.</param>\n /// <param name=\"conversionActionId\">The ID of a store sales conversion action.</param>\n /// <param name=\"customKey\">The custom key, or null if not uploading data with custom key\n /// and value.</param>\n /// <param name=\"itemId\">A unique identifier of a product, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"merchantCenterAccountId\">A Merchant Center Account ID, or null if not\n /// uploading with item attributes.</param>\n /// <param name=\"countryCode\">A two-letter country code, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"languageCode\">A two-letter language code, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"quantity\">The number of items sold, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"adPersonalizationConsent\">The consent status for ad personalization.\n /// </param>\n /// <param name=\"adUserDataConsent\">The consent status for ad user data.</param>\n private void AddTransactionsToOfflineUserDataJob(\n OfflineUserDataJobServiceClient offlineUserDataJobServiceClient, long customerId,\n string offlineUserDataJobResourceName, long conversionActionId, string customKey,\n string itemId, long? merchantCenterAccountId, string countryCode, string languageCode,\n long quantity, ConsentStatus? adPersonalizationConsent,\n ConsentStatus? adUserDataConsent)\n {\n // Constructions an operation for each transaction.\n List<OfflineUserDataJobOperation> userDataJobOperations =\n BuildOfflineUserDataJobOperations(customerId, conversionActionId, customKey, itemId,\n merchantCenterAccountId, countryCode, languageCode, quantity,\n adPersonalizationConsent, adUserDataConsent);\n\n // Constructs a request with partial failure enabled to add the operations to the\n // offline user data job, and enable_warnings set to true to retrieve warnings.\n AddOfflineUserDataJobOperationsRequest request =\n new AddOfflineUserDataJobOperationsRequest()\n {\n EnablePartialFailure = true,\n ResourceName = offlineUserDataJobResourceName,\n Operations = { userDataJobOperations },\n EnableWarnings = true,\n };\n\n AddOfflineUserDataJobOperationsResponse response = offlineUserDataJobServiceClient\n .AddOfflineUserDataJobOperations(request);\n\n // Prints the status message if any partial failure error is returned.\n // NOTE: The details of each partial failure error are not printed here, you can refer\n // to the example HandlePartialFailure.cs to learn more.\n if (response.PartialFailureError != null)\n {\n Console.WriteLine($\"Encountered {response.PartialFailureError.Details.Count} \" +\n $\"partial failure errors while adding {userDataJobOperations.Count} \" +\n \"operations to the offline user data job: \" +\n $\"'{response.PartialFailureError.Message}'. Only the successfully added \" +\n \"operations will be executed when the job runs.\");\n }\n else\n {\n Console.WriteLine($\"Successfully added {userDataJobOperations.Count} operations \" +\n \"to the offline user data job.\");\n }\n\n // Prints the number of warnings if any warnings are returned. You can access\n // details of each warning using the same approach you'd use for partial failure\n // errors.\n if (request.EnableWarnings && response.Warnings != null)\n {\n // Extracts the warnings from the response.\n GoogleAdsFailure warnings = response.Warnings;\n Console.WriteLine($\"{warnings.Errors.Count} warning(s) occurred\");\n }\n }\n\n /// <summary>\n /// Creates a list of offline user data job operations for sample transactions.\n /// </summary>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"conversionActionId\">The ID of a store sales conversion action.</param>\n /// <param name=\"customKey\">The custom key, or null if not uploading data with custom key\n /// and value.</param>\n /// <param name=\"itemId\">A unique identifier of a product, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"merchantCenterAccountId\">A Merchant Center Account ID, or null if not\n /// uploading with item attributes.</param>\n /// <param name=\"countryCode\">A two-letter country code, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"languageCode\">A two-letter language code, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"quantity\">The number of items sold, or null if not uploading with\n /// item attributes.</param>\n /// <param name=\"adPersonalizationConsent\">The consent status for ad personalization.\n /// </param>\n /// <param name=\"adUserDataConsent\">The consent status for ad user data.</param>\n /// <returns>A list of operations.</returns>\n private List<OfflineUserDataJobOperation> BuildOfflineUserDataJobOperations(long customerId,\n long conversionActionId, string customKey, string itemId, long? merchantCenterAccountId,\n string countryCode, string languageCode, long quantity,\n ConsentStatus? adPersonalizationConsent, ConsentStatus? adUserDataConsent)\n {\n // Create the first transaction for upload based on an email address and state.\n UserData userDataWithEmailAddress = new UserData()\n {\n UserIdentifiers =\n {\n new UserIdentifier()\n {\n // Email addresses must be normalized and hashed.\n HashedEmail = NormalizeAndHash(\"dana@example.com\")\n },\n new UserIdentifier()\n {\n AddressInfo = new OfflineUserAddressInfo()\n {\n State = \"NY\"\n }\n },\n },\n TransactionAttribute = new TransactionAttribute()\n {\n ConversionAction =\n ResourceNames.ConversionAction(customerId, conversionActionId),\n CurrencyCode = \"USD\",\n // Converts the transaction amount from $200 USD to micros.\n // If item attributes are provided, this value represents the total value of the\n // items after multiplying the unit price per item by the quantity provided in\n // the ItemAttribute.\n TransactionAmountMicros = 200L * 1_000_000L,\n // Specifies the date and time of the transaction. The format is\n // \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional\n // timezone offset from UTC. If the offset is absent, the API will\n // use the account's timezone as default. Examples: \"2018-03-05 09:15:00\"\n // or \"2018-02-01 14:34:30+03:00\".\n TransactionDateTime =\n DateTime.Today.AddDays(-2).ToString(\"yyyy-MM-dd HH:mm:ss\")\n }\n };\n\n // Set the custom value if a custom key was provided.\n if (!string.IsNullOrEmpty(customKey))\n {\n userDataWithEmailAddress.TransactionAttribute.CustomValue = CUSTOM_VALUE;\n }\n\n if (adUserDataConsent != null || adPersonalizationConsent != null)\n {\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy\n // for details.\n userDataWithEmailAddress.Consent = new Consent();\n\n if (adPersonalizationConsent != null)\n {\n userDataWithEmailAddress.Consent.AdPersonalization =\n (ConsentStatus)adPersonalizationConsent;\n }\n\n if (adUserDataConsent != null)\n {\n userDataWithEmailAddress.Consent.AdUserData = (ConsentStatus)adUserDataConsent;\n }\n }\n\n // Creates the second transaction for upload based on a physical address.\n UserData userDataWithPhysicalAddress = new UserData()\n {\n UserIdentifiers =\n {\n new UserIdentifier()\n {\n AddressInfo = new OfflineUserAddressInfo()\n {\n // Names must be normalized and hashed.\n HashedFirstName = NormalizeAndHash(\"Alex\"),\n HashedLastName = NormalizeAndHash(\"Quinn\"),\n CountryCode = \"US\",\n PostalCode = \"10011\"\n }\n }\n },\n TransactionAttribute = new TransactionAttribute()\n {\n ConversionAction =\n ResourceNames.ConversionAction(customerId, conversionActionId),\n CurrencyCode = \"EUR\",\n // Converts the transaction amount from 450 EUR to micros.\n TransactionAmountMicros = 450L * 1_000_000L,\n // Specifies the date and time of the transaction. This date and time will be\n // interpreted by the API using the Google Ads customer's time zone.\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n // e.g. \"2020-05-14 19:07:02\".\n TransactionDateTime = DateTime.Today.AddDays(-1).ToString(\"yyyy-MM-dd HH:mm:ss\")\n }\n };\n\n // Set the item attribute if provided.\n if (!string.IsNullOrEmpty(itemId))\n {\n userDataWithPhysicalAddress.TransactionAttribute.ItemAttribute = new ItemAttribute\n {\n ItemId = itemId,\n MerchantId = merchantCenterAccountId.Value,\n CountryCode = countryCode,\n LanguageCode = languageCode,\n // Quantity field should only be set when at least one of the other item\n // attributes is present.\n Quantity = quantity\n };\n }\n\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy\n // for details.\n userDataWithPhysicalAddress.Consent = new Consent()\n {\n AdPersonalization = ConsentStatus.Granted,\n AdUserData = ConsentStatus.Denied\n };\n\n\n // Creates the operations to add the two transactions.\n List<OfflineUserDataJobOperation> operations = new List<OfflineUserDataJobOperation>()\n {\n new OfflineUserDataJobOperation()\n {\n Create = userDataWithEmailAddress\n },\n new OfflineUserDataJobOperation()\n {\n Create = userDataWithPhysicalAddress\n }\n };\n\n return operations;\n }\n\n /// <summary>\n /// Normalizes and hashes a string value.\n /// </summary>\n /// <param name=\"value\">The value to normalize and hash.</param>\n /// <returns>The normalized and hashed value.</returns>\n private static string NormalizeAndHash(string value)\n {\n return ToSha256String(_digest, ToNormalizedValue(value));\n }\n\n /// <summary>\n /// Hash a string value using SHA-256 hashing algorithm.\n /// </summary>\n /// <param name=\"digest\">Provides the algorithm for SHA-256.</param>\n /// <param name=\"value\">The string value (e.g. an email address) to hash.</param>\n /// <returns>The hashed value.</returns>\n private static string ToSha256String(SHA256 digest, string value)\n {\n byte[] digestBytes = digest.ComputeHash(Encoding.UTF8.GetBytes(value));\n // Convert the byte array into an unhyphenated hexadecimal string.\n return BitConverter.ToString(digestBytes).Replace(\"-\", string.Empty);\n }\n\n /// <summary>\n /// Removes leading and trailing whitespace and converts all characters to\n /// lower case.\n /// </summary>\n /// <param name=\"value\">The value to normalize.</param>\n /// <returns>The normalized value.</returns>\n private static string ToNormalizedValue(string value)\n {\n return value.Trim().ToLower();\n }\n\n /// <summary>\n /// Retrieves, checks, and prints the status of the offline user data job.\n /// </summary>\n /// <param name=\"client\">The Google Ads client.</param>\n /// <param name=\"customerId\">The Google Ads customer ID for which the call is made.</param>\n /// <param name=\"offlineUserDataJobResourceName\">The resource name of the job whose status\n /// you wish to check.</param>\n private void CheckJobStatus(GoogleAdsClient client, long customerId,\n string offlineUserDataJobResourceName)\n {\n GoogleAdsServiceClient googleAdsServiceClient =\n client.GetService(Services.V25.GoogleAdsService);\n\n string query = $@\"SELECT offline_user_data_job.resource_name,\n offline_user_data_job.id,\n offline_user_data_job.status,\n offline_user_data_job.type,\n offline_user_data_job.failure_reason\n FROM offline_user_data_job\n WHERE offline_user_data_job.resource_name = '{offlineUserDataJobResourceName}'\";\n\n // Issues the query and gets the GoogleAdsRow containing the job from the response.\n GoogleAdsRow googleAdsRow = googleAdsServiceClient.Search(\n customerId.ToString(), query).First();\n\n OfflineUserDataJob offlineUserDataJob = googleAdsRow.OfflineUserDataJob;\n\n OfflineUserDataJobStatus jobStatus = offlineUserDataJob.Status;\n Console.WriteLine($\"Offline user data job ID {offlineUserDataJob.Id} with type \" +\n $\"'{offlineUserDataJob.Type}' has status {offlineUserDataJob.Status}.\");\n\n if (jobStatus == OfflineUserDataJobStatus.Failed)\n {\n Console.WriteLine($\"\\tFailure reason: {offlineUserDataJob.FailureReason}\");\n }\n else if (jobStatus == OfflineUserDataJobStatus.Pending |\n jobStatus == OfflineUserDataJobStatus.Running)\n {\n Console.WriteLine(\"\\nTo check the status of the job periodically, use the\" +\n $\"following GAQL query with GoogleAdsService.Search:\\n{query}\\n\");\n }\n }\n }\n}\nUploadStoreSalesTransactions.cs\n```\n\nExample:\n```text\n<?php\n\n/**\n * Copyright 2020 Google LLC\n *\n * 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.\n */\n\nnamespace Google\\Ads\\GoogleAds\\Examples\\Remarketing;\n\nrequire __DIR__ . '/../../vendor/autoload.php';\n\nuse GetOpt\\GetOpt;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentNames;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\ArgumentParser;\nuse Google\\Ads\\GoogleAds\\Examples\\Utils\\Helper;\nuse Google\\Ads\\GoogleAds\\Lib\\OAuth2TokenBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClient;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsClientBuilder;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsException;\nuse Google\\Ads\\GoogleAds\\Lib\\V25\\GoogleAdsServerStreamDecorator;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\GoogleAdsFailures;\nuse Google\\Ads\\GoogleAds\\Util\\V25\\ResourceNames;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\Consent;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\ItemAttribute;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\OfflineUserAddressInfo;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\StoreSalesMetadata;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\StoreSalesThirdPartyMetadata;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\TransactionAttribute;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\UserData;\nuse Google\\Ads\\GoogleAds\\V25\\Common\\UserIdentifier;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\ConsentStatusEnum\\ConsentStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\OfflineUserDataJobFailureReasonEnum\\OfflineUserDataJobFailureReason;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\OfflineUserDataJobStatusEnum\\OfflineUserDataJobStatus;\nuse Google\\Ads\\GoogleAds\\V25\\Enums\\OfflineUserDataJobTypeEnum\\OfflineUserDataJobType;\nuse Google\\Ads\\GoogleAds\\V25\\Errors\\GoogleAdsError;\nuse Google\\Ads\\GoogleAds\\V25\\Resources\\OfflineUserDataJob;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AddOfflineUserDataJobOperationsRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\AddOfflineUserDataJobOperationsResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\Client\\OfflineUserDataJobServiceClient;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CreateOfflineUserDataJobRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\CreateOfflineUserDataJobResponse;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\GoogleAdsRow;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\OfflineUserDataJobOperation;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\RunOfflineUserDataJobRequest;\nuse Google\\Ads\\GoogleAds\\V25\\Services\\SearchGoogleAdsStreamRequest;\nuse Google\\ApiCore\\ApiException;\n\n/**\n * Uploads offline data for store sales transactions.\n *\n * This feature is only available to allowlisted accounts. See\n * https://support.google.com/google-ads/answer/7620302 for more details.\n */\nclass UploadStoreSalesTransactions\n{\n private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';\n\n /**\n * The type of user data in the job (first or third party). If you have an official\n * store sales partnership with Google, use STORE_SALES_UPLOAD_THIRD_PARTY.\n * Otherwise, use STORE_SALES_UPLOAD_FIRST_PARTY or omit this parameter.\n */\n private const OFFLINE_USER_DATA_JOB_TYPE = 'STORE_SALES_UPLOAD_FIRST_PARTY';\n /** The ID of a store sales conversion action. */\n private const CONVERSION_ACTION_ID = 'INSERT_CONVERSION_ACTION_ID_HERE';\n /**\n * Optional (but recommended) external ID to identify the offline user data job.\n * The external ID for the offline user data job.\n */\n private const EXTERNAL_ID = null;\n /**\n * Only required after creating a custom key and custom values in the account.\n * Custom key and values are used to segment store sales conversions.\n * This measurement can be used to provide more advanced insights.\n */\n private const CUSTOM_KEY = null;\n\n // Optional: If uploading third party data, also specify the following values:\n /**\n * The date and time the advertiser uploaded data to the partner.\n * The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n */\n private const ADVERTISER_UPLOAD_DATE_TIME = null;\n /** The version of partner IDs to be used for uploads. */\n private const BRIDGE_MAP_VERSION_ID = null;\n /** The ID of the third party partner. */\n private const PARTNER_ID = null;\n // Optional: The consent status for ad personalization.\n private const AD_PERSONALIZATION_CONSENT = null;\n // Optional: The consent status for ad user data.\n private const AD_USER_DATA_CONSENT = null;\n\n // Optional: Below constants are only required if uploading with item attributes.\n /**\n * Specify a unique identifier of a product, either the Merchant Center\n * Item ID or Global Trade Item Number (GTIN).\n */\n private const ITEM_ID = null;\n /**\n * Specify a Merchant Center Account ID.\n */\n private const MERCHANT_CENTER_ACCOUNT_ID = null;\n /**\n * Specify a two-letter country code of the location associated with the\n * feed where your items are uploaded.\n * For a list of country codes see:\n * https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-16\n */\n private const COUNTRY_CODE = null;\n /**\n * Specify a two-letter language code of the language associated with\n * the feed where your items are uploaded.\n * For a list of language codes see:\n * https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7\n */\n private const LANGUAGE_CODE = null;\n /**\n * Specify a number of items sold.\n */\n private const QUANTITY = 1;\n\n public static function main()\n {\n // Either pass the required parameters for this example on the command line, or insert them\n // into the constants above.\n $options = (new ArgumentParser())->parseCommandArguments([\n ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::OFFLINE_USER_DATA_JOB_TYPE => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::CONVERSION_ACTION_ID => GetOpt::REQUIRED_ARGUMENT,\n ArgumentNames::AD_PERSONALIZATION_CONSENT => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::AD_USER_DATA_CONSENT => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::EXTERNAL_ID => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::CUSTOM_KEY => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::ADVERTISER_UPLOAD_DATE_TIME => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::BRIDGE_MAP_VERSION_ID => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::PARTNER_ID => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::ITEM_ID => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::MERCHANT_CENTER_ACCOUNT_ID => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::COUNTRY_CODE => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::LANGUAGE_CODE => GetOpt::OPTIONAL_ARGUMENT,\n ArgumentNames::QUANTITY => GetOpt::OPTIONAL_ARGUMENT\n ]);\n\n // Generate a refreshable OAuth2 credential for authentication.\n $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();\n\n // Construct a Google Ads client configured from a properties file and the\n // OAuth2 credentials above.\n $googleAdsClient = (new GoogleAdsClientBuilder())\n ->fromFile()\n ->withOAuth2Credential($oAuth2Credential)\n ->build();\n\n try {\n self::runExample(\n $googleAdsClient,\n $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,\n $options[ArgumentNames::OFFLINE_USER_DATA_JOB_TYPE]\n ?: self::OFFLINE_USER_DATA_JOB_TYPE,\n $options[ArgumentNames::CONVERSION_ACTION_ID] ?: self::CONVERSION_ACTION_ID,\n $options[ArgumentNames::AD_PERSONALIZATION_CONSENT]\n ? ConsentStatus::value($options[ArgumentNames::AD_PERSONALIZATION_CONSENT])\n : self::AD_PERSONALIZATION_CONSENT,\n $options[ArgumentNames::AD_USER_DATA_CONSENT]\n ? ConsentStatus::value($options[ArgumentNames::AD_USER_DATA_CONSENT])\n : self::AD_USER_DATA_CONSENT,\n $options[ArgumentNames::EXTERNAL_ID] ?: self::EXTERNAL_ID,\n $options[ArgumentNames::CUSTOM_KEY] ?: self::CUSTOM_KEY,\n $options[ArgumentNames::ADVERTISER_UPLOAD_DATE_TIME]\n ?: self::ADVERTISER_UPLOAD_DATE_TIME,\n $options[ArgumentNames::BRIDGE_MAP_VERSION_ID] ?: self::BRIDGE_MAP_VERSION_ID,\n $options[ArgumentNames::PARTNER_ID] ?: self::PARTNER_ID,\n $options[ArgumentNames::ITEM_ID] ?: self::ITEM_ID,\n $options[ArgumentNames::MERCHANT_CENTER_ACCOUNT_ID]\n ?: self::MERCHANT_CENTER_ACCOUNT_ID,\n $options[ArgumentNames::COUNTRY_CODE] ?: self::COUNTRY_CODE,\n $options[ArgumentNames::LANGUAGE_CODE] ?: self::LANGUAGE_CODE,\n $options[ArgumentNames::QUANTITY] ?: self::QUANTITY\n );\n } catch (GoogleAdsException $googleAdsException) {\n printf(\n \"Request with ID '%s' has failed.%sGoogle Ads failure details:%s\",\n $googleAdsException->getRequestId(),\n PHP_EOL,\n PHP_EOL\n );\n foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {\n /** @var GoogleAdsError $error */\n printf(\n \"\\t%s: %s%s\",\n $error->getErrorCode()->getErrorCode(),\n $error->getMessage(),\n PHP_EOL\n );\n }\n exit(1);\n } catch (ApiException $apiException) {\n printf(\n \"ApiException was thrown with message '%s'.%s\",\n $apiException->getMessage(),\n PHP_EOL\n );\n exit(1);\n }\n }\n\n /**\n * Runs the example.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param string|null $offlineUserDataJobType the type of offline user data in the job (first\n * party or third party). If you have an official store sales partnership with Google, use\n * `STORE_SALES_UPLOAD_THIRD_PARTY`. Otherwise, use `STORE_SALES_UPLOAD_FIRST_PARTY`\n * @param int $conversionActionId the ID of a store sales conversion action\n * @param int|null $adPersonalizationConsent the ad personalization consent status\n * @param int|null $adUserDataConsent the ad user data consent status\n * @param int|null $externalId optional (but recommended) external ID for the offline user data\n * job\n * @param string|null $customKey the custom key to segment store sales conversions. Only\n * required after creating a custom key and custom values in the account.\n * @param string|null $advertiserUploadDateTime date and time the advertiser uploaded data to\n * the partner. Only required for third party uploads\n * @param string|null $bridgeMapVersionId version of partner IDs to be used for uploads. Only\n * required for third party uploads\n * @param int|null $partnerId ID of the third party partner. Only required for third party\n * uploads\n * @param string|null $itemId a unique identifier of a product, either the Merchant Center Item\n * ID or Global Trade Item Number (GTIN)\n * @param int|null $merchantCenterAccountId a Merchant Center Account ID\n * @param string|null $countryCode a two-letter country code of the location associated with the\n * feed where your items are uploaded\n * @param string|null $languageCode a two-letter language code of the language associated with\n * the feed where your items are uploaded\n * @param int|null $quantity the number of items sold. Can only be set when at least one other\n * item attribute has been provided\n */\n public static function runExample(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n ?string $offlineUserDataJobType,\n int $conversionActionId,\n ?int $adPersonalizationConsent,\n ?int $adUserDataConsent,\n ?int $externalId,\n ?string $customKey,\n ?string $advertiserUploadDateTime,\n ?string $bridgeMapVersionId,\n ?int $partnerId,\n ?string $itemId,\n ?int $merchantCenterAccountId,\n ?string $countryCode,\n ?string $languageCode,\n ?int $quantity\n ) {\n $offlineUserDataJobServiceClient = $googleAdsClient->getOfflineUserDataJobServiceClient();\n\n // Creates an offline user data job for uploading transactions.\n $offlineUserDataJobResourceName = self::createOfflineUserDataJob(\n $offlineUserDataJobServiceClient,\n $customerId,\n $offlineUserDataJobType,\n $externalId,\n $customKey,\n $advertiserUploadDateTime,\n $bridgeMapVersionId,\n $partnerId\n );\n\n // Adds transactions to the job.\n self::addTransactionsToOfflineUserDataJob(\n $offlineUserDataJobServiceClient,\n $customerId,\n $offlineUserDataJobResourceName,\n $conversionActionId,\n $adPersonalizationConsent,\n $adUserDataConsent,\n $itemId,\n $merchantCenterAccountId,\n $countryCode,\n $languageCode,\n $quantity\n );\n\n // Issues an asynchronous request to run the offline user data job.\n $offlineUserDataJobServiceClient->runOfflineUserDataJob(\n RunOfflineUserDataJobRequest::build($offlineUserDataJobResourceName)\n );\n\n printf(\n \"Sent request to asynchronously run offline user data job: '%s'.%s\",\n $offlineUserDataJobResourceName,\n PHP_EOL\n );\n\n // Offline user data jobs may take up to 24 hours to complete, so instead of waiting for the\n // job to complete, retrieves and displays the job status once and then prints the query to\n // use to check the job again later.\n self::checkJobStatus($googleAdsClient, $customerId, $offlineUserDataJobResourceName);\n }\n\n /**\n * Creates an offline user data job for uploading store sales transactions.\n *\n * @param OfflineUserDataJobServiceClient $offlineUserDataJobServiceClient the offline user\n * data job service client\n * @param int $customerId the customer ID\n * @param string|null $offlineUserDataJobType the type of offline user data in the job (first\n * party or third party). If you have an official store sales partnership with Google, use\n * `STORE_SALES_UPLOAD_THIRD_PARTY`. Otherwise, use `STORE_SALES_UPLOAD_FIRST_PARTY`\n * @param int|null $externalId optional (but recommended) external ID for the offline user data\n * job\n * @param string|null $customKey the custom key to segment store sales conversions. Only\n * required after creating a custom key and custom values in the account.\n * @param string|null $advertiserUploadDateTime date and time the advertiser uploaded data to\n * the partner. Only required for third party uploads\n * @param string|null $bridgeMapVersionId version of partner IDs to be used for uploads. Only\n * required for third party uploads\n * @param int|null $partnerId ID of the third party partner. Only required for third party\n * uploads\n * @return string the resource name of the created job\n */\n private static function createOfflineUserDataJob(\n OfflineUserDataJobServiceClient $offlineUserDataJobServiceClient,\n int $customerId,\n ?string $offlineUserDataJobType,\n ?int $externalId,\n ?string $customKey,\n ?string $advertiserUploadDateTime,\n ?string $bridgeMapVersionId,\n ?int $partnerId\n ): string {\n // TIP: If you are migrating from the AdWords API, please note that Google Ads API uses the\n // term \"fraction\" instead of \"rate\". For example, loyaltyRate in the AdWords API is called\n // loyaltyFraction in the Google Ads API.\n // Please refer to https://support.google.com/google-ads/answer/7506124 for additional\n // details.\n $storeSalesMetadata = new StoreSalesMetadata([\n // Sets the fraction of your overall sales that you (or the advertiser, in the third\n // party case) can associate with a customer (email, phone number, address, etc.) in\n // your database or loyalty program.\n // For example, set this to 0.7 if you have 100 transactions over 30 days, and out of\n // those 100 transactions, you can identify 70 by an email address or phone number.\n 'loyalty_fraction' => 0.7,\n // Sets the fraction of sales you're uploading out of the overall sales that you (or the\n // advertiser, in the third party case) can associate with a customer. In most cases,\n // you will set this to 1.0.\n // Continuing the example above for loyalty fraction, a value of 1.0 here indicates that\n // you are uploading all 70 of the transactions that can be identified by an email\n // address or phone number.\n 'transaction_upload_fraction' => 1.0,\n ]);\n if (!is_null($customKey)) {\n $storeSalesMetadata->setCustomKey($customKey);\n }\n if (\n OfflineUserDataJobType::value($offlineUserDataJobType)\n === OfflineUserDataJobType::STORE_SALES_UPLOAD_THIRD_PARTY\n ) {\n // Creates additional metadata required for uploading third party data.\n $storeSalesThirdPartyMetadata = new StoreSalesThirdPartyMetadata([\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n 'advertiser_upload_date_time' => $advertiserUploadDateTime,\n // Sets the fraction of transactions you received from the advertiser that have\n // valid formatting and values. This captures any transactions the advertiser\n // provided to you but which you are unable to upload to Google due to formatting\n // errors or missing data.\n // In most cases, you will set this to 1.0.\n 'valid_transaction_fraction' => 1.0,\n // Sets the fraction of valid transactions (as defined above) you received from the\n // advertiser that you (the third party) have matched to an external user ID on your\n // side.\n // In most cases, you will set this to 1.0.\n 'partner_match_fraction' => 1.0,\n // Sets the fraction of transactions you (the third party) are uploading out of the\n // transactions you received from the advertiser that meet both of the following\n // criteria:\n // 1. Are valid in terms of formatting and values. See valid transaction fraction\n // above.\n // 2. You matched to an external user ID on your side. See partner match fraction\n // above.\n // In most cases, you will set this to 1.0.\n 'partner_upload_fraction' => 1.0,\n // Please speak with your Google representative to get the values to use for the\n // bridge map version and partner IDs.\n // Sets the version of partner IDs to be used for uploads.\n 'bridge_map_version_id' => $bridgeMapVersionId,\n // Sets the third party partner ID uploading the transactions.\n 'partner_id' => $partnerId,\n ]);\n $storeSalesMetadata->setThirdPartyMetadata($storeSalesThirdPartyMetadata);\n }\n // Creates a new offline user data job.\n $offlineUserDataJob = new OfflineUserDataJob([\n 'type' => OfflineUserDataJobType::value($offlineUserDataJobType),\n 'store_sales_metadata' => $storeSalesMetadata\n ]);\n if (!is_null($externalId)) {\n $offlineUserDataJob->setExternalId($externalId);\n }\n\n // Issues a request to create the offline user data job.\n /** @var CreateOfflineUserDataJobResponse $createOfflineUserDataJobResponse */\n $createOfflineUserDataJobResponse =\n $offlineUserDataJobServiceClient->createOfflineUserDataJob(\n CreateOfflineUserDataJobRequest::build($customerId, $offlineUserDataJob)\n );\n $offlineUserDataJobResourceName = $createOfflineUserDataJobResponse->getResourceName();\n printf(\n \"Created an offline user data job with resource name: '%s'.%s\",\n $offlineUserDataJobResourceName,\n PHP_EOL\n );\n\n return $offlineUserDataJobResourceName;\n }\n\n /**\n * Adds operations to the job for a set of sample transactions.\n *\n * @param OfflineUserDataJobServiceClient $offlineUserDataJobServiceClient the offline user\n * data job service client\n * @param int $customerId the customer ID\n * @param string $offlineUserDataJobResourceName the resource name of the created offline user\n * data job\n * @param int $conversionActionId the ID of a store sales conversion action\n * @param int|null $adPersonalizationConsent the ad personalization consent status\n * @param int|null $adUserDataConsent the ad user data consent status\n * @param string|null $itemId a unique identifier of a product, either the Merchant Center Item\n * ID or Global Trade Item Number (GTIN)\n * @param int|null $merchantCenterAccountId a Merchant Center Account ID\n * @param string|null $countryCode a two-letter country code of the location associated with the\n * feed where your items are uploaded\n * @param string|null $languageCode a two-letter language code of the language associated with\n * the feed where your items are uploaded\n * @param int|null $quantity the number of items sold. Can only be set when at least one other\n * item attribute has been provided\n */\n private static function addTransactionsToOfflineUserDataJob(\n OfflineUserDataJobServiceClient $offlineUserDataJobServiceClient,\n int $customerId,\n string $offlineUserDataJobResourceName,\n int $conversionActionId,\n ?int $adPersonalizationConsent,\n ?int $adUserDataConsent,\n ?string $itemId,\n ?int $merchantCenterAccountId,\n ?string $countryCode,\n ?string $languageCode,\n ?int $quantity\n ) {\n // Constructs the operation for each transaction.\n $userDataJobOperations = self::buildOfflineUserDataJobOperations(\n $customerId,\n $conversionActionId,\n $adPersonalizationConsent,\n $adUserDataConsent,\n $itemId,\n $merchantCenterAccountId,\n $countryCode,\n $languageCode,\n $quantity\n );\n\n // Issues a request to add the operations to the offline user data job.\n /** @var AddOfflineUserDataJobOperationsResponse $operationResponse */\n $request = AddOfflineUserDataJobOperationsRequest::build(\n $offlineUserDataJobResourceName,\n $userDataJobOperations\n );\n // (Optional) Enables partial failure and warnings.\n $request->setEnablePartialFailure(true)->setEnableWarnings(true);\n $response = $offlineUserDataJobServiceClient->addOfflineUserDataJobOperations($request);\n\n // Prints the status message if any partial failure error is returned.\n // NOTE: The details of each partial failure error are not printed here, you can refer to\n // the example HandlePartialFailure.php to learn more.\n if ($response->hasPartialFailureError()) {\n printf(\n \"Encountered %d partial failure errors while adding %d operations to the \"\n . \"offline user data job: '%s'. Only the successfully added operations will be \"\n . \"executed when the job runs.%s\",\n count($response->getPartialFailureError()->getDetails()),\n count($userDataJobOperations),\n $response->getPartialFailureError()->getMessage(),\n PHP_EOL\n );\n } else {\n printf(\n \"Successfully added %d operations to the offline user data job.%s\",\n count($userDataJobOperations),\n PHP_EOL\n );\n }\n\n // Prints the number of warnings if any warnings are returned. You can access\n // details of each warning using the same approach you'd use for partial failure\n // errors.\n if ($response->hasWarning()) {\n // Extracts all the warning errors from the response details into a single\n // GoogleAdsFailure object.\n $warningFailure = GoogleAdsFailures::fromAnys($response->getWarning()->getDetails());\n // Prints some information about the warnings encountered.\n printf(\n \"Encountered %d warning(s).%s\",\n count($warningFailure->getErrors()),\n PHP_EOL\n );\n }\n }\n\n /**\n * Creates a list of offline user data job operations for sample transactions.\n *\n * @param int $customerId the customer ID\n * @param int $conversionActionId the ID of a store sales conversion action\n * @param int|null $adPersonalizationConsent the ad personalization consent status\n * @param int|null $adUserDataConsent the ad user data consent status\n * @return OfflineUserDataJobOperation[] an array with the operations\n * @param string|null $itemId a unique identifier of a product, either the Merchant Center Item\n * ID or Global Trade Item Number (GTIN)\n * @param int|null $merchantCenterAccountId a Merchant Center Account ID\n * @param string|null $countryCode a two-letter country code of the location associated with the\n * feed where your items are uploaded\n * @param string|null $languageCode a two-letter language code of the language associated with\n * the feed where your items are uploaded\n * @param int|null $quantity the number of items sold. Can only be set when at least one other\n * item attribute has been provided\n */\n private static function buildOfflineUserDataJobOperations(\n $customerId,\n $conversionActionId,\n ?int $adPersonalizationConsent,\n ?int $adUserDataConsent,\n ?string $itemId,\n ?int $merchantCenterAccountId,\n ?string $countryCode,\n ?string $languageCode,\n ?int $quantity\n ): array {\n // Creates the first transaction for upload based on an email address and state.\n $userDataWithEmailAddress = new UserData([\n 'user_identifiers' => [\n new UserIdentifier([\n // Email addresses must be normalized and hashed.\n 'hashed_email' => self::normalizeAndHash('dana@example.com')\n ]),\n new UserIdentifier([\n 'address_info' => new OfflineUserAddressInfo(['state' => 'NY'])\n ])\n ],\n 'transaction_attribute' => new TransactionAttribute([\n 'conversion_action'\n => ResourceNames::forConversionAction($customerId, $conversionActionId),\n 'currency_code' => 'USD',\n // Converts the transaction amount from $200 USD to micros.\n 'transaction_amount_micros' => Helper::baseToMicro(200),\n // Specifies the date and time of the transaction. The format is\n // \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional\n // timezone offset from UTC. If the offset is absent, the API will\n // use the account's timezone as default. Examples: \"2018-03-05 09:15:00\"\n // or \"2018-02-01 14:34:30+03:00\".\n 'transaction_date_time' => '2020-05-01 23:52:12'\n // OPTIONAL: If uploading data with custom key and values, also specify the\n // following value:\n // 'custom_value' => 'INSERT_CUSTOM_VALUE_HERE'\n ])\n ]);\n\n // Adds consent information if specified.\n if (!empty($adPersonalizationConsent) || !empty($adUserDataConsent)) {\n $consent = new Consent();\n if (!empty($adPersonalizationConsent)) {\n $consent->setAdPersonalization($adPersonalizationConsent);\n }\n if (!empty($adUserDataConsent)) {\n $consent->setAdUserData($adUserDataConsent);\n }\n // Specifies whether user consent was obtained for the data you are uploading. See\n // https://www.google.com/about/company/user-consent-policy for details.\n $userDataWithEmailAddress->setConsent($consent);\n }\n\n // Creates the second transaction for upload based on a physical address.\n $userDataWithPhysicalAddress = new UserData([\n 'user_identifiers' => [\n new UserIdentifier([\n 'address_info' => new OfflineUserAddressInfo([\n // First and last name must be normalized and hashed.\n 'hashed_first_name' => self::normalizeAndHash('Dana'),\n 'hashed_last_name' => self::normalizeAndHash('Quinn'),\n // Country code and zip code are sent in plain text.\n 'country_code' => 'US',\n 'postal_code' => '10011'\n ])\n ])\n ],\n 'transaction_attribute' => new TransactionAttribute([\n 'conversion_action'\n => ResourceNames::forConversionAction($customerId, $conversionActionId),\n 'currency_code' => 'EUR',\n // Converts the transaction amount from 450 EUR to micros.\n 'transaction_amount_micros' => Helper::baseToMicro(450),\n // Specifies the date and time of the transaction. This date and time will be\n // interpreted by the API using the Google Ads customer's time zone.\n // The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n 'transaction_date_time' => '2020-05-14 19:07:02'\n ])\n ]);\n\n // Optional: If uploading data with item attributes, also assign these values\n // in the transaction attribute.\n if (!empty($itemId)) {\n $userDataWithPhysicalAddress->getTransactionAttribute()->setItemAttribute(\n new ItemAttribute([\n 'item_id' => $itemId,\n 'merchant_id' => $merchantCenterAccountId,\n 'country_code' => $countryCode,\n 'language_code' => $languageCode,\n // Quantity field should only be set when at least one of the other item\n // attribute fields is present.\n 'quantity' => $quantity\n ])\n );\n }\n\n // Creates the operations to add the two transactions.\n $operations = [];\n foreach ([$userDataWithEmailAddress, $userDataWithPhysicalAddress] as $userData) {\n $operations[] = new OfflineUserDataJobOperation(['create' => $userData]);\n }\n\n return $operations;\n }\n\n /**\n * Returns the result of normalizing and then hashing the string.\n * Private customer data must be hashed during upload, as described at\n * https://support.google.com/google-ads/answer/7506124.\n *\n * @param string $value the value to normalize and hash\n * @return string the normalized and hashed value\n */\n private static function normalizeAndHash(string $value): string\n {\n return hash('sha256', strtolower(trim($value)));\n }\n\n /**\n * Retrieves, checks, and prints the status of the offline user data job.\n *\n * @param GoogleAdsClient $googleAdsClient the Google Ads API client\n * @param int $customerId the customer ID\n * @param string $offlineUserDataJobResourceName the resource name of the created offline user\n * data job\n */\n private static function checkJobStatus(\n GoogleAdsClient $googleAdsClient,\n int $customerId,\n string $offlineUserDataJobResourceName\n ) {\n $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();\n\n // Creates a query that retrieves the offline user data.\n $query = \"SELECT offline_user_data_job.resource_name, \"\n . \"offline_user_data_job.id, \"\n . \"offline_user_data_job.status, \"\n . \"offline_user_data_job.type, \"\n . \"offline_user_data_job.failure_reason \"\n . \"FROM offline_user_data_job \"\n . \"WHERE offline_user_data_job.resource_name = '$offlineUserDataJobResourceName'\";\n\n // Issues a search stream request.\n /** @var GoogleAdsServerStreamDecorator $stream */\n $stream = $googleAdsServiceClient->searchStream(\n SearchGoogleAdsStreamRequest::build($customerId, $query)\n );\n\n // Prints out some information about the offline user data.\n /** @var GoogleAdsRow $googleAdsRow */\n $googleAdsRow = $stream->iterateAllElements()->current();\n $offlineUserDataJob = $googleAdsRow->getOfflineUserDataJob();\n printf(\n \"Offline user data job ID %d with type '%s' has status: %s.%s\",\n $offlineUserDataJob->getId(),\n OfflineUserDataJobType::name($offlineUserDataJob->getType()),\n OfflineUserDataJobStatus::name($offlineUserDataJob->getStatus()),\n PHP_EOL\n );\n\n if (OfflineUserDataJobStatus::FAILED === $offlineUserDataJob->getStatus()) {\n printf(\n \" Failure reason: %s%s\",\n OfflineUserDataJobFailureReason::name($offlineUserDataJob->getFailureReason()),\n PHP_EOL\n );\n } elseif (\n OfflineUserDataJobStatus::PENDING === $offlineUserDataJob->getStatus()\n || OfflineUserDataJobStatus::RUNNING === $offlineUserDataJob->getStatus()\n ) {\n printf(\n '%1$sTo check the status of the job periodically, use the following GAQL '\n . 'query with GoogleAdsService.search:%1$s%2$s%1$s.',\n PHP_EOL,\n $query\n );\n }\n }\n}\n\nUploadStoreSalesTransactions::main();\nUploadStoreSalesTransactions.php\n```\n\nExample:\n```text\n#!/usr/bin/env python\n# Copyright 2020 Google LLC\n#\n# 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.\n\"\"\"This example uploads offline conversion data for store sales transactions.\n\nThis feature is only available to allowlisted accounts.\nSee https://support.google.com/google-ads/answer/7620302 for more details.\n\"\"\"\n\nimport argparse\nfrom datetime import datetime\nimport hashlib\nimport logging\nimport sys\nfrom typing import List, Optional, Tuple\n\nfrom google.protobuf.any_pb2 import Any\nfrom google.rpc import status_pb2\n\nfrom google.ads.googleads.client import GoogleAdsClient\nfrom google.ads.googleads.errors import GoogleAdsException\nfrom google.ads.googleads.v24.common.types.offline_user_data import (\n ItemAttribute,\n StoreSalesMetadata,\n StoreSalesThirdPartyMetadata,\n UserData,\n UserIdentifier,\n)\nfrom google.ads.googleads.v24.enums.types.offline_user_data_job_status import (\n OfflineUserDataJobStatusEnum,\n)\nfrom google.ads.googleads.v24.enums.types.offline_user_data_job_type import (\n OfflineUserDataJobTypeEnum,\n)\nfrom google.ads.googleads.v24.errors.types.errors import (\n GoogleAdsError,\n GoogleAdsFailure,\n)\nfrom google.ads.googleads.v24.resources.types.offline_user_data_job import (\n OfflineUserDataJob,\n)\nfrom google.ads.googleads.v24.services.services.google_ads_service import (\n GoogleAdsServiceClient,\n)\nfrom google.ads.googleads.v24.services.services.offline_user_data_job_service import (\n OfflineUserDataJobServiceClient,\n)\nfrom google.ads.googleads.v24.services.types.google_ads_service import (\n GoogleAdsRow,\n)\nfrom google.ads.googleads.v24.services.types.offline_user_data_job_service import (\n AddOfflineUserDataJobOperationsRequest,\n AddOfflineUserDataJobOperationsResponse,\n CreateOfflineUserDataJobResponse,\n OfflineUserDataJobOperation,\n)\n\nlogger = logging.getLogger(\"google.ads.googleads.client\")\nlogger.addHandler(logging.StreamHandler(sys.stdout))\n\n\ndef main(\n client: GoogleAdsClient,\n customer_id: str,\n conversion_action_id: int,\n offline_user_data_job_type: int,\n external_id: Optional[int],\n advertiser_upload_date_time: Optional[str],\n bridge_map_version_id: Optional[str],\n partner_id: Optional[int],\n custom_key: Optional[str],\n custom_value: Optional[str],\n item_id: Optional[str],\n merchant_center_account_id: Optional[int],\n country_code: Optional[str],\n language_code: Optional[str],\n quantity: int,\n ad_user_data_consent: Optional[str],\n ad_personalization_consent: Optional[str],\n) -> None:\n \"\"\"Uploads offline conversion data for store sales transactions.\n\n Args:\n client: An initialized Google Ads client.\n customer_id: The Google Ads customer ID.\n conversion_action_id: The ID of a store sales conversion action.\n offline_user_data_job_type: Optional type of offline user data in the\n job (first party or third party). If you have an official store\n sales partnership with Google, use STORE_SALES_UPLOAD_THIRD_PARTY.\n Otherwise, use STORE_SALES_UPLOAD_FIRST_PARTY.\n external_id: Optional, but recommended, external ID for the offline\n user data job.\n advertiser_upload_date_time: Optional date and time the advertiser\n uploaded data to the partner. Only required for third party uploads.\n The format is 'yyyy-mm-dd hh:mm:ss+|-hh:mm', e.g.\n '2019-01-01 12:32:45-08:00'.\n bridge_map_version_id: Optional version of partner IDs to be used for\n uploads. Only required for third party uploads.\n partner_id: Optional ID of the third party partner. Only required for\n third party uploads.\n custom_key: A custom key str to segment store sales conversions. Only\n required after creating a custom key and custom values in the\n account.\n custom_value: A custom value str to segment store sales conversions.\n Only required after creating a custom key and custom values in the\n account.\n item_id: Optional str ID of the product. Either the Merchant Center Item\n ID or the Global Trade Item Number (GTIN). Only required if\n uploading with item attributes.\n merchant_center_account_id: Optional Merchant Center Account ID. Only\n required if uploading with item attributes.\n country_code: Optional two-letter country code of the location associated\n with the feed where your items are uploaded. Only required if\n uploading with item attributes.\n language_code: Optional two-letter country code of the language\n associated with the feed where your items are uploaded. Only\n required if uploading with item attributes.\n quantity: Optional number of items sold. Only required if uploading with\n item attributes.\n ad_user_data_consent: The consent status for ad user data for all\n members in the job.\n ad_personalization_consent: The personalization consent status for ad\n user data for all members in the job.\n \"\"\"\n # Get the OfflineUserDataJobService client.\n offline_user_data_job_service: OfflineUserDataJobServiceClient = (\n client.get_service(\"OfflineUserDataJobService\")\n )\n\n # Create an offline user data job for uploading transactions.\n offline_user_data_job_resource_name: str = create_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_type,\n external_id,\n advertiser_upload_date_time,\n bridge_map_version_id,\n partner_id,\n custom_key,\n )\n\n # Add transactions to the job.\n add_transactions_to_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_resource_name,\n conversion_action_id,\n custom_value,\n item_id,\n merchant_center_account_id,\n country_code,\n language_code,\n quantity,\n ad_user_data_consent,\n ad_personalization_consent,\n )\n\n # Issue an asynchronous request to run the offline user data job.\n offline_user_data_job_service.run_offline_user_data_job(\n resource_name=offline_user_data_job_resource_name\n )\n\n # Offline user data jobs may take up to 24 hours to complete, so\n # instead of waiting for the job to complete, retrieves and displays\n # the job status once and then prints the query to use to check the job\n # again later.\n check_job_status(client, customer_id, offline_user_data_job_resource_name)\n\n\ndef create_offline_user_data_job(\n client: GoogleAdsClient,\n offline_user_data_job_service: OfflineUserDataJobServiceClient,\n customer_id: str,\n offline_user_data_job_type: int,\n external_id: Optional[int],\n advertiser_upload_date_time: Optional[str],\n bridge_map_version_id: Optional[str],\n partner_id: Optional[int],\n custom_key: Optional[str],\n) -> str:\n \"\"\"Creates an offline user data job for uploading store sales transactions.\n\n Args:\n client: An initialized Google Ads API client.\n offline_user_data_job_service: The offline user data job service client.\n customer_id: The Google Ads customer ID.\n offline_user_data_job_type: Optional type of offline user data in the\n job (first party or third party).\n external_id: Optional external ID for the offline user data job.\n advertiser_upload_date_time: Optional date and time the advertiser\n uploaded data to the partner. Only required for third party uploads.\n bridge_map_version_id: Optional version of partner IDs to be used for\n uploads. Only required for third party uploads.\n partner_id: Optional ID of the third party partner. Only required for\n third party uploads.\n custom_key: A custom key str to segment store sales conversions. Only\n required after creating a custom key and custom values in the\n account.\n\n Returns:\n The string resource name of the created job.\n \"\"\"\n # TIP: If you are migrating from the AdWords API, please note that Google\n # Ads API uses the term \"fraction\" instead of \"rate\". For example,\n # loyalty_rate in the AdWords API is called loyalty_fraction in the Google\n # Ads API.\n\n # Create a new offline user data job.\n offline_user_data_job: OfflineUserDataJob = client.get_type(\n \"OfflineUserDataJob\"\n )\n offline_user_data_job.type_ = offline_user_data_job_type\n if external_id is not None:\n offline_user_data_job.external_id = external_id\n\n # Please refer to https://support.google.com/google-ads/answer/7506124 for\n # additional details.\n store_sales_metadata: StoreSalesMetadata = (\n offline_user_data_job.store_sales_metadata\n )\n # Set the fraction of your overall sales that you (or the advertiser,\n # in the third party case) can associate with a customer (email, phone\n # number, address, etc.) in your database or loyalty program.\n # For example, set this to 0.7 if you have 100 transactions over 30\n # days, and out of those 100 transactions, you can identify 70 by an\n # email address or phone number.\n store_sales_metadata.loyalty_fraction = 0.7\n # Set the fraction of sales you're uploading out of the overall sales\n # that you (or the advertiser, in the third party case) can associate\n # with a customer. In most cases, you will set this to 1.0.\n # Continuing the example above for loyalty fraction, a value of 1.0 here\n # indicates that you are uploading all 70 of the transactions that can\n # be identified by an email address or phone number.\n store_sales_metadata.transaction_upload_fraction = 1.0\n\n if custom_key:\n store_sales_metadata.custom_key = custom_key\n\n if (\n offline_user_data_job_type\n == client.enums.OfflineUserDataJobTypeEnum.STORE_SALES_UPLOAD_THIRD_PARTY\n ):\n # Create additional metadata required for uploading third party data.\n store_sales_third_party_metadata: StoreSalesThirdPartyMetadata = (\n store_sales_metadata.third_party_metadata\n )\n # The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n store_sales_third_party_metadata.advertiser_upload_date_time = (\n advertiser_upload_date_time\n )\n # Set the fraction of transactions you received from the advertiser\n # that have valid formatting and values. This captures any transactions\n # the advertiser provided to you but which you are unable to upload to\n # Google due to formatting errors or missing data.\n # In most cases, you will set this to 1.0.\n store_sales_third_party_metadata.valid_transaction_fraction = 1.0\n # Set the fraction of valid transactions (as defined above) you\n # received from the advertiser that you (the third party) have matched\n # to an external user ID on your side.\n # In most cases, you will set this to 1.0.\n store_sales_third_party_metadata.partner_match_fraction = 1.0\n # Set the fraction of transactions you (the third party) are uploading\n # out of the transactions you received from the advertiser that meet\n # both of the following criteria:\n # 1. Are valid in terms of formatting and values. See valid transaction\n # fraction above.\n # 2. You matched to an external user ID on your side. See partner match\n # fraction above.\n # In most cases, you will set this to 1.0.\n store_sales_third_party_metadata.partner_upload_fraction = 1.0\n # Set the version of partner IDs to be used for uploads.\n # Please speak with your Google representative to get the values to use\n # for the bridge map version and partner IDs.\n store_sales_third_party_metadata.bridge_map_version_id = (\n bridge_map_version_id\n )\n # Set the third party partner ID uploading the transactions.\n store_sales_third_party_metadata.partner_id = partner_id\n\n create_offline_user_data_job_response: CreateOfflineUserDataJobResponse = (\n offline_user_data_job_service.create_offline_user_data_job(\n customer_id=customer_id, job=offline_user_data_job\n )\n )\n offline_user_data_job_resource_name: str = (\n create_offline_user_data_job_response.resource_name\n )\n print(\n \"Created an offline user data job with resource name \"\n f\"'{offline_user_data_job_resource_name}'.\"\n )\n return offline_user_data_job_resource_name\n\n\ndef add_transactions_to_offline_user_data_job(\n client: GoogleAdsClient,\n offline_user_data_job_service: OfflineUserDataJobServiceClient,\n customer_id: str,\n offline_user_data_job_resource_name: str,\n conversion_action_id: int,\n custom_value: Optional[str],\n item_id: Optional[str],\n merchant_center_account_id: Optional[int],\n country_code: Optional[str],\n language_code: Optional[str],\n quantity: int,\n ad_user_data_consent: Optional[str],\n ad_personalization_consent: Optional[str],\n) -> None:\n \"\"\"Add operations to the job for a set of sample transactions.\n\n Args:\n client: An initialized Google Ads API client.\n offline_user_data_job_service: The offline user data job service client.\n customer_id: The Google Ads customer ID.\n offline_user_data_job_resource_name: The string resource name of the\n offline user data job that will receive the transactions.\n conversion_action_id: The ID of a store sales conversion action.\n custom_value: A custom value str to segment store sales conversions.\n Only required after creating a custom key and custom values in the\n account.\n item_id: Optional str ID of the product. Either the Merchant Center Item\n ID or the Global Trade Item Number (GTIN). Only required if\n uploading with item attributes.\n merchant_center_account_id: Optional Merchant Center Account ID. Only\n required if uploading with item attributes.\n country_code: Optional two-letter country code of the location associated\n with the feed where your items are uploaded. Only required if\n uploading with item attributes.\n language_code: Optional two-letter country code of the language\n associated with the feed where your items are uploaded. Only\n required if uploading with item attributes.\n quantity: Optional number of items sold. Only required if uploading with\n item attributes.\n ad_user_data_consent: The consent status for ad user data for all\n members in the job.\n ad_personalization_consent: The personalization consent status for ad\n user data for all members in the job.\n \"\"\"\n # Construct some sample transactions.\n operations: List[OfflineUserDataJobOperation] = (\n build_offline_user_data_job_operations(\n client,\n customer_id,\n conversion_action_id,\n custom_value,\n item_id,\n merchant_center_account_id,\n country_code,\n language_code,\n quantity,\n ad_user_data_consent,\n ad_personalization_consent,\n )\n )\n\n # Constructs a request with partial failure enabled to add the operations\n # to the offline user data job, and enable_warnings set to true to retrieve\n # warnings.\n request: AddOfflineUserDataJobOperationsRequest = client.get_type(\n \"AddOfflineUserDataJobOperationsRequest\"\n )\n request.resource_name = offline_user_data_job_resource_name\n request.enable_partial_failure = True\n request.enable_warnings = True\n request.operations = operations\n\n response: AddOfflineUserDataJobOperationsResponse = (\n offline_user_data_job_service.add_offline_user_data_job_operations(\n request=request,\n )\n )\n\n # Print the error message for any partial failure error that is returned.\n if response.partial_failure_error:\n print_google_ads_failures(client, response.partial_failure_error)\n else:\n print(\n f\"Successfully added {len(operations)} to the offline user data \"\n \"job.\"\n )\n\n # Print the message for any warnings that are returned.\n if response.warning:\n print_google_ads_failures(client, response.warning)\n\n\ndef print_google_ads_failures(\n client: GoogleAdsClient, status: status_pb2.Status\n) -> None:\n \"\"\"Prints the details for partial failure errors and warnings.\n\n Both partial failure errors and warnings are returned as Status instances,\n which include serialized GoogleAdsFailure objects. Here we deserialize\n each GoogleAdsFailure and print the error details it includes.\n\n Args:\n client: An initialized Google Ads API client.\n status: a google.rpc.Status instance.\n \"\"\"\n detail: Any\n for detail in status.details:\n google_ads_failure: GoogleAdsFailure = client.get_type(\n \"GoogleAdsFailure\"\n )\n # Retrieve the class definition of the GoogleAdsFailure instance\n # with type() in order to use the \"deserialize\" class method to parse\n # the detail string into a protobuf message instance.\n failure_instance: GoogleAdsFailure = type(\n google_ads_failure\n ).deserialize(detail.value)\n error: GoogleAdsError\n for error in failure_instance.errors:\n print(\n \"A partial failure or warning at index \"\n f\"{error.location.field_path_elements[0].index} occurred.\\n\"\n f\"Message: {error.message}\\n\"\n f\"Code: {error.error_code}\"\n )\n\n\ndef build_offline_user_data_job_operations(\n client: GoogleAdsClient,\n customer_id: str,\n conversion_action_id: int,\n custom_value: Optional[str],\n item_id: Optional[str],\n merchant_center_account_id: Optional[int],\n country_code: Optional[str],\n language_code: Optional[str],\n quantity: int,\n ad_user_data_consent: Optional[str],\n ad_personalization_consent: Optional[str],\n) -> List[OfflineUserDataJobOperation]:\n \"\"\"Create offline user data job operations for sample transactions.\n\n Args:\n client: An initialized Google Ads API client.\n customer_id: The Google Ads customer ID.\n conversion_action_id: The ID of a store sales conversion action.\n custom_value: A custom value str to segment store sales conversions.\n Only required after creating a custom key and custom values in the\n account.\n item_id: Optional str ID of the product. Either the Merchant Center Item\n ID or the Global Trade Item Number (GTIN). Only required if\n uploading with item attributes.\n merchant_center_account_id: Optional Merchant Center Account ID. Only\n required if uploading with item attributes.\n country_code: Optional two-letter country code of the location associated\n with the feed where your items are uploaded. Only required if\n uploading with item attributes.\n language_code: Optional two-letter country code of the language\n associated with the feed where your items are uploaded. Only\n required if uploading with item attributes.\n quantity: Optional number of items sold. Only required if uploading with\n item attributes.\n ad_user_data_consent: The consent status for ad user data for all\n members in the job.\n ad_personalization_consent: The personalization consent status for ad\n user data for all members in the job.\n\n Returns:\n A list of OfflineUserDataJobOperations.\n \"\"\"\n # Create the first transaction for upload with an email address and state.\n user_data_with_email_address_operation: OfflineUserDataJobOperation = (\n client.get_type(\"OfflineUserDataJobOperation\")\n )\n user_data_with_email_address: UserData = (\n user_data_with_email_address_operation.create\n )\n email_identifier: UserIdentifier = client.get_type(\"UserIdentifier\")\n # Hash normalized email addresses based on SHA-256 hashing algorithm.\n email_identifier.hashed_email = normalize_and_hash(\"dana@example.com\")\n state_identifier: UserIdentifier = client.get_type(\"UserIdentifier\")\n state_identifier.address_info.state = \"NY\"\n user_data_with_email_address.user_identifiers.extend(\n [email_identifier, state_identifier]\n )\n user_data_with_email_address.transaction_attribute.conversion_action = (\n client.get_service(\"ConversionActionService\").conversion_action_path(\n customer_id, conversion_action_id\n )\n )\n user_data_with_email_address.transaction_attribute.currency_code = \"USD\"\n # Convert the transaction amount from $200 USD to micros.\n user_data_with_email_address.transaction_attribute.transaction_amount_micros = (\n 200000000\n )\n # Specify the date and time of the transaction. The format is\n # \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional timezone\n # offset from UTC. If the offset is absent, the API will use the account's\n # timezone as default. Examples: \"2018-03-05 09:15:00\" or\n # \"2018-02-01 14:34:30+03:00\".\n user_data_with_email_address.transaction_attribute.transaction_date_time = (\n datetime.now() - datetime.timedelta(months=1)\n ).strftime(\"%Y-%m-%d %H:%M:%S\")\n\n # Specifies whether user consent was obtained for the data you are\n # uploading. For more details, see:\n # https://www.google.com/about/company/user-consent-policy\n if ad_user_data_consent:\n user_data_with_email_address.consent.ad_user_data = (\n client.enums.ConsentStatusEnum[ad_user_data_consent]\n )\n if ad_personalization_consent:\n user_data_with_email_address.consent.ad_personalization = (\n client.enums.ConsentStatusEnum[ad_personalization_consent]\n )\n\n if custom_value:\n user_data_with_email_address.transaction_attribute.custom_value = (\n custom_value\n )\n\n # Create the second transaction for upload based on a physical address.\n user_data_with_physical_address_operation: OfflineUserDataJobOperation = (\n client.get_type(\"OfflineUserDataJobOperation\")\n )\n user_data_with_physical_address: UserData = (\n user_data_with_physical_address_operation.create\n )\n address_identifier: UserIdentifier = client.get_type(\"UserIdentifier\")\n # First and last name must be normalized and hashed.\n address_identifier.address_info.hashed_first_name = normalize_and_hash(\n \"Dana\"\n )\n address_identifier.address_info.hashed_last_name = normalize_and_hash(\n \"Quinn\"\n )\n # Country and zip codes are sent in plain text.\n address_identifier.address_info.country_code = \"US\"\n address_identifier.address_info.postal_code = \"10011\"\n user_data_with_physical_address.user_identifiers.append(address_identifier)\n user_data_with_physical_address.transaction_attribute.conversion_action = (\n client.get_service(\"ConversionActionService\").conversion_action_path(\n customer_id, conversion_action_id\n )\n )\n user_data_with_physical_address.transaction_attribute.currency_code = \"EUR\"\n # Convert the transaction amount from 450 EUR to micros.\n user_data_with_physical_address.transaction_attribute.transaction_amount_micros = (\n 450000000\n )\n # Specify the date and time of the transaction. This date and time\n # will be interpreted by the API using the Google Ads customer's\n # time zone. The date/time must be in the format\n # \"yyyy-MM-dd hh:mm:ss\".\n user_data_with_physical_address.transaction_attribute.transaction_date_time = (\n datetime.now() - datetime.timedelta(days=1)\n ).strftime(\n \"%Y-%m-%d %H:%M:%S\"\n )\n\n # Optional: If uploading data with item attributes, also assign these\n # values in the transaction attribute\n if item_id:\n item_attribute: ItemAttribute = (\n user_data_with_physical_address.transaction_attribute.item_attribute\n )\n item_attribute.item_id = item_id\n item_attribute.merchant_id = merchant_center_account_id\n item_attribute.country_code = country_code\n item_attribute.language_code = language_code\n item_attribute.quantity = quantity\n\n return [\n user_data_with_email_address_operation,\n user_data_with_physical_address_operation,\n ]\n\n\ndef normalize_and_hash(s: str) -> str:\n \"\"\"Normalizes and hashes a string with SHA-256.\n\n Args:\n s: The string to perform this operation on.\n\n Returns:\n A normalized (lowercase, remove whitespace) and SHA-256 hashed string.\n \"\"\"\n return hashlib.sha256(s.strip().lower().encode()).hexdigest()\n\n\ndef check_job_status(\n client: GoogleAdsClient,\n customer_id: str,\n offline_user_data_job_resource_name: str,\n) -> None:\n \"\"\"Retrieves, checks, and prints the status of the offline user data job.\n\n Args:\n client: An initialized Google Ads API client.\n customer_id: The Google Ads customer ID.\n offline_user_data_job_resource_name: The resource name of the job whose\n status you wish to check.\n \"\"\"\n # Get the GoogleAdsService client.\n googleads_service: GoogleAdsServiceClient = client.get_service(\n \"GoogleAdsService\"\n )\n\n # Construct a query to fetch the job status.\n query: str = f\"\"\"\n SELECT\n offline_user_data_job.resource_name,\n offline_user_data_job.id,\n offline_user_data_job.status,\n offline_user_data_job.type,\n offline_user_data_job.failure_reason\n FROM offline_user_data_job\n WHERE offline_user_data_job.resource_name =\n '{offline_user_data_job_resource_name}'\"\"\"\n\n # Issue the query and get the GoogleAdsRow containing the job.\n googleads_row: GoogleAdsRow = next(\n iter(googleads_service.search(customer_id=customer_id, query=query))\n )\n offline_user_data_job: OfflineUserDataJob = (\n googleads_row.offline_user_data_job\n )\n\n offline_user_data_job_type_enum: (\n OfflineUserDataJobTypeEnum.OfflineUserDataJobType\n ) = client.enums.OfflineUserDataJobTypeEnum.OfflineUserDataJobType\n offline_user_data_job_status_enum: (\n OfflineUserDataJobStatusEnum.OfflineUserDataJobStatus\n ) = client.enums.OfflineUserDataJobStatusEnum.OfflineUserDataJobStatus\n\n job_status: int = offline_user_data_job.status\n print(\n f\"Offline user data job ID {offline_user_data_job.id} with type \"\n f\"'{offline_user_data_job_type_enum.Name(offline_user_data_job.type)}' \"\n f\"has status {offline_user_data_job_status_enum.Name(job_status)}.\"\n )\n\n offline_user_data_job_status_enum_wrapper: OfflineUserDataJobStatusEnum = (\n client.enums.OfflineUserDataJobStatusEnum\n )\n if job_status == offline_user_data_job_status_enum_wrapper.FAILED:\n print(f\"\\tFailure reason: {offline_user_data_job.failure_reason}\")\n elif (\n job_status == offline_user_data_job_status_enum_wrapper.PENDING\n or job_status == offline_user_data_job_status_enum_wrapper.RUNNING\n ):\n print(\n \"\\nTo check the status of the job periodically, use the \"\n f\"following GAQL query with GoogleAdsService.Search:\\n{query}\\n\"\n )\n elif job_status == offline_user_data_job_status_enum_wrapper.SUCCESS:\n print(\"\\nThe requested job has completed successfully.\")\n else:\n raise ValueError(\"Requested job has UNKNOWN or UNSPECIFIED status.\")\n\n\nif __name__ == \"__main__\":\n # GoogleAdsClient will read the google-ads.yaml configuration file in the\n # home directory if none is specified.\n googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(\n version=\"v24\"\n )\n\n parser: argparse.ArgumentParser = argparse.ArgumentParser(\n description=\"This example uploads offline data for store sales \"\n \"transactions.\"\n )\n # The following argument(s) should be provided to run the example.\n parser.add_argument(\n \"-c\",\n \"--customer_id\",\n type=str,\n required=True,\n help=\"The Google Ads customer ID.\",\n )\n parser.add_argument(\n \"-a\",\n \"--conversion_action_id\",\n type=int,\n required=True,\n help=\"The ID of a store sales conversion action.\",\n )\n group: argparse._MutuallyExclusiveGroup = (\n parser.add_mutually_exclusive_group(required=False)\n )\n group.add_argument(\n \"-k\",\n \"--custom_key\",\n type=str,\n help=\"Only required after creating a custom key and custom values in \"\n \"the account. Custom key and values are used to segment store sales \"\n \"conversions. This measurement can be used to provide more advanced \"\n \"insights. If provided, a custom value must also be provided\",\n )\n group.add_argument(\n \"-v\",\n \"--custom_value\",\n type=str,\n help=\"Only required after creating a custom key and custom values in \"\n \"the account. Custom key and values are used to segment store sales \"\n \"conversions. This measurement can be used to provide more advanced \"\n \"insights. If provided, a custom key must also be provided\",\n )\n parser.add_argument(\n \"-o\",\n \"--offline_user_data_job_type\",\n type=int,\n required=False,\n default=googleads_client.enums.OfflineUserDataJobTypeEnum.STORE_SALES_UPLOAD_FIRST_PARTY,\n help=\"Optional type of offline user data in the job (first party or \"\n \"third party). If you have an official store sales partnership with \"\n \"Google, use STORE_SALES_UPLOAD_THIRD_PARTY. Otherwise, defaults to \"\n \"STORE_SALES_UPLOAD_FIRST_PARTY.\",\n )\n parser.add_argument(\n \"-e\",\n \"--external_id\",\n type=int,\n required=False,\n default=None,\n help=\"Optional, but recommended, external ID for the offline user data \"\n \"job.\",\n )\n parser.add_argument(\n \"-d\",\n \"--advertiser_upload_date_time\",\n type=str,\n required=False,\n default=None,\n help=\"Optional date and time the advertiser uploaded data to the \"\n \"partner. Only required for third party uploads. The format is \"\n \"'yyyy-mm-dd hh:mm:ss+|-hh:mm', e.g. '2021-01-01 12:32:45-08:00'.\",\n )\n parser.add_argument(\n \"-b\",\n \"--bridge_map_version_id\",\n type=str,\n required=False,\n default=None,\n help=\"Optional version of partner IDs to be used for uploads. Only \"\n \"required for third party uploads.\",\n )\n parser.add_argument(\n \"-p\",\n \"--partner_id\",\n type=int,\n required=False,\n default=None,\n help=\"Optional ID of the third party partner. Only required for third \"\n \"party uploads.\",\n )\n parser.add_argument(\n \"-i\",\n \"--item_id\",\n type=str,\n required=False,\n default=None,\n help=\"Optional ID of the product. Either the Merchant Center Item ID \"\n \"or the Global Trade Item Number (GTIN). Only required if uploading \"\n \"with item attributes.\",\n )\n parser.add_argument(\n \"-m\",\n \"--merchant_center_account_id\",\n type=int,\n required=False,\n default=None,\n help=\"Optional Merchant Center Account ID. Only required if uploading \"\n \"with item attributes.\",\n )\n parser.add_argument(\n \"-r\",\n \"--country_code\",\n type=str,\n required=False,\n default=None,\n help=\"Optional two-letter country code of the location associated with \"\n \"the feed where your items are uploaded. Only required if uploading \"\n \"with item attributes.\",\n )\n parser.add_argument(\n \"-l\",\n \"--language_code\",\n type=str,\n required=False,\n default=None,\n help=\"Optional two-letter language code of the language associated \"\n \"with the feed where your items are uploaded. Only required if \"\n \"uploading with item attributes.\",\n )\n parser.add_argument(\n \"-q\",\n \"--quantity\",\n type=int,\n required=False,\n default=1,\n help=\"Optional number of items sold. Only required if uploading with \"\n \"item attributes.\",\n )\n parser.add_argument(\n \"--ad_user_data_consent\",\n type=str,\n choices=[\n e.name\n for e in googleads_client.enums.ConsentStatusEnum\n if e.name not in (\"UNSPECIFIED\", \"UNKNOWN\")\n ],\n help=(\n \"The data consent status for ad user data for all members in \"\n \"the job.\"\n ),\n )\n parser.add_argument(\n \"--ad_personalization_consent\",\n type=str,\n choices=[\n e.name\n for e in googleads_client.enums.ConsentStatusEnum\n if e.name not in (\"UNSPECIFIED\", \"UNKNOWN\")\n ],\n help=(\n \"The personalization consent status for ad user data for all \"\n \"members in the job.\"\n ),\n )\n args: argparse.Namespace = parser.parse_args()\n\n # Additional check to make sure that custom_key and custom_value are either\n # not provided or both provided together.\n required_together: Tuple[str, str] = (\"custom_key\", \"custom_value\")\n required_custom_vals: List[Optional[str]] = [\n getattr(args, field, None) for field in required_together\n ]\n if any(required_custom_vals) and not all(required_custom_vals):\n parser.error(\n \"--custom_key (-k) and --custom_value (-v) must be passed \"\n \"in together\"\n )\n\n try:\n main(\n googleads_client,\n args.customer_id,\n args.conversion_action_id,\n args.offline_user_data_job_type,\n args.external_id,\n args.advertiser_upload_date_time,\n args.bridge_map_version_id,\n args.partner_id,\n args.custom_key,\n args.custom_value,\n args.item_id,\n args.merchant_center_account_id,\n args.country_code,\n args.language_code,\n args.quantity,\n args.ad_user_data_consent,\n args.ad_personalization_consent,\n )\n except GoogleAdsException as ex:\n print(\n f\"Request with ID '{ex.request_id}' failed with status \"\n f\"'{ex.error.code().name}' and includes the following errors:\"\n )\n for error in ex.failure.errors:\n print(f\"\\tError with message '{error.message}'.\")\n if error.location:\n for field_path_element in error.location.field_path_elements:\n print(f\"\\t\\tOn field: {field_path_element.field_name}\")\n sys.exit(1)\nupload_store_sales_transactions.py\n```\n\nExample:\n```text\n#!/usr/bin/env ruby\n# Encoding: utf-8\n#\n# Copyright 2020 Google LLC\n#\n# 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.\n#\n# Uploads offline data for store sales transactions.\n#\n# This feature is only available to allowlisted accounts. See\n# https://support.google.com/google-ads/answer/7620302 for more details.\n\nrequire 'date'\nrequire 'digest'\nrequire 'google/ads/google_ads'\nrequire 'optparse'\n\ndef upload_store_sales_transactions(\n customer_id,\n offline_user_data_job_type,\n conversion_action_id,\n external_id,\n advertiser_upload_date_time,\n bridge_map_version_id,\n partner_id,\n custom_key,\n custom_value,\n item_id,\n merchant_center_account_id,\n region_code,\n language_code,\n quantity,\n ad_user_data_consent,\n ad_personalization_consent)\n # GoogleAdsClient will read a config file from\n # ENV['HOME']/google_ads_config.rb when called without parameters\n client = Google::Ads::GoogleAds::GoogleAdsClient.new\n offline_user_data_job_service = client.service.offline_user_data_job\n\n # Creates an offline user data job for uploading transactions.\n offline_user_data_job_resource_name = create_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_type,\n external_id,\n advertiser_upload_date_time,\n bridge_map_version_id,\n partner_id,\n custom_key,\n )\n\n # Add transactions to the job\n add_transactions_to_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_resource_name,\n conversion_action_id,\n custom_value,\n ad_user_data_consent,\n ad_personalization_consent\n )\n\n # Issues an asynchronous request to run the offline user data job.\n offline_user_data_job_service.run_offline_user_data_job(\n resource_name: offline_user_data_job_resource_name,\n )\n\n puts \"Sent request to asynchronously run offline user data job: \" \\\n \"#{offline_user_data_job_resource_name}\"\n\n # Offline user data jobs may take up to 24 hours to complete, so instead of\n # waiting for the job to complete, retrieves and displays the job status once\n # and then prints the query to use to check the job again later.\n check_job_status(client, customer_id, offline_user_data_job_resource_name)\nend\n\n# Creates an offline user data job for uploading store sales transactions.\ndef create_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_type,\n external_id,\n advertiser_upload_date_time,\n bridge_map_version_id,\n partner_id,\n custom_key)\n # TIP: If you are migrating from the AdWords API, please note tha Google Ads\n # API uses the term \"fraction\" instead of \"rate\". For example, loyalty_rate\n # in the AdWords API is called loyalty_fraction in the Google Ads API.\n # Please refer to https://support.google.com/google-ads/answer/7506124 for\n # additional details.\n store_sales_metadata = client.resource.store_sales_metadata do |s|\n # Sets the fraction of your overall sales that you (or the advertiser, in\n # the third party case) can associate with a customer (email, phone number,\n # address, etc.) in your database or loyalty program.\n # For example, set this to 0.7 if you have 100 transactions over 30 days,\n # and out of those 100 transactions, you can identify 70 by an email address\n # or phone number.\n s.loyalty_fraction = 0.7\n # Sets the fraction of sales you're uploading out of the overall sales that\n # you (or the advertiser, in the third party case) can associate with a\n # customer. In most cases, you will set this to 1.0.\n # Continuing the example above for loyalty fraction, a value of 1.0 here\n # indicates that you are uploading all 70 of the transactions that can be\n # identified by an email address or phone number.\n s.transaction_upload_fraction = 1.0\n s.custom_key = custom_key unless custom_key.nil?\n end\n\n # Creates additional metadata required for uploading third party data.\n if offline_user_data_job_type == :STORE_SALES_UPLOAD_THIRD_PARTY\n store_sales_metadata.third_party_metadata =\n client.resource.store_sales_third_party_metadata do |t|\n # The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n t.advertiser_upload_date_time = advertiser_upload_date_time\n # Sets the fraction of transactions you received from the advertiser\n # that have valid formatting and values. This captures any transactions\n # the advertiser provided to you but which you are unable to upload to\n # Google due to formatting errors or missing data.\n # In most cases, you will set this to 1.0.\n t.valid_transaction_fraction = 1.0\n # Sets the fraction of valid transactions (as defined above) you received\n # from the advertiser that you (the third party) have matched to an\n # external user ID on your side.\n # In most cases, you will set this to 1.0.\n t.partner_match_fraction = 1.0\n # Sets the fraction of transactions you (the third party) are uploading\n # out of the transactions you received from the advertiser that meet both\n # of the following criteria:\n # 1. Are valid in terms of formatting and values. See valid transaction\n # fraction above.\n # 2. You matched to an external user ID on your side. See partner match\n # fraction above.\n # In most cases, you will set this to 1.0.\n t.partner_upload_fraction = 1.0\n # Please speak with your Google representative to get the values to use\n # for the bridge map version and partner IDs.\n # Sets the version of partner IDs to be used for uploads.\n t.bridge_map_version_id = bridge_map_version_id\n # Sets the third party partner ID uploading the transactions.\n t.partner_id = partner_id.to_i\n end\n end\n\n # Creates a new offline user data job.\n offline_user_data_job = client.resource.offline_user_data_job do |job|\n job.type = offline_user_data_job_type\n job.store_sales_metadata = store_sales_metadata\n end\n\n unless external_id.nil?\n offline_user_data_job.external_id = external_id.to_i\n end\n\n # Issues a request to create the offline user data job.\n response = offline_user_data_job_service.create_offline_user_data_job(\n customer_id: customer_id,\n job: offline_user_data_job,\n )\n\n offline_user_data_job_resource_name = response.resource_name\n puts \"Created an offline user data job with resource name: \" \\\n \"#{offline_user_data_job_resource_name}.\"\n\n offline_user_data_job_resource_name\nend\n\n# Adds operations to the job for a set of sample transactions.\ndef add_transactions_to_offline_user_data_job(\n client,\n offline_user_data_job_service,\n customer_id,\n offline_user_data_job_resource_name,\n conversion_action_id,\n custom_value,\n ad_user_data_consent,\n ad_personalization_consent)\n # Constructs the operation for each transaction.\n user_data_job_operations = build_offline_user_data_job_operations(\n client, customer_id, conversion_action_id, custom_value, ad_user_data_consent,\n ad_personalization_consent)\n\n # Issues a request to add the operations to the offline user data job.\n response = offline_user_data_job_service.add_offline_user_data_job_operations(\n resource_name: offline_user_data_job_resource_name,\n operations: user_data_job_operations,\n enable_partial_failure: true,\n enable_warnings: true,\n )\n\n # Prints errors if any partial failure error is returned.\n if response.partial_failure_error\n failures = client.decode_partial_failure_error(response.partial_failure_error)\n failures.each do |failure|\n failure.errors.each do |error|\n human_readable_error_path = error\n .location\n .field_path_elements\n .map { |location_info|\n if location_info.index\n \"#{location_info.field_name}[#{location_info.index}]\"\n else\n \"#{location_info.field_name}\"\n end\n }.join(\" > \")\n\n errmsg = \"error occured while adding operations \" \\\n \"#{human_readable_error_path}\" \\\n \" with value: #{error.trigger&.string_value}\" \\\n \" because #{error.message.downcase}\"\n puts errmsg\n end\n end\n end\n\n if response.warning\n # Convert to a GoogleAdsFailure.\n warnings = client.decode_warning(response.warning)\n puts \"Encountered #{warnings.errors.size} warning(s).\"\n end\n\n puts \"Successfully added #{user_data_job_operations.size} operations to \" \\\n \"the offline user data job.\"\nend\n\n# Creates a list of offline user data job operations for sample transactions.\ndef build_offline_user_data_job_operations(\n client,\n customer_id,\n conversion_action_id,\n custom_value,\n ad_user_data_consent,\n ad_personalization_consent)\n operations = []\n\n # Creates the first transaction for upload based on an email address\n # and state.\n operations << client.operation.create_resource.offline_user_data_job do |op|\n op.user_identifiers << client.resource.user_identifier do |id|\n # Email addresses must be normalized and hashed.\n id.hashed_email = normalize_and_hash(\"dana@example.com\")\n end\n op.user_identifiers << client.resource.user_identifier do |id|\n id.address_info = client.resource.offline_user_address_info do |info|\n info.state = \"NY\"\n end\n end\n op.transaction_attribute = client.resource.transaction_attribute do |t|\n t.conversion_action = client.path.conversion_action(\n customer_id, conversion_action_id)\n t.currency_code = \"USD\"\n # Converts the transaction amount from $200 USD to micros.\n t.transaction_amount_micros = 200_000_000\n # Specifies the date and time of the transaction. The format is\n # \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional timezone\n # offset from UTC. If the offset is absent, the API will use the\n # account's timezone as default. Examples: \"2018-03-05 09:15:00\" or\n # \"2018-02-01 14:34:30+03:00\".\n t.transaction_date_time = \"2020-05-01 23:52:12\"\n t.custom_value = custom_value unless custom_value.nil?\n end\n if !ad_user_data_consent.nil? || !ad_personalization_consent.nil?\n op.consent = client.resource.consent do |c|\n # Specifies whether user consent was obtained for the data you are\n # uploading. For more details, see:\n # https://www.google.com/about/company/user-consent-policy\n unless ad_user_data_consent.nil?\n c.ad_user_data = ad_user_data_consent\n end\n unless ad_personalization_consent.nil?\n c.ad_personalization = ad_personalization_consent\n end\n end\n end\n end\n\n # Creates the second transaction for upload based on a physical address.\n operations << client.operation.create_resource.offline_user_data_job do |op|\n op.user_identifiers << client.resource.user_identifier do |id|\n id.address_info = client.resource.offline_user_address_info do |info|\n # First and last name must be normalized and hashed.\n info.hashed_first_name = normalize_and_hash(\"Dana\")\n info.hashed_last_name = normalize_and_hash(\"Quinn\")\n # Country code and zip code are sent in plain text.\n info.country_code = \"US\"\n info.postal_code = \"10011\"\n end\n end\n op.transaction_attribute = client.resource.transaction_attribute do |t|\n t.conversion_action = client.path.conversion_action(\n customer_id, conversion_action_id)\n t.currency_code = \"EUR\"\n # Converts the transaction amount from 450 EUR to micros.\n t.transaction_amount_micros = 450_000_000\n # Specifies the date and time of the transaction. This date and time will\n # be interpreted by the API using the Google Ads customer's time zone.\n # The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n t.transaction_date_time = \"2020-05-14 19:07:02\"\n t.custom_value = custom_value unless custom_value.nil?\n if item_id\n t.item_attribute = client.resource.item_attribute do |item|\n item.item_id = item_id\n item.merchant_id = merchant_center_account_id.to_i\n item.region_code = region_code\n item.language_code = language_code\n item.quantity = quantity.to_i\n end\n end\n end\n end\n\n # Returns the operations containing the two transactions.\n operations\nend\n\n# Returns the result of normalizing and then hashing the string.\n# Private customer data must be hashed during upload, as described at\n# https://support.google.com/google-ads/answer/7506124.\ndef normalize_and_hash(str)\n Digest::SHA256.hexdigest(str.strip.downcase)\nend\n\n# Retrieves, checks, and prints the status of the offline user data job.\ndef check_job_status(\n client,\n customer_id,\n offline_user_data_job_resource_name)\n # Creates a query that retrieves the offline user data.\n query = <<~QUERY\n SELECT offline_user_data_job.resource_name,\n offline_user_data_job.id,\n offline_user_data_job.status,\n offline_user_data_job.type,\n offline_user_data_job.failure_reason\n FROM offline_user_data_job\n WHERE offline_user_data_job.resource_name = \"#{offline_user_data_job_resource_name}\"\n QUERY\n\n puts query\n\n # Issues a search stream request.\n responses = client.service.google_ads.search_stream(\n customer_id: customer_id,\n query: query,\n )\n\n # Prints out some information about the offline user data.\n offline_user_data_job = responses.first.results.first.offline_user_data_job\n puts \"Offline user data job ID #{offline_user_data_job.id} \" \\\n \"with type #{offline_user_data_job.type} \" \\\n \"has status: #{offline_user_data_job.status}\"\n\n if offline_user_data_job.status == :FAILED\n puts \" Failure reason: #{offline_user_data_job.failure_reason}\"\n elsif offline_user_data_job.status == :PENDING \\\n || offline_user_data_job.status == :RUNNING\n puts \"To check the status of the job periodically, use the following GAQL \" \\\n \"query with google_ads.search:\"\n puts query\n end\nend\n\nif __FILE__ == $0\n options = {}\n # The following parameter(s) should be provided to run the example. You can\n # either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n # the command line.\n #\n # Parameters passed on the command line will override any parameters set in\n # code.\n #\n # Running the example with -h will print the command line usage.\n options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'\n options[:conversion_action_id] = 'INSERT_CONVERSION_ACTION_ID_HERE'\n options[:offline_user_data_job_type] = \"STORE_SALES_UPLOAD_FIRST_PARTY\"\n\n OptionParser.new do |opts|\n opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))\n\n opts.separator ''\n opts.separator 'Options:'\n\n opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|\n options[:customer_id] = v\n end\n\n opts.on('-c', '--conversion-action-id CONVERSION-ACTION-ID', String,\n 'The ID of a store sales conversion action') do |v|\n options[:conversion_action_id] = v\n end\n\n opts.on('-T', '--offline-user-data-job-type OFFLINE-USER-DATA-JOB-TYPE', String,\n '(Optional) The type of user data in the job (first or third party). ' \\\n 'If you have an official store sales partnership with Google, ' \\\n 'use STORE_SALES_UPLOAD_THIRD_PARTY. Otherwise, use ' \\\n 'STORE_SALES_UPLOAD_FIRST_PARTY or omit this parameter.') do |v|\n options[:offline_user_data_job_type] = v\n end\n\n opts.on('-E', '--external-id EXTERNAL-ID', String,\n '(Optional, but recommended) external ID to identify the offline ' \\\n 'user data job') do |v|\n options[:external_id] = v\n end\n\n opts.on('-U', '--advertiser-upload-date-time ADVERTISER-UPLOAD-DATE-TIME', String,\n '(Only required if uploading third party data) Specify the date and time ' \\\n 'the advertiser uploaded data to the partner. ' \\\n 'The format is \"yyyy-mm-dd hh:mm:ss\"') do |v|\n options[:advertiser_upload_date_time] = v\n end\n\n opts.on('-B', '--bridge-map-version-id BRIDGE-MAP-VERSION-ID', String,\n '(Only required if uploading third party data) ' \\\n 'The version of partner IDs to be used for uploads.') do |v|\n options[:bridge_map_version_id] = v\n end\n\n opts.on('-P', '--partner-id PARTNER-ID', String,\n '(Only required if uploading third party data) ' \\\n 'The ID of the third party partner. ') do |v|\n options[:partner_id] = v\n end\n\n opts.on('-k' '--custom-key CUSTOM-KEY', String,\n 'Only required after creating a custom key and custom values in ' \\\n 'the account. Custom key and values are used to segment store sales ' \\\n 'conversions. This measurement can be used to provide more advanced ' \\\n 'insights. If provided, a custom value must also be provided') do |v|\n options[:custom_key] = v\n end\n\n opts.on('-v' '--custom-value CUSTOM-VALUE', String,\n 'Only required after creating a custom key and custom values in ' \\\n 'the account. Custom key and values are used to segment store sales ' \\\n 'conversions. This measurement can be used to provide more advanced ' \\\n 'insights. If provided, a custom key must also be provided') do |v|\n options[:custom_value] = v\n end\n\n opts.on('-i', '--item-id ITEM-ID', String,\n 'Optional: Specify a unique identifier of a product, either the ' \\\n 'Merchant Center Item ID or Global Trade Item Number (GTIN). ' \\\n 'Only required if uploading with item attributes.') do |v|\n options[:item_id] = v\n end\n\n opts.on('-m', '--merchant-center-account-id MERCHANT-CENTER-ACCOUNT-ID', String,\n 'Optional: Specify a Merchant Center Account ID. Only required if ' \\\n 'uploading with item attributes.') do |v|\n options[:merchant_center_account_id] = v\n end\n\n opts.on('-r', '--region-code REGION-CODE', String,\n 'Optional: Specify a two-letter region code of the location associated ' \\\n 'with the feed where your items are uploaded. Only required if ' \\\n 'uploading with item attributes.') do |v|\n options[:region_code] = v\n end\n\n opts.on('-L', '--language-code LANGUAGE-CODE', String,\n 'Optional: Specify a two-letter language code of the language ' \\\n 'associated with the feed where your items are uploaded. Only required ' \\\n 'if uploading with item attributes.') do |v|\n options[:language_code] = v\n end\n\n opts.on('-q', '--quantity QUANTITY', String,\n 'Optional: Specify a number of items sold. Only required if uploading ' \\\n 'with item attributes.') do |v|\n options[:quantity] = v\n end\n\n opts.on('-d', '--ad-user-data-consent [AD-USER-DATA_CONSENT]', String,\n 'The personalization consent status for ad user data for all members in the job.' \\\n 'e.g. UNKNOWN, GRANTED, DENIED') do |v|\n options[:ad_user_data_consent] = v\n end\n\n opts.on('-p', '--ad-personalization-consent [AD-PERSONALIZATION-CONSENT]', String,\n 'The personalization consent status for ad user data for all members in the job.' \\\n 'e.g. UNKNOWN, GRANTED, DENIED') do |v|\n options[:ad_personalization_consent] = v\n end\n\n opts.separator ''\n opts.separator 'Help:'\n\n opts.on_tail('-h', '--help', 'Show this message') do\n puts opts\n exit\n end\n end.parse!\n\n begin\n upload_store_sales_transactions(\n options.fetch(:customer_id).tr(\"-\", \"\"),\n options[:offline_user_data_job_type].to_sym,\n options.fetch(:conversion_action_id),\n options[:external_id],\n options[:advertiser_upload_date_time],\n options[:bridge_map_version_id],\n options[:partner_id],\n options[:custom_key],\n options[:custom_value],\n options[:item_id],\n options[:merchant_center_account_id],\n options[:region_code],\n options[:language_code],\n options[:quantity],\n options[:ad_user_data_consent],\n options[:ad_personalization_consent],\n )\n rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e\n e.failure.errors.each do |error|\n STDERR.printf(\"Error with message: %s\\n\", error.message)\n if error.location\n error.location.field_path_elements.each do |field_path_element|\n STDERR.printf(\"\\tOn field: %s\\n\", field_path_element.field_name)\n end\n end\n error.error_code.to_h.each do |k, v|\n next if v == :UNSPECIFIED\n STDERR.printf(\"\\tType: %s\\n\\tCode: %s\\n\", k, v)\n end\n end\n end\nend\nupload_store_sales_transactions.rb\n```\n\nExample:\n```text\n#!/usr/bin/perl -w\n#\n# Copyright 2020, Google LLC\n#\n# 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# http://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.\n#\n# This example uploads offline data for store sales transactions.\n#\n# This feature is only available to allowlisted accounts.\n# See https://support.google.com/google-ads/answer/7620302 for more details.\n\nuse strict;\nuse warnings;\nuse utf8;\n\nuse FindBin qw($Bin);\nuse lib \"$Bin/../../lib\";\n\nuse Google::Ads::GoogleAds::Client;\nuse Google::Ads::GoogleAds::Utils::GoogleAdsHelper;\nuse Google::Ads::GoogleAds::V25::Resources::OfflineUserDataJob;\nuse Google::Ads::GoogleAds::V25::Common::Consent;\nuse Google::Ads::GoogleAds::V25::Common::ItemAttribute;\nuse Google::Ads::GoogleAds::V25::Common::OfflineUserAddressInfo;\nuse Google::Ads::GoogleAds::V25::Common::StoreSalesMetadata;\nuse Google::Ads::GoogleAds::V25::Common::StoreSalesThirdPartyMetadata;\nuse Google::Ads::GoogleAds::V25::Common::TransactionAttribute;\nuse Google::Ads::GoogleAds::V25::Common::UserData;\nuse Google::Ads::GoogleAds::V25::Common::UserIdentifier;\nuse Google::Ads::GoogleAds::V25::Enums::OfflineUserDataJobTypeEnum\n qw(STORE_SALES_UPLOAD_FIRST_PARTY STORE_SALES_UPLOAD_THIRD_PARTY);\nuse\n Google::Ads::GoogleAds::V25::Services::OfflineUserDataJobService::OfflineUserDataJobOperation;\nuse Google::Ads::GoogleAds::V25::Utils::ResourceNames;\n\nuse Getopt::Long qw(:config auto_help);\nuse Pod::Usage;\nuse Cwd qw(abs_path);\nuse Digest::SHA qw(sha256_hex);\n\nuse constant POLL_FREQUENCY_SECONDS => 1;\nuse constant POLL_TIMEOUT_SECONDS => 60;\n# If uploading data with custom key and values, specify the value.\nuse constant CUSTOM_VALUE => \"INSERT_CUSTOM_VALUE_HERE\";\n\n# The following parameter(s) should be provided to run the example. You can\n# either specify these by changing the INSERT_XXX_ID_HERE values below, or on\n# the command line.\n#\n# Parameters passed on the command line will override any parameters set in\n# code.\n#\n# Running the example with -h will print the command line usage.\nmy $customer_id = \"INSERT_CUSTOMER_ID_HERE\";\nmy $conversion_action_id = \"INSERT_CONVERSION_ACTION_ID_HERE\";\n\n# Optional: Specify the type of user data in the job (first or third party).\n# If you have an official store sales partnership with Google, use\n# STORE_SALES_UPLOAD_THIRD_PARTY.\n# Otherwise, use STORE_SALES_UPLOAD_FIRST_PARTY or omit this parameter.\nmy $offline_user_data_job_type = STORE_SALES_UPLOAD_FIRST_PARTY;\n# Optional: Specify an external ID below to identify the offline user data job.\n# If none is specified, this example will create an external ID.\nmy $external_id = undef;\n# Optional: Specify the custom key if uploading data with custom key and values.\nmy $custom_key = undef;\n# Optional: Specify an advertiser upload date time for third party data.\nmy $advertiser_upload_date_time = undef;\n# Optional: Specify a bridge map version ID for third party data.\nmy $bridge_map_version_id = undef;\n# Optional: Specify a partner ID for third party data.\nmy $partner_id = undef;\n# Optional: Specify a unique identifier of a product, either the Merchant Center\n# Item ID or Global Trade Item Number (GTIN). Only required if uploading with\n# item attributes.\nmy $item_id = undef;\n# Optional: Specify a Merchant Center Account ID. Only required if uploading\n# with item attributes.\nmy $merchant_center_account_id = undef;\n# Optional: Specify a two-letter country code of the location associated with the\n# feed where your items are uploaded. Only required if uploading with item\n# attributes.\nmy $country_code = undef;\n# Optional: Specify a two-letter language code of the language associated with\n# the feed where your items are uploaded. Only required if uploading with item\n# attributes.\nmy $language_code = undef;\n# Optional: Specify a number of items sold. Only required if uploading with item\n# attributes.\nmy $quantity = 1;\n# Optional: Specify the ad personalization consent status.\nmy $ad_personalization_consent = undef;\n# Optional: Specify the ad user data consent status.\nmy $ad_user_data_consent = undef;\n\nsub upload_store_sales_transactions {\n my (\n $api_client, $customer_id,\n $offline_user_data_job_type, $conversion_action_id,\n $external_id, $custom_key,\n $advertiser_upload_date_time, $bridge_map_version_id,\n $partner_id, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity,\n $$ad_personalization_consent, $ad_user_data_consent\n ) = @_;\n\n my $offline_user_data_job_service = $api_client->OfflineUserDataJobService();\n\n # Create an offline user data job for uploading transactions.\n my $offline_user_data_job_resource_name = create_offline_user_data_job(\n $offline_user_data_job_service, $customer_id,\n $offline_user_data_job_type, $external_id,\n $custom_key, $advertiser_upload_date_time,\n $bridge_map_version_id, $partner_id\n );\n\n # Add transactions to the job.\n add_transactions_to_offline_user_data_job(\n $offline_user_data_job_service, $customer_id,\n $offline_user_data_job_resource_name, $conversion_action_id,\n $custom_key, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity,\n );\n\n # Issue an asynchronous request to run the offline user data job.\n my $operation_response = $offline_user_data_job_service->run({\n resourceName => $offline_user_data_job_resource_name\n });\n print \"Asynchronous request to execute the added operations started.\\n\";\n print \"Waiting until operation completes.\\n\";\n\n # poll_until_done() implements a default back-off policy for retrying. You can\n # tweak the parameters like the poll timeout seconds by passing them to the\n # poll_until_done() method. Visit the OperationService.pm file for more details.\n my $lro = $api_client->OperationService()->poll_until_done({\n name => $operation_response->{name},\n pollFrequencySeconds => POLL_FREQUENCY_SECONDS,\n pollTimeoutSeconds => POLL_TIMEOUT_SECONDS\n });\n if ($lro->{done}) {\n printf \"Offline user data job with resource name '%s' has finished.\\n\",\n $offline_user_data_job_resource_name;\n } else {\n printf\n \"Offline user data job with resource name '%s' still pending after %d \" .\n \"seconds, continuing the execution of the code example anyway.\\n\",\n $offline_user_data_job_resource_name,\n POLL_TIMEOUT_SECONDS;\n }\n\n return 1;\n}\n\n# Creates an offline user data job for uploading store sales transactions.\n# Returns the resource name of the created job.\nsub create_offline_user_data_job {\n my (\n $offline_user_data_job_service, $customer_id,\n $offline_user_data_job_type, $external_id,\n $custom_key, $advertiser_upload_date_time,\n $bridge_map_version_id, $partner_id\n ) = @_;\n\n # TIP: If you are migrating from the AdWords API, please note that Google Ads\n # API uses the term \"fraction\" instead of \"rate\". For example, loyaltyRate in\n # the AdWords API is called loyaltyFraction in the Google Ads API.\n my $store_sales_metadata =\n # Please refer to https://support.google.com/google-ads/answer/7506124 for\n # additional details.\n Google::Ads::GoogleAds::V25::Common::StoreSalesMetadata->new({\n # Set the fraction of your overall sales that you (or the advertiser,\n # in the third party case) can associate with a customer (email, phone\n # number, address, etc.) in your database or loyalty program.\n # For example, set this to 0.7 if you have 100 transactions over 30\n # days, and out of those 100 transactions, you can identify 70 by an\n # email address or phone number.\n loyaltyFraction => 0.7,\n # Set the fraction of sales you're uploading out of the overall sales\n # that you (or the advertiser, in the third party case) can associate\n # with a customer. In most cases, you will set this to 1.0.\n # Continuing the example above for loyalty fraction, a value of 1.0 here\n # indicates that you are uploading all 70 of the transactions that can\n # be identified by an email address or phone number.\n transactionUploadFraction => 1.0\n });\n\n # Apply the custom key if provided.\n $store_sales_metadata->{customKey} = $custom_key if defined $custom_key;\n\n if ($offline_user_data_job_type eq STORE_SALES_UPLOAD_THIRD_PARTY) {\n # Create additional metadata required for uploading third party data.\n my $store_sales_third_party_metadata =\n Google::Ads::GoogleAds::V25::Common::StoreSalesThirdPartyMetadata->new({\n # The date/time must be in the format \"yyyy-MM-dd hh:mm:ss\".\n advertiserUploadDateTime => $advertiser_upload_date_time,\n\n # Set the fraction of transactions you received from the advertiser\n # that have valid formatting and values. This captures any transactions\n # the advertiser provided to you but which you are unable to upload to\n # Google due to formatting errors or missing data.\n # In most cases, you will set this to 1.0.\n validTransactionFraction => 1.0,\n # Set the fraction of valid transactions (as defined above) you received\n # from the advertiser that you (the third party) have matched to an\n # external user ID on your side.\n # In most cases, you will set this to 1.0.\n partnerMatchFraction => 1.0,\n\n # Set the fraction of transactions you (the third party) are uploading\n # out of the transactions you received from the advertiser that meet\n # both of the following criteria:\n # 1. Are valid in terms of formatting and values. See valid transaction\n # fraction above.\n # 2. You matched to an external user ID on your side. See partner match\n # fraction above.\n # In most cases, you will set this to 1.0.\n partnerUploadFraction => 1.0,\n\n # Please speak with your Google representative to get the values to use\n # for the bridge map version and partner IDs.\n\n # Set the version of partner IDs to be used for uploads.\n bridgeMapVersionId => $bridge_map_version_id,\n # Set the third party partner ID uploading the transactions.\n partnerId => $partner_id,\n });\n $store_sales_metadata->{thirdPartyMetadata} =\n $store_sales_third_party_metadata;\n }\n\n # Create a new offline user data job.\n my $offline_user_data_job =\n Google::Ads::GoogleAds::V25::Resources::OfflineUserDataJob->new({\n type => $offline_user_data_job_type,\n storeSalesMetadata => $store_sales_metadata,\n external_id => $external_id,\n });\n\n # Issue a request to create the offline user data job.\n my $create_offline_user_data_job_response =\n $offline_user_data_job_service->create({\n customerId => $customer_id,\n job => $offline_user_data_job\n });\n my $offline_user_data_job_resource_name =\n $create_offline_user_data_job_response->{resourceName};\n printf\n \"Created an offline user data job with resource name: '%s'.\\n\",\n $offline_user_data_job_resource_name;\n return $offline_user_data_job_resource_name;\n}\n\n# Adds operations to the job for a set of sample transactions.\nsub add_transactions_to_offline_user_data_job {\n my (\n $offline_user_data_job_service, $customer_id,\n $offline_user_data_job_resource_name, $conversion_action_id,\n $custom_key, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity\n ) = @_;\n\n # Construct the operation for each transaction.\n my $user_data_job_operations = build_offline_user_data_job_operations(\n $customer_id, $conversion_action_id,\n $custom_key, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity,\n $ad_personalization_consent, $ad_user_data_consent\n );\n\n # Issue a request to add the operations to the offline user data job.\n my $response = $offline_user_data_job_service->add_operations({\n resourceName => $offline_user_data_job_resource_name,\n enablePartialFailure => \"true\",\n # Enable warnings (optional).\n enableWarnings => \"true\",\n operations => $user_data_job_operations\n });\n\n # Print the status message if any partial failure error is returned.\n # Note: The details of each partial failure error are not printed here, you\n # can refer to the example handle_partial_failure.pl to learn more.\n if ($response->{partialFailureError}) {\n # Extract the partial failure from the response status.\n my $partial_failure = $response->{partialFailureError}{details}[0];\n foreach my $error (@{$partial_failure->{errors}}) {\n printf \"Partial failure occurred: '%s'\\n\", $error->{message};\n }\n printf \"Encountered %d partial failure errors while adding %d operations \" .\n \"to the offline user data job: '%s'. Only the successfully added \" .\n \"operations will be executed when the job runs.\\n\",\n scalar @{$partial_failure->{errors}}, scalar @$user_data_job_operations,\n $response->{partialFailureError}{message};\n } else {\n printf \"Successfully added %d operations to the offline user data job.\\n\",\n scalar @$user_data_job_operations;\n }\n\n # Print the number of warnings if any warnings are returned. You can access\n # details of each warning using the same approach you'd use for partial failure\n # errors.\n if ($response->{warning}) {\n # Extract the warnings from the response status.\n my $warnings_failure = $response->{warning}{details}[0];\n printf \"Encountered %d warning(s).\\n\",\n scalar @{$warnings_failure->{errors}};\n }\n}\n\n# Creates a list of offline user data job operations for sample transactions.\n# Returns a list of operations.\nsub build_offline_user_data_job_operations {\n my (\n $customer_id, $conversion_action_id,\n $custom_key, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity,\n $ad_personalization_consent, $ad_user_data_consent\n ) = @_;\n\n # Create the first transaction for upload based on an email address and state.\n my $user_data_with_email_address =\n Google::Ads::GoogleAds::V25::Common::UserData->new({\n userIdentifiers => [\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n # Hash normalized email addresses based on SHA-256 hashing algorithm.\n hashedEmail => normalize_and_hash('dana@example.com')}\n ),\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n addressInfo =>\n Google::Ads::GoogleAds::V25::Common::OfflineUserAddressInfo->new({\n state => \"NY\"\n })})\n ],\n transactionAttribute =>\n Google::Ads::GoogleAds::V25::Common::TransactionAttribute->new({\n conversionAction =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::conversion_action(\n $customer_id, $conversion_action_id\n ),\n currencyCode => \"USD\",\n # Convert the transaction amount from $200 USD to micros.\n transactionAmountMicros => 200000000,\n # Specify the date and time of the transaction. The format is\n # \"YYYY-MM-DD HH:MM:SS[+HH:MM]\", where [+HH:MM] is an optional timezone\n # offset from UTC. If the offset is absent, the API will use the\n # account's timezone as default. Examples: \"2018-03-05 09:15:00\"\n # or \"2018-02-01 14:34:30+03:00\".\n transactionDateTime => \"2020-05-01 23:52:12\",\n })});\n\n # Add consent information if specified.\n if ($ad_personalization_consent or $ad_user_data_consent) {\n # Specify whether user consent was obtained for the data you are uploading.\n # See https://www.google.com/about/company/user-consent-policy for details.\n $user_data_with_email_address->{consent} =\n Google::Ads::GoogleAds::V25::Common::Consent({\n adPersonalization => $ad_personalization_consent,\n adUserData => $ad_user_data_consent\n });\n }\n\n # Optional: If uploading data with custom key and values, also assign the\n # custom value.\n if (defined($custom_key)) {\n $user_data_with_email_address->{transactionAttribute}{customValue} =\n CUSTOM_VALUE;\n }\n\n # Create the second transaction for upload based on a physical address.\n my $user_data_with_physical_address =\n Google::Ads::GoogleAds::V25::Common::UserData->new({\n userIdentifiers => [\n Google::Ads::GoogleAds::V25::Common::UserIdentifier->new({\n addressInfo =>\n Google::Ads::GoogleAds::V25::Common::OfflineUserAddressInfo->new({\n # First and last name must be normalized and hashed.\n hashedFirstName => normalize_and_hash(\"Dana\"),\n hashedLastName => normalize_and_hash(\"Quinn\"),\n # Country code and zip code are sent in plain text.\n countryCode => \"US\",\n postalCode => \"10011\"\n })})\n ],\n transactionAttribute =>\n Google::Ads::GoogleAds::V25::Common::TransactionAttribute->new({\n conversionAction =>\n Google::Ads::GoogleAds::V25::Utils::ResourceNames::conversion_action(\n $customer_id,\n $conversion_action_id\n ),\n currencyCode => \"EUR\",\n # Convert the transaction amount from 450 EUR to micros.\n transactionAmountMicros => 450000000,\n # Specify the date and time of the transaction. This date and time\n # will be interpreted by the API using the Google Ads customer's\n # time zone. The date/time must be in the format\n # \"yyyy-MM-dd hh:mm:ss\".\n transactionDateTime => \"2020-05-14 19:07:02\",\n })});\n\n # Optional: If uploading data with item attributes, also assign these values\n # in the transaction attribute.\n if (defined($item_id)) {\n $user_data_with_physical_address->{transactionAttribute}{itemAttribute} =\n Google::Ads::GoogleAds::V25::Common::ItemAttribute->new({\n itemId => $item_id,\n merchantId => $merchant_center_account_id,\n countryCode => $country_code,\n languageCode => $language_code,\n # Quantity field should only be set when at least one of the other item\n # attributes is present.\n quantity => $quantity\n });\n\n }\n\n # Create the operations to add the two transactions.\n my $operations = [\n Google::Ads::GoogleAds::V25::Services::OfflineUserDataJobService::OfflineUserDataJobOperation\n ->new({\n create => $user_data_with_email_address\n }\n ),\n Google::Ads::GoogleAds::V25::Services::OfflineUserDataJobService::OfflineUserDataJobOperation\n ->new({\n create => $user_data_with_physical_address\n })];\n\n return $operations;\n}\n\n# Returns the result of normalizing and then hashing the string using the\n# provided digest. Private customer data must be hashed during upload, as\n# described at https://support.google.com/google-ads/answer/7506124\nsub normalize_and_hash {\n my $value = shift;\n\n $value =~ s/^\\s+|\\s+$//g;\n return sha256_hex(lc $value);\n}\n\n# Don't run the example if the file is being included.\nif (abs_path($0) ne abs_path(__FILE__)) {\n return 1;\n}\n\n# Get Google Ads Client, credentials will be read from ~/googleads.properties.\nmy $api_client = Google::Ads::GoogleAds::Client->new();\n\n# By default examples are set to die on any server returned fault.\n$api_client->set_die_on_faults(1);\n\n# Parameters passed on the command line will override any parameters set in code.\nGetOptions(\n \"customer_id=s\" => \\$customer_id,\n \"offline_user_data_job_type=s\" => \\$offline_user_data_job_type,\n \"conversion_action_id=i\" => \\$conversion_action_id,\n \"external_id=i\" => \\$external_id,\n \"custom_key=s\" => \\$custom_key,\n \"advertiser_upload_date_time=s\" => \\$advertiser_upload_date_time,\n \"bridge_map_version_id=i\" => \\$bridge_map_version_id,\n \"partner_id=i\" => \\$partner_id,\n \"item_id=s\" => \\$item_id,\n \"merchant_center_account_id=i\" => \\$merchant_center_account_id,\n \"country_code=s\" => \\$country_code,\n \"language_code=s\" => \\$language_code,\n \"quantity=i\" => \\$quantity,\n \"ad_personalization_consent=s\" => \\$ad_personalization_consent,\n \"ad_user_data_consent=s\" => \\$ad_user_data_consent\n);\n\n# Print the help message if the parameters are not initialized in the code nor\n# in the command line.\npod2usage(2)\n if not check_params($customer_id, $conversion_action_id);\n\n# Call the example.\nupload_store_sales_transactions(\n $api_client, $customer_id =~ s/-//gr,\n $offline_user_data_job_type, $conversion_action_id,\n $external_id, $custom_key,\n $advertiser_upload_date_time, $bridge_map_version_id,\n $partner_id, $item_id,\n $merchant_center_account_id, $country_code,\n $language_code, $quantity,\n $ad_personalization_consent, $ad_user_data_consent\n);\n\n=pod\n\n=head1 NAME\n\nupload_store_sales_transactions\n\n=head1 DESCRIPTION\n\nThis example uploads offline data for store sales transactions.\n\nThis feature is only available to allowlisted accounts.\nSee https://support.google.com/google-ads/answer/7620302 for more details.\n\n=head1 SYNOPSIS\n\nupload_store_sales_transactions.pl [options]\n\n -help Show the help message.\n -customer_id The Google Ads customer ID.\n -conversion_action_id The ID of a store sales conversion action.\n -offline_user_data_job_type [optional] The type of offline user data in the job (first party or third party).\n If you have an official store sales partnership with Google, use STORE_SALES_UPLOAD_THIRD_PARTY.\n Otherwise, use STORE_SALES_UPLOAD_FIRST_PARTY.\n -external_id [optional] (but recommended) external ID for the offline user data job.\n -custom_key [optional] Only required after creating a custom key and custom values in the account. Custom key\n and values are used to segment store sales conversions. This measurement can be used to provide\n more advanced insights.\n -advertiser_upload_date_time [optional] Date and time the advertiser uploaded data to the partner. Only required for third party uploads.\n The format is \"yyyy-mm-dd hh:mm:ss+|-hh:mm\", e.g. \"2019-01-01 12:32:45-08:00\".\n -bridge_map_version_id [optional] Version of partner IDs to be used for uploads. Only required for third party uploads.\n -partner_id [optional] ID of the third party partner. Only required for third party uploads.\n -item_id [optional] A unique identifier of a product, either the Merchant Center Item ID or Global Trade Item Number (GTIN).\n Only required if uploading with item attributes.\n -merchant_center_account_id [optional] A Merchant Center Account ID. Only required if uploading with item attributes.\n -country_code [optional] A two-letter country code of the location associated with the feed where your items are uploaded.\n Only required if uploading with item attributes.\n For a list of country codes see: https://developers.google.com/google-ads/api/reference/data/codes-formats#country-codes\n -language_code [optional] A two-letter language code of the language associated with the feed where your items are uploaded.\n Only required if uploading with item attributes.\n For a list of language codes see: https://developers.google.com/google-ads/api/reference/data/codes-formats#languages\n -quantity [optional] The number of items sold. Can only be set when at least one other item attribute has been provided.\n Only required if uploading with item attributes.\n -ad_personalization_consent\t\t[optional] The ad personalization consent status.\n\t-ad_user_data_consent\t\t\t[optional] The ad user data consent status.\n\n=cut\nupload_store_sales_transactions.pl\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.671Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":4383,"estimatedTokens":49259}}271{"id":"doc-banner_ads_custom_events_ios_google_for_develope-131c3687","source":"documentation","title":"Banner ads custom events | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/custom-events/banner","text":"Example:\n```text\nimport GoogleMobileAds\n\nclass SampleCustomEvent: NSObject, MediationAdapter {\n\n fileprivate var bannerAd: SampleCustomEventBanner?\n ...\n\n func loadBanner(\n for adConfiguration: MediationBannerAdConfiguration,\n completionHandler: @escaping GADMediationBannerLoadCompletionHandler\n ) {\n self.bannerAd = SampleCustomEventBanner()\n self.bannerAd?.loadBanner(\n for: adConfiguration, completionHandler: completionHandler)\n }\n}\n```\n\nExample:\n```text\n#import \"SampleCustomEvent.h\"\n\n@implementation SampleCustomEvent\n...\n\nSampleCustomEventBanner *sampleBanner;\n\n- (void)loadBannerForAdConfiguration:\n (GADMediationBannerAdConfiguration *)adConfiguration\n completionHandler:(GADMediationBannerLoadCompletionHandler)\n completionHandler {\n sampleBanner = [[SampleCustomEventBanner alloc] init];\n [sampleBanner loadBannerForAdConfiguration:adConfiguration\n completionHandler:completionHandler];\n}\n```\n\nExample:\n```text\nclass SampleCustomEventBanner: NSObject, MediationBannerAd {\n /// The Sample Ad Network banner ad.\n var bannerAd: SampleBanner?\n\n /// The ad event delegate to forward ad rendering events to Google Mobile Ads SDK.\n var delegate: MediationBannerAdEventDelegate?\n\n /// Completion handler called after ad load\n var completionHandler: GADMediationBannerLoadCompletionHandler?\n\n func loadBanner(\n for adConfiguration: MediationBannerAdConfiguration,\n completionHandler: @escaping GADMediationBannerLoadCompletionHandler\n ) {\n // Create the bannerView with the appropriate size.\n let adSize = adConfiguration.adSize\n bannerAd = SampleBanner(\n frame: CGRect(x: 0, y: 0, width: adSize.size.width, height: adSize.size.height))\n bannerAd?.delegate = self\n bannerAd?.adUnit = adConfiguration.credentials.settings[\"parameter\"] as? String\n let adRequest = SampleAdRequest()\n adRequest.testMode = adConfiguration.isTestRequest\n self.completionHandler = completionHandler\n bannerAd?.fetchAd(adRequest)\n }\n}\n```\n\nExample:\n```text\n#import \"SampleCustomEventBanner.h\"\n\n@interface SampleCustomEventBanner () <SampleBannerAdDelegate,\n GADMediationBannerAd> {\n /// The sample banner ad.\n SampleBanner *_bannerAd;\n\n /// The completion handler to call when the ad loading succeeds or fails.\n GADMediationBannerLoadCompletionHandler _loadCompletionHandler;\n\n /// The ad event delegate to forward ad rendering events to the Google Mobile\n /// Ads SDK.\n id <GADMediationBannerAdEventDelegate> _adEventDelegate;\n}\n@end\n\n@implementation SampleCustomEventBanner\n\n- (void)loadBannerForAdConfiguration:\n (GADMediationBannerAdConfiguration *)adConfiguration\n completionHandler:(GADMediationBannerLoadCompletionHandler)\n completionHandler {\n __block atomic_flag completionHandlerCalled = ATOMIC_FLAG_INIT;\n __block GADMediationBannerLoadCompletionHandler originalCompletionHandler =\n [completionHandler copy];\n\n _loadCompletionHandler = ^id<GADMediationBannerAdEventDelegate>(\n _Nullable id<GADMediationBannerAd> ad, NSError *_Nullable error) {\n // Only allow completion handler to be called once.\n if (atomic_flag_test_and_set(&completionHandlerCalled)) {\n return nil;\n }\n\n id<GADMediationBannerAdEventDelegate> delegate = nil;\n if (originalCompletionHandler) {\n // Call original handler and hold on to its return value.\n delegate = originalCompletionHandler(ad, error);\n }\n\n // Release reference to handler. Objects retained by the handler will also\n // be released.\n originalCompletionHandler = nil;\n\n return delegate;\n };\n\n NSString *adUnit = adConfiguration.credentials.settings[@\"parameter\"];\n _bannerAd = [[SampleBanner alloc]\n initWithFrame:CGRectMake(0, 0, adConfiguration.adSize.size.width,\n adConfiguration.adSize.size.height)];\n _bannerAd.adUnit = adUnit;\n _bannerAd.delegate = self;\n SampleAdRequest *adRequest = [[SampleAdRequest alloc] init];\n adRequest.testMode = adConfiguration.isTestRequest;\n [_bannerAd fetchAd:adRequest];\n}\n```\n\nExample:\n```text\nfunc bannerDidLoad(_ banner: SampleBanner) {\n if let handler = completionHandler {\n delegate = handler(self, nil)\n }\n}\n\nfunc banner(\n _ banner: SampleBanner, didFailToLoadAdWith errorCode: SampleErrorCode\n) {\n let error =\n SampleCustomEventUtilsSwift.SampleCustomEventErrorWithCodeAndDescription(\n code: SampleCustomEventErrorCodeSwift\n .SampleCustomEventErrorAdLoadFailureCallback,\n description:\n \"Sample SDK returned an ad load failure callback with error code: \\(errorCode)\"\n )\n if let handler = completionHandler {\n delegate = handler(nil, error)\n }\n}\n```\n\nExample:\n```text\n- (void)bannerDidLoad:(SampleBanner *)banner {\n _adEventDelegate = _loadCompletionHandler(self, nil);\n}\n\n- (void)banner:(SampleBanner *)banner\n didFailToLoadAdWithErrorCode:(SampleErrorCode)errorCode {\n NSError *error = SampleCustomEventErrorWithCodeAndDescription(\n SampleCustomEventErrorAdLoadFailureCallback,\n [NSString stringWithFormat:@\"Sample SDK returned an ad load failure \"\n @\"callback with error code: %@\",\n errorCode]);\n _adEventDelegate = _loadCompletionHandler(nil, error);\n}\n```\n\nExample:\n```text\nvar view: UIView {\n return bannerAd ?? UIView()\n}\n```\n\nExample:\n```text\n- (nonnull UIView *)view {\n return _bannerAd;\n}\n```\n\nExample:\n```text\nfunc bannerWillLeaveApplication(_ banner: SampleBanner) {\n delegate?.reportClick()\n}\n```\n\nExample:\n```text\n- (void)bannerWillLeaveApplication:(SampleBanner *)banner {\n [_adEventDelegate reportClick];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.673Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":199,"estimatedTokens":1454}}272{"id":"doc-integrate_unity_ads_with_admob_mediation_ios_goo-dbd777cb","source":"documentation","title":"Integrate Unity Ads with AdMob Mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/unity","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-unity.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationUnity'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nlet gdprMetaData = UADSMetaData()\ngdprMetaData.set(\"gdpr.consent\", value: true)\ngdprMetaData.commit()UnityAdsMediationSwiftSnippets.swift\n```\n\nExample:\n```text\nUADSMetaData *gdprMetaData = [[UADSMetaData alloc] init];\n[gdprMetaData set:@\"gdpr.consent\" value:@YES];\n[gdprMetaData commit];UnityAdsMediationObjectiveCSnippets.m\n```\n\nExample:\n```text\nlet ccpaMetaData = UADSMetaData()\nccpaMetaData.set(\"privacy.consent\", value: true)\nccpaMetaData.commit()UnityAdsMediationSwiftSnippets.swift\n```\n\nExample:\n```text\nUADSMetaData *ccpaMetaData = [[UADSMetaData alloc] init];\n[ccpaMetaData set:@\"privacy.consent\" value:@YES];\n[ccpaMetaData commit];UnityAdsMediationObjectiveCSnippets.m\n```\n\nExample:\n```text\nGADMAdapterUnity\nGADMediationAdapterUnity\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.678Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":50,"estimatedTokens":244}}273{"id":"doc-set_up_custom_events_ios_google_for_developers-96f7f270","source":"documentation","title":"Set up custom events | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/custom-events/setup","text":"Example:\n```text\nimport GoogleMobileAds\n\nclass SampleCustomEvent: NSObject, MediationAdapter {\n\n static func setUpWith(\n _ configuration: MediationServerConfiguration,\n completionHandler: @escaping GADMediationAdapterSetUpCompletionBlock\n ) {\n // This is where you will initialize the SDK that this custom event is built\n // for. Upon finishing the SDK initialization, call the completion handler\n // with success.\n completionHandler(nil)\n }\n}\n```\n\nExample:\n```text\n#import \"SampleCustomEvent.h\"\n\n@implementation SampleCustomEvent\n\n+ (void)setUpWithConfiguration:(nonnull GADMediationServerConfiguration *)configuration\n completionHandler:(nonnull GADMediationAdapterSetUpCompletionBlock)completionHandler {\n // This is where you initialize the SDK that this custom event is built\n // for. Upon finishing the SDK initialization, call the completion handler\n // with success.\n completionHandler(nil);\n}\n```\n\nExample:\n```text\nstatic func adSDKVersion() -> VersionNumber {\n let versionComponents = String(SampleSDKVersion).components(\n separatedBy: \".\")\n\n if versionComponents.count >= 3 {\n let majorVersion = Int(versionComponents[0]) ?? 0\n let minorVersion = Int(versionComponents[1]) ?? 0\n let patchVersion = Int(versionComponents[2]) ?? 0\n\n return VersionNumber(\n majorVersion: majorVersion, minorVersion: minorVersion, patchVersion: patchVersion)\n }\n\n return VersionNumber()\n}\n\nstatic func adapterVersion() -> VersionNumber {\n let versionComponents = String(SampleAdSDK.SampleAdSDKVersionNumber).components(\n separatedBy: \".\")\n var version = VersionNumber()\n if versionComponents.count == 4 {\n version.majorVersion = Int(versionComponents[0]) ?? 0\n version.minorVersion = Int(versionComponents[1]) ?? 0\n version.patchVersion = Int(versionComponents[2]) * 100 + Int(versionComponents[3])\n }\n return version\n}\n```\n\nExample:\n```text\n+ (GADVersionNumber)adSDKVersion {\n NSArray *versionComponents =\n [SampleSDKVersion componentsSeparatedByString:@\".\"];\n GADVersionNumber version = {0};\n if (versionComponents.count >= 3) {\n version.majorVersion = [versionComponents[0] integerValue];\n version.minorVersion = [versionComponents[1] integerValue];\n version.patchVersion = [versionComponents[2] integerValue];\n }\n return version;\n}\n\n+ (GADVersionNumber)adapterVersion {\n NSArray *versionComponents =\n [SampleCustomEventAdapterVersion componentsSeparatedByString:@\".\"];\n GADVersionNumber version = {0};\n if (versionComponents.count == 4) {\n version.majorVersion = [versionComponents[0] integerValue];\n version.minorVersion = [versionComponents[1] integerValue];\n version.patchVersion = [versionComponents[2] integerValue] * 100 +\n [versionComponents[3] integerValue];\n }\n return version;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.679Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":93,"estimatedTokens":711}}274{"id":"doc-interstitial_ads_custom_events_ios_google_for_de-238c2bc9","source":"documentation","title":"Interstitial ads custom events | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/custom-events/interstitial","text":"Example:\n```text\nimport GoogleMobileAds\n\nclass SampleCustomEvent: NSObject, MediationAdapter {\n\n fileprivate var interstitialAd: SampleCustomEventInterstitial?\n ...\n\n func loadInterstitial(\n for adConfiguration: MediationInterstitialAdConfiguration,\n completionHandler: @escaping GADMediationInterstitialLoadCompletionHandler\n ) {\n self.interstitialAd = SampleCustomEventInterstitial()\n self.interstitialAd?.loadInterstitial(\n for: adConfiguration, completionHandler: completionHandler)\n }\n}\n```\n\nExample:\n```text\n#import \"SampleCustomEvent.h\"\n\n@implementation SampleCustomEvent\n\nSampleCustomEventInterstitial *sampleInterstitial;\n\n- (void)loadInterstitialForAdConfiguration:\n (GADMediationInterstitialAdConfiguration *)adConfiguration\n completionHandler:\n (GADMediationInterstitialLoadCompletionHandler)\n completionHandler {\n sampleInterstitial = [[SampleCustomEventInterstitial alloc] init];\n [sampleInterstitial loadInterstitialForAdConfiguration:adConfiguration\n completionHandler:completionHandler];\n}\n```\n\nExample:\n```text\nimport GoogleMobileAds\n\nclass SampleCustomEventInterstitial: NSObject, MediationInterstitialAd {\n /// The Sample Ad Network interstitial ad.\n var interstitial: SampleInterstitial?\n\n /// The ad event delegate to forward ad rendering events to Google Mobile Ads SDK.\n var delegate: MediationInterstitialAdEventDelegate?\n\n var completionHandler: GADMediationInterstitialLoadCompletionHandler?\n\n func loadInterstitial(\n for adConfiguration: MediationInterstitialAdConfiguration,\n completionHandler: @escaping GADMediationInterstitialLoadCompletionHandler\n ) {\n interstitial = SampleInterstitial.init(\n adUnitID: adConfiguration.credentials.settings[\"parameter\"] as? String)\n interstitial?.delegate = self\n let adRequest = SampleAdRequest()\n adRequest.testMode = adConfiguration.isTestRequest\n self.completionHandler = completionHandler\n interstitial?.fetchAd(adRequest)\n }\n\n func present(from viewController: UIViewController) {\n if let interstitial = interstitial, interstitial.isInterstitialLoaded {\n interstitial.show()\n }\n }\n}\n```\n\nExample:\n```text\n#import \"SampleCustomEventInterstitial.h\"\n\n@interface SampleCustomEventInterstitial () <SampleInterstitialAdDelegate,\n GADMediationInterstitialAd> {\n /// The sample interstitial ad.\n SampleInterstitial *_interstitialAd;\n\n /// The completion handler to call when the ad loading succeeds or fails.\n GADMediationInterstitialLoadCompletionHandler _loadCompletionHandler;\n\n /// The ad event delegate to forward ad rendering events to the Google Mobile\n /// Ads SDK.\n id <GADMediationInterstitialAdEventDelegate> _adEventDelegate;\n}\n@end\n\n- (void)loadInterstitialForAdConfiguration:\n (GADMediationInterstitialAdConfiguration *)adConfiguration\n completionHandler:\n (GADMediationInterstitialLoadCompletionHandler)\n completionHandler {\n __block atomic_flag completionHandlerCalled = ATOMIC_FLAG_INIT;\n __block GADMediationInterstitialLoadCompletionHandler\n originalCompletionHandler = [completionHandler copy];\n\n _loadCompletionHandler = ^id<GADMediationInterstitialAdEventDelegate>(\n _Nullable id<GADMediationInterstitialAd> ad, NSError *_Nullable error) {\n // Only allow completion handler to be called once.\n if (atomic_flag_test_and_set(&completionHandlerCalled)) {\n return nil;\n }\n\n id<GADMediationInterstitialAdEventDelegate> delegate = nil;\n if (originalCompletionHandler) {\n // Call original handler and hold on to its return value.\n delegate = originalCompletionHandler(ad, error);\n }\n\n // Release reference to handler. Objects retained by the handler will also\n // be released.\n originalCompletionHandler = nil;\n\n return delegate;\n };\n\n NSString *adUnit = adConfiguration.credentials.settings[@\"parameter\"];\n _interstitialAd = [[SampleInterstitial alloc] initWithAdUnitID:adUnit];\n _interstitialAd.delegate = self;\n SampleAdRequest *adRequest = [[SampleAdRequest alloc] init];\n adRequest.testMode = adConfiguration.isTestRequest;\n [_interstitialAd fetchAd:adRequest];\n}\n```\n\nExample:\n```text\nfunc interstitialDidLoad(_ interstitial: SampleInterstitial) {\n if let handler = completionHandler {\n delegate = handler(self, nil)\n }\n}\n\nfunc interstitial(\n _ interstitial: SampleInterstitial,\n didFailToLoadAdWith errorCode: SampleErrorCode\n) {\n let error =\n SampleCustomEventUtilsSwift.SampleCustomEventErrorWithCodeAndDescription(\n code: SampleCustomEventErrorCodeSwift\n .SampleCustomEventErrorAdLoadFailureCallback,\n description:\n \"Sample SDK returned an ad load failure callback with error code: \\(errorCode)\"\n )\n if let handler = completionHandler {\n delegate = handler(nil, error)\n }\n}\n```\n\nExample:\n```text\n- (void)interstitialDidLoad:(SampleInterstitial *)interstitial {\n _adEventDelegate = _loadCompletionHandler(self, nil);\n}\n\n- (void)interstitial:(SampleInterstitial *)interstitial\n didFailToLoadAdWithErrorCode:(SampleErrorCode)errorCode {\n NSError *error = SampleCustomEventErrorWithCodeAndDescription(\n SampleCustomEventErrorAdLoadFailureCallback,\n [NSString stringWithFormat:@\"Sample SDK returned an ad load failure \"\n @\"callback with error code: %@\",\n errorCode]);\n _adEventDelegate = _loadCompletionHandler(nil, error);\n}\n```\n\nExample:\n```text\nfunc present(from viewController: UIViewController) {\n if let interstitial = interstitial, interstitial.isInterstitialLoaded {\n interstitial.show()\n }\n}\n```\n\nExample:\n```text\n- (void)presentFromViewController:(UIViewController *)viewController {\n if ([_interstitialAd isInterstitialLoaded]) {\n [_interstitialAd show];\n } else {\n NSError *error = SampleCustomEventErrorWithCodeAndDescription(\n SampleCustomEventErrorAdNotLoaded,\n [NSString stringWithFormat:@\"The interstitial ad failed to present \"\n @\"because the ad was not loaded.\"]);\n [_adEventDelegate didFailToPresentWithError:error]\n }\n}\n```\n\nExample:\n```text\nfunc interstitialWillPresentScreen(_ interstitial: SampleInterstitial) {\n delegate?.willPresentFullScreenView()\n delegate?.reportImpression()\n}\n\nfunc interstitialWillDismissScreen(_ interstitial: SampleInterstitial) {\n delegate?.willDismissFullScreenView()\n}\n\nfunc interstitialDidDismissScreen(_ interstitial: SampleInterstitial) {\n delegate?.didDismissFullScreenView()\n}\n\nfunc interstitialWillLeaveApplication(_ interstitial: SampleInterstitial) {\n delegate?.reportClick()\n}\n```\n\nExample:\n```text\n- (void)interstitialWillPresentScreen:(SampleInterstitial *)interstitial {\n [_adEventDelegate willPresentFullScreenView];\n [_adEventDelegate reportImpression];\n}\n\n- (void)interstitialWillDismissScreen:(SampleInterstitial *)interstitial {\n [_adEventDelegate willDismissFullScreenView];\n}\n\n- (void)interstitialDidDismissScreen:(SampleInterstitial *)interstitial {\n [_adEventDelegate didDismissFullScreenView];\n}\n\n- (void)interstitialWillLeaveApplication:(SampleInterstitial *)interstitial {\n [_adEventDelegate reportClick];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.680Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":236,"estimatedTokens":1851}}275{"id":"doc-native_ads_custom_events_ios_google_for_develope-a70d5d0a","source":"documentation","title":"Native ads custom events | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/custom-events/native","text":"Example:\n```text\nimport GoogleMobileAds\n\nclass SampleCustomEvent: NSObject, MediationAdapter {\n\n fileprivate var nativeAd: SampleCustomEventNativeAd?\n\n func loadNativeAd(\n for adConfiguration: MediationNativeAdConfiguration,\n completionHandler: @escaping GADMediationNativeAdLoadCompletionHandler\n ) {\n self.nativeAd = SampleCustomEventNativeAd()\n self.nativeAd?.loadNativeAd(\n for: adConfiguration, completionHandler: completionHandler)\n }\n}\n```\n\nExample:\n```text\n#import \"SampleCustomEvent.h\"\n\n@implementation SampleCustomEvent\n\nSampleCustomEventNativeAd *sampleNativeAd;\n\n- (void)loadNativeAdForAdConfiguration:\n (GADMediationNativeAdConfiguration *)adConfiguration\n completionHandler:\n (GADMediationNativeAdLoadCompletionHandler)\n completionHandler {\n sampleNative = [[SampleCustomEventNativeAd alloc] init];\n [sampleNative loadNativeAdForAdConfiguration:adConfiguration\n completionHandler:completionHandler];\n}\n```\n\nExample:\n```text\nclass SampleCustomEventNativeAd: NSObject, MediationNativeAd {\n /// The Sample Ad Network native ad.\n var nativeAd: SampleNativeAd?\n\n /// The ad event delegate to forward ad rendering events to the Google Mobile\n /// Ads SDK.\n var delegate: MediationNativeAdEventDelegate?\n\n /// Completion handler called after ad load\n var completionHandler: GADMediationNativeLoadCompletionHandler?\n\n func loadNativeAd(\n for adConfiguration: MediationNativeAdConfiguration,\n completionHandler: @escaping GADMediationNativeLoadCompletionHandler\n ) {\n let adLoader = SampleNativeAdLoader()\n let sampleRequest = SampleNativeAdRequest()\n\n // Google Mobile Ads SDK requires the image assets to be downloaded\n // automatically unless the publisher specifies otherwise by using the\n // GADNativeAdImageAdLoaderOptions object's disableImageLoading property. If\n // your network doesn't have an option like this and instead only ever\n // returns URLs for images (rather than the images themselves), your adapter\n // should download image assets on behalf of the publisher. This should be\n // done after receiving the native ad object from your network's SDK, and\n // before calling the connector's adapter:didReceiveMediatedNativeAd: method.\n sampleRequest.shouldDownloadImages = true\n sampleRequest.preferredImageOrientation = NativeAdImageOrientation.any\n sampleRequest.shouldRequestMultipleImages = false\n let options = adConfiguration.options\n for loaderOptions: AdLoaderOptions in options {\n if let imageOptions = loaderOptions as? NativeAdImageAdLoaderOptions {\n sampleRequest.shouldRequestMultipleImages =\n imageOptions.shouldRequestMultipleImages\n // If the GADNativeAdImageAdLoaderOptions' disableImageLoading property is\n // YES, the adapter should send just the URLs for the images.\n sampleRequest.shouldDownloadImages = !imageOptions.disableImageLoading\n } else if let mediaOptions = loaderOptions\n as? NativeAdMediaAdLoaderOptions\n {\n switch mediaOptions.mediaAspectRatio {\n case MediaAspectRatio.landscape:\n sampleRequest.preferredImageOrientation =\n NativeAdImageOrientation.landscape\n case MediaAspectRatio.portrait:\n sampleRequest.preferredImageOrientation =\n NativeAdImageOrientation.portrait\n default:\n sampleRequest.preferredImageOrientation = NativeAdImageOrientation.any\n }\n }\n }\n // This custom event uses the server parameter to carry an ad unit ID, which\n // is the most common use case.\n adLoader.delegate = self\n adLoader.adUnitID =\n adConfiguration.credentials.settings[\"parameter\"] as? String\n self.completionHandler = completionHandler\n adLoader.fetchAd(sampleRequest)\n }\n}\n```\n\nExample:\n```text\n#import \"SampleCustomEventNativeAd.h\"\n\n@interface SampleCustomEventNativeAd () <SampleNativeAdDelegate,\n GADMediationNativeAd> {\n /// The sample native ad.\n SampleNativeAd *_nativeAd;\n\n /// The completion handler to call when the ad loading succeeds or fails.\n GADMediationNativeLoadCompletionHandler _loadCompletionHandler;\n\n /// The ad event delegate to forward ad rendering events to the Google Mobile\n /// Ads SDK.\n id<GADMediationNativeAdEventDelegate> _adEventDelegate;\n}\n@end\n\n- (void)loadNativeAdForAdConfiguration:\n (GADMediationNativeAdConfiguration *)adConfiguration\n completionHandler:(GADMediationNativeLoadCompletionHandler)\n completionHandler {\n __block atomic_flag completionHandlerCalled = ATOMIC_FLAG_INIT;\n __block GADMediationNativeLoadCompletionHandler originalCompletionHandler =\n [completionHandler copy];\n\n _loadCompletionHandler = ^id<GADMediationNativeAdEventDelegate>(\n _Nullable id<GADMediationNativeAd> ad, NSError *_Nullable error) {\n // Only allow completion handler to be called once.\n if (atomic_flag_test_and_set(&completionHandlerCalled)) {\n return nil;\n }\n\n id<GADMediationNativeAdEventDelegate> delegate = nil;\n if (originalCompletionHandler) {\n // Call original handler and hold on to its return value.\n delegate = originalCompletionHandler(ad, error);\n }\n\n // Release reference to handler. Objects retained by the handler will also\n // be released.\n originalCompletionHandler = nil;\n\n return delegate;\n };\n\n SampleNativeAdLoader *adLoader = [[SampleNativeAdLoader alloc] init];\n SampleNativeAdRequest *sampleRequest = [[SampleNativeAdRequest alloc] init];\n\n // Google Mobile Ads SDK requires the image assets to be downloaded\n // automatically unless the publisher specifies otherwise by using the\n // GADNativeAdImageAdLoaderOptions object's disableImageLoading property. If\n // your network doesn't have an option like this and instead only ever returns\n // URLs for images (rather than the images themselves), your adapter should\n // download image assets on behalf of the publisher. This should be done after\n // receiving the native ad object from your network's SDK, and before calling\n // the connector's adapter:didReceiveMediatedNativeAd: method.\n sampleRequest.shouldDownloadImages = YES;\n\n sampleRequest.preferredImageOrientation = NativeAdImageOrientationAny;\n sampleRequest.shouldRequestMultipleImages = NO;\n sampleRequest.testMode = adConfiguration.isTestRequest;\n\n for (GADAdLoaderOptions *loaderOptions in adConfiguration.options) {\n if ([loaderOptions isKindOfClass:[GADNativeAdImageAdLoaderOptions class]]) {\n GADNativeAdImageAdLoaderOptions *imageOptions =\n (GADNativeAdImageAdLoaderOptions *)loaderOptions;\n sampleRequest.shouldRequestMultipleImages =\n imageOptions.shouldRequestMultipleImages;\n\n // If the GADNativeAdImageAdLoaderOptions' disableImageLoading property is\n // YES, the adapter should send just the URLs for the images.\n sampleRequest.shouldDownloadImages = !imageOptions.disableImageLoading;\n } else if ([loaderOptions\n isKindOfClass:[GADNativeAdMediaAdLoaderOptions class]]) {\n GADNativeAdMediaAdLoaderOptions *mediaOptions =\n (GADNativeAdMediaAdLoaderOptions *)loaderOptions;\n switch (mediaOptions.mediaAspectRatio) {\n case GADMediaAspectRatioLandscape:\n sampleRequest.preferredImageOrientation =\n NativeAdImageOrientationLandscape;\n break;\n case GADMediaAspectRatioPortrait:\n sampleRequest.preferredImageOrientation =\n NativeAdImageOrientationPortrait;\n break;\n default:\n sampleRequest.preferredImageOrientation = NativeAdImageOrientationAny;\n break;\n }\n } else if ([loaderOptions isKindOfClass:[GADNativeAdViewAdOptions class]]) {\n _nativeAdViewAdOptions = (GADNativeAdViewAdOptions *)loaderOptions;\n }\n }\n\n // This custom event uses the server parameter to carry an ad unit ID, which\n // is the most common use case.\n NSString *adUnit = adConfiguration.credentials.settings[@\"parameter\"];\n adLoader.adUnitID = adUnit;\n adLoader.delegate = self;\n\n [adLoader fetchAd:sampleRequest];\n}\n```\n\nExample:\n```text\nfunc adLoader(\n _ adLoader: SampleNativeAdLoader, didReceive nativeAd: SampleNativeAd\n) {\n extraAssets = [\n SampleCustomEventConstantsSwift.awesomenessKey: nativeAd.degreeOfAwesomeness\n ?? \"\"\n ]\n\n if let image = nativeAd.image {\n images = [NativeAdImage(image: image)]\n } else {\n let imageUrl = URL(fileURLWithPath: nativeAd.imageURL)\n images = [NativeAdImage(url: imageUrl, scale: nativeAd.imageScale)]\n }\n if let mappedIcon = nativeAd.icon {\n icon = NativeAdImage(image: mappedIcon)\n } else {\n let iconURL = URL(fileURLWithPath: nativeAd.iconURL)\n icon = NativeAdImage(url: iconURL, scale: nativeAd.iconScale)\n }\n\n adChoicesView = SampleAdInfoView()\n self.nativeAd = nativeAd\n if let handler = completionHandler {\n delegate = handler(self, nil)\n }\n}\n\nfunc adLoader(\n _ adLoader: SampleNativeAdLoader,\n didFailToLoadAdWith errorCode: SampleErrorCode\n) {\n let error =\n SampleCustomEventUtilsSwift.SampleCustomEventErrorWithCodeAndDescription(\n code: SampleCustomEventErrorCodeSwift\n .SampleCustomEventErrorAdLoadFailureCallback,\n description:\n \"Sample SDK returned an ad load failure callback with error code: \\(errorCode)\"\n )\n if let handler = completionHandler {\n delegate = handler(nil, error)\n }\n}\n```\n\nExample:\n```text\n- (void)adLoader:(SampleNativeAdLoader *)adLoader\n didReceiveNativeAd:(SampleNativeAd *)nativeAd {\n if (nativeAd.image) {\n _images = @[ [[GADNativeAdImage alloc] initWithImage:nativeAd.image] ];\n } else {\n NSURL *imageURL = [[NSURL alloc] initFileURLWithPath:nativeAd.imageURL];\n _images = @[ [[GADNativeAdImage alloc] initWithURL:imageURL\n scale:nativeAd.imageScale] ];\n }\n\n if (nativeAd.icon) {\n _icon = [[GADNativeAdImage alloc] initWithImage:nativeAd.icon];\n } else {\n NSURL *iconURL = [[NSURL alloc] initFileURLWithPath:nativeAd.iconURL];\n _icon = [[GADNativeAdImage alloc] initWithURL:iconURL\n scale:nativeAd.iconScale];\n }\n\n // The sample SDK provides an AdChoices view (SampleAdInfoView). If your SDK\n // provides image and click through URLs for its AdChoices icon instead of an\n // actual UIView, the adapter is responsible for downloading the icon image\n // and creating the AdChoices icon view.\n _adChoicesView = [[SampleAdInfoView alloc] init];\n _nativeAd = nativeAd;\n\n _adEventDelegate = _loadCompletionHandler(self, nil);\n}\n\n- (void)adLoader:(SampleNativeAdLoader *)adLoader\n didFailToLoadAdWithErrorCode:(SampleErrorCode)errorCode {\n NSError *error = SampleCustomEventErrorWithCodeAndDescription(\n SampleCustomEventErrorAdLoadFailureCallback,\n [NSString stringWithFormat:@\"Sample SDK returned an ad load failure \"\n @\"callback with error code: %@\",\n errorCode]);\n _adEventDelegate = _loadCompletionHandler(nil, error);\n}\n```\n\nExample:\n```text\nvar nativeAd: SampleNativeAd?\n\nvar headline: String? {\n return nativeAd?.headline\n}\n\nvar images: [NativeAdImage]?\n\nvar body: String? {\n return nativeAd?.body\n}\n\nvar icon: NativeAdImage?\n\nvar callToAction: String? {\n return nativeAd?.callToAction\n}\n\nvar starRating: NSDecimalNumber? {\n return nativeAd?.starRating\n}\n\nvar store: String? {\n return nativeAd?.store\n}\n\nvar price: String? {\n return nativeAd?.price\n}\n\nvar advertiser: String? {\n return nativeAd?.advertiser\n}\n\nvar extraAssets: [String: Any]? {\n return [\n SampleCustomEventConstantsSwift.awesomenessKey:\n nativeAd?.degreeOfAwesomeness\n ?? \"\"\n ]\n}\n\nvar adChoicesView: UIView?\n\nvar mediaView: UIView? {\n return nativeAd?.mediaView\n}\n```\n\nExample:\n```text\n/// Used to store the ad's images. In order to implement the\n/// GADMediationNativeAd protocol, we use this class to return the images\n/// property.\nNSArray<GADNativeAdImage *> *_images;\n\n/// Used to store the ad's icon. In order to implement the GADMediationNativeAd\n/// protocol, we use this class to return the icon property.\nGADNativeAdImage *_icon;\n\n/// Used to store the ad's ad choices view. In order to implement the\n/// GADMediationNativeAd protocol, we use this class to return the adChoicesView\n/// property.\nUIView *_adChoicesView;\n\n- (nullable NSString *)headline {\n return _nativeAd.headline;\n}\n\n- (nullable NSArray<GADNativeAdImage *> *)images {\n return _images;\n}\n\n- (nullable NSString *)body {\n return _nativeAd.body;\n}\n\n- (nullable GADNativeAdImage *)icon {\n return _icon;\n}\n\n- (nullable NSString *)callToAction {\n return _nativeAd.callToAction;\n}\n\n- (nullable NSDecimalNumber *)starRating {\n return _nativeAd.starRating;\n}\n\n- (nullable NSString *)store {\n return _nativeAd.store;\n}\n\n- (nullable NSString *)price {\n return _nativeAd.price;\n}\n\n- (nullable NSString *)advertiser {\n return _nativeAd.advertiser;\n}\n\n- (nullable NSDictionary<NSString *, id> *)extraAssets {\n return\n @{SampleCustomEventExtraKeyAwesomeness : _nativeAd.degreeOfAwesomeness};\n}\n\n- (nullable UIView *)adChoicesView {\n return _adChoicesView;\n}\n\n- (nullable UIView *)mediaView {\n return _nativeAd.mediaView;\n}\n\n- (BOOL)hasVideoContent {\n return self.mediaView != nil;\n}\n```\n\nExample:\n```text\nif let image = nativeAd.image {\n images = [NativeAdImage(image: image)]\n} else {\n let imageUrl = URL(fileURLWithPath: nativeAd.imageURL)\n images = [NativeAdImage(url: imageUrl, scale: nativeAd.imageScale)]\n}\n```\n\nExample:\n```text\nif (nativeAd.image) {\n _images = @[ [[GADNativeAdImage alloc] initWithImage:nativeAd.image] ];\n} else {\n NSURL *imageURL = [[NSURL alloc] initFileURLWithPath:nativeAd.imageURL];\n _images = @[ [[GADNativeAdImage alloc] initWithURL:imageURL\n scale:nativeAd.imageScale] ];\n}\n```\n\nExample:\n```text\nfunc didRecordImpression() {\n nativeAd?.recordImpression()\n}\n\nfunc didRecordClickOnAsset(\n withName assetName: GADUnifiedNativeAssetIdentifier,\n view: UIView,\n wController: UIViewController\n) {\n nativeAd?.handleClick(on: view)\n}\n```\n\nExample:\n```text\n- (void)didRecordImpression {\n if (self.nativeAd) {\n [self.nativeAd recordImpression];\n }\n}\n\n- (void)didRecordClickOnAssetWithName:(GADUnifiedNativeAssetIdentifier)assetName\n view:(UIView *)view\n viewController:(UIViewController *)viewController {\n if (self.nativeAd) {\n [self.nativeAd handleClickOnView:view];\n }\n}\n```\n\nExample:\n```text\nfunc handlesUserClicks() -> Bool {\n return true\n}\nfunc handlesUserImpressions() -> Bool {\n return true\n}\n\nfunc didRender(\n in view: UIView, clickableAssetViews: [GADNativeAssetIdentifier: UIView],\n nonclickableAssetViews: [GADNativeAssetIdentifier: UIView],\n viewController: UIViewController\n) {\n // This method is called when the native ad view is rendered. Here you would pass the UIView\n // back to the mediated network's SDK.\n self.nativeAd?.setNativeAdView(view)\n}\n```\n\nExample:\n```text\n- (BOOL)handlesUserClicks {\n return YES;\n}\n\n- (BOOL)handlesUserImpressions {\n return YES;\n}\n\n- (void)didRenderInView:(UIView *)view\n clickableAssetViews:(NSDictionary<GADNativeAssetIdentifier, UIView *> *)\n clickableAssetViews\n nonclickableAssetViews:(NSDictionary<GADNativeAssetIdentifier, UIView *> *)\n nonclickableAssetViews\n viewController:(UIViewController *)viewController {\n // This method is called when the native ad view is rendered. Here you would\n // pass the UIView back to the mediated network's SDK. Playing video using\n // SampleNativeAd's playVideo method\n [_nativeAd setNativeAdView:view];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.681Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":514,"estimatedTokens":3987}}276{"id":"doc-authorized_sellers_for_apps_app_ads_txt_ios_goog-b3cdbc0d","source":"documentation","title":"Authorized Sellers for Apps (app-ads.txt) | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/app-ads","text":"Example:\n```text\ngoogle.com, pub-00000000000000, DIRECT, f08c47fec0942fa0\n```\n\nExample:\n```text\nfirebase init\n```\n\nExample:\n```text\nfirebase deploy --only hosting\n```\n\nExample:\n```text\n\"hosting\": {\n ...\n \"redirects\": [\n {\n \"source\": \"/\",\n \"destination\": \"URL_TO_REDIRECT\",\n \"type\": 301\n }\n ]\n}\n```\n\nExample:\n```text\n\"hosting\": {\n ...\n \"redirects\": [\n {\n \"source\": \"/\",\n \"destination\": \"https://www.example.com\",\n \"type\": 301\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.682Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":44,"estimatedTokens":125}}277{"id":"doc-set_a_fixed_banner_size_ios_google_for_developer-a6d7e9ac","source":"documentation","title":"Set a fixed banner size | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/banner/fixed-size","text":"Example:\n```text\nlet adSize = adSizeFor(cgSize: CGSize(width: 250, height: 250))\n```\n\nExample:\n```text\nGADAdSize size = GADAdSizeFromCGSize(CGSizeMake(250, 250));\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.682Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":45}}278{"id":"doc-respond_to_video_events_ios_google_for_developer-a0b9ba65","source":"documentation","title":"Respond to video events | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/native/video-ads","text":"Example:\n```text\nif myNativeAd.mediaContent.hasVideoContent {\n let mediaAspectRatio = CGFloat(myNativeAd.mediaContent.aspectRatio)\n let duration = myNativeAd.mediaContent.duration\n}\n```\n\nExample:\n```text\nif(myNativeAd.mediaContent.hasVideoContent) {\n CGFloat mediaAspectRatio = myNativeAd.mediaContent.aspectRatio;\n NSTimeInterval duration = myNativeAd.mediaContent.duration;\n}\n```\n\nExample:\n```text\nclass ViewController: NativeAdLoaderDelegate, VideoControllerDelegate {\n private var adLoader: AdLoader?\n\n func viewDidLoad() {\n super.viewDidLoad()\n\n let videoOptions = VideoOptions()\n videoOptions.customControlsRequested = true\n adLoader = AdLoader(\n adUnitID: \"ca-app-pub-3940256099942544/3986624511\",\n rootViewController: self,\n adTypes: [.native],\n options: [videoOptions])\n adLoader?.delegate = self\n adLoader?.load(Request())\n\n }\n\n func adLoader(\n _ adLoader: AdLoader?,\n didReceive nativeAd: NativeAd?\n ) {\n // Set the videoController's delegate to be notified of video events.\n nativeAd?.mediaContent.videoController.delegate = self\n }\n\n // VideoControllerDelegate methods\n func videoControllerDidPlayVideo(_ videoController: VideoController) {\n // Implement this method to receive a notification when the video controller\n // begins playing the ad.\n }\n\n func videoControllerDidPauseVideo(_ videoController: VideoController) {\n // Implement this method to receive a notification when the video controller\n // pauses the ad.\n }\n\n func videoControllerDidEndVideoPlayback(_ videoController: VideoController) {\n // Implement this method to receive a notification when the video controller\n // stops playing the ad.\n }\n\n func videoControllerDidMuteVideo(_ videoController: VideoController) {\n // Implement this method to receive a notification when the video controller\n // mutes the ad.\n }\n\n func videoControllerDidUnmuteVideo(_ videoController: VideoController) {\n // Implement this method to receive a notification when the video controller\n // unmutes the ad.\n }\n}\n```\n\nExample:\n```text\n@interface ViewController () <GADNativeAdLoaderDelegate,\n GADVideoControllerDelegate>\n@property(nonatomic, strong) GADAdLoader *adLoader;\n\n@end\n\n@implementation ViewController\n- (void)viewDidLoad {\n [super viewDidLoad];\n\n GADVideoOptions *videoOptions = [[GADVideoOptions alloc] init];\n videoOptions.customControlsRequested = YES;\n self.adLoader =\n [[GADAdLoader alloc] initWithAdUnitID:@\"ca-app-pub-3940256099942544/3986624511\"\n rootViewController:self\n adTypes:@[ GADAdLoaderAdTypeNative ]\n options:@[ videoOptions ]];\n self.adLoader.delegate = self;\n [self.adLoader loadRequest:[GADRequest request]];\n\n}\n\n- (void)adLoader:(GADAdLoader *)adLoader\n didReceiveNativeAd:(GADNativeAd *)nativeAd {\n // Set the videoController's delegate to be notified of video events.\n nativeAd.mediaContent.videoController.delegate = self;\n}\n\n// GADVideoControllerDelegate methods\n- (void)videoControllerDidPlayVideo:(nonnull GADVideoController *)videoController {\n // Implement this method to receive a notification when the video controller\n // begins playing the ad.\n}\n\n- (void)videoControllerDidPauseVideo:(nonnull GADVideoController *)videoController {\n // Implement this method to receive a notification when the video controller\n // pauses the ad.\n}\n\n- (void)videoControllerDidEndVideoPlayback:(nonnull GADVideoController *)videoController {\n // Implement this method to receive a notification when the video controller\n // stops playing the ad.\n}\n\n- (void)videoControllerDidMuteVideo:(nonnull GADVideoController *)videoController {\n // Implement this method to receive a notification when the video controller\n // mutes the ad.\n}\n\n- (void)videoControllerDidUnmuteVideo:(nonnull GADVideoController *)videoController {\n // Implement this method to receive a notification when the video controller\n // unmutes the ad.\n}\n\n@end\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.683Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":132,"estimatedTokens":1016}}279{"id":"doc-display_a_full_screen_native_ad_ios_google_for_d-a1b5b8ed","source":"documentation","title":"Display a full-screen native ad | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/native/full-screen","text":"Example:\n```text\n-- Native Ad View\n -- Media View\n -- Container View 1\n -- Call To Action View\n -- Container View 2\n -- Headline View\n -- Container View 3\n -- Body View\n```\n\nExample:\n```text\nlet aspectRatioOption = NativeAdMediaAdLoaderOptions()\n aspectRatioOption.mediaAspectRatio = .portrait\n adLoader = AdLoader(\n adUnitID: \"<var>your ad unit ID</var>\",\n rootViewController: self,\n adTypes: adTypes,\n options: [aspectRatioOption])\n```\n\nExample:\n```text\nGADNativeAdMediaAdLoaderOptions *aspectRatioOption = [[GADNativeAdMediaAdLoaderOptions alloc] init];\n aspectRatioOption.mediaAspectRatio = GADMediaAspectRatioPortrait;\n self.adLoader = [[GADAdLoader alloc] initWithAdUnitID:@\"<var>your ad unit ID</var>\"\n rootViewController:self\n adTypes:@[ GADAdLoaderAdTypeNative ]\n options:@[ aspectRatioOption ]];\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.683Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":34,"estimatedTokens":249}}280{"id":"doc-style_ad_layouts_with_native_templates_ios_googl-78ed2798","source":"documentation","title":"Style ad layouts with native templates | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/native/templates","text":"Example:\n```text\n/// Step 1: Import the templates that you need.\n#import \"NativeTemplates/GADTSmallTemplateView.h\"\n#import \"NativeTemplates/GADTTemplateView.h\"\n...\n\n// STEP 2: Initialize your template view object.\nGADTSmallTemplateView *templateView =\n [[NSBundle mainBundle] loadNibNamed:@\"GADTSmallTemplateView\" owner:nil options:nil]\n .firstObject;\n\n// STEP 3: Template views are just GADNativeAdViews.\n_nativeAdView = templateView;\nnativeAd.delegate = self;\n\n// STEP 4: Add your template as a subview of whichever view you'd like.\n// This must be done before calling addHorizontalConstraintsToSuperviewWidth.\n// Please note: Our template objects are subclasses of GADNativeAdView so\n// you can insert them into whatever type of view you’d like, and don’t need to\n// create your own.\n[self.view addSubview:templateView];\n\n// STEP 5 (Optional): Create your styles dictionary. Set your styles dictionary\n// on the template property. A default dictionary is created for you if you do\n// not set this. Note - templates do not currently respect style changes in the\n// xib.\n\nNSString *myBlueColor = @\"#5C84F0\";\nNSDictionary *styles = @{\n GADTNativeTemplateStyleKeyCallToActionFont : [UIFont systemFontOfSize:15.0],\n GADTNativeTemplateStyleKeyCallToActionFontColor : UIColor.whiteColor,\n GADTNativeTemplateStyleKeyCallToActionBackgroundColor :\n [GADTTemplateView colorFromHexString:myBlueColor],\n GADTNativeTemplateStyleKeySecondaryFont : [UIFont systemFontOfSize:15.0],\n GADTNativeTemplateStyleKeySecondaryFontColor : UIColor.grayColor,\n GADTNativeTemplateStyleKeySecondaryBackgroundColor : UIColor.whiteColor,\n GADTNativeTemplateStyleKeyPrimaryFont : [UIFont systemFontOfSize:15.0],\n GADTNativeTemplateStyleKeyPrimaryFontColor : UIColor.blackColor,\n GADTNativeTemplateStyleKeyPrimaryBackgroundColor : UIColor.whiteColor,\n GADTNativeTemplateStyleKeyTertiaryFont : [UIFont systemFontOfSize:15.0],\n GADTNativeTemplateStyleKeyTertiaryFontColor : UIColor.grayColor,\n GADTNativeTemplateStyleKeyTertiaryBackgroundColor : UIColor.whiteColor,\n GADTNativeTemplateStyleKeyMainBackgroundColor : UIColor.whiteColor,\n GADTNativeTemplateStyleKeyCornerRadius : [NSNumber numberWithFloat:7.0],\n};\n\ntemplateView.styles = styles;\n\n// STEP 6: Set the ad for your template to render.\ntemplateView.nativeAd = nativeAd;\n\n// STEP 7 (Optional): If you'd like your template view to span the width of your\n// superview call this method.\n[templateView addHorizontalConstraintsToSuperviewWidth];\n[templateView addVerticalCenterConstraintToSuperview];\n```\n\nExample:\n```text\n/// Call to action font. Expects a UIFont.\nGADTNativeTemplateStyleKeyCallToActionFont\n\n/// Call to action font color. Expects a UIColor.\nGADTNativeTemplateStyleKeyCallToActionFontColor;\n\n/// Call to action background color. Expects a UIColor.\nGADTNativeTemplateStyleKeyCallToActionBackgroundColor;\n\n/// The font, font color and background color for the first row of text in the\n/// template.\n\n/// All templates have a primary text area which is populated by the native ad's\n/// headline.\n\n/// Primary text font. Expects a UIFont.\nGADTNativeTemplateStyleKeyPrimaryFont;\n\n/// Primary text font color. Expects a UIFont.\nGADTNativeTemplateStyleKeyPrimaryFontColor;\n\n/// Primary text background color. Expects a UIColor.\nGADTNativeTemplateStyleKeyPrimaryBackgroundColor;\n\n/// The font, font color and background color for the second row of text in the\n/// template.\n\n/// All templates have a secondary text area which is populated either by the\n/// body of the ad, or by the rating of the app.\n\n/// Secondary text font. Expects a UIFont.\nGADTNativeTemplateStyleKeySecondaryFont;\n\n/// Secondary text font color. Expects a UIColor.\nGADTNativeTemplateStyleKeySecondaryFontColor;\n\n/// Secondary text background color. Expects a UIColor.\nGADTNativeTemplateStyleKeySecondaryBackgroundColor;\n\n/// The font, font color and background color for the third row of text in the\n/// template. The third row is used to display store name or the default\n/// tertiary text.\n\n/// Tertiary text font. Expects a UIFont.\nGADTNativeTemplateStyleKeyTertiaryFont;\n\n/// Tertiary text font color. Expects a UIColor.\nGADTNativeTemplateStyleKeyTertiaryFontColor;\n\n/// Tertiary text background color. Expects a UIColor.\nGADTNativeTemplateStyleKeyTertiaryBackgroundColor;\n\n/// The background color for the bulk of the ad. Expects a UIColor.\nGADTNativeTemplateStyleKeyMainBackgroundColor;\n\n/// The corner rounding radius for the icon view and call to action. Expects an\n/// NSNumber.\nGADTNativeTemplateStyleKeyCornerRadius;\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.684Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":121,"estimatedTokens":1152}}281{"id":"doc-rewarded_ads_ios_google_for_developers-f53974f3","source":"documentation","title":"Rewarded ads | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/rewarded","text":"Example:\n```text\nfunc loadRewardedAd() async {\n do {\n rewardedAd = try await RewardedAd.load(\n // Replace this ad unit ID with your own ad unit ID.\n with: \"ca-app-pub-3940256099942544/1712485313\", request: Request())\n rewardedAd?.fullScreenContentDelegate = self\n } catch {\n print(\"Rewarded ad failed to load with error: \\(error.localizedDescription)\")\n }\n}ViewController.swift\n```\n\nExample:\n```text\nimport GoogleMobileAds\n\nclass RewardedViewModel: NSObject, ObservableObject, FullScreenContentDelegate {\n @Published var coins = 0\n private var rewardedAd: RewardedAd?\n\n func loadAd() async {\n do {\n rewardedAd = try await RewardedAd.load(\n with: \"ca-app-pub-3940256099942544/1712485313\", request: Request())\n rewardedAd?.fullScreenContentDelegate = self\n } catch {\n print(\"Failed to load rewarded ad with error: \\(error.localizedDescription)\")\n }\n }RewardedViewModel.swift\n```\n\nExample:\n```text\n// Replace this ad unit ID with your own ad unit ID.\n[GADRewardedAd loadWithAdUnitID:@\"ca-app-pub-3940256099942544/1712485313\"\n request:[GADRequest request]\n completionHandler:^(GADRewardedAd *ad, NSError *error) {\n if (error) {\n NSLog(@\"Rewarded ad failed to load with error: %@\", [error localizedDescription]);\n return;\n }\n self.rewardedAd = ad;\n self.rewardedAd.fullScreenContentDelegate = self;\n }];ViewController.m\n```\n\nExample:\n```readonly\nprivate func validateServerSideVerification() async {\n do {\n rewardedAd = try await RewardedAd.load(\n // Replace this ad unit ID with your own ad unit ID.\n with: \"ca-app-pub-3940256099942544/1712485313\", request: Request())\n let options = ServerSideVerificationOptions()\n options.customRewardText = \"SAMPLE_CUSTOM_DATA_STRING\"\n rewardedAd?.serverSideVerificationOptions = options\n } catch {\n print(\"Rewarded ad failed to load with error: \\(error.localizedDescription)\")\n }\n}RewardedAdSnippets.swift\n```\n\nExample:\n```readonly\n// Replace this ad unit ID with your own ad unit ID.\n[GADRewardedAd loadWithAdUnitID:@\"ca-app-pub-3940256099942544/1712485313\"\n request:[GADRequest request]\n completionHandler:^(GADRewardedAd *ad, NSError *error) {\n if (error) {\n NSLog(@\"Rewarded ad failed to load with error: %@\", error.localizedDescription);\n return;\n }\n self.rewardedAd = ad;\n GADServerSideVerificationOptions *options =\n [[GADServerSideVerificationOptions alloc] init];\n options.customRewardString = @\"SAMPLE_CUSTOM_DATA_STRING\";\n ad.serverSideVerificationOptions = options;\n }];RewardedAdSnippets.m\n```\n\nExample:\n```text\nrewardedAd?.fullScreenContentDelegate = selfViewController.swift\n```\n\nExample:\n```text\nrewardedAd?.fullScreenContentDelegate = selfRewardedViewModel.swift\n```\n\nExample:\n```text\nself.rewardedAd.fullScreenContentDelegate = self;ViewController.m\n```\n\nExample:\n```text\nfunc adDidRecordImpression(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called.\")\n}\n\nfunc adDidRecordClick(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called.\")\n}\n\nfunc adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called.\")\n}\n\nfunc adWillDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called.\")\n}\n\nfunc adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called.\")\n // Clear the rewarded ad.\n rewardedAd = nil\n}\n\nfunc ad(\n _ ad: FullScreenPresentingAd,\n didFailToPresentFullScreenContentWithError error: Error\n) {\n print(\"\\(#function) called with error: \\(error.localizedDescription).\")\n}ViewController.swift\n```\n\nExample:\n```text\nfunc adDidRecordImpression(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adDidRecordClick(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc ad(\n _ ad: FullScreenPresentingAd,\n didFailToPresentFullScreenContentWithError error: Error\n) {\n print(\"\\(#function) called\")\n}\n\nfunc adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adWillDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n // Clear the rewarded ad.\n rewardedAd = nil\n}RewardedViewModel.swift\n```\n\nExample:\n```text\n- (void)adDidRecordImpression:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)adDidRecordClick:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)adWillPresentFullScreenContent:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)adWillDismissFullScreenContent:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)adDidDismissFullScreenContent:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n // Clear the rewarded ad.\n self.rewardedAd = nil;\n}\n\n- (void)ad:(id)ad didFailToPresentFullScreenContentWithError:(NSError *)error {\n NSLog(@\"%s called with error: %@\", __PRETTY_FUNCTION__, error.localizedDescription);\n}ViewController.m\n```\n\nExample:\n```text\nrewardedAd.present(from: self) {\n let reward = rewardedAd.adReward\n print(\"Reward received with currency \\(reward.amount), amount \\(reward.amount.doubleValue)\")\n\n // TODO: Reward the user.\n}ViewController.swift\n```\n\nExample:\n```text\nvar body: some View {\n VStack(spacing: 20) {\n Button(\"Watch video for additional 10 coins\") {\n viewModel.showAd()\n showWatchVideoButton = false\n }RewardedContentView.swift\n```\n\nExample:\n```text\nfunc showAd() {\n guard let rewardedAd = rewardedAd else {\n return print(\"Ad wasn't ready.\")\n }\n\n rewardedAd.present(from: nil) {\n let reward = rewardedAd.adReward\n print(\"Reward amount: \\(reward.amount)\")\n self.addCoins(reward.amount.intValue)\n }\n}RewardedViewModel.swift\n```\n\nExample:\n```text\n[self.rewardedAd presentFromRootViewController:self\n userDidEarnRewardHandler:^{\n GADAdReward *reward = self.rewardedAd.adReward;\n NSString *rewardMessage = [NSString\n stringWithFormat:@\"Reward received with currency %@ , amount %lf\",\n reward.type, [reward.amount doubleValue]];\n NSLog(@\"%@\", rewardMessage);\n\n // TODO: Reward the user.\n }];ViewController.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.685Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":240,"estimatedTokens":1680}}282{"id":"doc-interstitial_ads_ios_google_for_developers-a7c70146","source":"documentation","title":"Interstitial ads | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/interstitial","text":"Example:\n```text\nfileprivate func loadInterstitial() async {\n do {\n interstitial = try await InterstitialAd.load(\n with: \"ca-app-pub-3940256099942544/4411468910\", request: Request())\n interstitial?.fullScreenContentDelegate = self\n } catch {\n print(\"Failed to load interstitial ad with error: \\(error.localizedDescription)\")\n }\n}ViewController.swift\n```\n\nExample:\n```text\nimport GoogleMobileAds\n\nclass InterstitialViewModel: NSObject, FullScreenContentDelegate {\n private var interstitialAd: InterstitialAd?\n\n func loadAd() async {\n do {\n interstitialAd = try await InterstitialAd.load(\n with: \"ca-app-pub-3940256099942544/4411468910\", request: Request())\n interstitialAd?.fullScreenContentDelegate = self\n } catch {\n print(\"Failed to load interstitial ad with error: \\(error.localizedDescription)\")\n }\n }InterstitialViewModel.swift\n```\n\nExample:\n```text\n[GADInterstitialAd\n loadWithAdUnitID:@\"ca-app-pub-3940256099942544/4411468910\"\n request:[GADRequest request]\n completionHandler:^(GADInterstitialAd *ad, NSError *error) {\n if (error) {\n NSLog(@\"Failed to load interstitial ad with error: %@\", [error localizedDescription]);\n return;\n }\n self.interstitial = ad;\n self.interstitial.fullScreenContentDelegate = self;\n }];ViewController.m\n```\n\nExample:\n```text\ninterstitial?.fullScreenContentDelegate = selfViewController.swift\n```\n\nExample:\n```text\ninterstitialAd?.fullScreenContentDelegate = selfInterstitialViewModel.swift\n```\n\nExample:\n```text\nself.interstitial.fullScreenContentDelegate = self;ViewController.m\n```\n\nExample:\n```text\nfunc adDidRecordImpression(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adDidRecordClick(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {\n print(\"\\(#function) called with error: \\(error.localizedDescription)\")\n // Clear the interstitial ad.\n interstitial = nil\n}\n\nfunc adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adWillDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n // Clear the interstitial ad.\n interstitial = nil\n}ViewController.swift\n```\n\nExample:\n```text\nfunc adDidRecordImpression(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adDidRecordClick(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc ad(\n _ ad: FullScreenPresentingAd,\n didFailToPresentFullScreenContentWithError error: Error\n) {\n print(\"\\(#function) called\")\n}\n\nfunc adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adWillDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n // Clear the interstitial ad.\n interstitialAd = nil\n}InterstitialViewModel.swift\n```\n\nExample:\n```text\n- (void)adDidRecordImpression:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)adDidRecordClick:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)ad:(id<GADFullScreenPresentingAd>)ad\n didFailToPresentFullScreenContentWithError:(NSError *)error {\n NSLog(@\"%s called with error: %@\", __PRETTY_FUNCTION__, error.localizedDescription);\n // Clear the interstitial ad.\n self.interstitial = nil;\n}\n\n- (void)adWillPresentFullScreenContent:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)adWillDismissFullScreenContent:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)adDidDismissFullScreenContent:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n // Clear the interstitial ad.\n self.interstitial = nil;\n}ViewController.m\n```\n\nExample:\n```text\nad.present(from: self!)ViewController.swift\n```\n\nExample:\n```text\nvar body: some View {\n // ...\n }\n .onChange(of: countdownTimer.isComplete) { newValue in\n showGameOverAlert = newValue\n }\n .alert(isPresented: $showGameOverAlert) {\n Alert(\n title: Text(\"Game Over\"),\n message: Text(\"You lasted \\(countdownTimer.countdownTime) seconds\"),\n dismissButton: .cancel(\n Text(\"OK\"),\n action: {\n viewModel.showAd()\n }))InterstitialContentView.swift\n```\n\nExample:\n```text\nfunc showAd() {\n guard let interstitialAd = interstitialAd else {\n return print(\"Ad wasn't ready.\")\n }\n\n interstitialAd.present(from: nil)\n}InterstitialViewModel.swift\n```\n\nExample:\n```text\n[self.interstitial presentFromRootViewController:self];ViewController.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.686Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":197,"estimatedTokens":1231}}283{"id":"doc-display_a_native_ad_ios_google_for_developers-440f7084","source":"documentation","title":"Display a native ad | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/native/advanced","text":"Example:\n```text\nfunc adLoader(_ adLoader: AdLoader, didReceive nativeAd: NativeAd) {\n // ...\n\n // Set ourselves as the native ad delegate to be notified of native ad events.\n nativeAd.delegate = self\n\n // Populate the native ad view with the native ad assets.\n // The headline and mediaContent are guaranteed to be present in every native ad.\n (nativeAdView.headlineView as? UILabel)?.text = nativeAd.headline\n nativeAdView.mediaView?.mediaContent = nativeAd.mediaContent\n\n // Some native ads will include a video asset, while others do not. Apps can use the\n // GADVideoController's hasVideoContent property to determine if one is present, and adjust their\n // UI accordingly.\n let mediaContent = nativeAd.mediaContent\n if mediaContent.hasVideoContent {\n // By acting as the delegate to the GADVideoController, this ViewController receives messages\n // about events in the video lifecycle.\n mediaContent.videoController.delegate = self\n videoStatusLabel.text = \"Ad contains a video asset.\"\n } else {\n videoStatusLabel.text = \"Ad does not contain a video.\"\n }\n\n // This app uses a fixed width for the GADMediaView and changes its height to match the aspect\n // ratio of the media it displays.\n if let mediaView = nativeAdView.mediaView, nativeAd.mediaContent.aspectRatio > 0 {\n let aspectRatioConstraint = NSLayoutConstraint(\n item: mediaView,\n attribute: .width,\n relatedBy: .equal,\n toItem: mediaView,\n attribute: .height,\n multiplier: CGFloat(nativeAd.mediaContent.aspectRatio),\n constant: 0)\n mediaView.addConstraint(aspectRatioConstraint)\n nativeAdView.layoutIfNeeded()\n }\n\n // These assets are not guaranteed to be present. Check that they are before\n // showing or hiding them.\n (nativeAdView.bodyView as? UILabel)?.text = nativeAd.body\n nativeAdView.bodyView?.isHidden = nativeAd.body == nil\n\n (nativeAdView.callToActionView as? UIButton)?.setTitle(nativeAd.callToAction, for: .normal)\n nativeAdView.callToActionView?.isHidden = nativeAd.callToAction == nil\n\n (nativeAdView.iconView as? UIImageView)?.image = nativeAd.icon?.image\n nativeAdView.iconView?.isHidden = nativeAd.icon == nil\n\n (nativeAdView.starRatingView as? UIImageView)?.image = imageOfStars(from: nativeAd.starRating)\n nativeAdView.starRatingView?.isHidden = nativeAd.starRating == nil\n\n (nativeAdView.storeView as? UILabel)?.text = nativeAd.store\n nativeAdView.storeView?.isHidden = nativeAd.store == nil\n\n (nativeAdView.priceView as? UILabel)?.text = nativeAd.price\n nativeAdView.priceView?.isHidden = nativeAd.price == nil\n\n (nativeAdView.advertiserView as? UILabel)?.text = nativeAd.advertiser\n nativeAdView.advertiserView?.isHidden = nativeAd.advertiser == nil\n\n // In order for the SDK to process touch events properly, user interaction should be disabled.\n nativeAdView.callToActionView?.isUserInteractionEnabled = false\n\n // Associate the native ad view with the native ad object. This is\n // required to make the ad clickable.\n // Note: this should always be done after populating the ad views.\n nativeAdView.nativeAd = nativeAd\n}ViewController.swift\n```\n\nExample:\n```text\nimport GoogleMobileAds\n\nclass NativeAdViewModel: NSObject, ObservableObject, NativeAdLoaderDelegate {\n @Published var nativeAd: NativeAd?\n private var adLoader: AdLoader!\n\n func refreshAd() {\n adLoader = AdLoader(\n adUnitID: \"ca-app-pub-3940256099942544/3986624511\",\n // The UIViewController parameter is optional.\n rootViewController: nil,\n adTypes: [.native], options: nil)\n adLoader.delegate = self\n adLoader.load(Request())\n }\n\n func adLoader(_ adLoader: AdLoader, didReceive nativeAd: NativeAd) {\n // Native ad data changes are published to its subscribers.\n self.nativeAd = nativeAd\n nativeAd.delegate = self\n }\n\n func adLoader(_ adLoader: AdLoader, didFailToReceiveAdWithError error: Error) {\n print(\"\\(adLoader) failed with error: \\(error.localizedDescription)\")\n }\n}NativeAdViewModel.swift\n```\n\nExample:\n```text\nprivate struct NativeAdViewContainer: UIViewRepresentable {\n typealias UIViewType = NativeAdView\n\n // Observer to update the UIView when the native ad value changes.\n @ObservedObject var nativeViewModel: NativeAdViewModel\n\n func makeUIView(context: Context) -> NativeAdView {\n return\n Bundle.main.loadNibNamed(\n \"NativeAdView\",\n owner: nil,\n options: nil)?.first as! NativeAdView\n }\n\n func updateUIView(_ nativeAdView: NativeAdView, context: Context) {\n guard let nativeAd = nativeViewModel.nativeAd else { return }\n\n // Each UI property is configurable using your native ad.\n (nativeAdView.headlineView as? UILabel)?.text = nativeAd.headline\n\n nativeAdView.mediaView?.mediaContent = nativeAd.mediaContent\n\n (nativeAdView.bodyView as? UILabel)?.text = nativeAd.body\n\n (nativeAdView.iconView as? UIImageView)?.image = nativeAd.icon?.image\n\n (nativeAdView.starRatingView as? UIImageView)?.image = imageOfStars(from: nativeAd.starRating)\n\n (nativeAdView.storeView as? UILabel)?.text = nativeAd.store\n\n (nativeAdView.priceView as? UILabel)?.text = nativeAd.price\n\n (nativeAdView.advertiserView as? UILabel)?.text = nativeAd.advertiser\n\n (nativeAdView.callToActionView as? UIButton)?.setTitle(nativeAd.callToAction, for: .normal)\n\n // For the SDK to process touch events properly, user interaction should be disabled.\n nativeAdView.callToActionView?.isUserInteractionEnabled = false\n\n // Associate the native ad view with the native ad object. This is required to make the ad\n // clickable.\n // Note: this should always be done after populating the ad views.\n nativeAdView.nativeAd = nativeAd\n }NativeContentView.swift\n```\n\nExample:\n```text\nstruct NativeContentView: View {\n // Single source of truth for the native ad data.\n @StateObject private var nativeViewModel = NativeAdViewModel()\n\n var body: some View {\n ScrollView {\n VStack(spacing: 20) {\n // Updates when the native ad data changes.\n NativeAdViewContainer(nativeViewModel: nativeViewModel)\n .frame(minHeight: 300) // minHeight determined from xib.NativeContentView.swift\n```\n\nExample:\n```text\n- (void)adLoader:(GADAdLoader *)adLoader didReceiveNativeAd:(GADNativeAd *)nativeAd {\n // ...\n\n GADNativeAdView *nativeAdView = self.nativeAdView;\n\n // Set ourselves as the ad delegate to be notified of native ad events.\n nativeAd.delegate = self;\n\n // Populate the native ad view with the native ad assets.\n // The headline and mediaContent are guaranteed to be present in every native ad.\n ((UILabel *)nativeAdView.headlineView).text = nativeAd.headline;\n nativeAdView.mediaView.mediaContent = nativeAd.mediaContent;\n\n // This app uses a fixed width for the GADMediaView and changes its height\n // to match the aspect ratio of the media content it displays.\n if (nativeAdView.mediaView != nil && nativeAd.mediaContent.aspectRatio > 0) {\n NSLayoutConstraint *aspectRatioConstraint =\n [NSLayoutConstraint constraintWithItem:nativeAdView.mediaView\n attribute:NSLayoutAttributeWidth\n relatedBy:NSLayoutRelationEqual\n toItem:nativeAdView.mediaView\n attribute:NSLayoutAttributeHeight\n multiplier:(nativeAd.mediaContent.aspectRatio)\n constant:0];\n [nativeAdView.mediaView addConstraint:aspectRatioConstraint];\n [nativeAdView layoutIfNeeded];\n }\n\n if (nativeAd.mediaContent.hasVideoContent) {\n // By acting as the delegate to the GADVideoController, this ViewController\n // receives messages about events in the video lifecycle.\n nativeAd.mediaContent.videoController.delegate = self;\n\n self.videoStatusLabel.text = @\"Ad contains a video asset.\";\n } else {\n self.videoStatusLabel.text = @\"Ad does not contain a video.\";\n }\n\n // These assets are not guaranteed to be present. Check that they are before\n // showing or hiding them.\n ((UILabel *)nativeAdView.bodyView).text = nativeAd.body;\n nativeAdView.bodyView.hidden = nativeAd.body ? NO : YES;\n\n [((UIButton *)nativeAdView.callToActionView) setTitle:nativeAd.callToAction\n forState:UIControlStateNormal];\n nativeAdView.callToActionView.hidden = nativeAd.callToAction ? NO : YES;\n\n ((UIImageView *)nativeAdView.iconView).image = nativeAd.icon.image;\n nativeAdView.iconView.hidden = nativeAd.icon ? NO : YES;\n\n ((UIImageView *)nativeAdView.starRatingView).image = [self imageForStars:nativeAd.starRating];\n nativeAdView.starRatingView.hidden = nativeAd.starRating ? NO : YES;\n\n ((UILabel *)nativeAdView.storeView).text = nativeAd.store;\n nativeAdView.storeView.hidden = nativeAd.store ? NO : YES;\n\n ((UILabel *)nativeAdView.priceView).text = nativeAd.price;\n nativeAdView.priceView.hidden = nativeAd.price ? NO : YES;\n\n ((UILabel *)nativeAdView.advertiserView).text = nativeAd.advertiser;\n nativeAdView.advertiserView.hidden = nativeAd.advertiser ? NO : YES;\n\n // In order for the SDK to process touch events properly, user interaction\n // should be disabled.\n nativeAdView.callToActionView.userInteractionEnabled = NO;\n\n // Associate the native ad view with the native ad object. This is\n // required to make the ad clickable.\n // Note: this should always be done after populating the ad views.\n nativeAdView.nativeAd = nativeAd;\n}ViewController.m\n```\n\nExample:\n```text\nnativeAdView.mediaView?.mediaContent = nativeAd.mediaContentViewController.swift\n```\n\nExample:\n```text\nnativeAdView.mediaView.mediaContent = nativeAd.mediaContent;ViewController.m\n```\n\nExample:\n```text\nnativeAdView.mediaView?.contentMode = .scaleAspectFitNativeAdSnippets.swift\n```\n\nExample:\n```text\nnativeAdView.mediaView.contentMode = UIViewContentModeScaleAspectFit;NativeAdSnippets.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.687Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":262,"estimatedTokens":2488}}284{"id":"doc-integrate_i_mobile_with_mediation_ios_google_for-9879a838","source":"documentation","title":"Integrate i-mobile with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/imobile","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-imobile.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationIMobi\n```\n\nExample:\n```text\npod install --repo-update\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.688Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":16,"estimatedTokens":53}}285{"id":"doc-set_advanced_native_features_ios_google_for_deve-a49161d3","source":"documentation","title":"Set advanced native features | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/native/options","text":"Example:\n```text\nlet nativeOptions = NativeAdMediaAdLoaderOptions()\nnativeOptions.mediaAspectRatio = .any\n\nadLoader = AdLoader(\n adUnitID: \"nativeAdUnitID\",\n rootViewController: self,\n adTypes: [.native],\n options: [nativeOptions])NativeAdOptionsSnippets.swift\n```\n\nExample:\n```text\nGADNativeAdMediaAdLoaderOptions *nativeOptions = [[GADNativeAdMediaAdLoaderOptions alloc] init];\nnativeOptions.mediaAspectRatio = GADMediaAspectRatioAny;\n\nself.adLoader = [[GADAdLoader alloc] initWithAdUnitID:\"nativeAdUnitID\"\n rootViewController:self\n adTypes:@[ GADAdLoaderAdTypeNative ]\n options:@[ nativeOptions ]];NativeAdOptionsSnippets.m\n```\n\nExample:\n```text\nlet nativeOptions = NativeAdImageAdLoaderOptions()\nnativeOptions.isImageLoadingDisabled = true\n\nadLoader = AdLoader(\n adUnitID: \"nativeAdUnitID\",\n rootViewController: self,\n adTypes: [.native],\n options: [nativeOptions])NativeAdOptionsSnippets.swift\n```\n\nExample:\n```text\nGADNativeAdImageAdLoaderOptions *nativeOptions = [[GADNativeAdImageAdLoaderOptions alloc] init];\nnativeOptions.disableImageLoading = YES;\n\nself.adLoader = [[GADAdLoader alloc] initWithAdUnitID:\"nativeAdUnitID\"\n rootViewController:self\n adTypes:@[ GADAdLoaderAdTypeNative ]\n options:@[ nativeOptions ]];NativeAdOptionsSnippets.m\n```\n\nExample:\n```text\nlet nativeOptions = NativeAdImageAdLoaderOptions()\nnativeOptions.shouldRequestMultipleImages = true\n\nadLoader = AdLoader(\n adUnitID: \"nativeAdUnitID\",\n rootViewController: self,\n adTypes: [.native],\n options: [nativeOptions])NativeAdOptionsSnippets.swift\n```\n\nExample:\n```text\nGADNativeAdImageAdLoaderOptions *nativeOptions = [[GADNativeAdImageAdLoaderOptions alloc] init];\nnativeOptions.shouldRequestMultipleImages = YES;\n\nself.adLoader = [[GADAdLoader alloc] initWithAdUnitID:\"nativeAdUnitID\"\n rootViewController:self\n adTypes:@[ GADAdLoaderAdTypeNative ]\n options:@[ nativeOptions ]];NativeAdOptionsSnippets.m\n```\n\nExample:\n```text\nlet nativeOptions = NativeAdViewAdOptions()\nnativeOptions.preferredAdChoicesPosition = .topRightCorner\n\nadLoader = AdLoader(\n adUnitID: \"nativeAdUnitID\",\n rootViewController: self,\n adTypes: [.native],\n options: [nativeOptions])NativeAdOptionsSnippets.swift\n```\n\nExample:\n```text\nGADNativeAdViewAdOptions *nativeOptions = [[GADNativeAdViewAdOptions alloc] init];\nnativeOptions.preferredAdChoicesPosition = GADAdChoicesPositionTopRightCorner;\n\nself.adLoader = [[GADAdLoader alloc] initWithAdUnitID:\"nativeAdUnitID\"\n rootViewController:self\n adTypes:@[ GADAdLoaderAdTypeNative ]\n options:@[ nativeOptions ]];NativeAdOptionsSnippets.m\n```\n\nExample:\n```text\nprivate func createAdChoicesView(nativeAdView: NativeAdView) {\n // Define a custom position for the AdChoices icon.\n let customRect = CGRect(x: 100, y: 100, width: 15, height: 15)\n let customAdChoicesView = AdChoicesView(frame: customRect)\n nativeAdView.addSubview(customAdChoicesView)\n nativeAdView.adChoicesView = customAdChoicesView\n}NativeAdOptionsSnippets.swift\n```\n\nExample:\n```text\n- (void)createAdChoicesViewWithNativeAdView:(GADNativeAdView *)nativeAdView {\n // Define a custom position for the AdChoices icon.\n CGRect customRect = CGRectMake(100, 100, 15, 15);\n GADAdChoicesView *customAdChoicesView = [[GADAdChoicesView alloc] initWithFrame:customRect];\n [nativeAdView addSubview:customAdChoicesView];\n nativeAdView.adChoicesView = customAdChoicesView;\n}NativeAdOptionsSnippets.m\n```\n\nExample:\n```text\nlet videoOptions = VideoOptions()\nvideoOptions.shouldStartMuted = false\n\nadLoader = AdLoader(\n adUnitID: \"nativeAdUnitID\",\n rootViewController: self,\n adTypes: [.native],\n options: [videoOptions])NativeAdOptionsSnippets.swift\n```\n\nExample:\n```text\nGADVideoOptions *videoOptions = [[GADVideoOptions alloc] init];\nvideoOptions.startMuted = NO;\n\nself.adLoader = [[GADAdLoader alloc] initWithAdUnitID:\"nativeAdUnitID\"\n rootViewController:self\n adTypes:@[ GADAdLoaderAdTypeNative ]\n options:@[ videoOptions ]];NativeAdOptionsSnippets.m\n```\n\nExample:\n```text\nlet videoOptions = VideoOptions()\nvideoOptions.areCustomControlsRequested = true\n\nadLoader = AdLoader(\n adUnitID: \"nativeAdUnitID\",\n rootViewController: self,\n adTypes: [.native],\n options: [videoOptions])NativeAdOptionsSnippets.swift\n```\n\nExample:\n```text\nGADVideoOptions *videoOptions = [[GADVideoOptions alloc] init];\nvideoOptions.customControlsRequested = YES;\n\nself.adLoader = [[GADAdLoader alloc] initWithAdUnitID:\"nativeAdUnitID\"\n rootViewController:self\n adTypes:@[ GADAdLoaderAdTypeNative ]\n options:@[ videoOptions ]];NativeAdOptionsSnippets.m\n```\n\nExample:\n```text\nprivate func checkCustomControlsEnabled(nativeAd: NativeAd) -> Bool {\n let videoController = nativeAd.mediaContent.videoController\n return videoController.areCustomControlsEnabled\n}NativeAdOptionsSnippets.swift\n```\n\nExample:\n```text\n- (BOOL)checkCustomControlsEnabledWithNativeAd:(GADNativeAd *)nativeAd {\n GADVideoController *videoController = nativeAd.mediaContent.videoController;\n return videoController.customControlsEnabled;\n}NativeAdOptionsSnippets.m\n```\n\nExample:\n```text\nlet swipeGestureOptions = NativeAdCustomClickGestureOptions(\n swipeGestureDirection: .right,\n tapsAllowed: true)\n\nadLoader = AdLoader(\n adUnitID: \"nativeAdUnitID\",\n rootViewController: self,\n adTypes: [.native],\n options: [swipeGestureOptions])NativeAdOptionsSnippets.swift\n```\n\nExample:\n```text\nGADNativeAdCustomClickGestureOptions *swipeGestureOptions =\n [[GADNativeAdCustomClickGestureOptions alloc]\n initWithSwipeGestureDirection:UISwipeGestureRecognizerDirectionRight\n tapsAllowed:YES];\n\nself.adLoader = [[GADAdLoader alloc] initWithAdUnitID:\"nativeAdUnitID\"\n rootViewController:self\n adTypes:@[ GADAdLoaderAdTypeNative ]\n options:@[ swipeGestureOptions ]];NativeAdOptionsSnippets.m\n```\n\nExample:\n```text\n// Called when a swipe gesture click is recorded, as configured in\n// NativeAdCustomClickGestureOptions.\nfunc nativeAdDidRecordSwipeGestureClick(_ nativeAd: NativeAd) {\n print(\"A swipe gesture click has occurred.\")\n}\n\n// Called when a swipe gesture click or a tap click is recorded.\nfunc nativeAdDidRecordClick(_ nativeAd: NativeAd) {\n print(\"A swipe gesture click or tap click has occurred.\")\n}NativeAdOptionsSnippets.swift\n```\n\nExample:\n```text\n// Called when a swipe gesture click is recorded, as configured in\n// GADNativeAdCustomClickGestureOptions.\n- (void)nativeAdDidRecordSwipeGestureClick:(GADNativeAd *)nativeAd {\n NSLog(@\"A swipe gesture click has occurred.\");\n}\n\n// Called when a swipe gesture click or a tap click is recorded.\n- (void)nativeAdDidRecordClick:(GADNativeAd *)nativeAd {\n NSLog(@\"A swipe gesture click or tap click has occurred.\");\n}NativeAdOptionsSnippets.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.689Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":20,"totalLines":231,"estimatedTokens":1887}}286{"id":"doc-rewarded_interstitial_ads_ios_google_for_develop-05bf5fba","source":"documentation","title":"Rewarded interstitial ads | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/rewarded-interstitial","text":"Example:\n```text\nfunc loadRewardedInterstitialAd() async {\n do {\n rewardedInterstitialAd = try await RewardedInterstitialAd.load(\n // Replace this ad unit ID with your own ad unit ID.\n with: \"adUnitID\", request: Request())\n rewardedInterstitialAd?.fullScreenContentDelegate = self\n } catch {\n print(\"Rewarded ad failed to load with error: \\(error.localizedDescription)\")\n }\n}RewardedInterstitialAdSnippets.swift\n```\n\nExample:\n```text\nimport GoogleMobileAds\n\nclass RewardedInterstitialViewModel: NSObject, ObservableObject,\n FullScreenContentDelegate\n{\n @Published var coins = 0\n private var rewardedInterstitialAd: RewardedInterstitialAd?\n\n func loadAd() async {\n do {\n rewardedInterstitialAd = try await RewardedInterstitialAd.load(\n with: \"ca-app-pub-3940256099942544/6978759866\", request: Request())\n rewardedInterstitialAd?.fullScreenContentDelegate = self\n } catch {\n print(\n \"Failed to load rewarded interstitial ad with error: \\(error.localizedDescription)\")\n }\n }RewardedInterstitialViewModel.swift\n```\n\nExample:\n```text\n- (void)loadRewardedInterstitialAd {\n [GADRewardedInterstitialAd loadWithAdUnitID:\"adUnitID\"\n request:[GADRequest request]\n completionHandler:^(GADRewardedInterstitialAd *ad, NSError *error) {\n if (error) {\n NSLog(@\"Failed to load rewarded interstitial ad with error: %@\",\n error.localizedDescription);\n return;\n }\n self.rewardedInterstitialAd = ad;\n self.rewardedInterstitialAd.fullScreenContentDelegate = self;\n }];\n}RewardedInterstitialAdSnippets.m\n```\n\nExample:\n```text\nprivate func validateServerSideVerification() async {\n do {\n rewardedInterstitialAd = try await RewardedInterstitialAd.load(\n // Replace this ad unit ID with your own ad unit ID.\n with: \"adUnitID\", request: Request())\n let options = ServerSideVerificationOptions()\n options.customRewardText = \"\"SAMPLE_CUSTOM_DATA_STRING\"\"\n rewardedInterstitialAd?.serverSideVerificationOptions = options\n } catch {\n print(\"Rewarded ad failed to load with error: \\(error.localizedDescription)\")\n }\n}RewardedInterstitialAdSnippets.swift\n```\n\nExample:\n```text\n- (void)validateServerSideVerification {\n // Replace this ad unit ID with your own ad unit ID.\n [GADRewardedInterstitialAd loadWithAdUnitID:\"adUnitID\"\n request:[GADRequest request]\n completionHandler:^(GADRewardedInterstitialAd *ad, NSError *error) {\n if (error) {\n NSLog(@\"Rewarded interstitial ad failed to load with error: %@\",\n error.localizedDescription);\n return;\n }\n self.rewardedInterstitialAd = ad;\n GADServerSideVerificationOptions *options =\n [[GADServerSideVerificationOptions alloc] init];\n options.customRewardString = @\"\"SAMPLE_CUSTOM_DATA_STRING\"\";\n ad.serverSideVerificationOptions = options;\n }];\n}RewardedInterstitialAdSnippets.m\n```\n\nExample:\n```text\nrewardedInterstitialAd?.fullScreenContentDelegate = selfRewardedInterstitialAdSnippets.swift\n```\n\nExample:\n```text\nrewardedInterstitialAd?.fullScreenContentDelegate = selfRewardedInterstitialViewModel.swift\n```\n\nExample:\n```text\nself.rewardedInterstitialAd.fullScreenContentDelegate = self;RewardedInterstitialAdSnippets.m\n```\n\nExample:\n```text\nfunc adDidRecordImpression(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called.\")\n}\n\nfunc adDidRecordClick(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called.\")\n}\n\nfunc adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called.\")\n}\n\nfunc adWillDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called.\")\n}\n\nfunc adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called.\")\n // Clear the rewarded interstitial ad.\n rewardedInterstitialAd = nil\n}\n\nfunc ad(\n _ ad: FullScreenPresentingAd,\n didFailToPresentFullScreenContentWithError error: Error\n) {\n print(\"\\(#function) called with error: \\(error.localizedDescription).\")\n}RewardedInterstitialAdSnippets.swift\n```\n\nExample:\n```text\nfunc adDidRecordImpression(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adDidRecordClick(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc ad(\n _ ad: FullScreenPresentingAd,\n didFailToPresentFullScreenContentWithError error: Error\n) {\n print(\"\\(#function) called\")\n}\n\nfunc adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adWillDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n}\n\nfunc adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"\\(#function) called\")\n // Clear the rewarded interstitial ad.\n rewardedInterstitialAd = nil\n}RewardedInterstitialViewModel.swift\n```\n\nExample:\n```text\n- (void)adDidRecordImpression:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)adDidRecordClick:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)adWillPresentFullScreenContent:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)adWillDismissFullScreenContent:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n}\n\n- (void)adDidDismissFullScreenContent:(id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"%s called\", __PRETTY_FUNCTION__);\n // Clear the rewarded interstitial ad.\n self.rewardedInterstitialAd = nil;\n}\n\n- (void)ad:(id)ad didFailToPresentFullScreenContentWithError:(NSError *)error {\n NSLog(@\"%s called with error: %@\", __PRETTY_FUNCTION__, error.localizedDescription);\n}RewardedInterstitialAdSnippets.m\n```\n\nExample:\n```text\nfunc showRewardedInterstitialAd() {\n guard let rewardedInterstitialAd = rewardedInterstitialAd else {\n return print(\"Ad wasn't ready.\")\n }\n\n // The UIViewController parameter is an optional.\n rewardedInterstitialAd.present(from: nil) {\n let reward = rewardedInterstitialAd.adReward\n print(\"Reward received with currency \\(reward.amount), amount \\(reward.amount.doubleValue)\")\n // TODO: Reward the user.\n }\n}RewardedInterstitialAdSnippets.swift\n```\n\nExample:\n```text\nvar rewardedInterstitialBody: some View {\n // ...\n }\n .onChange(\n of: showAd,\n perform: { newValue in\n if newValue {\n viewModel.showAd()\n }\n }\n )RewardedInterstitialContentView.swift\n```\n\nExample:\n```text\nfunc showAd() {\n guard let rewardedInterstitialAd = rewardedInterstitialAd else {\n return print(\"Ad wasn't ready.\")\n }\n\n rewardedInterstitialAd.present(from: nil) {\n let reward = rewardedInterstitialAd.adReward\n print(\"Reward amount: \\(reward.amount)\")\n self.addCoins(reward.amount.intValue)\n }\n}RewardedInterstitialViewModel.swift\n```\n\nExample:\n```text\n- (void)showRewardedInterstitialAd {\n [self.rewardedInterstitialAd presentFromRootViewController:self\n userDidEarnRewardHandler:^{\n GADAdReward *reward = self.rewardedInterstitialAd.adReward;\n\n NSString *rewardMessage = [NSString\n stringWithFormat:@\"Reward received with \"\n @\"currency %@ , amount %ld\",\n reward.type, [reward.amount longValue]];\n NSLog(@\"%@\", rewardMessage);\n // TODO: Reward the user.\n }];\n}RewardedInterstitialAdSnippets.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.690Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":262,"estimatedTokens":2055}}287{"id":"doc-integrate_nend_with_mediation_deprecated_ios_goo-c5e419a9","source":"documentation","title":"Integrate nend with mediation (Deprecated) | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/nend","text":"Example:\n```text\npod 'GoogleMobileAdsMediationNend'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.691Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":30}}288{"id":"doc-integrate_bidmachine_with_mediation_ios_google_f-ec52ee50","source":"documentation","title":"Integrate BidMachine with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/bidmachine","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-bidmachine.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationBidMachine'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nGADMediationAdapterBidMachine\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.691Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":21,"estimatedTokens":68}}289{"id":"doc-integrate_liftoff_monetize_with_mediation_ios_go-7053019e","source":"documentation","title":"Integrate Liftoff Monetize with Mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/liftoff-monetize","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-liftoffmonetize.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationVungle'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nVunglePrivacySettings.setCCPAStatus(true)LiftoffMonetizeMediationSwiftSnippets.swift\n```\n\nExample:\n```text\n[VunglePrivacySettings setCCPAStatus:YES];LiftoffMonetizeMediationObjectiveCSnippets.m\n```\n\nExample:\n```text\nlet adRequest = Request()\nlet extras = VungleAdNetworkExtras()\nextras.userId = \"myUserID\"\nextras.nativeAdOptionPosition = AdChoicesPosition.topRightCorner\n// ...\nadRequest.register(extras)LiftoffMonetizeMediationSwiftSnippets.swift\n```\n\nExample:\n```text\nGADRequest *adRequest = [GADRequest request];\nVungleAdNetworkExtras *extras = [[VungleAdNetworkExtras alloc] init];\nextras.userId = @\"myUserID\";\nextras.nativeAdOptionPosition = GADAdChoicesPositionTopRightCorner;\n// ...\n[adRequest registerAdNetworkExtras:extras];LiftoffMonetizeMediationObjectiveCSnippets.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.693Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":46,"estimatedTokens":256}}290{"id":"doc-integrate_maio_with_mediation_ios_google_for_dev-99fbcaaa","source":"documentation","title":"Integrate maio with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/maio","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-maio.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationMaio'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.694Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":16,"estimatedTokens":52}}291{"id":"doc-integrate_mintegral_with_mediation_ios_google_fo-de8c01f9","source":"documentation","title":"Integrate Mintegral with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/mintegral","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-mintegral.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationMintegral'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nimport MTGSDK\n// ...\n\nMTGSDK.sharedInstance().setConsentStatus(true)\n```\n\nExample:\n```text\n#import <MTGSDK/MTGSDK.h>\n// ...\n\n[[MTGSDK sharedInstance] setConsentStatus:YES];\n```\n\nExample:\n```text\nimport MTGSDK\n// ...\n\nMTGSDK.sharedInstance().setDoNotTrackStatus(false)\n```\n\nExample:\n```text\n#import <MTGSDK/MTGSDK.h>\n// ...\n\n[[MTGSDK sharedInstance] setDoNotTrackStatus:NO];\n```\n\nExample:\n```text\nGADMediationAdapterMintegral\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.696Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":53,"estimatedTokens":167}}292{"id":"doc-integrate_mytarget_with_mediation_ios_google_for-80f4cc6f","source":"documentation","title":"Integrate myTarget with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/mytarget","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-mytarget.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationMyTarget'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nimport MyTargetSDK\n// ...\n\nMTRGPrivacy.setUserConsent(true)\n```\n\nExample:\n```text\n#import <MyTargetSDK/MyTargetSDK.h>\n// ...\n\n[MTRGPrivacy setUserConsent:YES];\n```\n\nExample:\n```text\nimport MyTargetSDK\n// ...\n\nMTRGPrivacy.setUserAgeRestricted(true)\n```\n\nExample:\n```text\n#import <MyTargetSDK/MyTargetSDK.h>\n// ...\n\n[MTRGPrivacy setUserAgeRestricted:YES];\n```\n\nExample:\n```text\nimport MyTargetSDK\n// ...\n\nMTRGPrivacy.setCcpaUserConsent(true)\n```\n\nExample:\n```text\n#import <MyTargetSDK/MyTargetSDK.h>\n// ...\n\n[MTRGPrivacy setCcpaUserConsent:YES];\n```\n\nExample:\n```text\nlet request = GADRequest()\nlet extras = GADMAdapterMyTargetExtras()\nextras.isDebugMode = false\nadRequest.register(extras)\n```\n\nExample:\n```text\nGADRequest *request = [GADRequest request];\nGADMAdapterMyTargetExtras * extras = [[GADMAdapterMyTargetExtras alloc] init];\nextras.isDebugMode = NO;\n[request registerAdNetworkExtras:extras];\n```\n\nExample:\n```text\nGADMAdapterMyTarget\nGADMediationAdapterMyTargetNative\nGADMediationAdapterMyTargetRewarded\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.697Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":87,"estimatedTokens":313}}293{"id":"doc-integrate_inmobi_with_mediation_ios_google_for_d-bc083f8f","source":"documentation","title":"Integrate InMobi with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/inmobi","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-inmobi.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationInMobi'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nlet request = GADRequest()\nlet extras = GADInMobiExtras()\nextras.ageGroup = kIMSDKAgeGroupBetween35And54\nextras.areaCode = \"12345\"\nrequest.registerAdNetworkExtras(extras)\n```\n\nExample:\n```text\nGADRequest *request = [GADRequest request];\nGADInMobiExtras *extras = [[GADInMobiExtras alloc] init];\nextras.ageGroup = kIMSDKAgeGroupBetween35And54;\nextras.areaCode = @\"12345\";\n[request registerAdNetworkExtras:extras];\n```\n\nExample:\n```text\nGADMAdapterInMobi\nGADMediationAdapterInMobi\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.701Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":40,"estimatedTokens":179}}294{"id":"doc-integrate_dt_exchange_with_mediation_ios_google_-774574cb","source":"documentation","title":"Integrate DT Exchange with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/dt-exchange","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-dtexchange.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationFyber'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nIASDKCore.sharedInstance().ccpaString = usPrivacyStringDTExchangeMediationSwiftSnippets.swift\n```\n\nExample:\n```text\n[[IASDKCore sharedInstance] setCCPAString:kUSPrivacyString];DTExchangeMediationObjectiveCSnippets.m\n```\n\nExample:\n```text\nlet userData = IAUserData.build { builder in\n builder.age = 23\n builder.gender = IAUserGenderType.male\n builder.zipCode = \"1234\"\n}\n\nlet request = Request()\nlet extras = GADMAdapterFyberExtras()\nextras.userData = userData\nextras.muteAudio = true\nrequest.register(extras)DTExchangeMediationSwiftSnippets.swift\n```\n\nExample:\n```text\nIAUserData *userData = [IAUserData build:^(id<IAUserDataBuilder> _Nonnull builder) {\n builder.age = 23;\n builder.gender = IAUserGenderTypeMale;\n builder.zipCode = @\"1234\";\n}];\n\nGADRequest *request = [GADRequest request];\nGADMAdapterFyberExtras *extras = [[GADMAdapterFyberExtras alloc] init];\nextras.userData = userData;\nextras.muteAudio = YES;\n[request registerAdNetworkExtras:extras];DTExchangeMediationObjectiveCSnippets.m\n```\n\nExample:\n```text\nGADMediationAdapterFyber\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.702Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":61,"estimatedTokens":321}}295{"id":"doc-integrate_ironsource_ads_with_mediation_ios_goog-bfb7e4b6","source":"documentation","title":"Integrate ironSource Ads with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/ironsource","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-ironsource.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationIronSource'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\n// For Swift integration, you need to import the ironSource SDK in your Bridging Header.\n// For more details, see https://developers.is.com/ironsource-mobile/ios/ironsource-ios-sdk-integration-swift/\nIronSourceAds.setMetaDataWithKey(\"do_not_sell\", value: \"YES\")IronSourceMediationSwiftSnippets.swift\n```\n\nExample:\n```text\n[IronSourceAds setMetaDataWithKey:@\"do_not_sell\" value:@\"YES\"];IronSourceMediationObjectiveCSnippets.m\n```\n\nExample:\n```text\nGADMAdapterIronSource\nGADMAdapterIronSourceRewarded\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.704Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":34,"estimatedTokens":186}}296{"id":"doc-integrate_ly_ads_network_with_mediation_ios_goog-6ac354f4","source":"documentation","title":"Integrate LY Ads Network with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/line","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-line.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationLine'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nimport LineAdapter\n// ...\n\nGADMediationAdapterLine.testMode = true\n```\n\nExample:\n```text\n#import <LineAdapter/LineAdapter.h>\n// ...\n\nGADMediationAdapterLine.testMode = YES;\n```\n\nExample:\n```text\nimport LineAdapter\n// ...\n\nlet request = GADRequest()\nlet extras = GADMediationAdapterLineExtras()\nextras.adAudio = GADMediationAdapterLineAdAudio.unmuted\n// ...\nrequest.register(extras)\n```\n\nExample:\n```text\n#import <LineAdapter/LineAdapter.h>\n// ...\n\nGADRequest *request = [GADRequest request];\nGADMediationAdapterLineExtras *extras = [[GADMediationAdapterLineExtras alloc] init];\nextras.adAudio = GADMediationAdapterLineAdAudioUnmuted;\n// ...\n[request registerAdNetworkExtras:extras];\n```\n\nExample:\n```text\nGADMediationAdapterLine\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.705Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":61,"estimatedTokens":240}}297{"id":"doc-integrate_moloco_with_mediation_ios_google_for_d-45409fc5","source":"documentation","title":"Integrate Moloco with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/moloco","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-moloco.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationMoloco'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nimport MolocoSDK\n// ...\n\nMolocoPrivacySettings.hasUserConsent = true;\n```\n\nExample:\n```text\n#import <MolocoSDK/MolocoSDK-Swift.h>\n// ...\n\n[MolocoPrivacySettings setHasUserConsent:YES];\n```\n\nExample:\n```text\nimport MolocoSDK\n// ...\n\nMolocoPrivacySettings.isDoNotSell = true;\n```\n\nExample:\n```text\n#import <MolocoSDK/MolocoSDK-Swift.h>\n// ...\n\n[MolocoPrivacySettings setIsDoNotSell:YES];\n```\n\nExample:\n```text\nMolocoSDK.MolocoError\nGADMediationAdapterMoloco\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.706Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":54,"estimatedTokens":173}}298{"id":"doc-integrate_meta_audience_network_with_bidding_ios-fbb39d86","source":"documentation","title":"Integrate Meta Audience Network with bidding | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/meta","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-meta.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationFacebook'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\n$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\n$(SDKROOT)/usr/lib/swift\n```\n\nExample:\n```text\n/usr/lib/swift\n```\n\nExample:\n```text\n// Set the flag as true.\nFBAdSettings.setAdvertiserTrackingEnabled(true)MetaMediationSwiftSnippets.swift\n```\n\nExample:\n```text\n// Set the flag as true.\n[FBAdSettings setAdvertiserTrackingEnabled:YES];MetaMediationObjectiveCSnippets.m\n```\n\nExample:\n```text\nlet socialContext = nativeAd.extraAssets?[GADFBSocialContext] as? StringMetaMediationSwiftSnippets.swift\n```\n\nExample:\n```text\nNSString *socialContext = nativeAd.extraAssets[GADFBSocialContext];MetaMediationObjectiveCSnippets.m\n```\n\nExample:\n```text\nGADMAdapterFacebook\nGADMediationAdapterFacebook\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.708Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":55,"estimatedTokens":230}}299{"id":"doc-integrate_applovin_with_mediation_ios_google_for-11497bc6","source":"documentation","title":"Integrate AppLovin with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/applovin","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-applovin.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationAppLovin'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nALPrivacySettings.setHasUserConsent(true)AppLovinMediationSwiftSnippets.swift\n```\n\nExample:\n```text\n[ALPrivacySettings setHasUserConsent:YES];AppLovinMediationObjectiveCSnippets.m\n```\n\nExample:\n```text\nALPrivacySettings.setDoNotSell(true)AppLovinMediationSwiftSnippets.swift\n```\n\nExample:\n```text\n[ALPrivacySettings setDoNotSell:YES];AppLovinMediationObjectiveCSnippets.m\n```\n\nExample:\n```text\nGADMAdapterAppLovin\nGADMAdapterAppLovinRewardBasedVideoAd\nGADMediationAdapterAppLovin\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.710Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":43,"estimatedTokens":180}}300{"id":"doc-integrate_bigo_ads_sdk_with_mediation_ios_google-d0ae72ef","source":"documentation","title":"Integrate BIGO Ads SDK with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/bigo","text":"Example:\n```text\npod 'GoogleMobileAdsMediationBigo'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nBigoAdSdk.setUserConsentWithOption(BigoConsentOptionsCCPA, consent: true)\n```\n\nExample:\n```text\n[BigoAdSdk setUserConsentWithOption:BigoConsentOptionsCCPA consent:YES];\n```\n\nExample:\n```text\nGADMediationAdapterBigo\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.710Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":26,"estimatedTokens":89}}301{"id":"doc-integrate_chartboost_with_mediation_ios_google_f-b2df6b90","source":"documentation","title":"Integrate Chartboost with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/chartboost","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-chartboost.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationChartboost'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nlet dataUseConsent = CHBDataUseConsent.CCPA(CHBDataUseConsent.CCPA.Consent.optInSale)\nChartboost.addDataUseConsent(dataUseConsent)\n```\n\nExample:\n```text\nCHBCCPADataUseConsent *dataUseConsent = [CHBCCPADataUseConsent ccpaConsent:CHBCCPAConsentOptInSale];\n[Chartboost addDataUseConsent:dataUseConsent];\n```\n\nExample:\n```text\nGADMAdapterChartboost\nGADMediationAdapterChartboost\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.712Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":34,"estimatedTokens":155}}302{"id":"doc-integrate_vpon_with_mediation_ios_google_for_dev-dd710f74","source":"documentation","title":"Integrate Vpon with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/vpon","text":"Example:\n```text\nfunc adViewDidReceiveAd(_ bannerView: GADBannerView) {\n print(\"Banner adapter class name: \\(bannerView.adNetworkClassName)\")\n}\n```\n\nExample:\n```text\n- (void)adViewDidReceiveAd:(GADBannerView *)bannerView {\n NSLog(@\"Banner adapter class name: %@\", bannerView.adNetworkClassName);\n}\n```\n\nExample:\n```text\nfunc interstitialDidReceiveAd(_ ad: GADInterstitialAd) {\n print(\"Interstitial adapter class name: \\(ad.adNetworkClassName)\")\n}\n```\n\nExample:\n```text\n- (void)interstitialDidReceiveAd:(GADInterstitialAd *)interstitial {\n NSLog(@\"Interstitial adapter class name: %@\", interstitial.adNetworkClassName);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.712Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":29,"estimatedTokens":161}}303{"id":"doc-integrate_pubmatic_openwrap_beta_with_mediation_-0ef4356a","source":"documentation","title":"Integrate PubMatic OpenWrap (Beta) with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/pubmatic","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-pubmatic.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationPubMatic'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nGADMediationAdapterPubMatic\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.713Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":21,"estimatedTokens":67}}304{"id":"doc-launch_ad_inspector_ios_google_for_developers-ee1f5c99","source":"documentation","title":"Launch ad inspector | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/ad-inspector/launch-ad-inspector","text":"Example:\n```text\nMobileAds.shared.presentAdInspector(from: viewController) { error in\n // Error will be non-nil if there was an issue and the inspector was not displayed.\n}MobileAdsSnippets.swift\n```\n\nExample:\n```text\n[GADMobileAds.sharedInstance presentAdInspectorFromViewController:viewController\n completionHandler:^(NSError *error){\n // Error will be non-nil if there was an issue\n // and the inspector was not displayed.\n }];MobileAdsSnippets.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.714Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":17,"estimatedTokens":166}}305{"id":"doc-integrate_zucks_with_admob_mediation_ios_google_-8fe07973","source":"documentation","title":"Integrate Zucks with AdMob Mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/zucks","text":"Example:\n```text\nfunc adViewDidReceiveAd(_ bannerView: GADBannerView) {\n print(\"Banner adapter class name: \\(bannerView.adNetworkClassName)\")\n}\n```\n\nExample:\n```text\n- (void)adViewDidReceiveAd:(GADBannerView *)bannerView {\n NSLog(@\"Banner adapter class name: %@\", bannerView.adNetworkClassName);\n}\n```\n\nExample:\n```text\nfunc interstitialDidReceiveAd(_ ad: GADInterstitialAd) {\n print(\"Interstitial adapter class name: \\(ad.adNetworkClassName)\")\n}\n```\n\nExample:\n```text\n- (void)interstitialDidReceiveAd:(GADInterstitialAd *)interstitial {\n NSLog(@\"Interstitial adapter class name: %@\", interstitial.adNetworkClassName);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.714Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":29,"estimatedTokens":161}}306{"id":"doc-integrate_yahoo_with_mediation_deprecated_ios_go-0fe183a1","source":"documentation","title":"Integrate Yahoo with mediation (Deprecated) | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/yahoo","text":"Example:\n```text\npod 'GoogleMobileAdsMediationYahoo'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.715Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":30}}307{"id":"doc-integrate_tapjoy_with_admob_mediation_deprecated-8cc8c14d","source":"documentation","title":"Integrate Tapjoy with AdMob Mediation (Deprecated) | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/tapjoy","text":"Example:\n```text\npod 'GoogleMobileAdsMediationTapjoy'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.715Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":30}}308{"id":"doc-rewarded_ads_custom_events_ios_google_for_develo-953c587e","source":"documentation","title":"Rewarded ads custom events | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/custom-events/rewarded","text":"Example:\n```text\nimport GoogleMobileAds\n\nclass SampleCustomEvent: NSObject, MediationAdapter {\n\n fileprivate var rewardedAd: SampleCustomEventRewarded?\n ...\n\n func loadRewarded(\n for adConfiguration: MediationRewardedAdConfiguration,\n completionHandler: @escaping GADMediationRewardedLoadCompletionHandler\n ) {\n self.rewardedAd = SampleCustomEventRewarded()\n self.rewardedAd?.loadRewarded(\n for: adConfiguration, completionHandler: completionHandler)\n }\n}\n```\n\nExample:\n```text\n#import \"SampleCustomEvent.h\"\n\n@implementation SampleCustomEvent\n...\n\nSampleCustomEventRewarded *sampleRewarded;\n\n- (void)loadRewardedForAdConfiguration:\n (GADMediationRewardedAdConfiguration *)adConfiguration\n completionHandler:\n (GADMediationRewardedLoadCompletionHandler)\n completionHandler {\n sampleRewarded = [[SampleCustomEventRewarded alloc] init];\n [sampleRewarded loadRewardedForAdConfiguration:adConfiguration\n completionHandler:completionHandler];\n}\n```\n\nExample:\n```text\nclass SampleCustomEventRewarded: NSObject, MediationRewardedAd {\n /// The Sample Ad Network rewarded ad.\n var nativeAd: SampleRewarded?\n\n /// The ad event delegate to forward ad rendering events to Google Mobile Ads SDK.\n var delegate: MediationRewardedAdEventDelegate?\n\n /// Completion handler called after ad load.\n var completionHandler: GADMediationRewardedLoadCompletionHandler?\n\n func loadRewarded(\n for adConfiguration: MediationRewardedAdConfiguration,\n completionHandler: @escaping GADMediationRewardedLoadCompletionHandler\n ) {\n rewarded = SampleRewarded.init(\n adUnitID: adConfiguration.credentials.settings[\"parameter\"] as? String)\n rewarded?.delegate = self\n let adRequest = SampleAdRequest()\n adRequest.testMode = adConfiguration.isTestRequest\n self.completionHandler = completionHandler\n rewarded?.fetchAd(adRequest)\n }\n}\n```\n\nExample:\n```text\n#import \"SampleCustomEventRewarded.h\"\n\n@interface SampleCustomEventRe<warded () SampleRewardedAdDelegate,\n GADMediationRewardedAd> {\n /// The sample rewarded ad.\n SampleRewarded *_rewardedAd;\n\n /// The completion handler to call when the ad loading succeeds or fails.\n GADMediationRewardedLoadCompletionHandler _loadCompletionHandler;\n\n /// The ad event delegate to forward ad rendering events to Google Mobile A<ds SDK.\n id GADMediationRewardedAdEventDelegate> _adEventDelegate;\n}\n@end\n\n- (void)loadRewardedAdForAdConfiguration:(GADMediationRewardedAdConfiguration *)adConfiguration\n completionHandler:\n (GADMediationRewardedLoadCompletionHandler)completionHandler {\n __block atomic_flag completionHandlerCalled = ATOMIC_FLAG_INIT;\n __block GADMediationRewardedLoadCompletionHandler originalCompletionHandler =\n [completionHandler copy];\n\n _loadComplet<ionHandler = ^idGADMediationRewardedAdEventDelegate>(<\n _Nullable idGADMediationRewardedAd> ad, NSError *_Nullable error) {\n // Only allow completion handler to be called once.\n if (atomic_flag_test_and_set(&completionHandlerCalled)) {\n < return nil;\n }\n\n idGADMediationRewardedAdEventDelegate> delegate = nil;\n if (originalCompletionHandler) {\n // Call original handler and hold on to its return value.\n delegate = originalCompletionHandler(ad, error);\n }\n\n // Release reference to handler. Objects retained by the handler will also be released.\n originalCompletionHandler = nil;\n\n return delegate;\n };\n\n NSString *adUnit = adConfiguration.credentials.settings[@\"parameter\"];\n _rewardedAd = [[SampleRewardedAd alloc] initWithAdUnitID:adUnit];\n _rewardedAd.delegate = self;\n SampleAdRequest *adRequest = [[SampleAdRequest alloc] init];\n adRequest.testMode = adConfiguration.isTestRequest;\n [_rewardedAd fetchAd:adRequest];\n}\n```\n\nExample:\n```text\nfunc rewardedDidLoad(_ interstitial: SampleRewarded) {\n if let handler = completionHandler {\n delegate = handler(self, nil)\n }\n}\n\nfunc rewarded(\n rewarded: SampleRewarded, didFailToLoadAdWith errorCode: SampleErrorCode\n) {\n let error =\n SampleCustomEventUtils.SampleCustomEventErrorWithCodeAndDescription(\n code: SampleCustomEventErrorCode\n .SampleCustomEventErrorAdLoadFailureCallback,\n description:\n \"Sample SDK returned an ad load failure callback with error code: \\(errorCode)\"\n )\n if let handler = completionHandler {\n delegate = handler(nil, error)\n }\n}\n```\n\nExample:\n```text\n- (void)rewardedDidLoad:(SampleRewarded *)rewarded {\n _adEventDelegate = _loadCompletionHandler(self, nil);\n}\n\n- (void)rewarded:(SampleInterstitial *)rewarded\n didFailToLoadAdWithErrorCode:(SampleErrorCode)errorCode {\n NSError *error = SampleCustomEventErrorWithCodeAndDescription(\n SampleCustomEventErrorAdLoadFailureCallback,\n [NSString stringWithFormat:@\"Sample SDK returned an ad load failure \"\n @\"callback with error code: %@\",\n errorCode]);\n _adEventDelegate = _loadCompletionHandler(nil, error);\n}\n```\n\nExample:\n```text\nfunc present(from viewController: UIViewController) {\n if let rewarded = rewarded, rewarded.isRewardedLoaded {\n rewarded.show()\n }\n}\n```\n\nExample:\n```text\n- (void)presentFromViewController:(UIViewController *)viewController {\n if ([_rewardedAd isRewardedLoaded]) {\n [_rewardedAd show];\n } else {\n NSError *error = SampleCustomEventErrorWithCodeAndDescription(\n SampleCustomEventErrorAdNotLoaded,\n [NSString stringWithFormat:\n @\"The rewarded ad failed to present because the ad was not loaded.\"]);\n [_adEventDelegate didFailToPresentWithError:error]\n }\n}\n```\n\nExample:\n```text\nfunc rewardedAdDidPresent(_ rewarded: SampleRewardedAd) {\n delegate?.willPresentFullScreenVideo()\n delegate?.didStartVideo()\n}\n\nfunc rewardedAdUserDidEarnReward(_ rewarded: SampleRewardedAd) {\n AdReward aReward = AdReward(\"\", rewarded)\n delegate.didRewardUser()\n}\n```\n\nExample:\n```text\n- (void)rewardedAdDidPresent:(SampleRewardedAd *)rewardedAd {\n [_adEventDelegate willPresentFullScreenView];\n [_adEventDelegate didStartVideo];\n}\n\n- (void)rewardedAd:(nonnull SampleRewardedAd *)rewardedAd\n userDidEarnReward:(NSUInteger)reward {\n GADAdReward *aReward = [[GADAdReward alloc]\n initWithRewardType:@\"\"\n rewardAmount:[NSDecimalNumber numberWithUnsignedInt:reward]];\n [_adEventDelegate didRewardUserWithReward];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.716Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":214,"estimatedTokens":1641}}309{"id":"doc-integrate_pangle_with_mediation_ios_google_for_d-af4da95b","source":"documentation","title":"Integrate Pangle with mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/pangle","text":"Example:\n```text\nhttps://github.com/googleads/googleads-mobile-ios-mediation-pangle.git\n```\n\nExample:\n```text\npod 'GoogleMobileAdsMediationPangle'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```text\nGADMediationAdapterPangle.setPAConsent(PAGPAConsentType.consent.rawValue)PangleMediationSwiftSnippets.swift\n```\n\nExample:\n```text\n[GADMediationAdapterPangle setPAConsent:PAGPAConsentTypeConsent];PangleMediationObjectiveCSnippets.m\n```\n\nExample:\n```text\nGADMediationAdapterPangle\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.718Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":31,"estimatedTokens":129}}310{"id":"doc-enable_test_ads_ios_google_for_developers-c97c4cfc","source":"documentation","title":"Enable test ads | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/test-ads","text":"Example:\n```text\n<Google> To get test ads on this device, set:\nGADMobileAds.sharedInstance.requestConfiguration.testDeviceIdentifiers =\n@[ @\"2077ef9a63d2b398840261c8221a0c9b\" ];\n```\n\nExample:\n```text\nlet testDeviceIdentifiers = [\"2077ef9a63d2b398840261c8221a0c9b\"]\nMobileAds.shared.requestConfiguration.testDeviceIdentifiers = testDeviceIdentifiersRequestConfigurationSnippets.swift\n```\n\nExample:\n```text\nNSArray *testDeviceIdentifiers = @[ @\"2077ef9a63d2b398840261c8221a0c9b\" ];\nGADMobileAds.sharedInstance.requestConfiguration.testDeviceIdentifiers = testDeviceIdentifiers;RequestConfigurationSnippets.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.720Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":20,"estimatedTokens":156}}311{"id":"doc-support_multiple_windows_on_ipad_ios_google_for_-45dec5be","source":"documentation","title":"Support multiple windows on iPad | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/multiscene","text":"Example:\n```text\nfunc loadInterstitial() {\n let request = Request()\n request.scene = view.window?.windowScene\n\n InterstitialAd.load(with: \"[AD_UNIT_ID]\",\n request: request) { ad, error in }\n}\n```\n\nExample:\n```text\n- (void)loadInterstitial {\n GADRequest *request = [GADRequest request];\n request.scene = self.view.window.windowScene;\n\n [GADInterstitialAd loadWithAdUnitID:@\"[AD_UNIT_ID]\"\n request:request\n completionHandler:^(GADInterstitialAd *ad, NSError *error) {}];\n}\n```\n\nExample:\n```text\n<Google> Invalid Request. The GADRequest scene property should be set for\napplications that support multi-scene. Treating the unset property as an error\nwhile in test mode.\n```\n\nExample:\n```text\n<Google> Ad cannot be presented. The full screen ad content size exceeds the current window size.\n```\n\nExample:\n```text\noverride func viewDidAppear(_ animated: Bool) {\n super.viewDidAppear(animated)\n if !requestInitialized {\n loadInterstitial()\n requestInitialized = true\n }\n}\n```\n\nExample:\n```text\n- (void)viewDidAppear:(BOOL)animated {\n [super viewDidAppear:animated];\n if (!_requestInitialized) {\n [self loadInterstitial];\n _requestInitialized = YES;\n }\n}\n```\n\nExample:\n```text\noverride func viewWillTransition(to size: CGSize,\n with coordinator: UIViewControllerTransitionCoordinator) {\n super.viewWillTransition(to: size, with: coordinator)\n\n coordinator.animate(alongsideTransition: nil) { [self] context in\n do {\n try interstitial?.canPresent(from: self)\n } catch {\n loadInterstitial()\n }\n }\n}\n```\n\nExample:\n```text\n- (void)viewWillTransitionToSize:(CGSize)size\n withTransitionCoordinator:(id)coordinator {\n [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];\n\n [coordinator animateAlongsideTransition:nil\n completion:^(id _Nonnull context) {\n if (![self.interstitial canPresentFromRootViewController:self error:nil]) {\n [self loadInterstitial];\n }\n }];\n}\n```\n\nExample:\n```text\noverride func viewWillTransition(to size: CGSize,\n with coordinator: UIViewControllerTransitionCoordinator) {\n super.viewWillTransition(to: size, with: coordinator)\n\n coordinator.animate(alongsideTransition: nil) { [self] context in\n loadBanner()\n }\n}\n\nfunc loadBanner() {\n let bannerWidth = view.frame.size.width\n\n bannerView.adSize = currentOrientationAnchoredAdaptiveBanner(width: bannerWidth)\n\n let request = Request()\n request.scene = view.window?.windowScene\n bannerView.load(request)\n}\n```\n\nExample:\n```text\n- (void)viewWillTransitionToSize:(CGSize)size\n withTransitionCoordinator:(id)coordinator {\n [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];\n\n [coordinator animateAlongsideTransition:nil\n completion:^(id _Nonnull context) {\n [self loadBannerAd];\n }];\n}\n\n- (void)loadBannerAd {\n CGFloat bannerWidth = self.view.frame.size.width;\n\n self.bannerView.adSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(bannerWidth);\n\n GADRequest *request = [GADRequest request];\n request.scene = self.view.window.windowScene;\n [self.bannerView loadRequest:request];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.721Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":134,"estimatedTokens":785}}312{"id":"doc-use_agent_skills_ios_google_for_developers-a39f37ef","source":"documentation","title":"Use agent skills | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/agent-skills","text":"Example:\n```text\nnpx skills add google/skills/skills/ads\n```\n\nExample:\n```text\nnpx skills update --all\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.722Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":30}}313{"id":"doc-googlemobileads_framework_reference_ios_google_f-606a22de","source":"documentation","title":"GoogleMobileAds Framework Reference | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/api/reference/Classes","text":"Example:\n```swift\nclass AdChoicesView : UIView\n```\n\nExample:\n```objective_c\n@interface GADAdChoicesView : UIView\n```\n\nExample:\n```swift\nclass AdLoader : NSObject\n```\n\nExample:\n```objective_c\n@interface GADAdLoader : NSObject\n```\n\nExample:\n```swift\nclass GADAdLoaderOptions : NSObject\n```\n\nExample:\n```objective_c\n@interface GADAdLoaderOptions : NSObject\n```\n\nExample:\n```swift\nclass AdReward : NSObject\n```\n\nExample:\n```objective_c\n@interface GADAdReward : NSObject\n```\n\nExample:\n```swift\nclass AdValue : NSObject, NSCopying\n```\n\nExample:\n```objective_c\n@interface GADAdValue : NSObject <NSCopying>\n```\n\nExample:\n```swift\nclass AppOpenAd : NSObject, FullScreenPresentingAd\n```\n\nExample:\n```objective_c\n@interface GADAppOpenAd : NSObject <GADFullScreenPresentingAd>\n```\n\nExample:\n```swift\nclass AudioVideoManager : NSObject\n```\n\nExample:\n```objective_c\n@interface GADAudioVideoManager : NSObject\n```\n\nExample:\n```swift\nclass BannerView : UIView\n```\n\nExample:\n```objective_c\n@interface GADBannerView : UIView\n```\n\nExample:\n```swift\nclass CustomEventExtras : NSObject, AdNetworkExtras\n```\n\nExample:\n```objective_c\n@interface GADCustomEventExtras : NSObject <GADAdNetworkExtras>\n```\n\nExample:\n```swift\nclass CustomEventRequest : NSObject\n```\n\nExample:\n```objective_c\n@interface GADCustomEventRequest : NSObject\n```\n\nExample:\n```swift\nclass CustomNativeAd : NSObject\n```\n\nExample:\n```objective_c\n@interface GADCustomNativeAd : NSObject\n```\n\nExample:\n```swift\nclass DebugOptionsViewController : UIViewController\n```\n\nExample:\n```objective_c\n@interface GADDebugOptionsViewController : UIViewController\n```\n\nExample:\n```swift\nclass DisplayAdMeasurement : NSObject\n```\n\nExample:\n```objective_c\n@interface GADDisplayAdMeasurement : NSObject\n```\n\nExample:\n```swift\nclass Extras : NSObject, AdNetworkExtras\n```\n\nExample:\n```objective_c\n@interface GADExtras : NSObject <GADAdNetworkExtras>\n```\n\nExample:\n```swift\nclass AdapterStatus : NSObject, NSCopying\n```\n\nExample:\n```objective_c\n@interface GADAdapterStatus : NSObject <NSCopying>\n```\n\nExample:\n```swift\nclass InitializationStatus : NSObject, NSCopying\n```\n\nExample:\n```objective_c\n@interface GADInitializationStatus : NSObject <NSCopying>\n```\n\nExample:\n```swift\nclass InterstitialAd : NSObject, FullScreenPresentingAd\n```\n\nExample:\n```objective_c\n@interface GADInterstitialAd : NSObject <GADFullScreenPresentingAd>\n```\n\nExample:\n```swift\nclass MediaContent : NSObject\n```\n\nExample:\n```objective_c\n@interface GADMediaContent : NSObject\n```\n\nExample:\n```swift\nclass MediaView : UIView\n```\n\nExample:\n```objective_c\n@interface GADMediaView : UIView\n```\n\nExample:\n```swift\nclass MobileAds : NSObject\n```\n\nExample:\n```objective_c\n@interface GADMobileAds : NSObject\n```\n\nExample:\n```swift\nclass MultipleAdsAdLoaderOptions : GADAdLoaderOptions\n```\n\nExample:\n```objective_c\n@interface GADMultipleAdsAdLoaderOptions : GADAdLoaderOptions\n```\n\nExample:\n```swift\nclass MuteThisAdReason : NSObject\n```\n\nExample:\n```objective_c\n@interface GADMuteThisAdReason : NSObject\n```\n\nExample:\n```swift\nclass NativeAd : NSObject\n```\n\nExample:\n```objective_c\n@interface GADNativeAd : NSObject\n```\n\nExample:\n```swift\nclass NativeAdView : UIView\n```\n\nExample:\n```objective_c\n@interface GADNativeAdView : UIView\n```\n\nExample:\n```swift\nclass NativeAdCustomClickGestureOptions : GADAdLoaderOptions\n```\n\nExample:\n```objective_c\n@interface GADNativeAdCustomClickGestureOptions : GADAdLoaderOptions\n```\n\nExample:\n```swift\nclass NativeAdImage : NSObject\n```\n\nExample:\n```objective_c\n@interface GADNativeAdImage : NSObject\n```\n\nExample:\n```swift\nclass NativeAdImageAdLoaderOptions : GADAdLoaderOptions\n```\n\nExample:\n```objective_c\n@interface GADNativeAdImageAdLoaderOptions : GADAdLoaderOptions\n```\n\nExample:\n```swift\nclass NativeAdMediaAdLoaderOptions : GADAdLoaderOptions\n```\n\nExample:\n```objective_c\n@interface GADNativeAdMediaAdLoaderOptions : GADAdLoaderOptions\n```\n\nExample:\n```swift\nclass NativeAdViewAdOptions : GADAdLoaderOptions\n```\n\nExample:\n```objective_c\n@interface GADNativeAdViewAdOptions : GADAdLoaderOptions\n```\n\nExample:\n```swift\nclass NativeMuteThisAdLoaderOptions : GADAdLoaderOptions\n```\n\nExample:\n```objective_c\n@interface GADNativeMuteThisAdLoaderOptions : GADAdLoaderOptions\n```\n\nExample:\n```swift\nclass Request : NSObject, NSCopying\n```\n\nExample:\n```objective_c\n@interface GADRequest : NSObject <NSCopying>\n```\n\nExample:\n```swift\nclass RequestConfiguration : NSObject\n```\n\nExample:\n```objective_c\n@interface GADRequestConfiguration : NSObject\n```\n\nExample:\n```swift\nclass AdNetworkResponseInfo : NSObject\n```\n\nExample:\n```objective_c\n@interface GADAdNetworkResponseInfo : NSObject\n```\n\nExample:\n```swift\nclass ResponseInfo : NSObject\n```\n\nExample:\n```objective_c\n@interface GADResponseInfo : NSObject\n```\n\nExample:\n```swift\nclass RewardedAd : NSObject, AdMetadataProvider, FullScreenPresentingAd\n```\n\nExample:\n```objective_c\n@interface GADRewardedAd\n : NSObject <GADAdMetadataProvider, GADFullScreenPresentingAd>\n```\n\nExample:\n```swift\nclass RewardedInterstitialAd : NSObject, AdMetadataProvider, FullScreenPresentingAd\n```\n\nExample:\n```objective_c\n@interface GADRewardedInterstitialAd\n : NSObject <GADAdMetadataProvider, GADFullScreenPresentingAd>\n```\n\nExample:\n```swift\nclass ServerSideVerificationOptions : NSObject, NSCopying\n```\n\nExample:\n```objective_c\n@interface GADServerSideVerificationOptions : NSObject <NSCopying>\n```\n\nExample:\n```swift\nclass VideoController : NSObject\n```\n\nExample:\n```objective_c\n@interface GADVideoController : NSObject\n```\n\nExample:\n```swift\nclass VideoOptions : GADAdLoaderOptions\n```\n\nExample:\n```objective_c\n@interface GADVideoOptions : GADAdLoaderOptions\n```\n\nExample:\n```swift\nclass AdManagerBannerView : BannerView\n```\n\nExample:\n```objective_c\n@interface GAMBannerView : GADBannerView\n```\n\nExample:\n```swift\nclass AdManagerBannerViewOptions : GADAdLoaderOptions\n```\n\nExample:\n```objective_c\n@interface GAMBannerViewOptions : GADAdLoaderOptions\n```\n\nExample:\n```swift\nclass AdManagerInterstitialAd : InterstitialAd\n```\n\nExample:\n```objective_c\n@interface GAMInterstitialAd : GADInterstitialAd\n```\n\nExample:\n```swift\nclass AdManagerRequest : Request\n```\n\nExample:\n```objective_c\n@interface GAMRequest : GADRequest\n```\n\nExample:\n```swift\nclass MediatedUnifiedNativeAdNotificationSource : NSObject\n```\n\nExample:\n```objective_c\n@interface GADMediatedUnifiedNativeAdNotificationSource : NSObject\n```\n\nExample:\n```swift\nclass MediationAdConfiguration : NSObject\n```\n\nExample:\n```objective_c\n@interface GADMediationAdConfiguration : NSObject\n```\n\nExample:\n```swift\nclass MediationAppOpenAdConfiguration : MediationAdConfiguration\n```\n\nExample:\n```objective_c\n@interface GADMediationAppOpenAdConfiguration : GADMediationAdConfiguration\n```\n\nExample:\n```swift\nclass MediationBannerAdConfiguration : MediationAdConfiguration\n```\n\nExample:\n```objective_c\n@interface GADMediationBannerAdConfiguration : GADMediationAdConfiguration\n```\n\nExample:\n```swift\nclass MediationInterstitialAdConfiguration : MediationAdConfiguration\n```\n\nExample:\n```objective_c\n@interface GADMediationInterstitialAdConfiguration : GADMediationAdConfiguration\n```\n\nExample:\n```swift\nclass MediationNativeAdConfiguration : MediationAdConfiguration\n```\n\nExample:\n```objective_c\n@interface GADMediationNativeAdConfiguration : GADMediationAdConfiguration\n```\n\nExample:\n```swift\nclass MediationRewardedAdConfiguration : MediationAdConfiguration\n```\n\nExample:\n```objective_c\n@interface GADMediationRewardedAdConfiguration : GADMediationAdConfiguration\n```\n\nExample:\n```swift\nclass MediationCredentials : NSObject\n```\n\nExample:\n```objective_c\n@interface GADMediationCredentials : NSObject\n```\n\nExample:\n```swift\nclass MediationServerConfiguration : NSObject\n```\n\nExample:\n```objective_c\n@interface GADMediationServerConfiguration : NSObject\n```\n\nExample:\n```swift\nclass RTBMediationSignalsConfiguration : NSObject\n```\n\nExample:\n```objective_c\n@interface GADRTBMediationSignalsConfiguration : NSObject\n```\n\nExample:\n```swift\nclass RTBRequestParameters : NSObject\n```\n\nExample:\n```objective_c\n@interface GADRTBRequestParameters : NSObject\n```\n\nExample:\n```swift\nclass AppOpenSignalRequest : SignalRequest\n```\n\nExample:\n```objective_c\n@interface GADAppOpenSignalRequest : GADSignalRequest\n```\n\nExample:\n```swift\nclass BannerSignalRequest : SignalRequest\n```\n\nExample:\n```objective_c\n@interface GADBannerSignalRequest : GADSignalRequest\n```\n\nExample:\n```swift\nclass InterstitialSignalRequest : SignalRequest\n```\n\nExample:\n```objective_c\n@interface GADInterstitialSignalRequest : GADSignalRequest\n```\n\nExample:\n```swift\nclass NativeSignalRequest : SignalRequest\n```\n\nExample:\n```objective_c\n@interface GADNativeSignalRequest : GADSignalRequest\n```\n\nExample:\n```swift\nclass RewardedInterstitialSignalRequest : SignalRequest\n```\n\nExample:\n```objective_c\n@interface GADRewardedInterstitialSignalRequest : GADSignalRequest\n```\n\nExample:\n```swift\nclass RewardedSignalRequest : SignalRequest\n```\n\nExample:\n```objective_c\n@interface GADRewardedSignalRequest : GADSignalRequest\n```\n\nExample:\n```swift\nclass Signal : NSObject\n```\n\nExample:\n```objective_c\n@interface GADSignal : NSObject\n```\n\nExample:\n```swift\nclass SignalRequest : NSObject, NSCopying\n```\n\nExample:\n```objective_c\n@interface GADSignalRequest : NSObject <NSCopying>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.724Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":124,"totalLines":623,"estimatedTokens":2317}}314{"id":"doc-iphone_x_ad_rendering_ios_google_for_developers-87209242","source":"documentation","title":"iPhone X ad rendering | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/x-ad-rendering","text":"Example:\n```text\nclass ViewController: UIViewController {\n\n /// The banner view.\n @IBOutlet var bannerView: BannerView!\n\n override func viewDidLoad() {\n super.viewDidLoad()\n // Replace this ad unit ID with your own ad unit ID.\n bannerView.adUnitID = \"ca-app-pub-3940256099942544/2934735716\"\n bannerView.rootViewController = self\n bannerView.load(Request())\n }\n\n}\n```\n\nExample:\n```text\n@interface ViewController()\n\n@property(nonatomic, strong) IBOutlet GADBannerView *bannerView;\n\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n\n // Replace this ad unit ID with your own ad unit ID.\n self.bannerView.adUnitID = @\"ca-app-pub-3940256099942544/2934735716\";\n self.bannerView.rootViewController = self;\n GADRequest *request = [GADRequest request];\n [self.bannerView loadRequest:request];\n}\n```\n\nExample:\n```text\nclass ViewController: UIViewController {\n\n var bannerView: BannerView!\n\n override func viewDidLoad() {\n super.viewDidLoad()\n\n // Instantiate the banner view with your desired banner size.\n bannerView = BannerView(adSize: AdSizeBanner)\n addBannerViewToView(bannerView)\n bannerView.rootViewController = self\n // Set the ad unit ID to your own ad unit ID here.\n bannerView.adUnitID = \"ca-app-pub-3940256099942544/2934735716\"\n bannerView.load(Request())\n }\n\n func addBannerViewToView(_ bannerView: UIView) {\n bannerView.translatesAutoresizingMaskIntoConstraints = false\n view.addSubview(bannerView)\n if #available(iOS 11.0, *) {\n positionBannerAtBottomOfSafeArea(bannerView)\n }\n else {\n positionBannerAtBottomOfView(bannerView)\n }\n }\n\n @available (iOS 11, *)\n func positionBannerAtBottomOfSafeArea(_ bannerView: UIView) {\n // Position the banner. Stick it to the bottom of the Safe Area.\n // Centered horizontally.\n let guide: UILayoutGuide = view.safeAreaLayoutGuide\n\n NSLayoutConstraint.activate(\n [bannerView.centerXAnchor.constraint(equalTo: guide.centerXAnchor),\n bannerView.bottomAnchor.constraint(equalTo: guide.bottomAnchor)]\n )\n }\n\n func positionBannerAtBottomOfView(_ bannerView: UIView) {\n // Center the banner horizontally.\n view.addConstraint(NSLayoutConstraint(item: bannerView,\n attribute: .centerX,\n relatedBy: .equal,\n toItem: view,\n attribute: .centerX,\n multiplier: 1,\n constant: 0))\n // Lock the banner to the top of the bottom layout guide.\n view.addConstraint(NSLayoutConstraint(item: bannerView,\n attribute: .bottom,\n relatedBy: .equal,\n toItem: self.bottomLayoutGuide,\n attribute: .top,\n multiplier: 1,\n constant: 0))\n }\n\n}\n```\n\nExample:\n```text\n@interface ViewController()\n\n@property(nonatomic, strong) GADBannerView *bannerView;\n\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n\n // Instantiate the banner view with your desired banner size.\n self.bannerView = [[GADBannerView alloc] initWithAdSize:GADAdSizeBanner];\n [self addBannerViewToView:self.bannerView];\n\n // Replace this ad unit ID with your own ad unit ID.\n self.bannerView.adUnitID = @\"ca-app-pub-3940256099942544/2934735716\";\n self.bannerView.rootViewController = self;\n GADRequest *request = [GADRequest request];\n [self.bannerView loadRequest:request];\n}\n\n#pragma mark - view positioning\n\n-(void)addBannerViewToView:(UIView *_Nonnull)bannerView {\n self.bannerView.translatesAutoresizingMaskIntoConstraints = NO;\n [self.view addSubview:self.bannerView];\n if (@available(ios 11.0, *)) {\n [self positionBannerViewAtBottomOfSafeArea:bannerView];\n } else {\n [self positionBannerViewAtBottomOfView:bannerView];\n }\n}\n\n- (void)positionBannerViewAtBottomOfSafeArea:(UIView *_Nonnull)bannerView NS_AVAILABLE_IOS(11.0) {\n // Position the banner. Stick it to the bottom of the Safe Area.\n // Centered horizontally.\n UILayoutGuide *guide = self.view.safeAreaLayoutGuide;\n [NSLayoutConstraint activateConstraints:@[\n [bannerView.centerXAnchor constraintEqualToAnchor:guide.centerXAnchor],\n [bannerView.bottomAnchor constraintEqualToAnchor:guide.bottomAnchor]\n ]];\n}\n\n- (void)positionBannerViewAtBottomOfView:(UIView *_Nonnull)bannerView {\n [self.view addConstraint:[NSLayoutConstraint constraintWithItem:bannerView\n attribute:NSLayoutAttributeCenterX\n relatedBy:NSLayoutRelationEqual\n toItem:self.view\n attribute:NSLayoutAttributeCenterX\n multiplier:1\n constant:0]];\n [self.view addConstraint:[NSLayoutConstraint constraintWithItem:bannerView\n attribute:NSLayoutAttributeBottom\n relatedBy:NSLayoutRelationEqual\n toItem:self.bottomLayoutGuide\n attribute:NSLayoutAttributeTop\n multiplier:1\n constant:0]];\n}\n\n@end\n```\n\nExample:\n```text\nfunc addBannerViewToView(_ bannerView: BannerView) {\n bannerView.translatesAutoresizingMaskIntoConstraints = false\n view.addSubview(bannerView)\n if #available(iOS 11.0, *) {\n // In iOS 11, we need to constrain the view to the safe area.\n positionBannerViewFullWidthAtBottomOfSafeArea(bannerView)\n }\n else {\n // In lower iOS versions, safe area is not available so we use\n // bottom layout guide and view edges.\n positionBannerViewFullWidthAtBottomOfView(bannerView)\n }\n}\n\n// MARK: - view positioning\n@available (iOS 11, *)\nfunc positionBannerViewFullWidthAtBottomOfSafeArea(_ bannerView: UIView) {\n // Position the banner. Stick it to the bottom of the Safe Area.\n // Make it constrained to the edges of the safe area.\n let guide = view.safeAreaLayoutGuide\n NSLayoutConstraint.activate([\n guide.leftAnchor.constraint(equalTo: bannerView.leftAnchor),\n guide.rightAnchor.constraint(equalTo: bannerView.rightAnchor),\n guide.bottomAnchor.constraint(equalTo: bannerView.bottomAnchor)\n ])\n}\n\nfunc positionBannerViewFullWidthAtBottomOfView(_ bannerView: UIView) {\n view.addConstraint(NSLayoutConstraint(item: bannerView,\n attribute: .leading,\n relatedBy: .equal,\n toItem: view,\n attribute: .leading,\n multiplier: 1,\n constant: 0))\n view.addConstraint(NSLayoutConstraint(item: bannerView,\n attribute: .trailing,\n relatedBy: .equal,\n toItem: view,\n attribute: .trailing,\n multiplier: 1,\n constant: 0))\n view.addConstraint(NSLayoutConstraint(item: bannerView,\n attribute: .bottom,\n relatedBy: .equal,\n toItem: bottomLayoutGuide,\n attribute: .top,\n multiplier: 1,\n constant: 0))\n}\n```\n\nExample:\n```text\n- (void)addBannerViewToView:(UIView *)bannerView {\n bannerView.translatesAutoresizingMaskIntoConstraints = NO;\n [self.view addSubview:bannerView];\n if (@available(ios 11.0, *)) {\n // In iOS 11, we need to constrain the view to the safe area.\n [self positionBannerViewFullWidthAtBottomOfSafeArea:bannerView];\n } else {\n // In lower iOS versions, safe area is not available so we use\n // bottom layout guide and view edges.\n [self positionBannerViewFullWidthAtBottomOfView:bannerView];\n }\n}\n\n#pragma mark - view positioning\n\n- (void)positionBannerViewFullWidthAtBottomOfSafeArea:(UIView *_Nonnull)bannerView NS_AVAILABLE_IOS(11.0) {\n // Position the banner. Stick it to the bottom of the Safe Area.\n // Make it constrained to the edges of the safe area.\n UILayoutGuide *guide = self.view.safeAreaLayoutGuide;\n\n [NSLayoutConstraint activateConstraints:@[\n [guide.leftAnchor constraintEqualToAnchor:bannerView.leftAnchor],\n [guide.rightAnchor constraintEqualToAnchor:bannerView.rightAnchor],\n [guide.bottomAnchor constraintEqualToAnchor:bannerView.bottomAnchor]\n ]];\n}\n\n- (void)positionBannerViewFullWidthAtBottomOfView:(UIView *_Nonnull)bannerView {\n [self.view addConstraint:[NSLayoutConstraint constraintWithItem:bannerView\n attribute:NSLayoutAttributeLeading\n relatedBy:NSLayoutRelationEqual\n toItem:self.view\n attribute:NSLayoutAttributeLeading\n multiplier:1\n constant:0]];\n [self.view addConstraint:[NSLayoutConstraint constraintWithItem:bannerView\n attribute:NSLayoutAttributeTrailing\n relatedBy:NSLayoutRelationEqual\n toItem:self.view\n attribute:NSLayoutAttributeTrailing\n multiplier:1\n constant:0]];\n [self.view addConstraint:[NSLayoutConstraint constraintWithItem:bannerView\n attribute:NSLayoutAttributeBottom\n relatedBy:NSLayoutRelationEqual\n toItem:self.bottomLayoutGuide\n attribute:NSLayoutAttributeTop\n multiplier:1\n constant:0]];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.725Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":277,"estimatedTokens":2756}}315{"id":"doc-integrate_adcolony_with_mediation_deprecated_ios-6587852f","source":"documentation","title":"Integrate AdColony with mediation (Deprecated) | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation/adcolony","text":"Example:\n```text\npod 'GoogleMobileAdsMediationAdColony'\n```\n\nExample:\n```text\npod install --repo-update\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.726Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":31}}316{"id":"doc-use_inline_adaptive_for_scrolling_banners_ios_go-3a191a9e","source":"documentation","title":"Use inline adaptive for scrolling banners | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/banner/inline-adaptive","text":"Example:\n```text\n// Make sure the ad fits inside the readable area.\nlet adWidth = view.bounds.inset(by: view.safeAreaInsets).width\nbannerView.adSize = currentOrientationInlineAdaptiveBanner(width: adWidth)BannerSnippets.swift\n```\n\nExample:\n```text\n// Make sure the ad fits inside the readable area.\nCGFloat adWidth = CGRectGetWidth(UIEdgeInsetsInsetRect(view.bounds, view.safeAreaInsets));\nbannerView.adSize = GADCurrentOrientationInlineAdaptiveBannerAdSizeWithWidth(adWidth);BannerSnippets.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.726Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":15,"estimatedTokens":128}}317{"id":"doc-set_up_banner_ads_ios_google_for_developers-f469fd78","source":"documentation","title":"Set up banner ads | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/banner","text":"Example:\n```text\n// Initialize the banner view.\nbannerView = BannerView()\nbannerView.delegate = self\n\nbannerView.translatesAutoresizingMaskIntoConstraints = false\nview.addSubview(bannerView)\n\n// This example doesn't give width or height constraints, as the ad size gives the banner an\n// intrinsic content size to size the view.\nNSLayoutConstraint.activate([\n // Align the banner's bottom edge with the safe area's bottom edge\n bannerView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),\n // Center the banner horizontally in the view\n bannerView.centerXAnchor.constraint(equalTo: view.centerXAnchor),\n])BannerSnippets.swift\n```\n\nExample:\n```text\nprivate struct BannerViewContainer: UIViewRepresentable {\n typealias UIViewType = BannerView\n let adSize: AdSize\n\n init(_ adSize: AdSize) {\n self.adSize = adSize\n }\n\n func makeUIView(context: Context) -> BannerView {\n let banner = BannerView(adSize: adSize)\n banner.adUnitID = \"ca-app-pub-3940256099942544/2435281174\"\n banner.load(Request())\n banner.delegate = context.coordinator\n return banner\n }\n\n func updateUIView(_ uiView: BannerView, context: Context) {}\n\n func makeCoordinator() -> BannerCoordinator {\n return BannerCoordinator(self)\n }BannerContentView.swift\n```\n\nExample:\n```text\nvar body: some View {\n Spacer()\n // Request an anchored adaptive banner with a width of 375.\n let adSize = largeAnchoredAdaptiveBanner(width: 375)\n BannerViewContainer(adSize)\n .frame(width: adSize.size.width, height: adSize.size.height)\n}BannerContentView.swift\n```\n\nExample:\n```text\n// Initialize the banner view.\nGADBannerView *bannerView = [[GADBannerView alloc] init];\nbannerView.delegate = self;\nUIView *view = self.view;\n\nbannerView.translatesAutoresizingMaskIntoConstraints = NO;\n[view addSubview:bannerView];\n\n// This example doesn't give width or height constraints, as the ad size gives the banner an\n// intrinsic content size to size the view.\n[NSLayoutConstraint activateConstraints:@[\n // Align the banner's bottom edge with the safe area's bottom edge\n [bannerView.bottomAnchor\n constraintEqualToAnchor:view.safeAreaLayoutGuide.bottomAnchor],\n // Center the banner horizontally in the view\n [bannerView.centerXAnchor constraintEqualToAnchor:view.centerXAnchor],\n]];\n\nself.bannerView = bannerView;BannerSnippets.m\n```\n\nExample:\n```text\n// Request a large anchored adaptive banner with a width of 375.\nbannerView.adSize = largeAnchoredAdaptiveBanner(width: 375)BannerSnippets.swift\n```\n\nExample:\n```text\n// Request a large anchored adaptive banner with a width of 375.\nself.bannerView.adSize = GADLargeAnchoredAdaptiveBannerAdSizeWithWidth(375);BannerSnippets.m\n```\n\nExample:\n```text\nfunc loadBannerAd(bannerView: BannerView) {\n // Request a large anchored adaptive banner with a width of 375.\n bannerView.adSize = largeAnchoredAdaptiveBanner(width: 375)\n bannerView.load(Request())\n}BannerSnippets.swift\n```\n\nExample:\n```text\nbanner.adUnitID = \"ca-app-pub-3940256099942544/2435281174\"\nbanner.load(Request())BannerContentView.swift\n```\n\nExample:\n```text\n// Request a large anchored adaptive banner with a width of 375.\nself.bannerView.adSize = GADLargeAnchoredAdaptiveBannerAdSizeWithWidth(375);\n\n[self.bannerView loadRequest:[GADRequest request]];BannerSnippets.m\n```\n\nExample:\n```text\noverride func viewWillTransition(\n to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator\n) {\n coordinator.animate(alongsideTransition: { _ in\n // Load a new ad for the new orientation.\n })\n}BannerSnippets.swift\n```\n\nExample:\n```text\n- (void)viewWillTransitionToSize:(CGSize)size\n withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {\n [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {\n // Load a new ad for the new orientation.\n } completion:nil];\n}BannerSnippets.m\n```\n\nExample:\n```text\nbannerView.delegate = selfBannerSnippets.swift\n```\n\nExample:\n```text\nbanner.delegate = context.coordinatorBannerContentView.swift\n```\n\nExample:\n```text\nbannerView.delegate = self;BannerSnippets.m\n```\n\nExample:\n```text\nfunc bannerViewDidReceiveAd(_ bannerView: BannerView) {\n print(\"Banner ad loaded.\")\n}\n\nfunc bannerView(_ bannerView: BannerView, didFailToReceiveAdWithError error: Error) {\n print(\"Banner ad failed to load: \\(error.localizedDescription)\")\n}\n\nfunc bannerViewDidRecordImpression(_ bannerView: BannerView) {\n print(\"Banner ad recorded an impression.\")\n}\n\nfunc bannerViewDidRecordClick(_ bannerView: BannerView) {\n print(\"Banner ad recorded a click.\")\n}\n\nfunc bannerViewWillPresentScreen(_ bannerView: BannerView) {\n print(\"Banner ad will present screen.\")\n}\n\nfunc bannerViewWillDismissScreen(_ bannerView: BannerView) {\n print(\"Banner ad will dismiss screen.\")\n}\n\nfunc bannerViewDidDismissScreen(_ bannerView: BannerView) {\n print(\"Banner ad did dismiss screen.\")\n}BannerSnippets.swift\n```\n\nExample:\n```text\n- (void)bannerViewDidReceiveAd:(GADBannerView *)bannerView {\n NSLog(@\"bannerViewDidReceiveAd\");\n}\n\n- (void)bannerView:(GADBannerView *)bannerView didFailToReceiveAdWithError:(NSError *)error {\n NSLog(@\"bannerView:didFailToReceiveAdWithError: %@\", error.localizedDescription);\n}\n\n- (void)bannerViewDidRecordImpression:(GADBannerView *)bannerView {\n NSLog(@\"bannerViewDidRecordImpression\");\n}\n\n- (void)bannerViewWillPresentScreen:(GADBannerView *)bannerView {\n NSLog(@\"bannerViewWillPresentScreen\");\n}\n\n- (void)bannerViewWillDismissScreen:(GADBannerView *)bannerView {\n NSLog(@\"bannerViewWillDismissScreen\");\n}\n\n- (void)bannerViewDidDismissScreen:(GADBannerView *)bannerView {\n NSLog(@\"bannerViewDidDismissScreen\");\n}\nBannerSnippets.m\n```\n\nExample:\n```text\nfunc bannerViewDidReceiveAd(_ bannerView: BannerView) {\n // Add banner to view and add constraints.\n addBannerViewToView(bannerView)\n}\n```\n\nExample:\n```text\n- (void)bannerViewDidReceiveAd:(GADBannerView *)bannerView {\n // Add bannerView to view and add constraints as above.\n [self addBannerViewToView:self.bannerView];\n}\n```\n\nExample:\n```text\nfunc bannerViewDidReceiveAd(_ bannerView: BannerView) {\n bannerView.alpha = 0\n UIView.animate(withDuration: 1, animations: {\n bannerView.alpha = 1\n })\n}\n```\n\nExample:\n```text\n- (void)bannerViewDidReceiveAd:(GADBannerView *)bannerView {\n bannerView.alpha = 0;\n [UIView animateWithDuration:1.0 animations:^{\n bannerView.alpha = 1;\n }];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.727Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":20,"totalLines":245,"estimatedTokens":1606}}318{"id":"doc-app_open_ads_ios_google_for_developers-f0a0fcc7","source":"documentation","title":"App open ads | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/app-open","text":"Example:\n```text\nca-app-pub-3940256099942544/5575463023\n```\n\nExample:\n```text\nclass AppOpenAdManager: NSObject {\n /// The app open ad.\n var appOpenAd: AppOpenAd?\n /// Maintains a reference to the delegate.\n weak var appOpenAdManagerDelegate: AppOpenAdManagerDelegate?\n /// Keeps track of if an app open ad is loading.\n var isLoadingAd = false\n /// Keeps track of if an app open ad is showing.\n var isShowingAd = false\n /// Keeps track of the time when an app open ad was loaded to discard expired ad.\n var loadTime: Date?\n /// For more interval details, see https://support.google.com/admob/answer/9341964\n let timeoutInterval: TimeInterval = 4 * 3_600\n\n static let shared = AppOpenAdManager()AppOpenAdManager.swift\n```\n\nExample:\n```text\n@interface AppOpenAdManager ()\n\n/// The app open ad.\n@property(nonatomic, strong, nullable) GADAppOpenAd *appOpenAd;\n/// Keeps track of if an app open ad is loading.\n@property(nonatomic, assign) BOOL isLoadingAd;\n/// Keeps track of if an app open ad is showing.\n@property(nonatomic, assign) BOOL isShowingAd;\n/// Keeps track of the time when an app open ad was loaded to discard expired ad.\n@property(nonatomic, strong, nullable) NSDate *loadTime;\n\n@end\n\n/// For more interval details, see https://support.google.com/admob/answer/9341964\nstatic const NSInteger kTimeoutInterval = 4;\n\n@implementation AppOpenAdManager\n\n+ (nonnull AppOpenAdManager *)sharedInstance {\n static AppOpenAdManager *instance = nil;\n static dispatch_once_t onceToken;\n dispatch_once(&onceToken, ^{\n instance = [[AppOpenAdManager alloc] init];\n });\n return instance;\n}AppOpenAdManager.m\n```\n\nExample:\n```text\nprotocol AppOpenAdManagerDelegate: AnyObject {\n /// Method to be invoked when an app open ad life cycle is complete (i.e. dismissed or fails to\n /// show).\n func appOpenAdManagerAdDidComplete(_ appOpenAdManager: AppOpenAdManager)\n}AppOpenAdManager.swift\n```\n\nExample:\n```text\n@protocol AppOpenAdManagerDelegate <NSObject>\n/// Method to be invoked when an app open ad life cycle is complete (i.e. dismissed or fails to\n/// show).\n- (void)adDidComplete;\n@endAppOpenAdManager.h\n```\n\nExample:\n```text\nfunc loadAd() async {\n // Do not load ad if there is an unused ad or one is already loading.\n if isLoadingAd || isAdAvailable() {\n return\n }\n isLoadingAd = true\n\n do {\n appOpenAd = try await AppOpenAd.load(\n with: \"ca-app-pub-3940256099942544/5575463023\", request: Request())\n appOpenAd?.fullScreenContentDelegate = self\n loadTime = Date()\n } catch {\n print(\"App open ad failed to load with error: \\(error.localizedDescription)\")\n appOpenAd = nil\n loadTime = nil\n }\n isLoadingAd = false\n}AppOpenAdManager.swift\n```\n\nExample:\n```text\n- (void)loadAd {\n // Do not load ad if there is an unused ad or one is already loading.\n if ([self isAdAvailable] || self.isLoadingAd) {\n return;\n }\n self.isLoadingAd = YES;\n\n [GADAppOpenAd loadWithAdUnitID:@\"ca-app-pub-3940256099942544/5575463023\"\n request:[GADRequest request]\n completionHandler:^(GADAppOpenAd * _Nullable appOpenAd, NSError * _Nullable error) {\n self.isLoadingAd = NO;\n if (error) {\n NSLog(@\"App open ad failed to load with error: %@\", error);\n self.appOpenAd = nil;\n self.loadTime = nil;\n return;\n }\n self.appOpenAd = appOpenAd;\n self.appOpenAd.fullScreenContentDelegate = self;\n self.loadTime = [NSDate date];\n }];\n}AppOpenAdManager.m\n```\n\nExample:\n```text\nfunc showAdIfAvailable() {\n // If the app open ad is already showing, do not show the ad again.\n if isShowingAd {\n return print(\"App open ad is already showing.\")\n }\n\n // If the app open ad is not available yet but is supposed to show, load\n // a new ad.\n if !isAdAvailable() {\n print(\"App open ad is not ready yet.\")\n // The app open ad is considered to be complete in this example.\n appOpenAdManagerDelegate?.appOpenAdManagerAdDidComplete(self)\n // Load a new ad.\n return\n }\n\n if let appOpenAd {\n appOpenAd.present(from: nil)\n isShowingAd = true\n }\n}AppOpenAdManager.swift\n```\n\nExample:\n```text\n- (void)showAdIfAvailable {\n // If the app open ad is already showing, do not show the ad again.\n if (self.isShowingAd) {\n NSLog(@\"App open ad is already showing.\");\n return;\n }\n\n // If the app open ad is not available yet but is supposed to show, load\n // a new ad.\n if (![self isAdAvailable]) {\n NSLog(@\"App open ad is not ready yet.\");\n // The app open ad is considered to be complete in this example.\n [self adDidComplete];\n // Load a new ad.\n return;\n }\n\n [self.appOpenAd presentFromRootViewController:nil];\n self.isShowingAd = YES;\n}AppOpenAdManager.m\n```\n\nExample:\n```text\nfunc applicationDidBecomeActive(_ application: UIApplication) {\n // Show the app open ad when the app is foregrounded.\n AppOpenAdManager.shared.showAdIfAvailable()\n}AppDelegate.swift\n```\n\nExample:\n```text\n- (void) applicationDidBecomeActive:(UIApplication *)application {\n // Show the app open ad when the app is foregrounded.\n [AppOpenAdManager.sharedInstance showAdIfAvailable];\n}AppDelegate.m\n```\n\nExample:\n```text\nappOpenAd?.fullScreenContentDelegate = selfAppOpenAdManager.swift\n```\n\nExample:\n```text\nself.appOpenAd.fullScreenContentDelegate = self;AppOpenAdManager.m\n```\n\nExample:\n```text\nfunc adDidRecordImpression(_ ad: FullScreenPresentingAd) {\n print(\"App open ad recorded an impression.\")\n}\n\nfunc adDidRecordClick(_ ad: FullScreenPresentingAd) {\n print(\"App open ad recorded a click.\")\n}\n\nfunc adWillDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"App open ad will be dismissed.\")\n}\n\nfunc adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"App open ad will be presented.\")\n}\n\nfunc adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {\n print(\"App open ad was dismissed.\")\n appOpenAd = nil\n isShowingAd = false\n appOpenAdManagerDelegate?.appOpenAdManagerAdDidComplete(self)\n Task {\n await loadAd()\n }\n}\n\nfunc ad(\n _ ad: FullScreenPresentingAd,\n didFailToPresentFullScreenContentWithError error: Error\n) {\n print(\"App open ad failed to present with error: \\(error.localizedDescription)\")\n appOpenAd = nil\n isShowingAd = false\n appOpenAdManagerDelegate?.appOpenAdManagerAdDidComplete(self)\n Task {\n await loadAd()\n }\n}AppOpenAdManager.swift\n```\n\nExample:\n```text\n- (void)adDidRecordImpression:(nonnull id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"App open ad recorded an impression.\");\n}\n\n- (void)adDidRecordClick:(nonnull id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"App open ad recorded a click.\");\n}\n\n- (void)adWillPresentFullScreenContent:(nonnull id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"App open ad will be presented.\");\n}\n\n- (void)adWillDismissFullScreenContent:(nonnull id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"App open ad will be dismissed.\");\n}\n\n- (void)adDidDismissFullScreenContent:(nonnull id<GADFullScreenPresentingAd>)ad {\n NSLog(@\"App open ad was dismissed.\");\n self.appOpenAd = nil;\n self.isShowingAd = NO;\n [self adDidComplete];\n [self loadAd];\n}\n\n- (void)ad:(nonnull id<GADFullScreenPresentingAd>)ad\n didFailToPresentFullScreenContentWithError:(nonnull NSError *)error {\n NSLog(@\"App open ad failed to present with error: %@\", error.localizedDescription);\n self.appOpenAd = nil;\n self.isShowingAd = NO;\n [self adDidComplete];\n [self loadAd];\n}AppOpenAdManager.m\n```\n\nExample:\n```text\nprivate func wasLoadTimeLessThanNHoursAgo(timeoutInterval: TimeInterval) -> Bool {\n // Check if ad was loaded more than n hours ago.\n if let loadTime = loadTime {\n return Date().timeIntervalSince(loadTime) < timeoutInterval\n }\n return false\n}\n\nprivate func isAdAvailable() -> Bool {\n // Check if ad exists and can be shown.\n return appOpenAd != nil && wasLoadTimeLessThanNHoursAgo(timeoutInterval: timeoutInterval)\n}AppOpenAdManager.swift\n```\n\nExample:\n```text\n- (BOOL)wasLoadTimeLessThanNHoursAgo:(int)n {\n // Check if ad was loaded more than n hours ago.\n NSDate *now = [NSDate date];\n NSTimeInterval timeIntervalBetweenNowAndLoadTime = [now timeIntervalSinceDate:self.loadTime];\n double secondsPerHour = 3600.0;\n double intervalInHours = timeIntervalBetweenNowAndLoadTime / secondsPerHour;\n return intervalInHours < n;\n}\n\n- (BOOL)isAdAvailable {\n // Check if ad exists and can be shown.\n return _appOpenAd && [self wasLoadTimeLessThanNHoursAgo:kTimeoutInterval];\n}AppOpenAdManager.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.729Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":17,"totalLines":308,"estimatedTokens":2110}}319{"id":"doc-release_notes_ios_google_for_developers-8548c049","source":"documentation","title":"Release notes | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/rel-notes","text":"Example:\n```text\n-mediatedNativeAd:didRenderInView:clickableAssetViews:nonclickableAssetViews:viewController:\n```\n\nExample:\n```text\n-mediatedNativeAd:didRenderInView:viewController:\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.732Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":50}}320{"id":"doc-migrate_sdk_versions_ios_google_for_developers-df42742d","source":"documentation","title":"Migrate SDK versions | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/migration","text":"Example:\n```text\nimport GoogleMobileAds\n...\nvar request: GoogleMobileAds.Request?\n```\n\nExample:\n```text\nGADMobileAds.sharedInstance().sdkVersion\n```\n\nExample:\n```text\nGADGetStringFromVersionNumber(GADMobileAds.sharedInstance().versionNumber)\n```\n\nExample:\n```text\nimport GoogleMobileAds\nimport UIKit\n\nclass ViewController: UIViewController, GADInterstitialDelegate {\n\n var interstitial: GADInterstitial!\n\n override func viewDidLoad() {\n super.viewDidLoad()\n interstitial = GADInterstitial(adUnitID: \"ca-app-pub-3940256099942544/4411468910\")\n interstitial.delegate = self\n let request = GADRequest()\n interstitial.load(request)\n }\n\n /// Tells the delegate an ad request succeeded.\n func interstitialDidReceiveAd(_ ad: GADInterstitial) {\n print(\"Interstitial ad loaded.\")\n }\n\n /// Tells the delegate an ad request failed.\n func interstitial(_ ad: GADInterstitial, didFailToReceiveAdWithError error: GADRequestError) {\n print(\"Interstitial ad failed to load with error: \\(error.localizedDescription)\")\n }\n}\n```\n\nExample:\n```text\n@import GoogleMobileAds;\n@import UIKit;\n\n@interface ViewController () \n\n@property(nonatomic, strong) GADInterstitial *interstitial;\n\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n self.interstitial = [[GADInterstitial alloc]\n initWithAdUnitID:@\"ca-app-pub-3940256099942544/4411468910\"];\n self.interstitial.delegate = self;\n GADRequest *request = [GADRequest request];\n [self.interstitial loadRequest:request];\n}\n\n/// Tells the delegate an ad request succeeded.\n- (void)interstitialDidReceiveAd:(GADInterstitial *)ad {\n NSLog(@\"Insterstitial ad loaded.\");\n}\n\n/// Tells the delegate an ad request failed.\n- (void)interstitial:(GADInterstitial *)ad\n didFailToReceiveAdWithError:(GADRequestError *)error {\n NSLog(@\"Interstitial ad failed to load with error: %@\", [error localizedDescription]);\n}\n```\n\nExample:\n```text\nimport GoogleMobileAds\nimport UIKit\n\nclass ViewController: UIViewController, GADFullScreenContentDelegate {\n\n var interstitial: GADInterstitialAd?\n\n override func viewDidLoad() {\n super.viewDidLoad()\n let request = GADRequest()\n GADInterstitialAd.load(withAdUnitID:\"ca-app-pub-8123415297019784/4985798738\",\n request: request,\n completionHandler: { (ad, error) in\n if let error = error {\n print(\"Failed to load interstitial ad with error: \\(error.localizedDescription)\")\n return\n }\n self.interstitial = ad\n self.interstitial.fullScreenContentDelegate = self\n }\n )\n }\n}\n```\n\nExample:\n```text\n@import GoogleMobileAds;\n@import UIKit;\n\n@interface ViewController () \n\n@property(nonatomic, strong) GADInterstitialAd *interstitial;\n\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n GADRequest *request = [GADRequest request];\n [GADInterstitialAd loadWithAdUnitID:@\"ca-app-pub-3940256099942544/4411468910\"\n request:request\n completionHandler:^(GADInterstitialAd *ad, NSError *error) {\n if (error) {\n NSLog(@\"Failed to load interstitial ad with error: %@\", [error localizedDescription]);\n return;\n }\n self.interstitial = ad;\n self.interstitial.fullScreenContentDelegate = self;\n }];\n}\n```\n\nExample:\n```text\nfunc showInterstitial() {\n ...\n if interstitial.isReady {\n interstitial.present(fromRootViewController: self)\n } else {\n print(\"Ad wasn't ready\")\n }\n}\n```\n\nExample:\n```text\n- (void)showInterstitial: {\n ...\n if (self.interstitial.isReady) {\n [self.interstitial presentFromRootViewController:self];\n } else {\n NSLog(@\"Ad wasn't ready\");\n }\n}\n```\n\nExample:\n```text\nfunc showInterstitial() {\n ...\n if let ad = interstitial {\n ad.present(fromRootViewController: self)\n } else {\n print(\"Ad wasn't ready\")\n }\n}\n```\n\nExample:\n```text\n- (void)showInterstitial: {\n ...\n if (self.interstitial) {\n [self.interstitial presentFromRootViewController:self];\n } else {\n NSLog(@\"Ad wasn't ready\");\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\noverride func viewDidLoad() {\n super.viewDidLoad()\n interstitial = GADInterstitial(adUnitID: \"ca-app-pub-3940256099942544/4411468910\")\n interstitial.delegate = self\n ...\n}\n\n/// Tells the delegate that an interstitial will be presented.\nfunc interstitialWillPresentScreen(_ ad: GADInterstitial) {\n print(\"Interstitial ad will be presented.\")\n}\n\n/// Tells the delegate the interstitial is to be animated off the screen.\nfunc interstitialWillDismissScreen(_ ad: GADInterstitial) {\n print(\"Interstitial ad will be dismissed.\")\n}\n\n/// Tells the delegate the interstitial had been animated off the screen.\nfunc interstitialDidDismissScreen(_ ad: GADInterstitial) {\n print(\"Interstitial ad dismissed.\")\n}\n\n/// Tells the delegate that a user click will open another app\n/// (such as the App Store), backgrounding the current app.\n///\n/// This is not a reliable callback for an ad click event and is removed in\n/// version 8. If you wish to listen to an ad causing a user to leave the app,\n/// use applicationWillResignActive: or sceneWillResignActive: instead.\nfunc interstitialWillLeaveApplication(_ ad: GADInterstitial) {\n print(\"Interstitial ad will leave application.\")\n}\n```\n\nExample:\n```devsite-click-to-copy\n- (void)viewDidLoad {\n [super viewDidLoad];\n self.interstitial = [[GADInterstitial alloc] initWithAdUnitID:\"ca-app-pub-3940256099942544/4411468910\"];\n self.interstitial.delegate = self;\n ...\n}\n\n/// Tells the delegate that an interstitial will be presented.\n- (void)interstitialWillPresentScreen:(GADInterstitial *)ad {\n NSLog(@\"Interstitial ad will be presented.\");\n}\n\n/// Tells the delegate the interstitial is to be animated off the screen.\n- (void)interstitialWillDismissScreen:(GADInterstitial *)ad {\n NSLog(@\"Interstitial ad will be dismissed.\");\n}\n\n/// Tells the delegate the interstitial had been animated off the screen.\n- (void)interstitialDidDismissScreen:(GADInterstitial *)ad {\n NSLog(@\"Interstitial ad dismissed.\");\n}\n\n/// Tells the delegate that a user click will open another app\n/// (such as the App Store), backgrounding the current app.\n///\n/// This is not a reliable callback for an ad click event and is removed in\n/// version 8. If you wish to listen to an ad causing a user to leave the app,\n/// use applicationWillResignActive: or sceneWillResignActive: instead.\n- (void)interstitialWillLeaveApplication:(GADInterstitial *)ad {\n NSLog(@\"Interstitial ad will leave application.\");\n}\n```\n\nExample:\n```devsite-click-to-copy\noverride func viewDidLoad() {\n super.viewDidLoad()\n let request = GADRequest()\n GADInterstitialAd.load(withAdUnitID:\"ca-app-pub-8123415297019784/4985798738\",\n request: request,\n completionHandler: { (ad, error) in\n if let error = error {\n print(error.localizedDescription)\n return\n }\n self.interstitial = ad\n self.interstitial.fullScreenContentDelegate = self\n }\n )\n}\n\nfunc adDidPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {\n print(\"Ad did present full screen content.\")\n}\n\nfunc ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {\n print(\"Ad failed to present full screen content with error \\(error.localizedDescription).\")\n}\n\nfunc adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {\n print(\"Ad did dismiss full screen content.\")\n}\n```\n\nExample:\n```devsite-click-to-copy\n- (void)viewDidLoad {\n [super viewDidLoad];\n GADRequest *request = [GADRequest request];\n [GADInterstitialAd loadWithAdUnitID:@\"ca-app-pub-3940256099942544/4411468910\"\n request:request\n completionHandler:^(GADInterstitialAd *ad, NSError *error) {\n if (error) {\n NSLog(@\"interstitial:didFailToReceiveAdWithError: %@\", [error localizedDescription])\n return;\n }\n self.interstitial = ad;\n self.interstitial.fullScreenContentDelegate = self;\n }];\n}\n\n- (void)adDidPresentFullScreenContent:(id)ad {\n NSLog(@\"Ad did present full screen content.\");\n}\n\n- (void)ad:(id)ad didFailToPresentFullScreenContentWithError:(NSError *)error {\n NSLog(@\"Ad failed to present full screen content with error %@.\", [error localizedDescription]);\n}\n\n- (void)adDidDismissFullScreenContent:(id)ad {\n NSLog(@\"Ad did dismiss full screen content.\");\n}\n```\n\nExample:\n```text\nimport GoogleMobileAds\nimport UIKit\n\nclass ViewController: UIViewController, GADRewardedAdDelegate {\n /// The rewarded ad.\n var rewardedAd: GADRewardedAd?\n\n override func viewDidLoad() {\n super.viewDidLoad()\n rewardedAd = GADRewardedAd(adUnitID: \"ca-app-pub-3940256099942544/1712485313\")\n rewardedAd.delegate = self\n rewardedAd?.load(GADRequest()) { error in\n if let error = error {\n print(\"Rewarded ad failed to load with error: \\(error.localizedDescription)\")\n } else {\n print(\"Rewarded ad loaded.\")\n }\n }\n }\n}\n```\n\nExample:\n```text\n@import GoogleMobileAds;\n@import UIKit;\n\n@interface ViewController () \n\n@property(nonatomic, strong) GADRewardedAd *rewardedAd;\n\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n\n self.rewardedAd = [[GADRewardedAd alloc]\n initWithAdUnitID:@\"ca-app-pub-3940256099942544/1712485313\"];\n self.rewardedAd.delegate = self;\n GADRequest *request = [GADRequest request];\n [self.rewardedAd loadRequest:request completionHandler:^(GADRequestError * _Nullable error) {\n if (error) {\n NSLog(@\"Rewarded ad failed to load with error: %@\", [error localizedDescription]);\n } else {\n NSLog(@\"Rewarded ad loaded.\");\n }\n }];\n}\n```\n\nExample:\n```text\nimport GoogleMobileAds\nimport UIKit\n\nclass ViewController: UIViewController, GADFullScreenContentDelegate {\n /// The rewarded ad.\n var rewardedAd: GADRewardedAd?\n\n override func viewDidLoad() {\n super.viewDidLoad()\n let request = GADRequest()\n GADRewardedAd.load(withAdUnitID: \"ca-app-pub-8123415297019784/9501821136\",\n request: request, completionHandler: { (ad, error) in\n if let error = error {\n print(\"Rewarded ad failed to load with error: \\(error.localizedDescription)\")\n return\n }\n self.rewardedAd = ad\n self.rewardedAd?.fullScreenContentDelegate = self\n }\n )\n }\n}\n```\n\nExample:\n```text\n@import GoogleMobileAds;\n@import UIKit;\n\n@interface ViewController () \n\n@property(nonatomic, strong) GADRewardedAd *rewardedAd;\n\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n\n GADRequest *request = [GADRequest request];\n [GADRewardedAd loadWithAdUnitID:@\"ca-app-pub-3940256099942544/1712485313\"\n request:request\n completionHandler:^(GADRewardedAd *ad, NSError *error) {\n if (error) {\n NSLog(@\"Rewarded ad failed to load with error: %@\", [error localizedDescription]);\n return;\n }\n self.rewardedAd = ad;\n NSLog(@\"Rewarded ad loaded.\");\n self.rewardedAd.fullScreenContentDelegate = self;\n}\n```\n\nExample:\n```text\nfunc showRewardedAd() {\n ...\n if rewardedAd.isReady {\n rewardedAd.present(fromRootViewController: self delegate:self)\n } else {\n print(\"Ad wasn't ready\")\n }\n}\n\n/// Tells the delegate that the user earned a reward.\nfunc rewardedAd(_ rewardedAd: GADRewardedAd, userDidEarnReward: GADAdReward) {\n // TODO: Reward the user.\n}\n```\n\nExample:\n```text\n- (void)showRewardedAd: {\n ...\n if (self.rewardedAd.isReady) {\n [self.rewardedAd presentFromRootViewController:self delegate:self];\n } else {\n NSLog(@\"Ad wasn't ready\");\n }\n}\n\n/// Tells the delegate that the user earned a reward.\n- (void)rewardedAd:(GADRewardedAd *)rewardedAd userDidEarnReward:(GADAdReward *)reward {\n // TODO: Reward the user.\n}\n```\n\nExample:\n```text\nfunc showRewardedAd() {\n ...\n if let ad = rewardedAd {\n ad.present(fromRootViewController: self,\n userDidEarnRewardHandler: {\n let reward = ad.adReward\n // TODO: Reward the user.\n }\n )\n } else {\n print(\"Ad wasn't ready\")\n }\n}\n```\n\nExample:\n```text\n- (void)showRewardedAd: {\n ...\n if (self.rewardedAd) {\n [self.rewardedAd presentFromRootViewController:self\n userDidEarnRewardHandler:^ {\n GADAdReward *reward = self.rewardedAd.adReward;\n // TODO: Reward the user.\n }];\n } else {\n NSLog(@\"Ad wasn't ready\");\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\nfunc showRewardedAd() {\n ...\n if rewardedAd.isReady {\n rewardedAd.present(fromRootViewController: self delegate:self)\n } else {\n print(\"Ad wasn't ready\")\n }\n}\n\n/// Tells the delegate that the rewarded ad was presented.\nfunc rewardedAdDidPresent(_ rewardedAd: GADRewardedAd) {\n print(\"Rewarded ad presented.\")\n}\n/// Tells the delegate that the rewarded ad was dismissed.\nfunc rewardedAdDidDismiss(_ rewardedAd: GADRewardedAd) {\n print(\"Rewarded ad dismissed.\")\n}\n/// Tells the delegate that the rewarded ad failed to present.\nfunc rewardedAd(_ rewardedAd: GADRewardedAd, didFailToPresentWithError error: Error) {\n print(\"Rewarded ad failed to present with error: \\(error.localizedDescription).\")\n}\n```\n\nExample:\n```devsite-click-to-copy\n- (void)showRewardedAd: {\n ...\n if (self.rewardedAd.isReady) {\n [self.rewardedAd presentFromRootViewController:self delegate:self];\n } else {\n NSLog(@\"Ad wasn't ready\");\n }\n}\n\n/// Tells the delegate that the rewarded ad was presented.\n- (void)rewardedAdDidPresent:(GADRewardedAd *)rewardedAd {\n NSLog(@\"Rewarded ad presented.\");\n}\n\n/// Tells the delegate that the rewarded ad failed to present.\n- (void)rewardedAd:(GADRewardedAd *)rewardedAd didFailToPresentWithError:(NSError *)error {\n NSLog(@\"Rewarded ad failed to present with error: %@\",\n [error localizedDescription]);\n}\n\n/// Tells the delegate that the rewarded ad was dismissed.\n- (void)rewardedAdDidDismiss:(GADRewardedAd *)rewardedAd {\n NSLog(@\"Rewarded ad dismissed.\");\n}\n```\n\nExample:\n```devsite-click-to-copy\noverride func viewDidLoad() {\n super.viewDidLoad()\n let request = GADRequest()\n GADRewardedAd.load(withAdUnitID: \"ca-app-pub-8123415297019784/9501821136\",\n request: request, completionHandler: { (ad, error) in\n if let error = error {\n print(error.localizedDescription)\n return\n }\n self.rewardedAd = ad\n self.rewardedAd?.fullScreenContentDelegate = self\n }\n )\n}\n\n/// Tells the delegate that the rewarded ad was presented.\nfunc adDidPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {\n print(\"Rewarded ad presented.\")\n}\n/// Tells the delegate that the rewarded ad was dismissed.\nfunc adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {\n print(\"Rewarded ad dismissed.\")\n}\n/// Tells the delegate that the rewarded ad failed to present.\nfunc ad(_ ad: GADFullScreenPresentingAd,\n didFailToPresentFullScreenContentWithError error: Error) {\n print(\"Rewarded ad failed to present with error: \\(error.localizedDescription).\")\n}\n```\n\nExample:\n```devsite-click-to-copy\n- (void)viewDidLoad {\n [super viewDidLoad];\n\n GADRequest *request = [GADRequest request];\n [GADRewardedAd loadWithAdUnitID:@\"ca-app-pub-3940256099942544/1712485313\"\n request:request\n completionHandler:^(GADRewardedAd *ad, NSError *error) {\n if (error) {\n NSLog(@\"Rewarded ad failed to load with error: %@\", [error localizedDescription]);\n return;\n }\n self.rewardedAd = ad;\n NSLog(@\"Rewarded ad loaded.\");\n self.rewardedAd.fullScreenContentDelegate = self;\n}\n\n/// Tells the delegate that the rewarded ad was presented.\n- (void)adDidPresentFullScreenContent:(id)ad {\n NSLog(@\"Rewarded ad presented.\");\n}\n\n/// Tells the delegate that the rewarded ad failed to present.\n- (void)ad:(id)ad\n didFailToPresentFullScreenContentWithError:(NSError *)error {\n NSLog(@\"Rewarded ad failed to present with error: %@\",\n [error localizedDescription]);\n}\n\n/// Tells the delegate that the rewarded ad was dismissed.\n- (void)adDidDismissFullScreenContent:(id)ad {\n NSLog(@\"Rewarded ad dismissed.\");\n}\n```\n\nExample:\n```text\nclass ViewController: UIViewController {\n\n override func viewDidAppear(_ animated: Bool) {\n super.viewDidAppear(animated)\n // Note: The safe area is not known until viewWillAppear.\n let adSize = getFullWidthAdaptiveAdSize()\n }\n\n func getFullWidthAdaptiveAdSize() -> GADAdSize {\n // Here safe area is taken into account, hence the view frame is used after the\n // view has been laid out.\n let frame = { () -> CGRect in\n if #available(iOS 11.0, *) {\n return view.frame.inset(by: view.safeAreaInsets)\n } else {\n return view.frame\n }\n }()\n return GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(frame.size.width)\n }\n}\n```\n\nExample:\n```text\n@implementation ViewController\n\n- (void)viewDidAppear:(BOOL)animated {\n [super viewDidAppear:animated];\n // Note: The safe area is not known until viewWillAppear.\n GADAdSize adSize = [self getFullWidthAdaptiveAdSize];\n}\n\n- (GADAdSize)getFullWidthAdaptiveAdSize {\n CGRect frame = self.view.frame;\n // Here safe area is taken into account, hence the view frame is used after\n // the view has been laid out.\n if (@available(iOS 11.0, *)) {\n frame = UIEdgeInsetsInsetRect(self.view.frame, self.view.safeAreaInsets);\n }\n return GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(frame.size.width);\n}\n\n@end\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.735Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":29,"totalLines":662,"estimatedTokens":4510}}321{"id":"doc-googlesignin_framework_reference_sign_in_with_go-7cc84072","source":"documentation","title":"GoogleSignIn Framework Reference | Sign in with Google for iOS | Google for Developers","url":"https://developers.google.com/identity/sign-in/ios/reference/Classes","text":"Example:\n```swift\nclass GIDConfiguration : NSObject, NSCopying, NSSecureCoding\n```\n\nExample:\n```objective_c\n@interface GIDConfiguration : NSObject <NSCopying, NSSecureCoding>\n```\n\nExample:\n```swift\nclass GIDGoogleUser : NSObject, NSSecureCoding\n```\n\nExample:\n```objective_c\n@interface GIDGoogleUser : NSObject <NSSecureCoding>\n```\n\nExample:\n```swift\nclass GIDProfileData : NSObject, NSCopying, NSSecureCoding\n```\n\nExample:\n```objective_c\n@interface GIDProfileData : NSObject <NSCopying, NSSecureCoding>\n```\n\nExample:\n```swift\nclass GIDSignIn : NSObject\n```\n\nExample:\n```objective_c\n@interface GIDSignIn : NSObject\n```\n\nExample:\n```objective_c\n@interface GIDSignInButton : UIControl\n```\n\nExample:\n```swift\nclass GIDSignInResult : NSObject\n```\n\nExample:\n```objective_c\n@interface GIDSignInResult : NSObject\n```\n\nExample:\n```swift\nclass GIDToken : NSObject, NSSecureCoding\n```\n\nExample:\n```objective_c\n@interface GIDToken : NSObject <NSSecureCoding>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.736Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":66,"estimatedTokens":241}}322{"id":"doc-revoking_access_tokens_and_disconnecting_the_app-b40d7e56","source":"documentation","title":"Revoking access tokens and disconnecting the app | Sign in with Google for iOS | Google for Developers","url":"https://developers.google.com/identity/sign-in/ios/disconnect","text":"Example:\n```text\nGIDSignIn.sharedInstance.disconnect { error in\n guard error == nil else { return }\n\n // Google Account disconnected from your app.\n // Perform clean-up actions, such as deleting data associated with the\n // disconnected account.\n}\n```\n\nExample:\n```text\n[GIDSignIn.sharedInstance disconnectWithCompletion:^(NSError * _Nullable error) {\n if (error) { return; }\n\n // Google Account disconnected from your app.\n // Perform clean-up actions, such as deleting data associated with the\n // disconnected account.\n}];\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.736Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":23,"estimatedTokens":143}}323{"id":"doc-access_google_apis_in_an_ios_app_sign_in_with_go-ad1c9f4b","source":"documentation","title":"Access Google APIs in an iOS app | Sign in with Google for iOS | Google for Developers","url":"https://developers.google.com/identity/sign-in/ios/api-access","text":"Example:\n```text\nlet driveScope = \"https://www.googleapis.com/auth/drive.readonly\"\nlet grantedScopes = user.grantedScopes\nif grantedScopes == nil || !grantedScopes!.contains(driveScope) {\n // Request additional Drive scope.\n}\n```\n\nExample:\n```text\nNSString *driveScope = @\"https://www.googleapis.com/auth/drive.readonly\";\n\n// Check if the user has granted the Drive scope\nif (![user.grantedScopes containsObject:driveScope]) {\n // request additional drive scope\n}\n```\n\nExample:\n```text\nlet additionalScopes = [\"https://www.googleapis.com/auth/drive.readonly\"]\nguard let currentUser = GIDSignIn.sharedInstance.currentUser else {\n return ; /* Not signed in. */\n}\n\ncurrentUser.addScopes(additionalScopes, presenting: self) { signInResult, error in\n guard error == nil else { return }\n guard let signInResult = signInResult else { return }\n\n // Check if the user granted access to the scopes you requested.\n}\n```\n\nExample:\n```text\nNSArray *additionalScopes = @[ @\"https://www.googleapis.com/auth/drive.readonly\" ];\nGIDGoogleUser *currentUser = GIDSignIn.sharedInstance.currentUser;\n\n[currentUser addScopes:additionalScopes\n presentingViewController:self\n completion:^(GIDSignInResult * _Nullable signInResult,\n NSError * _Nullable error) {\n if (error) { return; }\n if (signInResult == nil) { return; }\n\n // Check if the user granted access to the scopes you requested.\n}];\n```\n\nExample:\n```text\ncurrentUser.refreshTokensIfNeeded { user, error in\n guard error == nil else { return }\n guard let user = user else { return }\n\n // Get the access token to attach it to a REST or gRPC request.\n let accessToken = user.accessToken.tokenString\n\n // Or, get an object that conforms to GTMFetcherAuthorizationProtocol for\n // use with GTMAppAuth and the Google APIs client library.\n let authorizer = user.fetcherAuthorizer()\n}\n```\n\nExample:\n```text\n[currentUser refreshTokensIfNeededWithCompletion:^(\n GIDGoogleUser * _Nullable user,\n NSError * _Nullable error) {\n if (error) { return; }\n if (user == nil) { return; }\n\n // Get the access token to attach it to a REST or gRPC request.\n NSString *accessToken = user.accessToken.tokenString;\n\n // Or, get an object that conforms to GTMFetcherAuthorizationProtocol for\n // use with GTMAppAuth and the Google APIs client library.\n id<GTMFetcherAuthorizationProtocol> authorizer = [user fetcherAuthorizer];\n}];\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.737Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":83,"estimatedTokens":638}}324{"id":"doc-try_ios_sample_app_authentication_google_for_dev-5de9aaec","source":"documentation","title":"Try iOS Sample App | Authentication | Google for Developers","url":"https://developers.google.com/identity/sign-in/ios/sample-app","text":"Example:\n```text\ngit clone https://github.com/google/GoogleSignIn-iOS\n```\n\nExample:\n```text\ncd GoogleSignIn-iOS/Samples/Swift/DaysUntilBirthday\n```\n\nExample:\n```text\npod install\n```\n\nExample:\n```text\nopen DaysUntilBirthdayForPod.xcworkspace\n```\n\nExample:\n```text\nopen GoogleSignIn-iOS/Samples/Swift/DaysUntilBirthday/DaysUntilBirthday.xcodeproj\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.737Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":26,"estimatedTokens":91}}325{"id":"doc-getting_profile_information_sign_in_with_google_-5d3779cb","source":"documentation","title":"Getting profile information | Sign in with Google for iOS | Google for Developers","url":"https://developers.google.com/identity/sign-in/ios/people","text":"Example:\n```text\nGIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in\n guard error == nil else { return }\n guard let signInResult = signInResult else { return }\n\n let user = signInResult.user\n\n let emailAddress = user.profile?.email\n\n let fullName = user.profile?.name\n let givenName = user.profile?.givenName\n let familyName = user.profile?.familyName\n\n let profilePicUrl = user.profile?.imageURL(withDimension: 320)\n}\n```\n\nExample:\n```text\n[GIDSignIn.sharedInstance signInWithPresentingViewController:self\n completion:^(GIDSignInResult * _Nullable signInResult,\n NSError * _Nullable error) {\n if (error) { return; }\n if (signInResult == nil) { return; }\n\n GIDGoogleUser *user = signInResult.user;\n\n NSString *emailAddress = user.profile.email;\n\n NSString *name = user.profile.name;\n NSString *givenName = user.profile.givenName;\n NSString *familyName = user.profile.familyName;\n\n NSURL *profilePic = [user.profile imageURLWithDimension:320];\n}];\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.739Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":39,"estimatedTokens":288}}326{"id":"doc-example_share_credentials_across_multiple_websit-4416cda2","source":"documentation","title":"Example: Share credentials across multiple websites and multiple Android apps | Samples | Google for Developers","url":"https://developers.google.com/identity/credential-sharing/example-multiple-websites-apps","text":"Example:\n```text\n[\n {\n \"relation\": [\"delegate_permission/common.get_login_creds\"],\n \"target\": {\n \"namespace\": \"web\",\n \"site\": \"https://example.com\"\n }\n },\n {\n \"relation\": [\"delegate_permission/common.get_login_creds\"],\n \"target\": {\n \"namespace\": \"web\",\n \"site\": \"https://example.org\"\n }\n },\n {\n \"relation\": [\"delegate_permission/common.get_login_creds\"],\n \"target\": {\n \"namespace\": \"web\",\n \"site\": \"https://example.net\"\n }\n },\n {\n \"relation\": [\"delegate_permission/common.get_login_creds\"],\n \"target\": {\n \"namespace\": \"web\",\n \"site\": \"https://myownpersonaldomain.com\"\n }\n },\n {\n \"relation\" : [\n \"delegate_permission/common.get_login_creds\"\n ],\n \"target\" : {\n \"namespace\" : \"android_app\",\n \"package_name\" : \"com.example.android.myapplication\",\n \"sha256_cert_fingerprints\" : [ \"AA:BB:CC:DD:EE:FF:11:22:33:44:55:66:77:88:99:00:AA:BB:CC:DD:EE:FF:11:22:33:44:55:66:77:88:99:00\"\n ]\n }\n },\n {\n \"relation\" : [\n \"delegate_permission/common.get_login_creds\"\n ],\n \"target\" : {\n \"namespace\" : \"android_app\",\n \"package_name\" : \"com.example.appname\",\n \"sha256_cert_fingerprints\" : [ \"00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF\"\n ]\n }\n }\n]\n```\n\nExample:\n```text\n[\n {\n \"relation\": [\"delegate_permission/common.get_login_creds\"],\n \"target\": {\n \"namespace\": \"web\",\n \"site\": \"example.com\"\n }\n }\n]\n```\n\nExample:\n```text\n<meta-data android:name=\"asset_statements\" android:resource=\"@string/asset_statements\"/>\n```\n\nExample:\n```text\n<string name=\"asset_statements\" translatable=\"false\">\n[{\n \\\"include\\\": \\\"https://example.com/.well-known/assetlinks.json\\\"\n}]\n</string>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.739Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":84,"estimatedTokens":451}}327{"id":"doc-identity_linking_oauth_2_0_google_universal_comm-d7c31c51","source":"documentation","title":"Identity Linking - OAuth 2.0 | Google Universal Commerce Protocol (UCP) Guide | Google for Developers","url":"https://developers.google.com/merchant/ucp/guides/identity-linking","text":"Example:\n```text\n{\n \"issuer\": \"https://merchant.example.com\",\n \"authorization_endpoint\": \"https://merchant.example.com/oauth2/authorize\",\n \"token_endpoint\": \"https://merchant.example.com/oauth2/token\",\n \"revocation_endpoint\": \"https://merchant.example.com/oauth2/revoke\",\n \"scopes_supported\": [\n \"dev.ucp.shopping.order:read\",\n \"dev.ucp.shopping.checkout:manage\"\n ],\n \"response_types_supported\": [\n \"code\"\n ],\n \"grant_types_supported\": [\n \"authorization_code\",\n \"refresh_token\"\n ],\n \"token_endpoint_auth_methods_supported\": [\n \"client_secret_basic\"\n ],\n \"service_documentation\": \"https://merchant.example.com/docs/oauth2\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.739Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":26,"estimatedTokens":168}}328{"id":"doc-validate_your_native_ads_ios_google_for_develope-c925e518","source":"documentation","title":"Validate your native ads | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/native/validator","text":"Example:\n```text\n<key>GADNativeAdValidatorEnabled</key>\n<false/>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.740Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":7,"estimatedTokens":21}}329{"id":"doc-use_collapsible_banners_ios_google_for_developer-e3ca70b1","source":"documentation","title":"Use collapsible banners | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/banner/collapsible","text":"Example:\n```text\nfunc loadBannerAd() {\n bannerView.adUnitID = \"ca-app-pub-3940256099942544/8388050270\"\n bannerView.rootViewController = self\n let viewWidth = FRAME_WIDTH\n bannerView.adSize = currentOrientationAnchoredAdaptiveBanner(width: viewWidth)\n\n let request = Request()\n\n // Create an extra parameter that aligns the bottom of the expanded ad to\n // the bottom of the bannerView.\n let extras = Extras()\n extras.additionalParameters = [\"collapsible\" : \"bottom\"]\n request.register(extras)\n\n bannerView.load(request)\n }\n```\n\nExample:\n```text\n- (void)loadBannerAd {\n self.bannerView.adUnitID = @\"ca-app-pub-3940256099942544/8388050270\";\n CGFloat viewWidth = FRAME_WIDTH;\n self.bannerView.adSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(viewWidth);\n\n GADRequest *request = [GADRequest request];\n\n // Create an extra parameter that aligns the bottom of the expanded ad to the\n // bottom of the bannerView.\n GADExtras *extras = [[GADExtras alloc] init];\n extras.additionalParameters = @{@\"collapsible\" : @\"bottom\"};\n [request registerAdNetworkExtras:extras];\n\n [self.bannerView loadRequest:request];\n}\n```\n\nExample:\n```text\nfunc bannerViewDidReceiveAd(_ bannerView: BannerView) {\n print(\"The last loaded banner is \\(bannerView.isCollapsible ? \"\" : \"not\") collapsible.\")\n}\n```\n\nExample:\n```text\n- (void)bannerViewDidReceiveAd:(GADBannerView *)bannerView {\n NSLog(@\"The last loaded banner is %@collapsible.\", (bannerView.isCollapsible ? @\"\" : @\"not \"));\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.740Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":54,"estimatedTokens":381}}330{"id":"doc-set_up_admob_mediation_ios_google_for_developers-621c64f9","source":"documentation","title":"Set up AdMob Mediation | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/mediation","text":"Example:\n```text\nMobileAds.shared.start { initializationStatus in\n // Check each adapter's initialization status.\n for (adapterName, status) in initializationStatus.adapterStatusesByClassName {\n print(\n \"Adapter: \\(adapterName), Description: \\(status.description), Latency: \\(status.latency)\")\n }\n}MediationSnippets.swift\n```\n\nExample:\n```text\n[[GADMobileAds sharedInstance]\n startWithCompletionHandler:^(GADInitializationStatus *_Nonnull status) {\n // Check each adapter's initialization status.\n NSDictionary<NSString *, GADAdapterStatus *> *adapterStatuses =\n status.adapterStatusesByClassName;\n for (NSString *adapterName in adapterStatuses) {\n GADAdapterStatus *adapterStatus = adapterStatuses[adapterName];\n NSLog(@\"Adapter: %@, Description: %@, Latency: %f\", adapterName,\n adapterStatus.description, adapterStatus.latency);\n }\n }];MediationSnippets.m\n```\n\nExample:\n```text\nprint(\n \"Adapter class name: \\(ad.responseInfo?.loadedAdNetworkResponseInfo?.adNetworkClassName ?? \"Unknown\")\"\n)ResponseInfoSnippets.swift\n```\n\nExample:\n```text\nNSLog(@\"Adapter class name: %@\",\n ad.responseInfo.loadedAdNetworkResponseInfo.adNetworkClassName ?: @\"Unknown\");ResponseInfoSnippets.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.741Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":40,"estimatedTokens":318}}331{"id":"doc-integrating_google_sign_in_into_your_ios_or_maco-5133b16b","source":"documentation","title":"Integrating Google Sign-In into your iOS or macOS app | Sign in with Google for iOS | Google for Developers","url":"https://developers.google.com/identity/sign-in/ios/sign-in","text":"Example:\n```text\nfunc application(\n _ app: UIApplication,\n open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]\n) -> Bool {\n var handled: Bool\n\n handled = GIDSignIn.sharedInstance.handle(url)\n if handled {\n return true\n }\n\n // Handle other custom URL types.\n\n // If not handled by this app, return false.\n return false\n}\n```\n\nExample:\n```text\n- (BOOL)application:(UIApplication *)app\n openURL:(NSURL *)url\n options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {\n BOOL handled;\n\n handled = [GIDSignIn.sharedInstance handleURL:url];\n if (handled) {\n return YES;\n }\n\n // Handle other custom URL types.\n\n // If not handled by this app, return NO.\n return NO;\n}\n```\n\nExample:\n```text\nfunc applicationDidFinishLaunching(_ notification: Notification) {\n // Register for GetURL events.\n let appleEventManager = NSAppleEventManager.shared()\n appleEventManager.setEventHandler(\n self,\n andSelector: \"handleGetURLEvent:replyEvent:\",\n forEventClass: AEEventClass(kInternetEventClass),\n andEventID: AEEventID(kAEGetURL)\n )\n}\n```\n\nExample:\n```text\n- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {\n // Register for GetURL events.\n NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];\n [appleEventManager setEventHandler:self\n andSelector:@selector(handleGetURLEvent:withReplyEvent:)\n forEventClass:kInternetEventClass\n andEventID:kAEGetURL];\n}\n```\n\nExample:\n```text\nfunc handleGetURLEvent(event: NSAppleEventDescriptor?, replyEvent: NSAppleEventDescriptor?) {\n if let urlString =\n event?.paramDescriptor(forKeyword: AEKeyword(keyDirectObject))?.stringValue{\n let url = NSURL(string: urlString)\n GIDSignIn.sharedInstance.handle(url)\n }\n}\n```\n\nExample:\n```text\n- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event\n withReplyEvent:(NSAppleEventDescriptor *)replyEvent {\n NSString *URLString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];\n NSURL *URL = [NSURL URLWithString:URLString];\n [GIDSignIn.sharedInstance handleURL:url];\n}\n```\n\nExample:\n```text\n@main\nstruct MyApp: App {\n\n var body: some Scene {\n WindowGroup {\n ContentView()\n // ...\n .onOpenURL { url in\n GIDSignIn.sharedInstance.handle(url)\n }\n }\n }\n}\n```\n\nExample:\n```text\nfunc application(\n _ application: UIApplication,\n didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n) -> Bool {\n GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in\n if error != nil || user == nil {\n // Show the app's signed-out state.\n } else {\n // Show the app's signed-in state.\n }\n }\n return true\n}\n```\n\nExample:\n```text\n- (BOOL)application:(UIApplication *)application\n didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n [GIDSignIn.sharedInstance restorePreviousSignInWithCompletion:^(GIDGoogleUser * _Nullable user,\n NSError * _Nullable error) {\n if (error) {\n // Show the app's signed-out state.\n } else {\n // Show the app's signed-in state.\n }\n }];\n return YES;\n}\n```\n\nExample:\n```text\n@main\nstruct MyApp: App {\n var body: some Scene {\n WindowGroup {\n ContentView()\n // ...\n .onAppear {\n GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in\n // Check if `user` exists; otherwise, do something with `error`\n }\n }\n }\n }\n}\n```\n\nExample:\n```text\nimport GoogleSignInSwift\n```\n\nExample:\n```text\nextension UIApplication {\n // Minimal implementation to retrieve the active root view\n // controller for presentation. Apps presenting sign-in from deeper\n // within an existing view hierarchy should ensure they select the\n // appropriate view controller.\n var rootViewController: UIViewController? {\n let windowScene = connectedScenes\n .compactMap { scene in scene as? UIWindowScene }\n .first { scene in scene.activationState == .foregroundActive }\n return windowScene?.windows.first(where: { window in window.isKeyWindow })?.rootViewController\n }\n}\n```\n\nExample:\n```text\nGoogleSignInButton(action: handleSignInButton)\n```\n\nExample:\n```text\nfunc handleSignInButton() {\n guard let rootViewController = UIApplication.shared.rootViewController else {\n // Handle error\n return\n }\n\n GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController) { signInResult, error in\n guard let result = signInResult else {\n // Inspect error\n return\n }\n // If sign in succeeded, display the app's main content view.\n }\n}\n```\n\nExample:\n```text\n@IBAction func signIn(sender: Any) {\n GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in\n guard error == nil else { return }\n\n // If sign in succeeded, display the app's main content View.\n }\n}\n```\n\nExample:\n```text\n- (IBAction)signIn:(id)sender {\n [GIDSignIn.sharedInstance\n signInWithPresentingViewController:self\n completion:^(GIDSignInResult * _Nullable signInResult,\n NSError * _Nullable error) {\n if (error) {\n return;\n }\n\n // If sign in succeeded, display the app's main content View.\n }];\n}\n```\n\nExample:\n```text\nButton(\"Sign Out\") {\n GIDSignIn.sharedInstance.signOut()\n // Calling signOut() may not automatically trigger UI updates.\n // Update your app's state as needed.\n}\n```\n\nExample:\n```text\n@IBAction func signOut(sender: Any) {\n GIDSignIn.sharedInstance.signOut()\n}\n```\n\nExample:\n```text\n- (IBAction)signOut:(id)sender {\n [GIDSignIn.sharedInstance signOut];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.742Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":19,"totalLines":249,"estimatedTokens":1449}}332{"id":"doc-authenticate_with_a_backend_server_sign_in_with_-1c58c022","source":"documentation","title":"Authenticate with a backend server | Sign in with Google for iOS | Google for Developers","url":"https://developers.google.com/identity/sign-in/ios/backend-auth","text":"Example:\n```text\nGIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in\n guard error == nil else { return }\n guard let signInResult = signInResult else { return }\n\n signInResult.user.refreshTokensIfNeeded { user, error in\n guard error == nil else { return }\n guard let user = user else { return }\n\n let idToken = user.idToken\n // Send ID token to backend (example below).\n }\n}\n```\n\nExample:\n```text\n[GIDSignIn.sharedInstance signInWithPresentingViewController:self\n completion:^(GIDSignInResult * _Nullable signInResult,\n NSError * _Nullable error) {\n if (error) { return; }\n if (signInResult == nil) { return; }\n\n [signInResult.user refreshTokensIfNeededWithCompletion:^(GIDGoogleUser * _Nullable user,\n NSError * _Nullable error) {\n if (error) { return; }\n if (user == nil) { return; }\n\n NSString *idToken = user.idToken;\n // Send ID token to backend (example below).\n }];\n}];\n```\n\nExample:\n```text\nfunc tokenSignInExample(idToken: String) {\n guard let authData = try? JSONEncoder().encode([\"idToken\": idToken]) else {\n return\n }\n let url = URL(string: \"https://yourbackend.example.com/tokensignin\")!\n var request = URLRequest(url: url)\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n\n let task = URLSession.shared.uploadTask(with: request, from: authData) { data, response, error in\n // Handle response from your backend.\n }\n task.resume()\n}\n```\n\nExample:\n```text\nNSString *signinEndpoint = @\"https://yourbackend.example.com/tokensignin\";\nNSDictionary *params = @{@\"idtoken\": idToken};\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:signinEndpoint];\n[request setValue:@\"application/x-www-form-urlencoded\" forHTTPHeaderField:@\"Content-Type\"];\n[request setHTTPMethod:@\"POST\"];\n[request setHTTPBody:[self httpBodyForParamsDictionary:params]];\n\nNSOperationQueue *queue = [[NSOperationQueue alloc] init];\n[NSURLConnection sendAsynchronousRequest:request\n queue:queue\n completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {\n if (error) {\n NSLog(@\"Error: %@\", error.localizedDescription);\n } else {\n NSLog(@\"Signed in as %@\", data.bytes);\n }\n }];\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;\n\n...\n\nGoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)\n // Specify the WEB_CLIENT_ID of the app that accesses the backend:\n .setAudience(Collections.singletonList(WEB_CLIENT_ID))\n // Or, if multiple clients access the backend:\n //.setAudience(Arrays.asList(WEB_CLIENT_ID_1, WEB_CLIENT_ID_2, WEB_CLIENT_ID_3))\n .build();\n\n// (Receive idTokenString by HTTPS POST)\n\nGoogleIdToken idToken = verifier.verify(idTokenString);\nif (idToken != null) {\n Payload payload = idToken.getPayload();\n\n // Print user identifier. This ID is unique to each Google Account, making it suitable for\n // use as a primary key during account lookup. Email is not a good choice because it can be\n // changed by the user.\n String userId = payload.getSubject();\n System.out.println(\"User ID: \" + userId);\n\n // Get profile information from payload\n String email = payload.getEmail();\n boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());\n String name = (String) payload.get(\"name\");\n String pictureUrl = (String) payload.get(\"picture\");\n String locale = (String) payload.get(\"locale\");\n String familyName = (String) payload.get(\"family_name\");\n String givenName = (String) payload.get(\"given_name\");\n\n // Use or store profile information\n // ...\n\n} else {\n System.out.println(\"Invalid ID token.\");\n}\n```\n\nExample:\n```text\nnpm install google-auth-library --save\n```\n\nExample:\n```text\nconst {OAuth2Client} = require('google-auth-library');\nconst client = new OAuth2Client();\nasync function verify() {\n const ticket = await client.verifyIdToken({\n idToken: token,\n audience: WEB_CLIENT_ID, // Specify the WEB_CLIENT_ID of the app that accesses the backend\n // Or, if multiple clients access the backend:\n //[WEB_CLIENT_ID_1, WEB_CLIENT_ID_2, WEB_CLIENT_ID_3]\n });\n const payload = ticket.getPayload();\n // This ID is unique to each Google Account, making it suitable for use as a primary key\n // during account lookup. Email is not a good choice because it can be changed by the user.\n const userid = payload['sub'];\n // If the request specified a Google Workspace domain:\n // const domain = payload['hd'];\n}\nverify().catch(console.error);\n```\n\nExample:\n```text\ncomposer require google/apiclient\n```\n\nExample:\n```text\nrequire_once 'vendor/autoload.php';\n\n// Get $id_token via HTTPS POST.\n\n$client = new Google_Client(['client_id' => $WEB_CLIENT_ID]); // Specify the WEB_CLIENT_ID of the app that accesses the backend\n$payload = $client->verifyIdToken($id_token);\nif ($payload) {\n // This ID is unique to each Google Account, making it suitable for use as a primary key\n // during account lookup. Email is not a good choice because it can be changed by the user.\n $userid = $payload['sub'];\n // If the request specified a Google Workspace domain\n //$domain = $payload['hd'];\n} else {\n // Invalid ID token\n}\n```\n\nExample:\n```text\nfrom google.oauth2 import id_token\nfrom google.auth.transport import requests\n\n# (Receive token by HTTPS POST)\n# ...\n\ntry:\n # Specify the WEB_CLIENT_ID of the app that accesses the backend:\n idinfo = id_token.verify_oauth2_token(token, requests.Request(), WEB_CLIENT_ID)\n\n # Or, if multiple clients access the backend server:\n # idinfo = id_token.verify_oauth2_token(token, requests.Request())\n # if idinfo['aud'] not in [WEB_CLIENT_ID_1, WEB_CLIENT_ID_2, WEB_CLIENT_ID_3]:\n # raise ValueError('Could not verify audience.')\n\n # If the request specified a Google Workspace domain\n # if idinfo['hd'] != DOMAIN_NAME:\n # raise ValueError('Wrong domain name.')\n\n # ID token is valid. Get the user's Google Account ID from the decoded token.\n # This ID is unique to each Google Account, making it suitable for use as a primary key\n # during account lookup. Email is not a good choice because it can be changed by the user.\n userid = idinfo['sub']\nexcept ValueError:\n # Invalid token\n pass\n```\n\nExample:\n```text\nhttps://oauth2.googleapis.com/tokeninfo?id_token=XYZ123\n```\n\nExample:\n```text\n{\n // These six fields are included in all Google ID Tokens.\n \"iss\": \"https://accounts.google.com\",\n \"sub\": \"110169484474386276334\",\n \"azp\": \"1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com\",\n \"aud\": \"1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com\",\n \"iat\": \"1433978353\",\n \"exp\": \"1433981953\",\n\n // These seven fields are only included when the user has granted the \"profile\" and\n // \"email\" OAuth scopes to the application.\n \"email\": \"testuser@gmail.com\",\n \"email_verified\": \"true\",\n \"name\" : \"Test User\",\n \"picture\": \"https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg\",\n \"given_name\": \"Test\",\n \"family_name\": \"User\",\n \"locale\": \"en\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.743Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":228,"estimatedTokens":1930}}333{"id":"doc-load_a_native_ad_ios_google_for_developers-2c02717c","source":"documentation","title":"Load a native ad | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/native","text":"Example:\n```text\nadLoader = AdLoader(\n adUnitID: \"nativeAdUnitID\",\n // The UIViewController parameter is optional.\n rootViewController: self,\n // To receive native ads, the ad loader's delegate must\n // conform to the NativeAdLoaderDelegate protocol.\n adTypes: [.native],\n // Use nil for default options.\n options: nil)\n// Set the delegate before making an ad request.\nadLoader.delegate = selfNativeAdSnippets.swift\n```\n\nExample:\n```text\nself.adLoader =\n [[GADAdLoader alloc] initWithAdUnitID:\"kNativeAdUnitID\"\n // The UIViewController parameter is optional.\n rootViewController:self\n // To receive native ads, the ad loader's delegate must\n // conform to the NativeAdLoaderDelegate protocol.\n adTypes:@[ GADAdLoaderAdTypeNative ]\n // Use nil for default options.\n options:nil];\n// Set the delegate before making an ad request.\nself.adLoader.delegate = self;NativeAdSnippets.m\n```\n\nExample:\n```text\nfunc adLoader(_ adLoader: AdLoader, didReceive nativeAd: NativeAd) {\n // Set the delegate to receive notifications for interactions with the native ad.\n nativeAd.delegate = self\n\n // TODO: Display the native ad.\n}NativeAdSnippets.swift\n```\n\nExample:\n```text\n- (void)adLoader:(GADAdLoader *)adLoader didReceiveNativeAd:(GADNativeAd *)nativeAd {\n // Set the delegate to receive notifications for interactions with the native ad.\n nativeAd.delegate = self;\n\n // TODO: Display the native ad.\n}NativeAdSnippets.m\n```\n\nExample:\n```text\nadLoader.load(Request())NativeAdSnippets.swift\n```\n\nExample:\n```text\n[self.adLoader loadRequest:[GADRequest request]];NativeAdSnippets.m\n```\n\nExample:\n```text\nlet multipleAdOptions = MultipleAdsAdLoaderOptions()\nmultipleAdOptions.numberOfAds = 5\nadLoader = AdLoader(\n adUnitID: \"nativeAdUnitID\",\n // The UIViewController parameter is optional.\n rootViewController: self,\n adTypes: [.native],\n options: [multipleAdOptions])NativeAdSnippets.swift\n```\n\nExample:\n```text\nGADMultipleAdsAdLoaderOptions *multipleAdOptions = [[GADMultipleAdsAdLoaderOptions alloc] init];\nmultipleAdOptions.numberOfAds = 5;\n\nself.adLoader = [[GADAdLoader alloc] initWithAdUnitID:\"kNativeAdUnitID\"\n // The UIViewController parameter is optional.\n rootViewController:self\n adTypes:@[ GADAdLoaderAdTypeNative ]\n options:@[ multipleAdOptions ]];NativeAdSnippets.m\n```\n\nExample:\n```text\nfunc adLoaderDidFinishLoading(_ adLoader: AdLoader) {\n // The adLoader has finished loading ads.\n}NativeAdSnippets.swift\n```\n\nExample:\n```text\n- (void)adLoaderDidFinishLoading:(GADAdLoader *)adLoader {\n // The adLoader has finished loading ads.\n}NativeAdSnippets.m\n```\n\nExample:\n```text\nfunc adLoader(_ adLoader: AdLoader, didFailToReceiveAdWithError error: any Error) {\n // The adLoader failed to receive an ad.\n}NativeAdSnippets.swift\n```\n\nExample:\n```text\n- (void)adLoader:(GADAdLoader *)adLoader didFailToReceiveAdWithError:(NSError *)error {\n // The adLoader failed to receive an ad.\n}NativeAdSnippets.m\n```\n\nExample:\n```text\nnativeAd.delegate = selfNativeAdSnippets.swift\n```\n\nExample:\n```text\nnativeAd.delegate = self;NativeAdSnippets.m\n```\n\nExample:\n```text\nfunc nativeAdDidRecordImpression(_ nativeAd: NativeAd) {\n // The native ad was shown.\n}\n\nfunc nativeAdDidRecordClick(_ nativeAd: NativeAd) {\n // The native ad was clicked on.\n}\n\nfunc nativeAdWillPresentScreen(_ nativeAd: NativeAd) {\n // The native ad will present a full screen view.\n}\n\nfunc nativeAdWillDismissScreen(_ nativeAd: NativeAd) {\n // The native ad will dismiss a full screen view.\n}\n\nfunc nativeAdDidDismissScreen(_ nativeAd: NativeAd) {\n // The native ad did dismiss a full screen view.\n}\n\nfunc nativeAdWillLeaveApplication(_ nativeAd: NativeAd) {\n // The native ad will cause the app to become inactive and\n // open a new app.\n}NativeAdSnippets.swift\n```\n\nExample:\n```text\n- (void)nativeAdDidRecordImpression:(GADNativeAd *)nativeAd {\n // The native ad was shown.\n}\n\n- (void)nativeAdDidRecordClick:(GADNativeAd *)nativeAd {\n // The native ad was clicked on.\n}\n\n- (void)nativeAdWillPresentScreen:(GADNativeAd *)nativeAd {\n // The native ad will present a full screen view.\n}\n\n- (void)nativeAdWillDismissScreen:(GADNativeAd *)nativeAd {\n // The native ad will dismiss a full screen view.\n}\n\n- (void)nativeAdDidDismissScreen:(GADNativeAd *)nativeAd {\n // The native ad did dismiss a full screen view.\n}\n\n- (void)nativeAdWillLeaveApplication:(GADNativeAd *)nativeAd {\n // The native ad will cause the app to become inactive and\n // open a new app.\n}NativeAdSnippets.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.743Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":179,"estimatedTokens":1214}}334{"id":"doc-smart_banners_ios_google_for_developers-eb5b917f","source":"documentation","title":"Smart banners | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/banner/smart","text":"Example:\n```text\nlet bannerView = GADBannerView(adSize: kGADAdSizeSmartBannerPortrait)\n```\n\nExample:\n```text\nGADBannerView *bannerView = [[GADBannerView alloc]\n initWithAdSize:kGADAdSizeSmartBannerPortrait];\n```\n\nExample:\n```text\nfunc addBannerViewToView(_ bannerView: GADBannerView) {\n bannerView.translatesAutoresizingMaskIntoConstraints = false\n view.addSubview(bannerView)\n if #available(iOS 11.0, *) {\n // In iOS 11, we need to constrain the view to the safe area.\n positionBannerViewFullWidthAtBottomOfSafeArea(bannerView)\n }\n else {\n // In lower iOS versions, safe area is not available so we use\n // bottom layout guide and view edges.\n positionBannerViewFullWidthAtBottomOfView(bannerView)\n }\n}\n\n// MARK: - view positioning\n@available (iOS 11, *)\nfunc positionBannerViewFullWidthAtBottomOfSafeArea(_ bannerView: UIView) {\n // Position the banner. Stick it to the bottom of the Safe Area.\n // Make it constrained to the edges of the safe area.\n let guide = view.safeAreaLayoutGuide\n NSLayoutConstraint.activate([\n guide.leftAnchor.constraint(equalTo: bannerView.leftAnchor),\n guide.rightAnchor.constraint(equalTo: bannerView.rightAnchor),\n guide.bottomAnchor.constraint(equalTo: bannerView.bottomAnchor)\n ])\n}\n\nfunc positionBannerViewFullWidthAtBottomOfView(_ bannerView: UIView) {\n view.addConstraint(NSLayoutConstraint(item: bannerView,\n attribute: .leading,\n relatedBy: .equal,\n toItem: view,\n attribute: .leading,\n multiplier: 1,\n constant: 0))\n view.addConstraint(NSLayoutConstraint(item: bannerView,\n attribute: .trailing,\n relatedBy: .equal,\n toItem: view,\n attribute: .trailing,\n multiplier: 1,\n constant: 0))\n view.addConstraint(NSLayoutConstraint(item: bannerView,\n attribute: .bottom,\n relatedBy: .equal,\n toItem: bottomLayoutGuide,\n attribute: .top,\n multiplier: 1,\n constant: 0))\n}\n```\n\nExample:\n```text\n- (void)addBannerViewToView:(UIView *)bannerView {\n bannerView.translatesAutoresizingMaskIntoConstraints = NO;\n [self.view addSubview:bannerView];\n if (@available(ios 11.0, *)) {\n // In iOS 11, we need to constrain the view to the safe area.\n [self positionBannerViewFullWidthAtBottomOfSafeArea:bannerView];\n } else {\n // In lower iOS versions, safe area is not available so we use\n // bottom layout guide and view edges.\n [self positionBannerViewFullWidthAtBottomOfView:bannerView];\n }\n}\n\n#pragma mark - view positioning\n\n- (void)positionBannerViewFullWidthAtBottomOfSafeArea:(UIView *_Nonnull)bannerView NS_AVAILABLE_IOS(11.0) {\n // Position the banner. Stick it to the bottom of the Safe Area.\n // Make it constrained to the edges of the safe area.\n UILayoutGuide *guide = self.view.safeAreaLayoutGuide;\n\n [NSLayoutConstraint activateConstraints:@[\n [guide.leftAnchor constraintEqualToAnchor:bannerView.leftAnchor],\n [guide.rightAnchor constraintEqualToAnchor:bannerView.rightAnchor],\n [guide.bottomAnchor constraintEqualToAnchor:bannerView.bottomAnchor]\n ]];\n}\n\n- (void)positionBannerViewFullWidthAtBottomOfView:(UIView *_Nonnull)bannerView {\n [self.view addConstraint:[NSLayoutConstraint constraintWithItem:bannerView\n attribute:NSLayoutAttributeLeading\n relatedBy:NSLayoutRelationEqual\n toItem:self.view\n attribute:NSLayoutAttributeLeading\n multiplier:1\n constant:0]];\n [self.view addConstraint:[NSLayoutConstraint constraintWithItem:bannerView\n attribute:NSLayoutAttributeTrailing\n relatedBy:NSLayoutRelationEqual\n toItem:self.view\n attribute:NSLayoutAttributeTrailing\n multiplier:1\n constant:0]];\n [self.view addConstraint:[NSLayoutConstraint constraintWithItem:bannerView\n attribute:NSLayoutAttributeBottom\n relatedBy:NSLayoutRelationEqual\n toItem:self.bottomLayoutGuide\n attribute:NSLayoutAttributeTop\n multiplier:1\n constant:0]];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.745Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":120,"estimatedTokens":1367}}335{"id":"doc-make_an_api_call_content_api_for_shopping_google-74544436","source":"documentation","title":"Make an API call | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/quickstart/making-an-api-call","text":"Example:\n```text\nfrom __future__ import print_function\nimport sys\n\n# The common module provides setup functionality used by the samples,\n# such as authentication and unique id generation.\nfrom shopping.content import common\n```\n\nExample:\n```text\noffer_id = 'book#%s' % common.get_unique_id()\nproduct = {\n 'offerId':\n offer_id,\n 'title':\n 'A Tale of Two Cities',\n 'description':\n 'A classic novel about the French Revolution',\n 'link':\n 'http://my-book-shop.com/tale-of-two-cities.html',\n 'imageLink':\n 'http://my-book-shop.com/tale-of-two-cities.jpg',\n 'contentLanguage':\n 'en',\n 'targetCountry':\n 'US',\n 'channel':\n 'online',\n 'availability':\n 'in stock',\n 'condition':\n 'new',\n 'googleProductCategory':\n 'Media > Books',\n 'gtin':\n '9780007350896',\n 'price': {\n 'value': '2.50',\n 'currency': 'USD'\n },\n 'shipping': [{\n 'country': 'US',\n 'service': 'Standard shipping',\n 'price': {\n 'value': '0.99',\n 'currency': 'USD'\n }\n }],\n 'shippingWeight': {\n 'value': '200',\n 'unit': 'grams'\n }\n}\n```\n\nExample:\n```text\ndef main(argv):\n # Construct the service object to interact with the Content API.\n service, config, _ = common.init(argv, __doc__)\n\n # Get the merchant ID from merchant-info.json.\n merchant_id = config['merchantId']\n\n # Create the request with the merchant ID and product object.\n request = service.products().insert(merchantId=merchant_id, body=product)\n\n # Execute the request and print the result.\n result = request.execute()\n print('Product with offerId \"%s\" was created.' % (result['offerId']))\n\n# Allow the function to be called with arguments passed from the command line.\nif __name__ == '__main__':\n main(sys.argv)\n```\n\nExample:\n```text\npython -m shopping.content.products.my-insert\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.745Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":84,"estimatedTokens":496}}336{"id":"doc-set_up_a_client_library_content_api_for_shopping-58d5b3ca","source":"documentation","title":"Set up a client library | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/quickstart/setting-up-a-client-library","text":"Example:\n```text\n{\n\"merchantId\": your Merchant Center merchant ID,\n\"accountSampleUser\": \"the email address associated with your Merchant Center account\"\n}\n```\n\nExample:\n```text\npip install -r requirements.txt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.746Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":14,"estimatedTokens":57}}337{"id":"doc-manage_data_efficiently_google_ads_api_google_fo-fafb1c8a","source":"documentation","title":"Manage data efficiently | Google Ads API | Google for Developers","url":"https://developers.google.com/google-ads/api/docs/productionize/manage-data-efficiently","text":"Example:\n```text\nSELECT campaign.id, campaign.name, metrics.cost_micros FROM campaign WHERE\n segments.date = \"YYYY-MM-DD\"\n```\n\nExample:\n```text\nList<long> adGroupIds = FetchAdGroupIdsFromLocalDatabase();\n\n foreach (long adGroupId in adGroupIds)\n {\n string query = \"SELECT ad_group.id, ad_group.name, metrics.clicks, \" +\n \"metrics.cost_micros, metrics.impressions, segments.date FROM \" +\n \"ad_group WHERE segments.date DURING LAST_7_DAYS AND \" +\n \"ad_group.id = ${adGroupId}\";\n List<GoogleAdsRow> rows = RunGoogleAdsReport(customerId, query);\n InsertRowsIntoStatsTable(adGroupId, rows);\n }\n```\n\nExample:\n```text\nHashset<long> adGroupIds = FetchAdGroupIdsFromLocalDatabase();\n\n string query = \"SELECT ad_group.id, ad_group.name, metrics.clicks, \" +\n \"metrics.cost_micros, metrics.impressions, segments.date FROM \" +\n \"ad_group WHERE segments.date DURING LAST_7_DAYS\";\n List<GoogleAdsRow> rows = RunGoogleAdsReport(customer_id, query);\n\n var memoryMap = new Dictionary<long, List<GoogleAdsRow>>();\n for each (GoogleAdsRow row in rows)\n {\n var adGroupId = row.AdGroup.Id;\n\n if (adGroupIds.Contains(adGroupId))\n {\n CheckAndAddRowIntoMemoryMap(row, adGroupId, memoryMap);\n }\n }\n foreach (long adGroupId in memoryMap.Keys())\n {\n InsertRowsIntoStatsTable(adGroupId, rows);\n }\n```\n\nExample:\n```text\nSELECT\n ad_group.id,\n ad_group.name,\n metrics.clicks,\n metrics.cost_micros,\n metrics.impressions,\n segments.date\nFROM ad_group\nWHERE segments.date DURING LAST_7_DAYS\n AND ad_group.id IN (id1, id2, ...)\nLIMIT 100000\n```\n\nExample:\n```text\nSELECT\n customer.id,\n customer.currency_code,\n campaign.id,\n campaign.name,\n ad_group.id,\n ad_group.name,\n ad_group_criterion.keyword.match_type,\n ad_group_criterion.keyword.text,\n ad_group_criterion.criterion_id,\n ad_group_criterion.quality_info.creative_quality_score,\n ad_group_criterion.system_serving_status,\n ad_group_criterion.negative,\n ad_group_criterion.quality_info.quality_score,\n ad_group_criterion.quality_info.search_predicted_ctr,\n ad_group_criterion.quality_info.post_click_quality_score,\n metrics.historical_landing_page_quality_score,\n metrics.search_click_share,\n metrics.historical_creative_quality_score,\n metrics.clicks,\n metrics.impressions\nFROM keyword_view\nWHERE segments.date DURING LAST_7_DAYS\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.750Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":89,"estimatedTokens":592}}338{"id":"doc-testing_the_products_resource_content_api_for_sh-1a56860f","source":"documentation","title":"Testing the products resource | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/products/testing","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/merchantId/products\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/merchantId/products/productId\n```\n\nExample:\n```text\nDELETE https://shoppingcontent.googleapis.com/content/v2.1/merchantId/products/productId\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.750Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":16,"estimatedTokens":83}}339{"id":"doc-rich_product_data_for_product_description_pages_-efdd564a","source":"documentation","title":"Rich product data for product description pages | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/products/rich-product-data","text":"Example:\n```text\n\"channel\": \"online\",\n\"contentLanguage\": \"en\",\n\"targetCountry\": \"US\",\n\"feedLabel\": \"US\",\n\"offerId\": \"1111111111\"\n```\n\nExample:\n```text\n\"title\": \"Google Pixel 4 64GB Unlocked Smartphone 5.7' FHD Display 6GB RAM 4G Clear White\"\n```\n\nExample:\n```text\n\"description\": \"The Google phone. MotionSense, an evolved camera, and the new\nGoogle Assistant make Pixel 4 our most helpful phone yet. Studio-like photos.\nWithout the studio. Shoot without the flash. Capture rich detail and color, even\nin the dark, with the next generation of Night Sight. Capture the cosmos. The\ncamera that can take photos of the Milky Way.\"\n```\n\nExample:\n```text\n\"imageLink\": \"https://example.com/gallery/500/image1.jpg\",\n\"additionalImageLinks\": [\n \"https://example.com/gallery/500/image2.jpg\",\n \"https://example.com/gallery/500/image3.jpg\",\n \"https://example.com/gallery/500/image4.jpg\",\n \"https://example.com/gallery/500/image5.jpg\"\n ]\n```\n\nExample:\n```text\n\"itemGroupId\": \"pixels\"\n```\n\nExample:\n```text\n\"productHighlights\": [\n \"6GB RAM lets you enjoy multitasking conveniently\",\n \"Touch screen feature offers user friendly interface\",\n \"Its 16MP and 12MP rear cameras allow you capture high-quality pictures\"\n ]\n```\n\nExample:\n```text\n\"productDetails\": [\n {\n \"sectionName\": \"General\",\n \"attributeName\": \"Product Type\",\n \"attributeValue\": \"Smartphone\"\n },\n {\n \"sectionName\": \"Display\",\n \"attributeName\": \"Resolution\",\n \"attributeValue\": \"FHD Display 6GB RAM\"\n }\n ]\n```\n\nExample:\n```text\n\"gtin\": \"842776114952\",\n\"brand\": \"Google\",\n\"mpn\": \"GA01188-US\"\n```\n\nExample:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products\n```\n\nExample:\n```text\n{\n \"channel\": \"online\",\n \"contentLanguage\": \"en\",\n \"offerId\": \"pixel4\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"title\": \"Google Pixel 4 64GB Unlocked Smartphone 5.7' FHD Display 6GB RAM 4G Clear White\",\n \"description\": \"The Google phone. Motion Sense, an evolved camera, and the new Google Assistant make Pixel 4 our most helpful phone yet. Studio-like photos. Without the studio. Shoot without the flash. Capture rich detail and color, even in the dark, with the next generation of Night Sight. Capture the cosmos. The camera that can take photos of the Milky Way.\",\n \"imageLink\": \"https://example.com/gallery/500/image1.jpg\",\n \"additionalImageLinks\": [\n \"https://example.com/gallery/500/image2.jpg\",\n \"https://example.com/gallery/500/image3.jpg\",\n \"https://example.com/gallery/500/image4.jpg\",\n \"https://example.com/gallery/500/image5.jpg\"\n ],\n \"brand\": \"Google\",\n \"googleProductCategory\": \"Electronics > Communications > Telephony > Mobile Phones\",\n \"gtin\": \"842776114952\",\n \"mpn\": \"GA01188-US\",\n \"price\": {\n \"currency\": \"USD\",\n \"value\": \"549.99\"\n },\n \"salePrice\": {\n \"currency\": \"USD\",\n \"value\": \"549.99\"\n },\n \"productHighlights\": [\n \"6GB RAM lets you enjoy multitasking conveniently\",\n \"Touch screen feature offers user friendly interface\",\n \"Its 16MP and 12MP rear cameras allow you capture high-quality pictures\"\n ],\n \"productDetails\": [\n {\n \"sectionName\": \"General\",\n \"attributeName\": \"Product Type\",\n \"attributeValue\": \"Smartphone\"\n },\n {\n \"sectionName\": \"Display\",\n \"attributeName\": \"Resolution\",\n \"attributeValue\": \"FHD Display 6GB RAM\"\n }\n ],\n \"availability\": \"in stock\",\n \"condition\": \"new\",\n \"includedDestinations\": [\n \"Shopping Actions\"\n ],\n \"excludedDestinations\": [\n \"Shopping Ads\"\n ],\n \"sellOnGoogleQuantity\": 100,\n \"shippingLabel\": \"US_Test\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.751Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":136,"estimatedTokens":906}}340{"id":"doc-product_collections_content_api_for_shopping_goo-b0713e25","source":"documentation","title":"Product collections | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/product-collections","text":"Example:\n```text\nhttps://shoppingcontent.googleapis.com/content/v2.1/merchantId/collections\n```\n\nExample:\n```text\n{\n \"id\": \"exampleCollection\"\n \"language\": \"en\",\n \"productCountry\": \"UK\",\n \"imageLink\": [\"www.imageLink.example\"],\n \"featuredProduct\": [\n{\n \"offerId\": '432',\n \"x\": 0.11,\n \"y\": 0.99\n},\n{ \"offerId\": '433',\n \"x\": 0.53,\n \"y\": 0.89\n}\n],\n \"link\": \"www.link.example\",\n \"mobileLink\": \"www.mobileLink.example\",\n \"headline\": \"www.link.example\",\n \"customLabel0\": \"Organize\",\n \"customLabel1\": \"Your\",\n \"customLabel2\": \"Bidding/Reporting\",\n \"customLabel3\": \"With\",\n \"customLabel4\": \"Me\"\n}\n```\n\nExample:\n```text\nhttps://shoppingcontent.googleapis.com/content/v2.1/merchantID/collectionstatuses/collection ID\n```\n\nExample:\n```text\n{\n \"id\": \"exampleCollection\",\n \"creationDate\": \"2020-09-22T00:26:51Z\",\n \"lastUpdateDate\": \"2020-09-22T00:26:51Z\",\n \"collectionLevelIssues\": [\n {\n \"code\": \"invalid_url\",\n \"servability\": \"unaffected\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"link\",\n \"description\": \"Invalid URL [link]\",\n \"detail\": \"Use a complete URL that starts with http:// or https:// and\n links to a valid destination such as an image or a landing page\",\n \"documentation\": \"https://support.google.com/merchants/answer/7052112\"\n },\n {\n \"code\": \"invalid_url\",\n \"servability\": \"unaffected\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"imageLink\",\n \"description\": \"Invalid URL [imageLink]\",\n \"detail\": \"Use a complete URL that starts with http:// or https:// and\n links to a valid destination such as an image or a landing page\",\n \"documentation\": \"https://support.google.com/merchants/answer/7052112\"\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.752Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":71,"estimatedTokens":442}}341{"id":"doc-use_supplemental_feeds_with_the_content_api_cont-26a1471a","source":"documentation","title":"Use supplemental feeds with the Content API | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/products/supplemental-feeds/using-supplemental-feeds","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/merchantId/products?feedId=feedId\n```\n\nExample:\n```text\n{\n \"offerId\": \"1111111111\",\n \"contentLanguage\": \"en\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"availability\": \"out of stock\",\n}\n```\n\nExample:\n```text\nDELETE https://shoppingcontent.googleapis.com/content/v2.1/merchantId/products/productId?feedId=feedId\n```\n\nExample:\n```text\nhttps://shoppingcontent.googleapis.com/content/v2.1/products/batch\n```\n\nExample:\n```text\n{\n \"entries\": [\n {\n \"batchId\": 1111,\n \"merchantId\": 1234567,\n \"method\": \"insert\",\n \"feedId\": \"7654321\",\n \"product\": {\n \"offerId\": \"1111111111\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"price\": {\n \"value\": \"30.99\",\n \"currency\": \"USD\"\n }\n }\n },\n {\n \"batchId\": 1112,\n \"merchantId\": 1234567,\n \"method\": \"insert\",\n \"feedId\": \"7654321\",\n \"product\": {\n \"offerId\": \"2222222222\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"price\": {\n \"value\": \"33.99\",\n \"currency\": \"USD\"\n },\n },\n }\n}\n```\n\nExample:\n```text\n{\n \"entries\": [\n {\n \"batchId\": 1115,\n \"merchantId\": 1234567,\n \"method\": \"delete\",\n \"feedId\": \"7654321\",\n \"productId\": \"online:en:US:1111111111\"\n },\n {\n \"batchId\": 1116,\n \"merchantId\": 1234567,\n \"method\": \"delete\",\n \"feedId\": \"7654321\",\n \"productId\": \"online:en:US:2222222222\"\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.754Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":90,"estimatedTokens":421}}342{"id":"doc-product_statuses_content_api_for_shopping_google-ca5f6972","source":"documentation","title":"Product statuses | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/productstatuses","text":"Example:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/{merchantID}/productstatuses/{productId}?destinations=Shopping&fields=productId%2Ctitle\n```\n\nExample:\n```text\n{\n\"kind\": \"content#productStatus\",\n\"productId\": \"online:en:US:63\",\n\"title\": \"Third Product\",\n\"link\": \"http://examplemenc.com/\",\n\"destinationStatuses\": [\n {\n \"destination\": \"Shopping\",\n \"status\": \"disapproved\",\n \"disapprovedCountries\": [\n \"US\", \"UK\"\n ]\n },\n {\n \"destination\": \"ShoppingActions\",\n \"status\": \"disapproved\",\n \"disapprovedCountries\": [\n \"US\"\n ]\n },\n {\n \"destination\": \"SurfacesAcrossGoogle\",\n \"status\": \"disapproved\",\n \"disapprovedCountries\": [\n \"US\"\n ]\n }\n],\n\"itemLevelIssues\": [\n {\n \"code\": \"strong_id_inaccurate\",\n \"servability\": \"disapproved\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"mpn\",\n \"destination\": \"Shopping\",\n \"description\": \"Incorrect product identifier [mpn]\",\n \"detail\": \"Use the manufacturer's product identifiers (GTIN, brand, MPN)\",\n \"documentation\": \"https://support.google.com/merchants/answer/160161\",\n \"applicableCountries\": [\n \"US\", \"UK\"\n ]\n },\n {\n \"code\": \"image_link_internal_error\",\n \"servability\": \"disapproved\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"image link\",\n \"destination\": \"Shopping\",\n \"description\": \"Processing failed [image link]\",\n \"detail\": \"Wait for the product image to be crawled again (up to 3 days)\",\n \"documentation\": \"https://support.google.com/merchants/answer/6240184\",\n \"applicableCountries\": [\n \"US, UK\"\n ]\n },\n {\n \"code\": \"landing_page_error\",\n \"servability\": \"disapproved\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"link\",\n \"destination\": \"Shopping\",\n \"description\": \"Unavailable desktop landing page\",\n \"detail\": \"Update your website or landing page URL to enable access from desktop devices\",\n \"documentation\": \"https://support.google.com/merchants/answer/6098155\",\n \"applicableCountries\": [\n \"US\", \"UK\"\n ]\n },\n {\n \"code\": \"missing_condition_microdata\",\n \"servability\": \"unaffected\",\n \"resolution\": \"merchant_action\",\n \"destination\": \"Shopping\",\n \"description\": \"Missing or invalid data [condition]\",\n \"detail\": \"Add valid structured data markup to your landing page\",\n \"documentation\": \"https://support.google.com/merchants/answer/6183460\",\n \"applicableCountries\": [\n \"US\", \"UK\"\n ]\n },\n {\n \"code\": \"mobile_landing_page_error\",\n \"servability\": \"disapproved\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"link\",\n \"destination\": \"Shopping\",\n \"description\": \"Unavailable mobile landing page\",\n \"detail\": \"Update your website or landing page URL to enable access from mobile devices\",\n \"documentation\": \"https://support.google.com/merchants/answer/6098296\",\n \"applicableCountries\": [\n \"US\", \"UK\"\n ]\n }\n],\n\"creationDate\": \"2019-02-15T20:30:15Z\",\n\"lastUpdateDate\": \"2019-02-26T16:40:11Z\",\n\"googleExpirationDate\": \"2019-03-28T16:40:11Z\"\n}\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/{merchantID}/productstatuses?destinations=Shopping&maxResults=3&pageToken=5108b52782905aa9\n```\n\nExample:\n```text\n{\n\"kind\": \"content#productstatusesListResponse\",\n\"nextPageToken\": \"632fd090c95712c6\",\n\"resources\": [\n {\n \"kind\": \"content#productStatus\",\n \"productId\": \"online:en:US:online-en-US-GGL614\",\n \"title\": \"Green Headphones\",\n \"link\": \"https://example.com/green-headphones/\",\n \"destinationStatuses\": [\n {\n \"destination\": \"Shopping\",\n \"status\": \"disapproved\",\n \"disapprovedCountries\": [\n \"US\", \"UK\"\n ]\n },\n {\n \"destination\": \"ShoppingActions\",\n \"status\": \"disapproved\",\n \"disapprovedCountries\": [\n \"US\"\n ]\n },\n {\n \"destination\": \"SurfacesAcrossGoogle\",\n \"status\": \"disapproved\",\n \"disapprovedCountries\": [\n \"US\"\n ]\n }\n ],\n \"itemLevelIssues\": [\n {\n \"code\": \"mobile_landing_page_crawling_not_allowed\",\n \"servability\": \"disapproved\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"link\",\n \"destination\": \"Shopping\",\n \"description\": \"Mobile page not crawlable due to robots.txt\",\n \"detail\": \"Update your robots.txt file to allow user-agents \\\"Googlebot\\\" and \\\"Googlebot-Image\\\" to crawl your site\",\n \"documentation\": \"https://support.google.com/merchants/answer/6098296\",\n \"applicableCountries\": [\n \"US\"\n ]\n },\n {\n \"code\": \"pending_initial_policy_review\",\n \"servability\": \"disapproved\",\n \"resolution\": \"pending_processing\",\n \"destination\": \"Shopping\",\n \"description\": \"Pending initial review\",\n \"documentation\": \"https://support.google.com/merchants/answer/2948694\",\n \"applicableCountries\": [\n \"US, UK\"\n ]\n },\n {\n \"code\": \"ambiguous_gtin\",\n \"servability\": \"unaffected\",\n \"resolution\": \"merchant_action\",\n \"attributeName\": \"gtin\",\n \"destination\": \"Shopping\",\n \"description\": \"Ambiguous value [gtin]\",\n \"detail\": \"Use the full GTIN. Include leading zeroes, and use the full UPC, EAN, JAN, ISBN-13, or ITF-14.\",\n \"documentation\": \"https://support.google.com/merchants/answer/7000891\",\n \"applicableCountries\": [\n \"US\", \"UK\"\n ]\n }\n ],\n \"creationDate\": \"2020-01-09T15:36:39Z\",\n \"lastUpdateDate\": \"2020-01-14T19:17:02Z\",\n \"googleExpirationDate\": \"2020-02-13T19:17:02Z\"\n },\n {\n \"kind\": \"content#productStatus\",\n \"productId\": \"online:en:US:43\",\n \"title\": \"Green shirt\",\n \"link\": \"https://example.com/shirt-green/\",\n \"destinationStatuses\": [\n {\n \"destination\": \"ShoppingActions\",\n \"status\": \"approved\",\n \"approvedCountries\": [\n \"US\"\n ]\n },\n {\n \"destination\": \"SurfacesAcrossGoogle\",\n \"status\": \"approved\",\n \"approvedCountries\": [\n \"US\"\n ]\n }\n ],\n \"creationDate\": \"2019-01-29T21:14:36Z\",\n \"lastUpdateDate\": \"2019-02-21T18:47:44Z\",\n \"googleExpirationDate\": \"2019-03-23T18:47:44Z\"\n },\n {\n \"kind\": \"content#productStatus\",\n \"productId\": \"online:en:US:40\",\n \"title\": \"Black hat\",\n \"link\": \"https://example.com/hat-black/\",\n \"destinationStatuses\": [\n {\n \"destination\": \"SurfacesAcrossGoogle\",\n \"status\": \"approved\",\n \"approvedCountries\": [\n \"US\"\n ]\n }\n ],\n \"creationDate\": \"2019-01-29T21:14:36Z\",\n \"lastUpdateDate\": \"2019-02-21T18:47:44Z\",\n \"googleExpirationDate\": \"2019-03-23T18:47:44Z\"\n }\n]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.754Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":237,"estimatedTokens":1637}}343{"id":"doc-products_resource_calls_content_api_for_shopping-02693b71","source":"documentation","title":"products resource calls | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/products/products-api","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products\n```\n\nExample:\n```text\n{\n \"kind\": \"content#product\",\n \"offerId\": \"1111111111\",\n \"title\": \"Google Tee Black\",\n \"description\": \"The Black Google Tee is available in unisex sizing.\",\n \"link\": \"http://my.site.com/blacktee/\",\n \"imageLink\": \"https://shop.example.com/.../images/GGOEGXXX1100.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"ageGroup\": \"adult\",\n \"availability\": \"in stock\",\n \"availabilityDate\": \"2019-01-25T13:00:00-08:00\",\n \"brand\": \"Google\",\n \"color\": \"black\",\n \"condition\": \"new\",\n \"gender\": \"male\",\n \"googleProductCategory\": \"1604\",\n \"gtin\": \"608802531656\",\n \"itemGroupId\": \"google_tee\",\n \"mpn\": \"608802531656\",\n \"price\": {\n \"value\": \"21.99\",\n \"currency\": \"USD\"\n },\n \"sizes\": [\n \"Large\"\n ]\n}\n```\n\nExample:\n```text\n\"customAttributes\": [\n {\n \"name\": \"purchase_quantity_limit\",\n \"value\": \"4\"\n }\n]\n```\n\nExample:\n```text\n{\n \"kind\": \"content#product\",\n \"id\": \"online:en:US:1111111111\",\n \"offerId\": \"1111111111\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\"\n}\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products/{productId}\n```\n\nExample:\n```text\n{\n \"kind\": \"content#product\",\n \"id\": \"online:en:US:1111111111\",\n \"offerId\": \"1111111111\",\n \"source\": \"api\",\n \"title\": \"Google Tee Black\",\n \"description\": \"The Black Google Tee is available in unisex sizing.\",\n \"link\": \"http://my.site.com/blacktee/\",\n \"imageLink\": \"https://shop.example.com/.../images/GGOEGXXX1100.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"ageGroup\": \"adult\",\n \"availability\": \"in stock\",\n \"availabilityDate\": \"2019-01-25T13:00:00-08:00\",\n \"brand\": \"Google\",\n \"color\": \"black\",\n \"condition\": \"new\",\n \"gender\": \"male\",\n \"googleProductCategory\": \"1604\",\n \"gtin\": \"608802531656\",\n \"itemGroupId\": \"google_tee\",\n \"mpn\": \"608802531656\",\n \"price\": {\n \"value\": \"21.99\",\n \"currency\": \"USD\"\n },\n \"sizes\": [\n \"Large\"\n ]\n}\n```\n\nExample:\n```text\nPATCH https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products/{productId}\n```\n\nExample:\n```text\n{\n \"title\": \"Google Tee Black Limited Edition\",\n \"description\": \"The Limited Edition Tee is available in unisex sizing and features a retail fit.\"\n}\n```\n\nExample:\n```text\n{\n \"salePrice\": {\n \"value\": \"17.99\",\n \"currency\": \"USD\"\n }\n}\n```\n\nExample:\n```text\nPATCH https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products/{productId}?updateMask=description,availability\n```\n\nExample:\n```text\n{\n \"title\": \"Google Tee Black\",\n \"description\": \"This Limited Edition is out of print.\",\n \"availability\": \"out of stock\"\n}\n```\n\nExample:\n```text\nPATCH https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products/{productId}?updateMask=salePrice\n```\n\nExample:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/products/batch\n```\n\nExample:\n```text\n{\n \"entries\": [{\n \"batchId\": 1,\n \"merchantId\": \"MERCHANT_ID\",\n \"productId\": \"online:en:US:1111111111\",\n \"method\": \"update\",\n \"product\": {\n \"title\": \"Google Tee Black\",\n \"description\": \"The Black Google Tee is available in unisex sizing.\",\n \"availability\": \"in stock\",\n \"price\": {\n \"value\": \"19.99\",\n \"currency\": \"USD\"\n }\n },\n \"updateMask\": \"availability,price\"\n }]\n}\n```\n\nExample:\n```text\nDELETE https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products/{productId}\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/{merchantId}/products\n```\n\nExample:\n```text\n{\n \"kind\": \"content#productsListResponse\",\n \"resources\": [\n {\n \"kind\": \"content#product\",\n \"id\": \"online:en:US:1111111111\",\n \"offerId\": \"1111111111\",\n \"source\": \"api\",\n \"title\": \"Google Tee Black\",\n \"description\": \"The Black Google Tee is available in unisex sizing.\",\n \"link\": \"http://my.site.com/blacktee/\",\n \"imageLink\": \"https://shop.example.com/.../images/GGOEGXXX1100.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"ageGroup\": \"adult\",\n \"availability\": \"in stock\",\n \"availabilityDate\": \"2019-01-25T13:00:00-08:00\",\n \"brand\": \"Google\",\n \"color\": \"black\",\n \"condition\": \"new\",\n \"gender\": \"male\",\n \"googleProductCategory\": \"1604\",\n \"gtin\": \"608802531656\",\n \"itemGroupId\": \"google_tee\",\n \"mpn\": \"608802531656\",\n \"price\": {\n \"value\": \"21.99\",\n \"currency\": \"USD\"\n },\n \"sizes\": [\n \"Large\"\n ]\n },\n {\n \"kind\": \"content#product\",\n \"id\": \"online:en:US:2222222222\",\n \"offerId\": \"2222222222\",\n \"source\": \"api\",\n \"title\": \"Google Tee Green\",\n \"description\": \"100% cotton jersey fabric sets this Google t-shirt above the crowd.\n Features the google logo across the chest. Unisex sizing.\",\n \"link\": \"http://my.site.com/greentee/\",\n \"imageLink\": \"https://shop.example.com/.../images/GGOEGXXX0906.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"ageGroup\": \"adult\",\n \"availability\": \"in stock\",\n \"availabilityDate\": \"2019-01-25T13:00:00-08:00\",\n \"brand\": \"Google\",\n \"color\": \"green\",\n \"condition\": \"new\",\n \"gender\": \"male\",\n \"googleProductCategory\": \"1604\",\n \"gtin\": \"608802531649\",\n \"itemGroupId\": \"google_tee\",\n \"mpn\": \"608802531649\",\n \"price\": {\n \"value\": \"21.99\",\n \"currency\": \"USD\"\n },\n \"sizes\": [\n \"Medium\"\n ]\n },\n {\n \"kind\": \"content#product\",\n \"id\": \"online:en:US:3333333333\",\n \"offerId\": \"3333333333\",\n \"source\": \"api\",\n \"title\": \"Google Twill Cap\",\n \"description\": \"Classic urban styling distinguishes this Google cap.\n Retains its shape, even when not being worn.\",\n \"link\": \"http://my.site.com/blackhat/\",\n \"imageLink\": \"https://shop.example.com/.../images/GGOEGHPB071610.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"ageGroup\": \"adult\",\n \"availability\": \"in stock\",\n \"availabilityDate\": \"2019-01-07T13:00:00-08:00\",\n \"brand\": \"Google\",\n \"color\": \"black\",\n \"condition\": \"new\",\n \"gender\": \"male\",\n \"googleProductCategory\": \"173\",\n \"gtin\": \"689355417246\",\n \"mpn\": \"689355417246\",\n \"price\": {\n \"value\": \"10.99\",\n \"currency\": \"USD\"\n },\n \"sizes\": [\n \"Medium\"\n ]\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.755Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":17,"totalLines":289,"estimatedTokens":1602}}344{"id":"doc-batch_mode_content_api_for_shopping_google_for_d-29331542","source":"documentation","title":"Batch mode | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/products/batch-mode","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/products/batch\n```\n\nExample:\n```text\n{\n \"entries\": [\n {\n \"batchId\": 1111,\n \"merchantId\": 1234567,\n \"method\": \"insert\",\n \"product\": {\n \"kind\": \"content#product\",\n \"offerId\": \"1111111111\",\n\n \"title\": \"Google Tee Black\",\n \"description\": \"The Black Google Tee is available in unisex sizing and\n features a retail fit.\",\n \"link\": \"http://my.site.com/blacktee/\",\n \"imageLink\": \"https://shop.example.com/.../images/GGOEGXXX1100.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"ageGroup\": \"adult\",\n \"availability\": \"in stock\",\n \"availabilityDate\": \"2019-01-25T13:00:00-08:00\",\n \"brand\": \"Google\",\n \"color\": \"black\",\n \"condition\": \"new\",\n \"gender\": \"male\",\n \"googleProductCategory\": \"1604\",\n \"gtin\": \"608802531656\",\n \"itemGroupId\": \"google_tee\",\n \"mpn\": \"608802531656\",\n \"price\": {\n \"value\": \"21.99\",\n \"currency\": \"USD\"\n },\n \"sizes\": [\n \"Large\"\n ],\n \"includedDestination\": [\n \"Shopping\"\n ]\n }\n },\n {\n \"batchId\": 1112,\n \"merchantId\": 1234567,\n \"method\": \"insert\",\n \"product\": {\n \"kind\": \"content#product\",\n \"offerId\": \"2222222222\",\n\n \"title\": \"Google Tee Green\",\n \"description\": \"100% cotton jersey fabric sets this Google t-shirt above\n the crowd. Features the google logo across the chest. Unisex sizing.\",\n \"link\": \"http://my.site.com/greentee/\",\n \"imageLink\": \"https://shop.example.com/.../images/GGOEGXXX0906.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"ageGroup\": \"adult\",\n \"availability\": \"in stock\",\n \"availabilityDate\": \"2019-01-25T13:00:00-08:00\",\n \"brand\": \"Google\",\n \"color\": \"green\",\n \"condition\": \"new\",\n \"gender\": \"male\",\n \"googleProductCategory\": \"1604\",\n \"gtin\": \"608802531649\",\n \"itemGroupId\": \"google_tee\",\n \"mpn\": \"608802531649\",\n \"price\": {\n \"value\": \"21.99\",\n \"currency\": \"USD\"\n },\n \"sizes\": [\n \"Medium\"\n ],\n \"includedDestination\": [\n \"Shopping\"\n ]\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"kind\": \"content#productsCustomBatchResponse\",\n \"entries\": [\n {\n \"kind\": \"content#productsCustomBatchResponseEntry\",\n \"batchId\": 1111,\n \"product\": {\n \"kind\": \"content#product\",\n \"id\": \"online:en:US:1111111111\",\n \"offerId\": \"1111111111\",\n \"title\": \"Google Tee Black\",\n \"description\": \"The Black Google Tee is available in unisex sizing and\n features a retail fit.\",\n \"link\": \"http://my.site.com/blacktee/\",\n \"imageLink\": \"https://shop.example.com/.../images/GGOEGXXX1100.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"ageGroup\": \"adult\",\n \"availability\": \"in stock\",\n \"availabilityDate\": \"2019-01-25T13:00:00-08:00\",\n \"brand\": \"Google\",\n \"color\": \"black\",\n \"condition\": \"new\",\n \"gender\": \"male\",\n \"googleProductCategory\": \"1604\",\n \"gtin\": \"608802531656\",\n \"itemGroupId\": \"google_tee\",\n \"mpn\": \"608802531656\",\n \"price\": {\n \"value\": \"21.99\",\n \"currency\": \"USD\"\n },\n \"sizes\": [\n \"Large\"\n ],\n \"includedDestination\": [\n \"Shopping\"\n ]\n }\n },\n {\n \"kind\": \"content#productsCustomBatchResponseEntry\",\n \"batchId\": 1112,\n \"product\": {\n \"kind\": \"content#product\",\n \"id\": \"online:en:US:2222222222\",\n \"offerId\": \"2222222222\",\n \"title\": \"Google Tee Green\",\n \"description\": \"100% cotton jersey fabric sets this Google t-shirt above\n the crowd. Features the google logo across the chest. Unisex sizing.\",\n \"link\": \"http://my.site.com/greentee/\",\n \"imageLink\": \"https://shop.example.com/.../images/GGOEGXXX0906.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"ageGroup\": \"adult\",\n \"availability\": \"in stock\",\n \"availabilityDate\": \"2019-01-25T13:00:00-08:00\",\n \"brand\": \"Google\",\n \"color\": \"green\",\n \"condition\": \"new\",\n \"gender\": \"male\",\n \"googleProductCategory\": \"1604\",\n \"gtin\": \"608802531649\",\n \"itemGroupId\": \"google_tee\",\n \"mpn\": \"608802531649\",\n \"price\": {\n \"value\": \"21.99\",\n \"currency\": \"USD\"\n },\n \"sizes\": [\n \"Medium\"\n ],\n \"includedDestination\": [\n \"Shopping\"\n ]\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"entries\": [\n {\n \"batchId\": 1113,\n \"merchantId\": 1234567,\n \"method\": \"get\",\n \"productId\": \"online:en:US:1111111111\"\n },\n {\n \"batchId\": 1114,\n \"merchantId\": 1234567,\n \"method\": \"get\",\n \"productId\": \"online:en:US:2222222222\"\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"kind\": \"content#productsCustomBatchResponse\",\n \"entries\": [\n {\n \"kind\": \"content#productsCustomBatchResponseEntry\",\n \"batchId\": 1113,\n \"product\": {\n \"kind\": \"content#product\",\n \"id\": \"online:en:US:1111111111\",\n \"offerId\": \"1111111111\",\n \"title\": \"Google Tee Black\",\n \"description\": \"The Black Google Tee is available in unisex sizing and features a retail fit.\",\n \"link\": \"http://my.site.com/blacktee/\",\n \"imageLink\": \"https://shop.example.com/.../images/GGOEGXXX1100.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"ageGroup\": \"adult\",\n \"availability\": \"in stock\",\n \"availabilityDate\": \"2019-01-25T13:00:00-08:00\",\n \"brand\": \"Google\",\n \"color\": \"black\",\n \"condition\": \"new\",\n \"gender\": \"male\",\n \"googleProductCategory\": \"1604\",\n \"gtin\": \"608802531656\",\n \"itemGroupId\": \"google_tee\",\n \"mpn\": \"608802531656\",\n \"price\": {\n \"value\": \"21.99\",\n \"currency\": \"USD\"\n },\n \"sizes\": [\n \"Large\"\n ],\n \"includedDestination\": [\n \"Shopping\"\n ]\n }\n },\n {\n \"kind\": \"content#productsCustomBatchResponseEntry\",\n \"batchId\": 1114,\n \"product\": {\n \"kind\": \"content#product\",\n \"id\": \"online:en:US:2222222222\",\n \"offerId\": \"2222222222\",\n \"title\": \"Google Tee Green\",\n \"description\": \"100% cotton jersey fabric sets this Google t-shirt above the crowd.\n Features the google logo across the chest. Unisex sizing.\",\n \"link\": \"http://my.site.com/greentee/\",\n \"imageLink\": \"https://shop.example.com/.../images/GGOEGXXX0906.jpg\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"US\",\n \"feedLabel\": \"US\",\n \"channel\": \"online\",\n \"ageGroup\": \"adult\",\n \"availability\": \"in stock\",\n \"availabilityDate\": \"2019-01-25T13:00:00-08:00\",\n \"brand\": \"Google\",\n \"color\": \"green\",\n \"condition\": \"new\",\n \"gender\": \"male\",\n \"googleProductCategory\": \"1604\",\n \"gtin\": \"608802531649\",\n \"itemGroupId\": \"google_tee\",\n \"mpn\": \"608802531649\",\n \"price\": {\n \"value\": \"21.99\",\n \"currency\": \"USD\"\n },\n \"sizes\": [\n \"Medium\"\n ],\n \"includedDestination\": [\n \"Shopping\"\n ]\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"entries\": [\n {\n \"batchId\": 1115,\n \"merchantId\": 1234567,\n \"method\": \"delete\",\n \"productId\": \"online:en:US:1111111111\"\n },\n {\n \"batchId\": 1116,\n \"merchantId\": 1234567,\n \"method\": \"delete\",\n \"productId\": \"online:en:US:2222222222\"\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"kind\": \"content#productsCustomBatchResponse\",\n \"entries\": [\n {\n \"kind\": \"content#productsCustomBatchResponseEntry\",\n \"batchId\": 1115\n },\n {\n \"kind\": \"content#productsCustomBatchResponseEntry\",\n \"batchId\": 1116\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.756Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":324,"estimatedTokens":1955}}345{"id":"doc-target_multiple_countries_content_api_for_shoppi-96eda13d","source":"documentation","title":"Target multiple countries | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/targeting-ads-in-multiple-countries","text":"Example:\n```text\n{\n \"offerId\": \"1111111111\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"DE\",\n \"feedLabel\": \"DE\",\n \"channel\": \"online\",\n\n...\n\n \"shoppingAdsExcludedCountries\": [‘ES’, ‘BE’],\n}\n```\n\nExample:\n```text\n{\n \"offerId\": \"2222222222\",\n \"contentLanguage\": \"en\",\n \"targetCountry\": \"DE\",\n \"feedLabel\": \"DE\",\n \"channel\": \"online\",\n\n...\n\n \"shipping\": [\n { \"country\": \"FR\" },\n { \"country\": \"IT\" }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.756Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":34,"estimatedTokens":106}}346{"id":"doc-getting_started_credential_sharing_google_for_de-b7f6bdc3","source":"documentation","title":"Getting Started | Credential Sharing | Google for Developers","url":"https://developers.google.com/identity/credential-sharing/digital-asset-links","text":"Example:\n```text\n[{\n \"relation\": [\"delegate_permission/common.handle_all_urls\"],\n \"target\" : { \"namespace\": \"android_app\", \"package_name\": \"com.example.app\",\n \"sha256_cert_fingerprints\": [\"hash_of_app_certificate\"] }\n }]\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.757Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":66}}347{"id":"doc-set_up_seamless_credential_sharing_across_androi-e4ed7739","source":"documentation","title":"Set up seamless credential sharing across Android apps and websites | Credential Sharing | Google for Developers","url":"https://developers.google.com/identity/credential-sharing/set-up","text":"Example:\n```text\n[\n {\n \"relation\":[\n \"delegate_permission/common.get_login_creds\"\n ],\n \"target\":{\n \"namespace\":\"web\",\n \"site\":URL\n }\n },\n {\n \"relation\":[\n \"delegate_permission/common.get_login_creds\"\n ],\n \"target\":{\n \"namespace\":\"android_app\",\n \"package_name\":\"APP_ID\",\n \"sha256_cert_fingerprints\":[\n \"SHA_HEX_VALUE\"\n ]\n }\n }\n]\n```\n\nExample:\n```text\n<string name=\"asset_statements\" translatable=\"false\">\n[{\n \\\"include\\\": \\\"https://DOMAIN[:OPTIONAL_PORT]/.well-known/assetlinks.json\\\"\n}]\n</string>\n```\n\nExample:\n```text\n<meta-data android:name=\"asset_statements\" android:resource=\"@string/asset_statements\"/>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.758Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":42,"estimatedTokens":176}}348{"id":"doc-link_your_business_profile_content_api_for_shopp-1a576672","source":"documentation","title":"Link your Business Profile | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/how-tos/lia/link-gmb","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/12345/liasettings/67890/requestgmbaccess?gmbEmail=admin@example.com\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/12345/liasettings/67890/accessiblegmbaccounts\n\nResponse:\n200 OK\n{\n \"kind\": \"content#liasettingsGetAccessibleGmbAccountsResponse\",\n \"accountId\": 67890,\n \"gmbAccounts\": [{\n \"type\" : \"user\",\n \"email\" : \"admin@example.com\",\n \"name\" : \"admin@example.com\",\n \"listingCount\": 82\n },\n {\n \"type\" : \"business\",\n \"email\" : \"california@example.com\",\n \"name\" : \"Golden-State\",\n \"listingCount\" : 20\n },\n {\n \"type\" : \"business\",\n \"email\" : \"florida@example.com\",\n \"name\" : \"Sunshine-State\",\n \"listingCount\" : 15\n },\n {\n \"type\" : \"business\",\n \"email\" : \"newyork@example.com\",\n \"name\" : \"Empire-State\",\n \"listingCount\" : 25\n }]\n}\n```\n\nExample:\n```text\nPUT https://shoppingcontent.googleapis.com/content/v2/12345/accounts/67890\n{\n \"googleMyBusinessLink\" : {\n \"gmbEmail\": \"california@example.com\"\n }\n}\n\nResponse:\n200 OK\n{\n \"kind\": \"content#account\",\n \"id\": 67890,\n \"googleMyBusinessLink\" : {\n \"gmbEmail\": \"california@example.com\",\n \"status\" : \"active\"\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.760Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":63,"estimatedTokens":312}}349{"id":"doc-configure_target_countries_content_api_for_shopp-4a5027f2","source":"documentation","title":"Configure target countries | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/how-tos/lia/configure-targets","text":"Example:\n```text\nPUT https://shoppingcontent.googleapis.com/content/v2.1/12345/liasettings/67890\n{\n \"accountId\" : 67890,\n \"countrySettings\" : [{\n \"country\" : \"US\"\n }]\n}\n```\n\nExample:\n```text\nPUT https://shoppingcontent.googleapis.com/content/v2.1/12345/liasettings/67890\n{\n \"accountId\" : 67890,\n \"countrySettings\" : [{\n \"country\" : \"US\"\n },\n {\n \"country\" : \"DE\",\n \"about\" : {\n \"url\" : \"https://www.example.com/de/about\"\n }\n }]\n}\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/12345/liasettings/67890\n\nResponse:\n200 OK\n{\n \"kind\": \"content#liaSettings\",\n \"accountId\" : 67890,\n \"countrySettings\" : [{\n \"country\" : \"US\"\n },\n {\n \"country\" : \"DE\",\n \"about\" : {\n \"url\" : \"https://www.example.com/de/about\",\n \"status\" : \"pending\"\n }\n }]\n}\n```\n\nExample:\n```text\nPUT https://shoppingcontent.googleapis.com/content/v2.1/12345/liasettings/67890\n{\n \"accountId\" : 67890,\n \"countrySettings\" : [{\n \"country\" : \"US\",\n \"onDisplayToOrder\": {\n \"shippingCostPolicyUrl\" : \"https://www.example.com/inStoreOrderPolicy\"\n }\n }]\n}\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/12345/liasettings/67890\n\nResponse:\n200 OK\n{\n \"kind\": \"content#liaSettings\",\n \"accountId\" : 67890,\n \"countrySettings\" : [{\n \"country\" : \"US\",\n \"onDisplayToOrder\": {\n \"shippingCostPolicyUrl\" : \"https://www.example.com/inStoreOrderPolicy\",\n \"status\" : \"pending\"\n }\n }]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.760Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":84,"estimatedTokens":374}}350{"id":"doc-regions_content_api_for_shopping_google_for_deve-49d6d39c","source":"documentation","title":"Regions | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/regions","text":"Example:\n```text\n// Copyright 2023 Google LLC\n//\n// 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.\n\npackage shopping.content.v2_1.samples.regions;\n\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.services.content.model.Region;\nimport com.google.api.services.content.model.RegionPostalCodeArea;\nimport com.google.api.services.content.model.RegionPostalCodeAreaPostalCodeRange;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport shopping.content.v2_1.samples.ContentSample;\n\n/**\n * Creates a region. The region created here can be used with the regional inventory service.\n * Regional availability and pricing lets you provide product availability and variable pricing\n * based on your business presence and the location of your customer base. Regional availability and\n * pricing is available for products advertised through Shopping ads on Google Search, and listed in\n * free listings on the Shopping tab.\n */\npublic class RegionCreateSample extends ContentSample {\n public RegionCreateSample(String[] args) throws IOException {\n super(args);\n }\n\n @Override\n public void execute() throws IOException {\n checkNonMCA();\n\n // Creates a List of Postal Code Area Postal Code Ranges.\n // This allows you to flexibly define regions as combinations of postal code\n // ranges. Each postal code range in the list has its own start and end zip code.\n List<RegionPostalCodeAreaPostalCodeRange> postalCodeRanges =\n new ArrayList<RegionPostalCodeAreaPostalCodeRange>();\n\n // Creates a new postal code range from two postal code values.\n // This range is equivalent to all postal codes in the USA state of New York (00501 - 14925)\n RegionPostalCodeAreaPostalCodeRange postalCodeRange =\n new RegionPostalCodeAreaPostalCodeRange().setBegin(\"00501\").setEnd(\"14925\");\n\n // Adds the NY State postal code range into the list of postal code ranges that a postal\n // code area accepts.\n postalCodeRanges.add(postalCodeRange);\n\n // Creates Postal Code Area for the Region that will be inserted, using the NY State postal code\n // ranges, and the US CLDR territory/country code that the postal code ranges applies to.\n RegionPostalCodeArea postalCodeArea =\n new RegionPostalCodeArea().setPostalCodes(postalCodeRanges).setRegionCode(\"US\");\n\n // Creates a region with example values for displayName and postalCodeArea\n Region region = new Region().setDisplayName(\"NYState\").setPostalCodeArea(postalCodeArea);\n\n // Tries to create the region, and catches any exceptions\n try {\n System.out.println(\"Creating region\");\n Region result =\n content\n .regions()\n .create(this.config.getMerchantId().longValue(), region)\n .setRegionId(\"12345678\") // User-defined, numeric, minimum of 6 digits\n .execute();\n System.out.println(\"Listing succesfully created region\");\n System.out.println(result);\n } catch (GoogleJsonResponseException e) {\n checkGoogleJsonResponseException(e);\n }\n }\n\n public static void main(String[] args) throws IOException {\n new RegionCreateSample(args).execute();\n }\n}\n```\n\nExample:\n```text\nhttps://shoppingcontent.googleapis.com/content/v2.1/merchantId/regions?regionId=456789\n```\n\nExample:\n```text\n{\n postalCodeArea: {\n regionCode: \"US\",\n postalCodes: [\n {\n begin: \"850*\",\n end: \"860*\"\n }\n ]\n }\n}\n```\n\nExample:\n```text\nhttps://shoppingcontent.googleapis.com/content/v2.1/merchantId/regions?regionId=123456\n```\n\nExample:\n```text\n{\n geoTargetAreas: {\n geotargetCriteriaId: [20106, 20102, 20101] //Sao Paulo, Rio de Janeiro, Parana\n }\n}\n```\n\nExample:\n```text\nhttps://shoppingcontent.googleapis.com/content/v2.1/merchantId/products/{productId}/regionalinventory\n```\n\nExample:\n```text\n{\n 'regionId': \"456789\"\n 'price': {\n value: '10'\n currency: 'USD'\n },\n 'availability': 'in stock'\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.761Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":140,"estimatedTokens":1126}}351{"id":"doc-order_tracking_signals_content_api_for_shopping_-33aa2e8b","source":"documentation","title":"Order tracking signals | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/order-tracking-signals","text":"Example:\n```text\nhttps://shoppingcontent.googleapis.com/content/v2.1/merchantId/ordertrackingsignals\n```\n\nExample:\n```text\n{\n \"merchantId\": \"987654321\",\n \"orderCreatedTime\": {\n \"year\": 2020,\n \"month\": 1,\n \"day\": 2,\n \"hours\": 0,\n \"minutes\": 0,\n \"seconds\": 0,\n \"timeZone\": {\n \"id\": \"America/Los_Angeles\"\n }\n },\n \"orderId\": \"123456789\",\n \"shippingInfo\": [\n {\n \"shipmentId\": \"1\",\n \"trackingId\": \"100\",\n \"carrierName\": \"FEDEX\",\n \"carrierServiceName\": \"GROUND\",\n \"shippedTime\": {\n \"year\": 2020,\n \"month\": 1,\n \"day\": 3,\n \"hours\": 0,\n \"minutes\": 0,\n \"seconds\": 0,\n \"timeZone\": {\n \"id\": \"America/Los_Angeles\"\n }\n },\n \"shippingStatus\": \"DELIVERED\"\n },\n {\n \"shipmentId\": \"2\",\n \"earliestDeliveryPromiseTime\": {\n \"year\": 2020,\n \"month\": 1,\n \"day\": 4,\n \"hours\": 0,\n \"minutes\": 0,\n \"seconds\": 0,\n \"timeZone\": {\n \"id\": \"America/Los_Angeles\"\n }\n },\n \"latestDeliveryPromiseTime\": {\n \"year\": 2020,\n \"month\": 1,\n \"day\": 5,\n \"hours\": 0,\n \"minutes\": 0,\n \"seconds\": 0,\n \"timeZone\": {\n \"id\": \"America/Los_Angeles\"\n }\n },\n \"actualDeliveryTime\": {\n \"year\": 2020,\n \"month\": 1,\n \"day\": 5,\n \"hours\": 0,\n \"minutes\": 0,\n \"seconds\": 0,\n \"timeZone\": {\n \"id\": \"America/Los_Angeles\"\n }\n },\n \"shippedTime\": {\n \"year\": 2020,\n \"month\": 1,\n \"day\": 3,\n \"hours\": 0,\n \"minutes\": 0,\n \"seconds\": 0,\n \"timeZone\": {\n \"id\": \"America/Los_Angeles\"\n }\n },\n \"shippingStatus\": \"DELIVERED\"\n }\n ],\n \"lineItems\": [\n {\n \"lineItemId\": \"item1\",\n \"productId\": \"online:en:US:item1\",\n \"quantity\": \"3\"\n },\n {\n \"lineItemId\": \"item2\",\n \"productId\": \"online:en:US:item2\",\n \"quantity\": \"5\"\n }\n ],\n \"shipmentLineItemMapping\": [\n {\n \"shipmentId\": \"1\",\n \"lineItemId\": \"item1\",\n \"quantity\": \"1\"\n },\n {\n \"shipmentId\": \"2\",\n \"lineItemId\": \"item1\",\n \"quantity\": \"2\"\n },\n {\n \"shipmentId\": \"1\",\n \"lineItemId\": \"item2\",\n \"quantity\": \"4\"\n },\n {\n \"shipmentId\": \"2\",\n \"lineItemId\": \"item2\",\n \"quantity\": \"1\"\n }\n ],\n \"customerShippingFee\": {\n \"value\": \"4.5\",\n \"currency\": \"USD\"\n },\n \"deliveryPostalCode\": \"94043\",\n \"deliveryRegionCode\": \"US\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.761Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":133,"estimatedTokens":645}}352{"id":"doc-verify_your_inventory_content_api_for_shopping_g-b30143ba","source":"documentation","title":"Verify your inventory | Content API for Shopping | Google for Developers","url":"https://developers.google.com/shopping-content/guides/how-tos/lia/verify-inventory","text":"Example:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/12345/liasettings/67890/setinventoryverificationcontact?contactEmail=invcheck@example.com&contactName=Inventory%20Manager&country=US&language=en\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/12345/liasettings/67890\n\nResponse:\n200 OK\n{\n \"kind\": \"content#liaSettings\",\n \"accountId\" : 67890,\n \"countrySettings\" : [{\n \"country\" : \"US\",\n \"inventory\" : {\n \"inventoryVerificationContactName\" : \"Inventory Manager\",\n \"inventoryVerificationContactEmail\" : \"invcheck@example.com\",\n \"inventoryVerificationContactStatus\" : \"pending\"\n }\n }]\n}\n```\n\nExample:\n```text\nPOST https://shoppingcontent.googleapis.com/content/v2.1/12345/liasettings/67890/requestinventoryverification/US\n```\n\nExample:\n```text\nGET https://shoppingcontent.googleapis.com/content/v2.1/12345/liasettings/67890\n\nResponse:\n200 OK\n{\n \"kind\": \"content#liaSettings\",\n \"accountId\" : 67890,\n \"countrySettings\" : [{\n \"country\" : \"US\",\n \"inventory\": {\n \"inventoryVerificationContactName\" : \"Inventory Manager\",\n \"inventoryVerificationContactEmail\" : \"invcheck@example.com\",\n \"inventoryVerificationContactStatus\" : \"active\",\n \"status\" : \"pending\"\n }\n }]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.762Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":52,"estimatedTokens":322}}353{"id":"doc-example_seamless_credential_sharing_across_multi-957cde03","source":"documentation","title":"Example: Seamless credential sharing across multiple websites | Samples | Google for Developers","url":"https://developers.google.com/identity/credential-sharing/example-multiple-websites","text":"Example:\n```text\n[\n {\n \"relation\": [\"delegate_permission/common.get_login_creds\"],\n \"target\": {\n \"namespace\": \"web\",\n \"site\": \"https://example.com\"\n }\n },\n {\n \"relation\": [\"delegate_permission/common.get_login_creds\"],\n \"target\": {\n \"namespace\": \"web\",\n \"site\": \"https://example.org\"\n }\n },\n {\n \"relation\": [\"delegate_permission/common.get_login_creds\"],\n \"target\": {\n \"namespace\": \"web\",\n \"site\": \"https://example.net\"\n }\n },\n {\n \"relation\": [\"delegate_permission/common.get_login_creds\"],\n \"target\": {\n \"namespace\": \"web\",\n \"site\": \"https://myownpersonaldomain.com\"\n }\n }\n ]\n```\n\nExample:\n```text\n[\n {\n \"relation\": [\"delegate_permission/common.get_login_creds\"],\n \"target\": {\n \"namespace\": \"web\",\n \"site\": \"https://example.com\"\n }\n }\n ]\n```\n\nExample:\n```text\n...\n {\n \"relation\": [\"delegate_permission/common.get_login_creds\"],\n \"target\": {\n \"namespace\": \"web\",\n \"site\": https://NEW_DOMAIN\n }\n }\n ...\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.762Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":61,"estimatedTokens":280}}354{"id":"doc-test_ad_units_ios_google_for_developers-dfe4ed8f","source":"documentation","title":"Test ad units | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/ad-inspector/test-ad-units","text":"Example:\n```text\nAd Unit has no applicable adapter for single ad source testing on network: AD_SOURCE_ADAPTER_CLASS_NAME\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.763Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":35}}355{"id":"doc-ad_load_errors_ios_google_for_developers-7bc3cee2","source":"documentation","title":"Ad load errors | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/ad-load-errors","text":"Example:\n```text\nfunc bannerView(_ bannerView: BannerView, didFailToReceiveAdWithError error: Error)\n```\n\nExample:\n```text\n- (void)bannerView:(nonnull GADBannerView *)bannerView\n didFailToReceiveAdWithError:(nonnull NSError *)error;\n```\n\nExample:\n```text\nfunc bannerView(_ bannerView: BannerView, didFailToReceiveAdWithError error: Error) {\n // Gets the domain from which the error came.\n let errorDomain = error.domain\n // Gets the error code. See\n // https://developers.google.com/admob/ios/api/reference/Enums/GADErrorCode\n // for a list of possible codes.\n let errorCode = error.code\n // Gets an error message.\n // For example \"Account not approved yet\". See\n // https://support.google.com/admob/answer/9905175 for explanations of\n // common errors.\n let errorMessage = error.localizedDescription\n // Gets additional response information about the request. See\n // https://developers.google.com/admob/ios/response-info for more information.\n let responseInfo = (error as NSError).userInfo[GADErrorUserInfoKeyResponseInfo] as? ResponseInfo\n // Gets the underlyingError, if available.\n let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? Error\n if let responseInfo = responseInfo {\n print(\"Received error with domain: \\(errorDomain), code: \\(errorCode),\"\n + \"message: \\(errorMessage), responseInfo: \\(responseInfo),\"\n + \"underlyingError: \\(underlyingError?.localizedDescription ?? \"nil\")\")\n }\n}\n```\n\nExample:\n```text\n- (void)bannerView:(GADBannerView *)bannerView\n didFailToReceiveAdWithError:(NSError *)error {\n // Gets the domain from which the error came.\n NSString *errorDomain = error.domain;\n // Gets the error code. See\n // https://developers.google.com/admob/ios/api/reference/Enums/GADErrorCode\n // for a list of possible codes.\n int errorCode = error.code;\n // Gets an error message.\n // For example \"Account not approved yet\". See\n // https://support.google.com/admob/answer/9905175 for explanations of\n // common errors.\n NSString *errorMessage = error.localizedDescription;\n // Gets additional response information about the request. See\n // https://developers.google.com/admob/ios/response-info for more\n // information.\n GADResponseInfo *responseInfo = error.userInfo[GADErrorUserInfoKeyResponseInfo];\n // Gets the underlyingError, if available.\n NSError *underlyingError = error.userInfo[NSUnderlyingErrorKey];\n NSLog(@\"Received error with domain: %@, code: %ld, message: %@, \"\n @\"responseInfo: %@, underlyingError: %@\",\n errorDomain, errorCode, errorMessage, responseInfo,\n underlyingError.localizedDescription);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.764Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":67,"estimatedTokens":675}}356{"id":"doc-server_side_verification_ios_google_for_develope-ca150e77","source":"documentation","title":"Server-side verification | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/ssv","text":"Example:\n```text\nRewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()\n .fetchVerifyingPublicKeysWith(\n RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)\n .build();\nString rewardUrl = ...;\nverifier.verify(rewardUrl);\n```\n\nExample:\n```devsite-click-to-copy\nRewardedAd.load(with:\"AD_UNIT_ID\",\n request: request,\n completionHandler: { [self] ad, error in\n if let error != error {\n rewardedAd = ad\n let options = ServerSideVerificationOptions()\n options.customRewardString = \"SAMPLE_CUSTOM_DATA_STRING\"\n rewardedAd.serverSideVerificationOptions = options\n }\n})\n```\n\nExample:\n```devsite-click-to-copy\nGADRequest *request = [GADRequest request];\n[GADRewardedAd loadWithAdUnitID:@\"AD_UNIT_ID\"\n request:request\n completionHandler:^(GADRewardedAd *ad, NSError *error) {\n if (error) {\n // Handle Error\n return;\n }\n self.rewardedAd = ad;\n GADServerSideVerificationOptions *options =\n [[GADServerSideVerificationOptions alloc] init];\n options.customRewardString = @\"SAMPLE_CUSTOM_DATA_STRING\";\n ad.serverSideVerificationOptions = options;\n }];\n```\n\nExample:\n```text\n{\n \"keys\": [\n {\n keyId: 1916455855,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...YTPcw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFkwEwYHKoZIzj0CAQYI...ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw==\"\n },\n {\n keyId: 3901585526,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...aDUsw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFYwEAYHKoZIzj0CAQYF...4akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw==\"\n },\n ],\n}\n```\n\nExample:\n```text\nString url = ...;\nNetHttpTransport httpTransport = new NetHttpTransport.Builder().build();\nHttpRequest httpRequest =\n httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(url));\nHttpResponse httpResponse = httpRequest.execute();\nif (httpResponse.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) {\n throw new IOException(\"Unexpected status code = \" + httpResponse.getStatusCode());\n}\nString data;\nInputStream contentStream = httpResponse.getContent();\ntry {\n InputStreamReader reader = new InputStreamReader(contentStream, UTF_8);\n data = readerToString(reader);\n} finally {\n contentStream.close();\n}\n```\n\nExample:\n```text\nprivate static Map<Integer, ECPublicKey> parsePublicKeysJson(String publicKeysJson)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = new HashMap<>();\n try {\n JSONArray keys = new JSONObject(publicKeysJson).getJSONArray(\"keys\");\n for (int i = 0; i < keys.length(); i++) {\n JSONObject key = keys.getJSONObject(i);\n publicKeys.put(\n key.getInt(\"keyId\"),\n EllipticCurves.getEcPublicKey(Base64.decode(key.getString(\"base64\"))));\n }\n } catch (JSONException e) {\n throw new GeneralSecurityException(\"failed to extract trusted signing public keys\", e);\n }\n if (publicKeys.isEmpty()) {\n throw new GeneralSecurityException(\"No trusted keys are available.\");\n }\n return publicKeys;\n}\n```\n\nExample:\n```devsite-click-to-copy\nhttps://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins\n×tamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887\n```\n\nExample:\n```text\npublic static final String SIGNATURE_PARAM_NAME = \"signature=\";\n...\nURI uri;\ntry {\n uri = new URI(rewardUrl);\n} catch (URISyntaxException ex) {\n throw new GeneralSecurityException(ex);\n}\nString queryString = uri.getQuery();\nint i = queryString.indexOf(SIGNATURE_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a signature query parameter\");\n}\nbyte[] queryParamContentData =\n queryString\n .substring(0, i - 1)\n // i - 1 instead of i because of & in the query string\n .getBytes(Charset.forName(\"UTF-8\"));\n```\n\nExample:\n```text\npublic static final String KEY_ID_PARAM_NAME = \"key_id=\";\n...\nString sigAndKeyId = queryString.substring(i);\ni = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a key_id query parameter\");\n}\nString sig =\n sigAndKeyId.substring(\n SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */);\nint keyId = Integer.valueOf(sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length()));\n```\n\nExample:\n```text\nprivate void verify(final byte[] dataToVerify, int keyId, final byte[] signature)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = parsePublicKeysJson();\n if (publicKeys.containsKey(keyId)) {\n foundKeyId = true;\n ECPublicKey publicKey = publicKeys.get(keyId);\n EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, HashType.SHA256, EcdsaEncoding.DER);\n verifier.verify(signature, dataToVerify);\n } else {\n throw new GeneralSecurityException(\"cannot find verifying key with key ID: \" + keyId);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.765Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":163,"estimatedTokens":1263}}357{"id":"doc-targeting_ios_google_for_developers-a93d0110","source":"documentation","title":"Targeting | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/targeting","text":"Example:\n```text\nlet requestConfiguration = MobileAds.shared.requestConfiguration\n```\n\nExample:\n```text\nGADRequestConfiguration requestConfiguration = GADMobileAds.sharedInstance.requestConfiguration;\n```\n\nExample:\n```text\n// Indicates that ad requests should have child age treatment.\nMobileAds.shared.requestConfiguration.ageRestrictedTreatment = .childRequestConfigurationSnippets.swift\n```\n\nExample:\n```text\n// Indicates that ad requests should have child age treatment.\nGADMobileAds.sharedInstance.requestConfiguration.ageRestrictedTreatment =\n GADAgeRestrictedTreatmentChild;RequestConfigurationSnippets.m\n```\n\nExample:\n```text\nMobileAds.shared.requestConfiguration.tagForChildDirectedTreatment = true\n```\n\nExample:\n```text\nGADMobileAds.sharedInstance.requestConfiguration.tagForChildDirectedTreatment = @YES;\n```\n\nExample:\n```text\nMobileAds.shared.requestConfiguration.tagForUnderAgeOfConsent = true\n```\n\nExample:\n```text\nGADMobileAds.sharedInstance.requestConfiguration.tagForUnderAgeOfConsent = @YES;\n```\n\nExample:\n```text\nMobileAds.shared.requestConfiguration.maxAdContentRating =\n GADMaxAdContentRating.general\n```\n\nExample:\n```text\nGADMobileAds.sharedInstance.requestConfiguration.maxAdContentRating =\n GADMaxAdContentRatingGeneral;\n```\n\nExample:\n```text\nMobileAds.shared.requestConfiguration.publisherPrivacyPersonalizationState =\n .disabled\n```\n\nExample:\n```text\nGADMobileAds.sharedInstance.requestConfiguration.publisherPrivacyPersonalizationState =\n GADPublisherPrivacyPersonalizationStateDisabled;\n```\n\nExample:\n```text\nlet request = Request()\nlet extras = Extras()\nextras.additionalParameters = [\"collapsible\": \"bottom\"]\nrequest.register(extras)\nadLoader?.load(request)\n```\n\nExample:\n```text\nGADRequest *request = [GADRequest request];\nGADExtras *extras = [[GADExtras alloc] init];\nextras.additionalParameters = @{@\"collapsible\": @\"bottom\"};\n[request registerAdNetworkExtras:extras];\n[self.adLoader loadRequest:request];\n```\n\nExample:\n```text\nlet request = Request()\nrequest.contentURL = \"https://www.example.com\"\n```\n\nExample:\n```text\nGADRequest *request = [GADRequest request];\nrequest.contentURL = @\"https://www.example.com\";\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.767Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":98,"estimatedTokens":545}}358{"id":"doc-full_screen_native_ads_android_google_for_develo-a7dd01d1","source":"documentation","title":"Full-screen native ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/native/full-screen","text":"Example:\n```text\nval adRequest = NativeAdRequest.Builder(adUnitId, listOf(NativeAd.NativeAdType.NATIVE))\n .setMediaAspectRatio(NativeAd.NativeMediaAspectRatio.PORTRAIT)\n .build()\n```\n\nExample:\n```text\nList<NativeAd.NativeAdType> adTypes = Arrays.asList(NativeAd.NativeAdType.NATIVE);\nNativeAdRequest adRequest = new NativeAdRequest.Builder(adUnitId, adTypes)\n .setMediaAspectRatio(NativeAd.NativeMediaAspectRatio.PORTRAIT)\n .build();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.767Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":16,"estimatedTokens":115}}359{"id":"doc-load_a_single_rewarded_interstitial_ad_android_g-69c0d5c0","source":"documentation","title":"Load a single rewarded interstitial ad | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/rewarded-interstitial/single-load","text":"Example:\n```text\n// Load ads after you initialize MobileAds.\nRewardedInterstitialAd.load(\n AdRequest.Builder(adUnitId).build(),\n object : AdLoadCallback<RewardedInterstitialAd> {\n override fun onAdLoaded(ad: RewardedInterstitialAd) {\n // Rewarded interstitial ad loaded.\n rewardedInterstitialAd = ad\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // Rewarded interstitial ad failed to load.\n Log.e(TAG, \"Rewarded interstitial ad failed to load: ${adError.message}\")\n rewardedInterstitialAd = null\n }\n },\n)\n```\n\nExample:\n```text\n// Load ads after you initialize MobileAds.\nRewardedInterstitialAd.load(\n new AdRequest.Builder(adUnitId).build(),\n new AdLoadCallback<RewardedInterstitialAd>() {\n @Override\n public void onAdLoaded(@NonNull RewardedInterstitialAd ad) {\n // Rewarded interstitial ad loaded.\n rewardedInterstitialAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Rewarded interstitial ad failed to load.\n Log.e(TAG, \"Rewarded interstitial ad failed to load: \" + adError.getMessage());\n rewardedInterstitialAd = null;\n }\n });\n```\n\nExample:\n```text\nprivate fun showAd(rewardedInterstitialAd: RewardedInterstitialAd, activity: Activity) {\n // Show the ad.\n rewardedInterstitialAd.show(\n activity,\n object : OnUserEarnedRewardListener {\n override fun onUserEarnedReward(rewardItem: RewardItem) {\n // User earned the reward.\n val rewardAmount = rewardItem.amount\n val rewardType = rewardItem.type\n }\n },\n )\n}\n```\n\nExample:\n```text\nprivate void showAd(RewardedInterstitialAd rewardedInterstitialAd, Activity activity) {\n // Show the ad.\n rewardedInterstitialAd.show(\n activity,\n new OnUserEarnedRewardListener() {\n @Override\n public void onUserEarnedReward(@NonNull RewardItem rewardItem) {\n // User earned the reward.\n int rewardAmount = rewardItem.getAmount();\n String rewardType = rewardItem.getType();\n }\n });\n}\n```\n\nExample:\n```text\nprivate fun listenToAdEvents() {\n // Listen for ad events.\n val ad = rewardedInterstitialAd\n if (ad == null) {\n Log.e(TAG, \"Rewarded interstitial ad is not ready yet.\")\n return\n }\n\n ad.adEventCallback =\n object : RewardedInterstitialAdEventCallback {\n override fun onAdShowedFullScreenContent() {\n // Rewarded interstitial ad did show.\n }\n\n override fun onAdDismissedFullScreenContent() {\n // Rewarded interstitial ad did dismiss.\n rewardedInterstitialAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(\n fullScreenContentError: FullScreenContentError\n ) {\n // Rewarded interstitial ad failed to show.\n Log.e(TAG, \"Rewarded interstitial ad failed to show: ${fullScreenContentError.message}\")\n }\n\n override fun onAdImpression() {\n // Rewarded interstitial ad did record an impression.\n }\n\n override fun onAdClicked() {\n // Rewarded interstitial ad did record a click.\n }\n }\n}\n```\n\nExample:\n```text\nprivate void listenToAdEvents() {\n // Listen for ad events.\n if (rewardedInterstitialAd == null) {\n Log.e(TAG, \"Rewarded interstitial ad is not ready yet.\");\n return;\n }\n\n rewardedInterstitialAd.setAdEventCallback(\n new RewardedInterstitialAdEventCallback() {\n @Override\n public void onAdShowedFullScreenContent() {\n // Rewarded interstitial ad did show.\n }\n\n @Override\n public void onAdDismissedFullScreenContent() {\n // Rewarded interstitial ad did dismiss.\n rewardedInterstitialAd = null;\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(\n @NonNull FullScreenContentError fullScreenContentError) {\n // Rewarded interstitial ad failed to show.\n Log.e(\n TAG,\n \"Rewarded interstitial ad failed to show: \" + fullScreenContentError.getMessage());\n }\n\n @Override\n public void onAdImpression() {\n // Rewarded interstitial ad did record an impression.\n }\n\n @Override\n public void onAdClicked() {\n // Rewarded interstitial ad did record a click.\n }\n });\n}\n```\n\nExample:\n```text\nRewardedInterstitialAd.load(\n context,\n AD_UNIT_ID,\n AdRequest.Builder().build(),\n object : RewardedInterstitialAdLoadCallback() {\n override fun onAdLoaded(ad: RewardedInterstitialAd) {\n rewardedInterstitialAd = ad\n val options =\n ServerSideVerificationOptions.Builder().setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\").build()\n rewardedInterstitialAd?.setServerSideVerificationOptions(options)\n }\n },\n)\n```\n\nExample:\n```text\nRewardedInterstitialAd.load(\n context,\n AD_UNIT_ID,\n new AdRequest.Builder().build(),\n new RewardedInterstitialAdLoadCallback() {\n @Override\n public void onAdLoaded(RewardedInterstitialAd ad) {\n rewardedInterstitialAd = ad;\n ServerSideVerificationOptions options =\n new ServerSideVerificationOptions.Builder()\n .setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\")\n .build();\n rewardedInterstitialAd.setServerSideVerificationOptions(options);\n }\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.768Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":195,"estimatedTokens":1342}}360{"id":"doc-integrate_bidmachine_with_mediation_android_goog-9cab1b53","source":"documentation","title":"Integrate Bidmachine with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/bidmachine","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:bidmachine:3.7.1.1\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:bidmachine:3.7.1.1'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nio.bidmachine\ncom.google.ads.mediation.bidmachine\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.769Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":33,"estimatedTokens":219}}361{"id":"doc-native_validator_android_google_for_developers-138a9bc8","source":"documentation","title":"Native validator | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/native/validator","text":"Example:\n```text\nMobileAds.initialize(\n this@MainActivity,\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n InitializationConfig.Builder(\"SAMPLE_APP_ID\")\n .setNativeValidatorDisabled()\n .build()\n ) {\n // Adapter initialization is complete.\n}\n```\n\nExample:\n```text\nMobileAds.initialize(\n this,\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n new InitializationConfig.Builder(\"SAMPLE_APP_ID\")\n .setNativeValidatorDisabled()\n .build(),\n initializationStatus -> {\n // Adapter initialization is complete.\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.769Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":27,"estimatedTokens":151}}362{"id":"doc-integrate_bigo_ads_sdk_with_mediation_android_go-7e25dbd8","source":"documentation","title":"Integrate BIGO Ads SDK with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/bigo","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:bigo:5.10.1.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:bigo:5.10.1.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nBigoAdSdk.setUserConsent(context, ConsentOptions.CCPA, true);BigoMediationSnippets.java\n```\n\nExample:\n```text\nBigoAdSdk.setUserConsent(context, ConsentOptions.CCPA, true)BigoMediationSnippets.kt\n```\n\nExample:\n```text\nsg.bigo.ads\ncom.google.ads.mediation.bigo.BigoMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.770Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":43,"estimatedTokens":274}}363{"id":"doc-load_a_single_rewarded_ad_android_google_for_dev-a21f8565","source":"documentation","title":"Load a single rewarded ad | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/rewarded/single-load","text":"Example:\n```text\n// Load ads after you initialize MobileAds.\nRewardedAd.load(\n AdRequest.Builder(adUnitId).build(),\n object : AdLoadCallback<RewardedAd> {\n override fun onAdLoaded(ad: RewardedAd) {\n // Rewarded ad loaded.\n rewardedAd = ad\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // Rewarded ad failed to load.\n Log.e(TAG, \"Rewarded ad failed to load: ${adError.message}\")\n rewardedAd = null\n }\n },\n)\n```\n\nExample:\n```text\n// Load ads after you initialize MobileAds.\nRewardedAd.load(\n new AdRequest.Builder(adUnitId).build(),\n new AdLoadCallback<RewardedAd>() {\n @Override\n public void onAdLoaded(@NonNull RewardedAd ad) {\n // Rewarded ad loaded.\n rewardedAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Rewarded ad failed to load.\n Log.e(TAG, \"Rewarded ad failed to load: \" + adError.getMessage());\n rewardedAd = null;\n }\n });\n```\n\nExample:\n```text\nprivate fun showAd(rewardedAd: RewardedAd, activity: Activity) {\n // Show the ad.\n rewardedAd.show(\n activity,\n object : OnUserEarnedRewardListener {\n override fun onUserEarnedReward(rewardItem: RewardItem) {\n // User earned the reward.\n val rewardAmount = rewardItem.amount\n val rewardType = rewardItem.type\n }\n },\n )\n}\n```\n\nExample:\n```text\nprivate void showAd(RewardedAd rewardedAd, Activity activity) {\n // Show the ad.\n rewardedAd.show(\n activity,\n new OnUserEarnedRewardListener() {\n @Override\n public void onUserEarnedReward(@NonNull RewardItem rewardItem) {\n // User earned the reward.\n int rewardAmount = rewardItem.getAmount();\n String rewardType = rewardItem.getType();\n }\n });\n}\n```\n\nExample:\n```text\nprivate fun listenToAdEvents() {\n // Listen for ad events.\n val ad = rewardedAd\n if (ad == null) {\n Log.e(TAG, \"Rewarded ad is not ready yet.\")\n return\n }\n\n ad.adEventCallback =\n object : RewardedAdEventCallback {\n override fun onAdShowedFullScreenContent() {\n // Rewarded ad did show.\n }\n\n override fun onAdDismissedFullScreenContent() {\n // Rewarded ad did dismiss.\n rewardedAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(\n fullScreenContentError: FullScreenContentError\n ) {\n // Rewarded ad failed to show.\n Log.e(TAG, \"Rewarded ad failed to show: ${fullScreenContentError.message}\")\n }\n\n override fun onAdImpression() {\n // Rewarded ad did record an impression.\n }\n\n override fun onAdClicked() {\n // Rewarded ad did record a click.\n }\n }\n}\n```\n\nExample:\n```text\nprivate void listenToAdEvents() {\n // Listen for ad events.\n if (rewardedAd == null) {\n Log.e(TAG, \"Rewarded ad is not ready yet.\");\n return;\n }\n\n rewardedAd.setAdEventCallback(\n new RewardedAdEventCallback() {\n @Override\n public void onAdShowedFullScreenContent() {\n // Rewarded ad did show.\n }\n\n @Override\n public void onAdDismissedFullScreenContent() {\n // Rewarded ad did dismiss.\n rewardedAd = null;\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(\n FullScreenContentError fullScreenContentError) {\n // Rewarded ad failed to show.\n Log.e(TAG, \"Rewarded ad failed to show: \" + fullScreenContentError.getMessage());\n }\n\n @Override\n public void onAdImpression() {\n // Rewarded ad did record an impression.\n }\n\n @Override\n public void onAdClicked() {\n // Rewarded ad did record a click.\n }\n });\n}\n```\n\nExample:\n```text\nRewardedAd.load(\n context,\n AD_UNIT_ID,\n AdRequest.Builder().build(),\n object : RewardedAdLoadCallback() {\n override fun onAdLoaded(ad: RewardedAd) {\n rewardedAd = ad\n val options =\n ServerSideVerificationOptions.Builder().setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\").build()\n rewardedAd?.setServerSideVerificationOptions(options)\n }\n },\n)\n```\n\nExample:\n```text\nRewardedAd.load(\n context,\n AD_UNIT_ID,\n new AdRequest.Builder().build(),\n new RewardedAdLoadCallback() {\n @Override\n public void onAdLoaded(RewardedAd ad) {\n rewardedAd = ad;\n ServerSideVerificationOptions options =\n new ServerSideVerificationOptions.Builder()\n .setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\")\n .build();\n rewardedAd.setServerSideVerificationOptions(options);\n }\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.771Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":193,"estimatedTokens":1168}}364{"id":"doc-native_ads_android_google_for_developers-3ca130e7","source":"documentation","title":"Native ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/native/advanced","text":"Example:\n```text\n<com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdView\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"wrap_content\">\n <LinearLayout\n android:orientation=\"vertical\">\n <LinearLayout\n android:orientation=\"horizontal\">\n <ImageView\n android:id=\"@+id/ad_app_icon\" />\n <TextView\n android:id=\"@+id/ad_headline\" />\n </LinearLayout>\n <!--Add remaining assets such as the image and media view.-->\n </LinearLayout>\n</com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdView>\n```\n\nExample:\n```text\nimport com.google.android.gms.compose_util.NativeAdAttribution\n import com.google.android.gms.compose_util.NativeAdView\n\n @Composable\n /** Display a native ad with a user defined template. */\n fun DisplayNativeAdView(nativeAd: NativeAd) {\n NativeAdView {\n // Display the ad attribution.\n NativeAdAttribution(text = context.getString(\"Ad\"))\n // Add remaining assets such as the image and media view.\n }\n }\n```\n\nExample:\n```text\n// Build an ad request with native ad options to customize the ad.\nval adTypes = listOf(NativeAd.NativeAdType.NATIVE)\nval adRequest = NativeAdRequest\n .Builder(\"ca-app-pub-3940256099942544/2247696110\", adTypes)\n .build()\n\nval adCallback =\n object : NativeAdLoaderCallback {\n override fun onNativeAdLoaded(nativeAd: NativeAd) {\n activity?.runOnUiThread {\n\n val nativeAdBinding = NativeAdBinding.inflate(layoutInflater)\n val adView = nativeAdBinding.root\n val frameLayout = myActivityLayout.nativeAdPlaceholder\n\n // Populate and register the native ad asset views.\n displayNativeAd(nativeAd, nativeAdBinding)\n\n // Remove all old ad views and add the new native ad\n // view to the view hierarchy.\n frameLayout.removeAllViews()\n frameLayout.addView(adView)\n }\n }\n }\n\n// Load the native ad with our request and callback.\nNativeAdLoader.load(adRequest, adCallback)\n```\n\nExample:\n```text\n// Build an ad request with native ad options to customize the ad.\nList<NativeAd.NativeAdType> adTypes = Arrays.asList(NativeAd.NativeAdType.NATIVE);\nNativeAdRequest adRequest = new NativeAdRequest\n .Builder(\"ca-app-pub-3940256099942544/2247696110\", adTypes)\n .build();\n\nNativeAdLoaderCallback adCallback = new NativeAdLoaderCallback() {\n @Override\n public void onNativeAdLoaded(NativeAd nativeAd) {\n if (getActivity() != null) {\n getActivity()\n .runOnUiThread(() -> {\n // Inflate the native ad view and add it to the view hierarchy.\n NativeAdBinding nativeAdBinding = NativeAdBinding.inflate(getLayoutInflater());\n NativeAdView adView = (NativeAdView) nativeAdBinding.getRoot();\n FrameLayout frameLayout = myActivityLayout.nativeAdPlaceholder;\n\n // Populate and register the native ad asset views.\n displayNativeAd(nativeAd, nativeAdBinding);\n\n // Remove all old ad views and add the new native ad\n // view to the view hierarchy.\n frameLayout.removeAllViews();\n frameLayout.addView(adView);\n });\n }\n }\n};\n\n// Load the native ad with our request and callback.\nNativeAdLoader.load(adRequest, adCallback);\n```\n\nExample:\n```text\nprivate fun displayNativeAd(nativeAd: NativeAd, nativeAdBinding : NativeAdBinding) {\n // Set the native ad view elements.\n val nativeAdView = nativeAdBinding.root\n nativeAdView.advertiserView = nativeAdBinding.adAdvertiser\n nativeAdView.bodyView = nativeAdBinding.adBody\n nativeAdView.callToActionView = nativeAdBinding.adCallToAction\n nativeAdView.headlineView = nativeAdBinding.adHeadline\n nativeAdView.iconView = nativeAdBinding.adAppIcon\n nativeAdView.priceView = nativeAdBinding.adPrice\n nativeAdView.starRatingView = nativeAdBinding.adStars\n nativeAdView.storeView = nativeAdBinding.adStore\n\n // Set the view element with the native ad assets.\n nativeAdBinding.adAdvertiser.text = nativeAd.advertiser\n nativeAdBinding.adBody.text = nativeAd.body\n nativeAdBinding.adCallToAction.text = nativeAd.callToAction\n nativeAdBinding.adHeadline.text = nativeAd.headline\n nativeAdBinding.adAppIcon.setImageDrawable(nativeAd.icon?.drawable)\n nativeAdBinding.adPrice.text = nativeAd.price\n nativeAd.starRating?.toFloat().let { value ->\n nativeAdBinding.adStars.rating = value\n }\n nativeAdBinding.adStore.text = nativeAd.store\n\n // Hide views for assets that don't have data.\n nativeAdBinding.adAdvertiser.visibility = getAssetViewVisibility(nativeAd.advertiser)\n nativeAdBinding.adBody.visibility = getAssetViewVisibility(nativeAd.body)\n nativeAdBinding.adCallToAction.visibility = getAssetViewVisibility(nativeAd.callToAction)\n nativeAdBinding.adHeadline.visibility = getAssetViewVisibility(nativeAd.headline)\n nativeAdBinding.adAppIcon.visibility = getAssetViewVisibility(nativeAd.icon)\n nativeAdBinding.adPrice.visibility = getAssetViewVisibility(nativeAd.price)\n nativeAdBinding.adStars.visibility = getAssetViewVisibility(nativeAd.starRating)\n nativeAdBinding.adStore.visibility = getAssetViewVisibility(nativeAd.store)\n\n // Inform GMA Next-Gen SDK that you have finished populating\n // the native ad views with this native ad.\n nativeAdView.registerNativeAd(nativeAd, nativeAdBinding.adMedia)\n}\n\n/**\n* Determines the visibility of an asset view based on the presence of its asset.\n*\n* @param asset The native ad asset to check for nullability.\n* @return [View.VISIBLE] if the asset is not null, [View.INVISIBLE] otherwise.\n*/\nprivate fun getAssetViewVisibility(asset: Any?): Int {\n return if (asset == null) View.INVISIBLE else View.VISIBLE\n}\n```\n\nExample:\n```text\nprivate void displayNativeAd(ad: NativeAd, nativeAdBinding : NativeAdBinding) {\n // Set the native ad view elements.\n NativeAdView nativeAdView = nativeAdBinding.getRoot();\n nativeAdView.setAdvertiserView(nativeAdBinding.adAdvertiser);\n nativeAdView.setBodyView(nativeAdBinding.adBody);\n nativeAdView.setCallToActionView(nativeAdBinding.adCallToAction);\n nativeAdView.setHeadlineView(nativeAdBinding.adHeadline);\n nativeAdView.setIconView(nativeAdBinding.adAppIcon);\n nativeAdView.setPriceView(nativeAdBinding.adPrice);\n nativeAdView.setStarRatingView(nativeAdBinding.adStars);\n nativeAdView.setStoreView(nativeAdBinding.adStore);\n\n // Set the view element with the native ad assets.\n nativeAdBinding.adAdvertiser.setText(nativeAd.getAdvertiser());\n nativeAdBinding.adBody.setText(nativeAd.getBody());\n nativeAdBinding.adCallToAction.setText(nativeAd.getCallToAction());\n nativeAdBinding.adHeadline.setText(nativeAd.getHeadline());\n if (nativeAd.getIcon() != null) {\n nativeAdBinding.adAppIcon.setImageDrawable(nativeAd.getIcon().getDrawable());\n }\n nativeAdBinding.adPrice.setText(nativeAd.getPrice());\n if (nativeAd.getStarRating() != null) {\n nativeAdBinding.adStars.setRating(nativeAd.getStarRating().floatValue());\n }\n nativeAdBinding.adStore.setText(nativeAd.getStore());\n\n // Hide views for assets that don't have data.\n nativeAdBinding.adAdvertiser.setVisibility(getAssetViewVisibility(nativeAd.getAdvertiser()));\n nativeAdBinding.adBody.setVisibility(getAssetViewVisibility(nativeAd.getBody()));\n nativeAdBinding.adCallToAction.setVisibility(getAssetViewVisibility(nativeAd.getCallToAction()));\n nativeAdBinding.adHeadline.setVisibility(getAssetViewVisibility(nativeAd.getHeadline()));\n nativeAdBinding.adAppIcon.setVisibility(getAssetViewVisibility(nativeAd.getIcon()));\n nativeAdBinding.adPrice.setVisibility(getAssetViewVisibility(nativeAd.getPrice()));\n nativeAdBinding.adStars.setVisibility(getAssetViewVisibility(nativeAd.getStarRating()));\n nativeAdBinding.adStore.setVisibility(getAssetViewVisibility(nativeAd.getStore()));\n\n // Inform GMA Next-Gen SDK that you have finished populating\n // the native ad views with this native ad.\n nativeAdView.registerNativeAd(nativeAd, nativeAdBinding.adMedia);\n}\n\n/**\n* Determines the visibility of an asset view based on the presence of its asset.\n*\n* @param asset The native ad asset to check for nullability.\n* @return {@link View#VISIBLE} if the asset is not null, {@link View#INVISIBLE} otherwise.\n*/\nprivate int getAssetViewVisibility(Object asset) {\n return (asset == null) ? View.INVISIBLE : View.VISIBLE;\n}\n```\n\nExample:\n```text\nprivate fun setEventCallback(nativeAd: NativeAd) {\n nativeAd.adEventCallback =\n object : NativeAdEventCallback {\n override fun onAdClicked() {\n Log.d(Constant.TAG, \"Native ad recorded a click.\")\n }\n }\n}\n```\n\nExample:\n```text\nprivate void setEventCallback(NativeAd nativeAd) {\n nativeAd.setAdEventCallback(new NativeAdEventCallback() {\n @Override\n public void onAdClicked() {\n Log.d(Constant.TAG, \"Native ad recorded a click.\");\n }\n });\n}\n```\n\nExample:\n```text\nnativeAdViewBinding.mediaView.imageScaleType = ImageView.ScaleType.CENTER_CROP\n```\n\nExample:\n```text\nnativeAdViewBinding.mediaView.setImageScaleType(ImageView.ScaleType.CENTER_CROP);\n```\n\nExample:\n```text\nnativeAd.destroy()\n```\n\nExample:\n```text\nnativeAd.destroy();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.772Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":254,"estimatedTokens":2322}}365{"id":"doc-native_ad_videos_android_google_for_developers-efb9b9e5","source":"documentation","title":"Native ad videos | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/native/video-ads","text":"Example:\n```text\nif (nativeAd.getMediaContent() != null) {\n MediaContent mediaContent = nativeAd.getMediaContent();\n float mediaAspectRatio = mediaContent.getAspectRatio();\n if (mediaContent.hasVideoContent()) {\n float duration = mediaContent.getDuration();\n }\n}NativeVideoAdsSnippets.java\n```\n\nExample:\n```text\nnativeAd.mediaContent?.let { mediaContent ->\n val mediaAspectRatio: Float = mediaContent.aspectRatio\n if (mediaContent.hasVideoContent()) {\n val duration: Float = mediaContent.duration\n }\n}NativeVideoAdsSnippets.kt\n```\n\nExample:\n```text\nif (nativeAd.getMediaContent() != null) {\n VideoController videoController = nativeAd.getMediaContent().getVideoController();\n if (videoController != null) {\n videoController.setVideoLifecycleCallbacks(\n new VideoController.VideoLifecycleCallbacks() {\n @Override\n public void onVideoStart() {\n Log.d(TAG, \"Video started.\");\n }\n\n @Override\n public void onVideoPlay() {\n Log.d(TAG, \"Video played.\");\n }\n\n @Override\n public void onVideoPause() {\n Log.d(TAG, \"Video paused.\");\n }\n\n @Override\n public void onVideoEnd() {\n Log.d(TAG, \"Video ended.\");\n }\n\n @Override\n public void onVideoMute(boolean isMuted) {\n Log.d(TAG, \"Video isMuted: \" + isMuted + \".\");\n }\n });\n }\n}NativeVideoAdsSnippets.java\n```\n\nExample:\n```text\nval videoLifecycleCallbacks =\n object : VideoController.VideoLifecycleCallbacks() {\n override fun onVideoStart() {\n Log.d(TAG, \"Video started.\")\n }\n\n override fun onVideoPlay() {\n Log.d(TAG, \"Video played.\")\n }\n\n override fun onVideoPause() {\n Log.d(TAG, \"Video paused.\")\n }\n\n override fun onVideoEnd() {\n Log.d(TAG, \"Video ended.\")\n }\n\n override fun onVideoMute(isMuted: Boolean) {\n Log.d(TAG, \"Video isMuted: $isMuted.\")\n }\n }\nnativeAd.mediaContent?.videoController?.videoLifecycleCallbacks = videoLifecycleCallbacksNativeVideoAdsSnippets.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.772Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":85,"estimatedTokens":526}}366{"id":"doc-integrate_chartboost_with_mediation_android_goog-ebd63bc2","source":"documentation","title":"Integrate Chartboost with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/chartboost","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://cboost.jfrog.io/artifactory/chartboost-ads/\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:chartboost:9.13.0.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:chartboost:9.13.0.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nDataUseConsent dataUseConsent = new CCPA(CCPA.CCPA_CONSENT.OPT_IN_SALE);\nChartboost.addDataUseConsent(context, dataUseConsent);\n```\n\nExample:\n```text\nval dataUseConsent = CCPA(CCPA.CCPA_CONSENT.OPT_IN_SALE)\nChartboost.addDataUseConsent(context, dataUseConsent)\n```\n\nExample:\n```text\nandroid:configChanges=\"keyboardHidden|orientation|screenSize\"\n```\n\nExample:\n```text\n<uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n```\n\nExample:\n```text\ncom.google.ads.mediation.chartboost.ChartboostAdapter\ncom.google.ads.mediation.chartboost.ChartboostMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.774Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":68,"estimatedTokens":401}}367{"id":"doc-set_advanced_native_features_android_google_for_-498f692c","source":"documentation","title":"Set advanced native features | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/native/options","text":"Example:\n```text\nval adRequest = NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n listOf(NativeAd.NativeAdType.NATIVE))\n .setMediaAspectRatio(NativeAd.NATIVE_MEDIA_ASPECT_RATIO_LANDSCAPE)\n .build()\n```\n\nExample:\n```text\nNativeAdRequest adRequest = new NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n List.of(NativeAd.NativeAdType.NATIVE))\n .setMediaAspectRatio(NativeAd.NATIVE_MEDIA_ASPECT_RATIO_LANDSCAPE)\n .build();\n```\n\nExample:\n```text\nval adRequest = NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n listOf(NativeAd.NativeAdType.NATIVE))\n .setMediaAspectRatio(NativeAd.NATIVE_MEDIA_ASPECT_RATIO_LANDSCAPE)\n .disableImageDownloading()\n .build()\n\nval adCallback: NativeAdLoaderCallback =\n object : NativeAdLoaderCallback {\n override fun onNativeAdLoaded(nativeAd: NativeAd) {\n // Get the image uri.\n val imageUri = nativeAd.image?.uri\n }\n };\n\n// Load the native ad with the ad request and callback.\nNativeAdLoader.load(adRequest, adLoaderCallback);\n```\n\nExample:\n```text\nNativeAdRequest adRequest = new NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n List.of(NativeAd.NativeAdType.NATIVE))\n .disableImageDownloading()\n .build();\n\nNativeAdLoaderCallback adLoaderCallback =\n new NativeAdLoaderCallback() {\n @Override\n public void onNativeAdLoaded(@NonNull NativeAd nativeAd) {\n // Get the image uri.\n Uri imageUri = nativeAd.getImage().getUri();\n }\n };\n\n// Load the native ad with the ad request and callback.\nNativeAdLoader.load(adRequest, adLoaderCallback);\n```\n\nExample:\n```text\nval adRequest = NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n listOf(NativeAd.NativeAdType.NATIVE))\n .setAdChoicesPlacement(NativeAdOptions.ADCHOICES_BOTTOM_RIGHT)\n .build()\n```\n\nExample:\n```text\nNativeAdRequest adRequest = new NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n List.of(NativeAd.NativeAdType.NATIVE))\n .setAdChoicesPlacement(NativeAdOptions.ADCHOICES_BOTTOM_RIGHT)\n .build();\n```\n\nExample:\n```text\noverride fun onNativeAdLoaded(nativeAd: NativeAd) {\n val nativeAdView = NativeAdView(applicationContext)\n val adChoicesView = AdChoicesView(this)\n nativeAdView.adChoicesView = adChoicesView\n}\n```\n\nExample:\n```text\npublic void onNativeAdLoaded(@NonNull NativeAd nativeAd) {\n NativeAdView nativeAdView = new NativeAdView(getApplicationContext());\n AdChoicesView adChoicesView = new AdChoicesView(this);\n nativeAdView.setAdChoicesView(adChoicesView);\n}\n```\n\nExample:\n```text\nval videoOptions = VideoOptions.Builder()\n .setStartMuted(false)\n .build()\n\nval adRequest = NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n listOf(NativeAd.NativeAdType.NATIVE))\n .setVideoOptions(videoOptions)\n .build()\n```\n\nExample:\n```text\nVideoOptions videoOptions = VideoOptions.Builder()\n .setStartMuted(false)\n .build()\n\nNativeAdRequest adRequest = new NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n List.of(NativeAd.NativeAdType.NATIVE))\n .setVideoOptions(videoOptions)\n .build()\n```\n\nExample:\n```text\nval videoOptions: VideoOptions.Builder()\n .setCustomControlsRequested(true)\n .build()\n\nval adRequest = new NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n listOf(NativeAd.NativeAdType.NATIVE))\n .setVideoOptions(videoOptions)\n .build()\n```\n\nExample:\n```text\nVideoOptions VideoOptions = VideoOptions.Builder()\n .setCustomControlsRequested(true)\n .build()\n\nNativeAdRequest adRequest = new NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n List.of(NativeAd.NativeAdType.NATIVE))\n .setVideoOptions(videoOptions)\n .build()\n```\n\nExample:\n```text\nval adCallback: NativeAdLoaderCallback =\n object : NativeAdLoaderCallback {\n override fun onNativeAdLoaded(nativeAd: NativeAd) {\n val mediaContent = nativeAd.mediaContent;\n if (mediaContent != null) {\n val videoController = mediaContent.videoController;\n val canShowCustomControls = videoController?.isCustomControlsEnabled();\n }\n }\n };\n```\n\nExample:\n```text\nNativeAdLoaderCallback adCallback =\n new NativeAdLoaderCallback() {\n @Override\n public void onNativeAdLoaded(@NonNull NativeAd nativeAd) {\n MediaContent mediaContent = nativeAd.getMediaContent();\n if (mediaContent != null) {\n VideoController videoController = mediaContent.getVideoController();\n if (videoController != null) {\n boolean canShowCustomControls = videoController.isCustomControlsEnabled();\n }\n }\n }\n };\n```\n\nExample:\n```text\nval adOptions = NativeAdOptions\n .Builder()\n .enableCustomClickGestureDirection(\n /* swipeDirection */ NativeAdOptions.SWIPE_GESTURE_DIRECTION_RIGHT,\n /* tapsAllowed= */ true)\n .build();\n\n// You can use the following sample ad unit ID to test custom click gestures.\nval adRequest = NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n listOf(NativeAd.NativeAdType.NATIVE))\n .withNativeAdOptions(adOptions)\n .build();\n```\n\nExample:\n```text\nNativeAdOptions adOptions = new NativeAdOptions\n .Builder()\n .enableCustomClickGestureDirection(\n /* swipeDirection */ NativeAdOptions.SWIPE_GESTURE_DIRECTION_RIGHT,\n /* tapsAllowed= */ true)\n .build();\n\n// You can use the following sample ad unit ID to test custom click gestures.\nNativeAdRequest adRequest = new NativeAdRequest.Builder(\n \"ca-app-pub-3940256099942544/2247696110\",\n List.of(NativeAd.NativeAdType.NATIVE))\n .withNativeAdOptions(adOptions)\n .build();\n```\n\nExample:\n```text\nval adCallback: NativeAdLoaderCallback =\n object : NativeAdLoaderCallback {\n override fun onNativeAdLoaded(nativeAd: NativeAd) {\n // Implement the onAdSwipeGestureClicked() method.\n val nativeAdCallback: NativeAdEventCallback = object : NativeAdEventCallback {\n override fun onAdSwipeGestureClicked() {\n // A swipe gesture click has occurred.\n }\n }\n }\n }\n // Load the native ad with the ad request and callback.\n NativeAdLoader.load(adRequest, adCallback)\n```\n\nExample:\n```text\nNativeAdLoaderCallback adCallback =\n new NativeAdLoaderCallback() {\n @Override\n public void onNativeAdLoaded(@NonNull NativeAd nativeAd) {\n // Implement the onAdSwipeGestureClicked() method.\n NativeAdEventCallback nativeAdCallback = new NativeAdEventCallback() {\n @Override\n public void onAdSwipeGestureClicked() {\n // A swipe gesture click has occurred.\n }\n };\n }\n };\n // Load the native ad with the ad request and callback.\n NativeAdLoader.load(adRequest, adCallback);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.775Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":250,"estimatedTokens":1681}}368{"id":"doc-set_up_admob_mediation_android_google_for_develo-d7a4c082","source":"documentation","title":"Set up AdMob Mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation","text":"Example:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds\nimport com.google.android.libraries.ads.mobile.sdk.initialization.InitializationConfig\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\n\nclass MainActivity : AppCompatActivity() {\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n\n val backgroundScope = CoroutineScope(Dispatchers.IO)\n backgroundScope.launch {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(this@MainActivity, InitializationConfig.Builder(\"SAMPLE_APP_ID\").build()) {\n initializationStatus ->\n for ((adapterName, adapterStatus) in initializationStatus.adapterStatusMap) {\n Log.d(\n \"MyApp\",\n String.format(\n \"Adapter name: %s, Status code: %s, Status string: %s, Latency: %d\",\n adapterName,\n adapterStatus.initializationState,\n adapterStatus.description,\n adapterStatus.latency,\n ),\n )\n }\n // Adapter initialization is complete.\n }\n // Other methods on MobileAds can now be called.\n }\n }\n}\n```\n\nExample:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds;\nimport com.google.android.libraries.ads.mobile.sdk.initialization.AdapterStatus;\nimport com.google.android.libraries.ads.mobile.sdk.initialization.InitializationConfig;\n\npublic class MainActivity extends AppCompatActivity {\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n\n new Thread(\n () -> {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this,\n new InitializationConfig.Builder(\"SAMPLE_APP_ID\")\n .build(),\n initializationStatus -> {\n Map<String, AdapterStatus> adapterStatusMap =\n initializationStatus.getAdapterStatusMap();\n for (String adapterClass : adapterStatusMap.keySet()) {\n AdapterStatus adapterStatus = adapterStatusMap.get(adapterClass);\n Log.d(\n \"MyApp\",\n String.format(\n \"Adapter name: %s, Status code: %s, Status description: %s,\"\n + \" Latency: %d\",\n adapterClass,\n adapterStatus.getInitializationState(),\n adapterStatus.getDescription(),\n adapterStatus.getLatency()));\n }\n // Adapter initialization is complete.\n });\n // Other methods on MobileAds can now be called.\n })\n .start();\n }\n}\n```\n\nExample:\n```text\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```text\nconfigurations.configureEach {\n exclude group: \"com.google.android.gms\", module: \"play-services-ads\"\n exclude group: \"com.google.android.gms\", module: \"play-services-ads-lite\"\n}\n```\n\nExample:\n```text\nBannerAd.load(\n BannerAdRequest.Builder(\"AD_UNIT_ID\", AdSize.BANNER).build(),\n object : AdLoadCallback<BannerAd> {\n override fun onAdLoaded(ad: BannerAd) {\n Log.d(\n \"MyApp\", \"Adapter class name: \" +\n ad.getResponseInfo().mediationAdapterClassName\n )\n }\n }\n)\n```\n\nExample:\n```text\nBannerAd.load(\n new BannerAdRequest.Builder(\"AD_UNIT_ID\", AdSize.BANNER).build(),\n new AdLoadCallback<BannerAd>() {\n @Override\n public void onAdLoaded(@NonNull BannerAd ad) {\n Log.d(\"MyApp\",\n \"Adapter class name: \" + ad.getResponseInfo().getMediationAdapterClassName());\n }\n }\n);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.776Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":126,"estimatedTokens":1028}}369{"id":"doc-load_a_native_ad_android_google_for_developers-63cc077d","source":"documentation","title":"Load a native ad | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/native","text":"Example:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.common.LoadAdError\nimport com.google.android.libraries.ads.mobile.sdk.nativead.NativeAd\nimport com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdLoader\nimport com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdLoaderCallback\nimport com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdRequest\n\nclass NativeFragment : Fragment() {\n\n private var nativeAd: NativeAd? = null\n\n override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n super.onViewCreated(view, savedInstanceState)\n loadAd()\n }\n\n private fun loadAd() {\n // Build an ad request with native ad options to customize the ad.\n val adRequest = NativeAdRequest\n .Builder(AD_UNIT_ID, listOf(NativeAd.NativeAdType.NATIVE))\n .build()\n\n val adCallback =\n object : NativeAdLoaderCallback {\n override fun onNativeAdLoaded(nativeAd: NativeAd) {\n // Called when a native ad has loaded.\n }\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // Called when a native ad has failed to load.\n }\n }\n\n // Load the native ad with our request and callback.\n NativeAdLoader.load(adRequest, adCallback)\n }\n\n companion object {\n // Sample native ad unit ID.\n const val AD_UNIT_ID = \"ca-app-pub-3940256099942544/2247696110\"\n }\n}\n```\n\nExample:\n```text\nnativeAd.adEventCallback =\n object : NativeAdEventCallback {\n override fun onAdShowedFullScreenContent() {\n // Native ad showed full screen content.\n }\n override fun onAdDismissedFullScreenContent() {\n // Native ad dismissed full screen content.\n }\n override fun onAdFailedToShowFullScreenContent {\n // Native ad failed to show full screen content.\n }\n override fun onAdImpression() {\n // Native ad recorded an impression.\n }\n override fun onAdClicked() {\n // Native ad recorded a click.\n }\n }\n```\n\nExample:\n```text\nprivate fun loadAd() {\n // Build an ad request with native ad options to customize the ad.\n val adRequest = NativeAdRequest\n .Builder(AD_UNIT_ID, listOf(NativeAd.NativeAdType.NATIVE))\n .build()\n\n val adCallback =\n object : NativeAdLoaderCallback {\n override fun onNativeAdLoaded(nativeAd: NativeAd) {\n // Called when a native ad has loaded.\n }\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // Called when a native ad has failed to load.\n }\n override fun onAdLoadingCompleted() {\n // Called when all native ads have loaded.\n }\n }\n\n // Load the native ad with our request and callback.\n NativeAdLoader.load(adRequest, 3, adCallback)\n}\n```\n\nExample:\n```text\n<application android:hardwareAccelerated=\"true\">\n <!-- For activities that use ads, hardwareAcceleration should be true. -->\n <activity android:hardwareAccelerated=\"true\" />\n <!-- For activities that don't use ads, hardwareAcceleration can be false. -->\n <activity android:hardwareAccelerated=\"false\" />\n</application>\nHardwareAccelerationSnippet.xml\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.777Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":104,"estimatedTokens":780}}370{"id":"doc-set_up_rewarded_ads_android_google_for_developer-b259cdc7","source":"documentation","title":"Set up rewarded ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/rewarded","text":"Example:\n```text\nval adRequest = AdRequest.Builder(adUnitId).build()\nval preloadConfig = PreloadConfiguration(adRequest)\nRewardedAdPreloader.start(adUnitId, preloadConfig)\n```\n\nExample:\n```text\nAdRequest adRequest = new AdRequest.Builder(adUnitId).build();\nPreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest);\nRewardedAdPreloader.start(adUnitId, preloadConfig);\n```\n\nExample:\n```text\nprivate fun pollAndShowAd(activity: Activity, adUnitId: String) {\n // Polling returns the next available ad and loads another ad in the background.\n val ad = RewardedAdPreloader.pollAd(adUnitId)\n if (ad == null) {\n Log.e(TAG, \"Rewarded ad is not available.\")\n return\n }\n\n // Interact with the ad object as needed.\n Log.d(TAG, \"Rewarded ad response info: ${ad.getResponseInfo()}\")\n ad.adEventCallback =\n object : RewardedAdEventCallback {\n override fun onAdImpression() {\n Log.d(TAG, \"Rewarded ad recorded an impression.\")\n }\n }\n ad.show(activity) { rewardItem -> Log.d(TAG, \"User earned reward: ${rewardItem.amount}\") }\n}\n```\n\nExample:\n```text\nprivate void pollAndShowAd(Activity activity, String adUnitId) {\n // Polling returns the next available ad and loads another ad in the background.\n final RewardedAd ad = RewardedAdPreloader.pollAd(adUnitId);\n\n // Interact with the ad object as needed.\n if (ad == null) {\n Log.e(TAG, \"Rewarded ad is not available.\");\n return;\n }\n\n Log.d(TAG, \"Rewarded ad response info: \" + ad.getResponseInfo());\n ad.setAdEventCallback(\n new RewardedAdEventCallback() {\n @Override\n public void onAdImpression() {\n Log.d(TAG, \"Rewarded ad recorded an impression.\");\n }\n });\n\n // Show the ad.\n ad.show(\n activity,\n rewardItem -> {\n Log.d(TAG, \"User earned reward: \" + rewardItem.getAmount());\n });\n}\n```\n\nExample:\n```text\nprivate fun listenToAdEvents() {\n // Listen for ad events.\n val ad = rewardedAd\n if (ad == null) {\n Log.e(TAG, \"Rewarded ad is not ready yet.\")\n return\n }\n\n ad.adEventCallback =\n object : RewardedAdEventCallback {\n override fun onAdShowedFullScreenContent() {\n // Rewarded ad did show.\n }\n\n override fun onAdDismissedFullScreenContent() {\n // Rewarded ad did dismiss.\n rewardedAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(\n fullScreenContentError: FullScreenContentError\n ) {\n // Rewarded ad failed to show.\n Log.e(TAG, \"Rewarded ad failed to show: ${fullScreenContentError.message}\")\n }\n\n override fun onAdImpression() {\n // Rewarded ad did record an impression.\n }\n\n override fun onAdClicked() {\n // Rewarded ad did record a click.\n }\n }\n}\n```\n\nExample:\n```text\nprivate void listenToAdEvents() {\n // Listen for ad events.\n if (rewardedAd == null) {\n Log.e(TAG, \"Rewarded ad is not ready yet.\");\n return;\n }\n\n rewardedAd.setAdEventCallback(\n new RewardedAdEventCallback() {\n @Override\n public void onAdShowedFullScreenContent() {\n // Rewarded ad did show.\n }\n\n @Override\n public void onAdDismissedFullScreenContent() {\n // Rewarded ad did dismiss.\n rewardedAd = null;\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(\n FullScreenContentError fullScreenContentError) {\n // Rewarded ad failed to show.\n Log.e(TAG, \"Rewarded ad failed to show: \" + fullScreenContentError.getMessage());\n }\n\n @Override\n public void onAdImpression() {\n // Rewarded ad did record an impression.\n }\n\n @Override\n public void onAdClicked() {\n // Rewarded ad did record a click.\n }\n });\n}\n```\n\nExample:\n```text\nRewardedAd.load(\n context,\n AD_UNIT_ID,\n AdRequest.Builder().build(),\n object : RewardedAdLoadCallback() {\n override fun onAdLoaded(ad: RewardedAd) {\n rewardedAd = ad\n val options =\n ServerSideVerificationOptions.Builder().setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\").build()\n rewardedAd?.setServerSideVerificationOptions(options)\n }\n },\n)\n```\n\nExample:\n```text\nRewardedAd.load(\n context,\n AD_UNIT_ID,\n new AdRequest.Builder().build(),\n new RewardedAdLoadCallback() {\n @Override\n public void onAdLoaded(RewardedAd ad) {\n rewardedAd = ad;\n ServerSideVerificationOptions options =\n new ServerSideVerificationOptions.Builder()\n .setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\")\n .build();\n rewardedAd.setServerSideVerificationOptions(options);\n }\n });\n```\n\nExample:\n```text\nval preloadCallback =\n object : PreloadCallback {\n override fun onAdFailedToPreload(preloadId: String, adError: LoadAdError) {\n Log.d(TAG, \"Rewarded preload ad $preloadId failed to load with error: ${adError.message}\")\n }\n\n override fun onAdsExhausted(preloadId: String) {\n Log.i(TAG, \"Rewarded preload ad $preloadId is not available\")\n }\n\n override fun onAdPreloaded(preloadId: String, responseInfo: ResponseInfo) {\n Log.i(TAG, \"Rewarded preload ad $preloadId is available\")\n }\n }\nval adRequest = AdRequest.Builder(adUnitId).build()\nval preloadConfig = PreloadConfiguration(adRequest)\nRewardedAdPreloader.start(adUnitId, preloadConfig, preloadCallback)\n```\n\nExample:\n```text\nPreloadCallback preloadCallback =\n new PreloadCallback() {\n @Override\n public void onAdFailedToPreload(String preloadId, LoadAdError adError) {\n Log.d(\n TAG,\n String.format(\n \"Rewarded preload ad %s failed to load with error: %s\",\n preloadId, adError.getMessage()));\n }\n\n @Override\n public void onAdsExhausted(String preloadId) {\n Log.i(TAG, \"Rewarded preload ad \" + preloadId + \" is not available\");\n }\n\n @Override\n public void onAdPreloaded(String preloadId, ResponseInfo responseInfo) {\n Log.i(TAG, \"Rewarded preload ad \" + preloadId + \" is available\");\n }\n };\n\nAdRequest adRequest = new AdRequest.Builder(adUnitId).build();\nPreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest);\nRewardedAdPreloader.start(adUnitId, preloadConfig, preloadCallback);\n```\n\nExample:\n```text\nprivate fun isAdAvailable(adUnitId: String): Boolean {\n return RewardedAdPreloader.isAdAvailable(adUnitId)\n}\n```\n\nExample:\n```text\nprivate boolean isAdAvailable(String adUnitId) {\n return RewardedAdPreloader.isAdAvailable(adUnitId);\n}\n```\n\nExample:\n```text\nval adRequest = AdRequest.Builder(adUnitId).build()\n// Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\nval preloadConfig = PreloadConfiguration(adRequest, bufferSize = 2)\nRewardedAdPreloader.start(adUnitId, preloadConfig)\n```\n\nExample:\n```text\nAdRequest adRequest = new AdRequest.Builder(adUnitId).build();\n// Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\nPreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest, 2);\nRewardedAdPreloader.start(adUnitId, preloadConfig);\n```\n\nExample:\n```text\nprivate fun stopPreloading(adUnitId: String) {\n // Stops the preloading and destroy preloaded ads.\n RewardedAdPreloader.destroy(adUnitId)\n}\n```\n\nExample:\n```text\nprivate void stopPreloading(String adUnitId) {\n // Stops the preloading and destroy preloaded ads.\n RewardedAdPreloader.destroy(adUnitId);\n}\n```\n\nExample:\n```text\nval responseInfo = RewardedAdPreloader.peekAdResponseInfo(preloadId)\nif (responseInfo == null) {\n Log.e(TAG, \"Failed to peek ad response info.\")\n return\n}\n\nLog.d(TAG, \"Peeked ad response ID: ${responseInfo.responseId}\")\n```\n\nExample:\n```text\nResponseInfo responseInfo = RewardedAdPreloader.peekAdResponseInfo(preloadId);\nif (responseInfo == null) {\n Log.e(TAG, \"Failed to peek ad response info.\");\n return;\n}\n\nLog.d(TAG, \"Peeked ad response ID: \" + responseInfo.getResponseId());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.777Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":302,"estimatedTokens":1994}}371{"id":"doc-integrate_applovin_with_mediation_android_google-c707fba5","source":"documentation","title":"Integrate AppLovin with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/applovin","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:applovin:13.6.4.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:applovin:13.6.4.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nAppLovinPrivacySettings.setHasUserConsent(true);AppLovinMediationSnippets.java\n```\n\nExample:\n```text\nAppLovinPrivacySettings.setHasUserConsent(true)AppLovinMediationSnippets.kt\n```\n\nExample:\n```text\nAppLovinPrivacySettings.setDoNotSell(true);AppLovinMediationSnippets.java\n```\n\nExample:\n```text\nAppLovinPrivacySettings.setDoNotSell(true)AppLovinMediationSnippets.kt\n```\n\nExample:\n```text\nBundle extras = new AppLovinExtras.Builder()\n .setMuteAudio(true)\n .build();\nAdRequest request = new AdRequest.Builder(AD_UNIT_ID)\n .putAdSourceExtrasBundle(ApplovinAdapter.class, extras)\n .build();\n```\n\nExample:\n```text\nval extras = AppLovinExtras.Builder()\n .setMuteAudio(true)\n .build()\nval request = AdRequest.Builder(AD_UNIT_ID)\n .putAdSourceExtrasBundle(ApplovinAdapter::class.java, extras)\n .build()\n```\n\nExample:\n```text\ncom.google.ads.mediation.applovin.ApplovinAdapter\ncom.google.ads.mediation.applovin.AppLovinMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.779Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":73,"estimatedTokens":441}}372{"id":"doc-integrate_vpon_with_mediation_android_google_for-6c9737b8","source":"documentation","title":"Integrate Vpon with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/vpon","text":"Example:\n```text\ndependencies {\n implementation(fileTree(mapOf(\"dir\" to \"libs\", \"include\" to listOf(\"*.aar\", \"*.jar\"))))\n // ...\n}\n```\n\nExample:\n```text\ndependencies {\n implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'])\n // ...\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.780Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":17,"estimatedTokens":69}}373{"id":"doc-integrate_moloco_with_mediation_android_google_f-30b6b4cf","source":"documentation","title":"Integrate Moloco with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/moloco","text":"Example:\n```text\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:moloco:4.11.0.0\")\n}\n\nconfigurations {\n all {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n }\n}\n```\n\nExample:\n```text\nval privacySettings = PrivacySettings(isUserConsent = true)\nMolocoPrivacy.setPrivacy(privacySettings)MolocoMediationSnippets.kt\n```\n\nExample:\n```text\nPrivacySettings privacySettings =\n new PrivacySettings(\n /* isUserConsent= */ true, /* isAgeRestrictedUser= */ false, /* isDoNotSell= */ false);\nMolocoPrivacy.setPrivacy(privacySettings);MolocoMediationSnippets.java\n```\n\nExample:\n```text\nval privacySettings = PrivacySettings(isDoNotSell = true)\nMolocoPrivacy.setPrivacy(privacySettings)MolocoMediationSnippets.kt\n```\n\nExample:\n```text\ncom.moloco.sdk\ncom.google.ads.mediation.moloco.MolocoMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.781Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":42,"estimatedTokens":260}}374{"id":"doc-set_up_rewarded_interstitial_ads_android_google_-a832ef47","source":"documentation","title":"Set up rewarded interstitial ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/rewarded-interstitial","text":"Example:\n```text\nprivate fun startPreloading(adUnitId: String) {\n // Call start() once after SDK initialization.\n // Preload only one ad unit per format to optimize performance.\n val adRequest = AdRequest.Builder(adUnitId).build()\n val preloadConfig = PreloadConfiguration(adRequest)\n RewardedInterstitialAdPreloader.start(adUnitId, preloadConfig)\n}\n```\n\nExample:\n```text\nprivate void startPreloading(String adUnitId) {\n // Call start() once after SDK initialization.\n // Preload only one ad unit per format to optimize performance.\n AdRequest adRequest = new AdRequest.Builder(adUnitId).build();\n PreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest);\n RewardedInterstitialAdPreloader.start(adUnitId, preloadConfig);\n}\n```\n\nExample:\n```text\nprivate fun pollAndShowAd(activity: Activity, adUnitId: String) {\n // Polling returns the next available ad and loads another ad in the background.\n val ad = RewardedInterstitialAdPreloader.pollAd(adUnitId)\n if (ad == null) {\n Log.e(TAG, \"Rewarded interstitial ad is not available.\")\n return\n }\n\n // Interact with the ad object as needed.\n Log.d(TAG, \"Rewarded interstitial ad response info: ${ad.getResponseInfo()}\")\n ad.adEventCallback =\n object : RewardedInterstitialAdEventCallback {\n override fun onAdImpression() {\n Log.d(TAG, \"Rewarded interstitial ad recorded an impression.\")\n }\n }\n ad.show(activity) { rewardItem -> Log.d(TAG, \"User earned reward: ${rewardItem.amount}\") }\n}\n```\n\nExample:\n```text\nprivate void pollAndShowAd(Activity activity, String adUnitId) {\n // Polling returns the next available ad and loads another ad in the background.\n final RewardedInterstitialAd ad = RewardedInterstitialAdPreloader.pollAd(adUnitId);\n\n // Interact with the ad object as needed.\n if (ad == null) {\n Log.e(TAG, \"Rewarded interstitial ad is not available.\");\n return;\n }\n\n Log.d(TAG, \"Rewarded interstitial ad response info: \" + ad.getResponseInfo());\n ad.setAdEventCallback(\n new RewardedInterstitialAdEventCallback() {\n @Override\n public void onAdImpression() {\n Log.d(TAG, \"Rewarded interstitial ad recorded an impression.\");\n }\n });\n\n // Show the ad.\n ad.show(\n activity,\n rewardItem -> {\n Log.d(TAG, \"User earned reward: \" + rewardItem.getAmount());\n });\n}\n```\n\nExample:\n```text\nprivate fun listenToAdEvents() {\n // Listen for ad events.\n val ad = rewardedInterstitialAd\n if (ad == null) {\n Log.e(TAG, \"Rewarded interstitial ad is not ready yet.\")\n return\n }\n\n ad.adEventCallback =\n object : RewardedInterstitialAdEventCallback {\n override fun onAdShowedFullScreenContent() {\n // Rewarded interstitial ad did show.\n }\n\n override fun onAdDismissedFullScreenContent() {\n // Rewarded interstitial ad did dismiss.\n rewardedInterstitialAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(\n fullScreenContentError: FullScreenContentError\n ) {\n // Rewarded interstitial ad failed to show.\n Log.e(TAG, \"Rewarded interstitial ad failed to show: ${fullScreenContentError.message}\")\n }\n\n override fun onAdImpression() {\n // Rewarded interstitial ad did record an impression.\n }\n\n override fun onAdClicked() {\n // Rewarded interstitial ad did record a click.\n }\n }\n}\n```\n\nExample:\n```text\nprivate void listenToAdEvents() {\n // Listen for ad events.\n if (rewardedInterstitialAd == null) {\n Log.e(TAG, \"Rewarded interstitial ad is not ready yet.\");\n return;\n }\n\n rewardedInterstitialAd.setAdEventCallback(\n new RewardedInterstitialAdEventCallback() {\n @Override\n public void onAdShowedFullScreenContent() {\n // Rewarded interstitial ad did show.\n }\n\n @Override\n public void onAdDismissedFullScreenContent() {\n // Rewarded interstitial ad did dismiss.\n rewardedInterstitialAd = null;\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(\n @NonNull FullScreenContentError fullScreenContentError) {\n // Rewarded interstitial ad failed to show.\n Log.e(\n TAG,\n \"Rewarded interstitial ad failed to show: \" + fullScreenContentError.getMessage());\n }\n\n @Override\n public void onAdImpression() {\n // Rewarded interstitial ad did record an impression.\n }\n\n @Override\n public void onAdClicked() {\n // Rewarded interstitial ad did record a click.\n }\n });\n}\n```\n\nExample:\n```text\nRewardedInterstitialAd.load(\n context,\n AD_UNIT_ID,\n AdRequest.Builder().build(),\n object : RewardedInterstitialAdLoadCallback() {\n override fun onAdLoaded(ad: RewardedInterstitialAd) {\n rewardedInterstitialAd = ad\n val options =\n ServerSideVerificationOptions.Builder().setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\").build()\n rewardedInterstitialAd?.setServerSideVerificationOptions(options)\n }\n },\n)\n```\n\nExample:\n```text\nRewardedInterstitialAd.load(\n context,\n AD_UNIT_ID,\n new AdRequest.Builder().build(),\n new RewardedInterstitialAdLoadCallback() {\n @Override\n public void onAdLoaded(RewardedInterstitialAd ad) {\n rewardedInterstitialAd = ad;\n ServerSideVerificationOptions options =\n new ServerSideVerificationOptions.Builder()\n .setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\")\n .build();\n rewardedInterstitialAd.setServerSideVerificationOptions(options);\n }\n });\n```\n\nExample:\n```text\nval preloadCallback =\n // [Important] Don't call ad preloader start() or pollAd() within the PreloadCallback.\n object : PreloadCallback {\n override fun onAdFailedToPreload(preloadId: String, adError: LoadAdError) {\n Log.d(\n TAG,\n \"Rewarded interstitial preload ad $preloadId failed to load with error: ${adError.message}\",\n )\n }\n\n override fun onAdsExhausted(preloadId: String) {\n Log.i(TAG, \"Rewarded interstitial preload ad $preloadId is not available\")\n // [Important] Don't call ad preloader start() or pollAd() from onAdsExhausted.\n }\n\n override fun onAdPreloaded(preloadId: String, responseInfo: ResponseInfo) {\n Log.i(TAG, \"Rewarded interstitial preload ad $preloadId is available\")\n }\n }\nval adRequest = AdRequest.Builder(adUnitId).build()\nval preloadConfig = PreloadConfiguration(adRequest)\nRewardedInterstitialAdPreloader.start(adUnitId, preloadConfig, preloadCallback)\n```\n\nExample:\n```text\nPreloadCallback preloadCallback =\n // [Important] Don't call ad preloader start() or pollAd() within the PreloadCallback.\n new PreloadCallback() {\n @Override\n public void onAdFailedToPreload(@NonNull String preloadId, @NonNull LoadAdError adError) {\n Log.d(\n TAG,\n String.format(\n \"Rewarded interstitial preload ad %s failed to load with error: %s\",\n preloadId, adError.getMessage()));\n // [Optional] Get the error response info for additional details.\n // ResponseInfo responseInfo = adError.getResponseInfo();\n }\n\n @Override\n public void onAdsExhausted(@NonNull String preloadId) {\n Log.i(TAG, \"Rewarded interstitial preload ad \" + preloadId + \" is not available\");\n // [Important] Don't call ad preloader start() or pollAd() from onAdsExhausted.\n }\n\n @Override\n public void onAdPreloaded(@NonNull String preloadId, @NonNull ResponseInfo responseInfo) {\n Log.i(TAG, \"Rewarded interstitial preload ad \" + preloadId + \" is available\");\n }\n };\nAdRequest adRequest = new AdRequest.Builder(adUnitId).build();\nPreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest);\nRewardedInterstitialAdPreloader.start(adUnitId, preloadConfig, preloadCallback);\n```\n\nExample:\n```text\nprivate fun isAdAvailable(adUnitId: String): Boolean {\n return RewardedInterstitialAdPreloader.isAdAvailable(adUnitId)\n}\n```\n\nExample:\n```text\nprivate boolean isAdAvailable(String adUnitId) {\n return RewardedInterstitialAdPreloader.isAdAvailable(adUnitId);\n}\n```\n\nExample:\n```text\nprivate fun setBufferSize(adUnitId: String) {\n val adRequest = AdRequest.Builder(adUnitId).build()\n // Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\n val preloadConfig = PreloadConfiguration(adRequest, bufferSize = 2)\n RewardedInterstitialAdPreloader.start(adUnitId, preloadConfig)\n}\n```\n\nExample:\n```text\nprivate void setBufferSize(String adUnitId) {\n AdRequest adRequest = new AdRequest.Builder(adUnitId).build();\n // Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\n PreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest, 2);\n RewardedInterstitialAdPreloader.start(adUnitId, preloadConfig);\n}\n```\n\nExample:\n```text\nprivate fun stopPreloading(adUnitId: String) {\n // Stops the preloading and destroy preloaded ads.\n RewardedInterstitialAdPreloader.destroy(adUnitId)\n}\n```\n\nExample:\n```text\nprivate void stopPreloading(String adUnitId) {\n // Stops the preloading and destroy preloaded ads.\n RewardedInterstitialAdPreloader.destroy(adUnitId);\n}\n```\n\nExample:\n```text\nval responseInfo = RewardedInterstitialAdPreloader.peekAdResponseInfo(preloadId)\nif (responseInfo == null) {\n Log.e(TAG, \"Failed to peek ad response info.\")\n return\n}\n\nLog.d(TAG, \"Peeked ad response ID: ${responseInfo.responseId}\")\n```\n\nExample:\n```text\nResponseInfo responseInfo = RewardedInterstitialAdPreloader.peekAdResponseInfo(preloadId);\nif (responseInfo == null) {\n Log.e(TAG, \"Failed to peek ad response info.\");\n return;\n}\n\nLog.d(TAG, \"Peeked ad response ID: \" + responseInfo.getResponseId());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.782Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":324,"estimatedTokens":2444}}375{"id":"doc-integrate_pangle_with_mediation_android_google_f-0a87e696","source":"documentation","title":"Integrate Pangle with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/pangle","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://artifact.bytedance.com/repository/pangle/\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:pangle:8.2.0.4.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:pangle:8.2.0.4.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nPangleMediationAdapter.setPAConsent(PAGConstant.PAGPAConsentType.PAG_PA_CONSENT_TYPE_CONSENT);PangleMediationSnippets.java\n```\n\nExample:\n```text\nPangleMediationAdapter.setPAConsent(PAGConstant.PAGPAConsentType.PAG_PA_CONSENT_TYPE_CONSENT)PangleMediationSnippets.kt\n```\n\nExample:\n```text\ncom.pangle.ads\ncom.google.ads.mediation.pangle.PangleMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.783Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":56,"estimatedTokens":344}}376{"id":"doc-integrate_zucks_with_mediation_android_google_fo-bcd11d45","source":"documentation","title":"Integrate Zucks with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/zucks","text":"Example:\n```text\ndependencies {\n implementation(fileTree(mapOf(\"dir\" to \"libs\", \"include\" to listOf(\"*.aar\", \"*.jar\"))))\n // ...\n}\n```\n\nExample:\n```text\ndependencies {\n implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'])\n // ...\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.784Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":17,"estimatedTokens":69}}377{"id":"doc-integrate_inmobi_with_mediation_android_google_f-373f3021","source":"documentation","title":"Integrate InMobi with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/inmobi","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:inmobi:11.4.0.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:inmobi:11.4.0.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\n<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putString(InMobiNetworkKeys.AGE_GROUP, InMobiNetworkValues.BETWEEN_35_AND_44)\nextras.putString(InMobiNetworkKeys.AREA_CODE, AREA_CODE_VALUE)\nval request = AdRequest.Builder(AD_UNIT_ID)\n .putAdSourceExtrasBundle(InMobiAdapter::class.java, extras)\n .build()\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(InMobiNetworkKeys.AGE_GROUP, InMobiNetworkValues.BETWEEN_35_AND_44);\nextras.putString(InMobiNetworkKeys.AREA_CODE, AREA_CODE_VALUE);\nAdRequest request = new AdRequest.Builder(AD_UNIT_ID)\n .putAdSourceExtrasBundle(InMobiAdapter.class, extras)\n .build();\n```\n\nExample:\n```text\ncom.google.ads.mediation.inmobi.InMobiAdapter\ncom.google.ads.mediation.inmobi.InMobiMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.786Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":60,"estimatedTokens":450}}378{"id":"doc-integrate_mintegral_with_mediation_android_googl-1862b5df","source":"documentation","title":"Integrate Mintegral with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/mintegral","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:mintegral:17.1.71.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:mintegral:17.1.71.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nMBridgeSDK sdk = MBridgeSDKFactory.getMBridgeSDK();\nsdk.setConsentStatus(context, MBridgeConstans.IS_SWITCH_ON);MintegralMediationSnippets.java\n```\n\nExample:\n```text\nval sdk = MBridgeSDKFactory.getMBridgeSDK()\nsdk.setConsentStatus(context, MBridgeConstans.IS_SWITCH_ON)MintegralMediationSnippets.kt\n```\n\nExample:\n```text\nMBridgeSDK mBridgeSDK = MBridgeSDKFactory.getMBridgeSDK();\nmBridgeSDK.setDoNotTrackStatus(false);MintegralMediationSnippets.java\n```\n\nExample:\n```text\nval sdk = MBridgeSDKFactory.getMBridgeSDK()\nsdk.setDoNotTrackStatus(false)MintegralMediationSnippets.kt\n```\n\nExample:\n```text\ncom.mbridge.msdk\ncom.google.ads.mediation.mintegral.MintegralMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.787Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":70,"estimatedTokens":433}}379{"id":"doc-integrate_i_mobile_with_mediation_android_google-d0da951b","source":"documentation","title":"Integrate i-mobile with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/imobile","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://imobile.github.io/adnw-sdk-android\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:imobile:2.3.2.4\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:imobile:2.3.2.4'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.789Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":40,"estimatedTokens":247}}380{"id":"doc-integrate_unity_ads_with_mediation_android_googl-cf58b407","source":"documentation","title":"Integrate Unity Ads with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/unity","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.unity3d.ads:unity-ads:4.19.0\")\n implementation(\"com.google.ads.mediation:unity:4.19.0.1\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.unity3d.ads:unity-ads:4.19.0'\n implementation 'com.google.ads.mediation:unity:4.19.0.1'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nMetaData gdprMetaData = new MetaData(this);\ngdprMetaData.set(\"gdpr.consent\", true);\ngdprMetaData.commit();UnityAdsMediationSnippets.java\n```\n\nExample:\n```text\nval gdprMetaData = MetaData(this)\ngdprMetaData[\"gdpr.consent\"] = true\ngdprMetaData.commit()UnityAdsMediationSnippets.kt\n```\n\nExample:\n```text\nMetaData ccpaMetaData = new MetaData(this);\nccpaMetaData.set(\"privacy.consent\", true);\nccpaMetaData.commit();UnityAdsMediationSnippets.java\n```\n\nExample:\n```text\nval ccpaMetaData = MetaData(this)\nccpaMetaData[\"privacy.consent\"] = true\nccpaMetaData.commit()UnityAdsMediationSnippets.kt\n```\n\nExample:\n```text\ncom.google.ads.mediation.unity.UnityAdapter\ncom.google.ads.mediation.unity.UnityMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.790Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":63,"estimatedTokens":408}}381{"id":"doc-integrate_ly_with_mediation_android_google_for_d-aa79bc08","source":"documentation","title":"Integrate LY with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/line","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:line:3.1.1.1\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:line:3.1.1.1'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nLineMediationAdapter.Companion.setTestMode(true);LineMediationSnippets.java\n```\n\nExample:\n```text\nLineMediationAdapter.setTestMode(true)LineMediationSnippets.kt\n```\n\nExample:\n```text\nLineExtras lineExtras = new LineExtras(/* enableAdSound: */ true);\nBundle extras = lineExtras.build();\n\nAdRequest request =\n new AdRequest.Builder().addNetworkExtrasBundle(LineMediationAdapter.class, extras).build();LineMediationSnippets.java\n```\n\nExample:\n```text\nval lineExtras = LineExtras(enableAdSound = true)\nval extras = lineExtras.build()\n\nval request =\n AdRequest.Builder().addNetworkExtrasBundle(LineMediationAdapter::class.java, extras).build()LineMediationSnippets.kt\n```\n\nExample:\n```text\ncom.line.ads\ncom.google.ads.mediation.line.LineMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.791Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":61,"estimatedTokens":392}}382{"id":"doc-integrate_dt_exchange_with_mediation_android_goo-cd984024","source":"documentation","title":"Integrate DT Exchange with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/dt-exchange","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:fyber:8.4.7.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:fyber:8.4.7.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nInneractiveAdManager.setUSPrivacyString(US_PRIVACY_STRING);DTExchangeMediationSnippets.java\n```\n\nExample:\n```text\nInneractiveAdManager.setUSPrivacyString(US_PRIVACY_STRING)DTExchangeMediationSnippets.kt\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putInt(InneractiveMediationDefs.KEY_AGE, 10);\nextras.putBoolean(FyberMediationAdapter.KEY_MUTE_VIDEO, false);\n\nAdRequest request =\n new AdRequest.Builder().addNetworkExtrasBundle(FyberMediationAdapter.class, extras).build();DTExchangeMediationSnippets.java\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putInt(InneractiveMediationDefs.KEY_AGE, 10)\nextras.putBoolean(FyberMediationAdapter.KEY_MUTE_VIDEO, false)\n\nval request =\n AdRequest.Builder().addNetworkExtrasBundle(FyberMediationAdapter::class.java, extras).build()DTExchangeMediationSnippets.kt\n```\n\nExample:\n```text\ncom.google.ads.mediation.fyber.FyberMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.793Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":62,"estimatedTokens":429}}383{"id":"doc-integrate_meta_audience_network_with_bidding_and-12b9cae5","source":"documentation","title":"Integrate Meta Audience Network with bidding | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/meta","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:facebook:6.22.0.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:facebook:6.22.0.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nval extras = nativeAd.getExtras()\nif (extras.containsKey(FacebookMediationAdapter.KEY_SOCIAL_CONTEXT_ASSET)) {\n var socialContext = extras.getString(FacebookMediationAdapter.KEY_SOCIAL_CONTEXT_ASSET)\n // ...\n}\n```\n\nExample:\n```text\nBundle extras = nativeAd.getExtras();\nif (extras.containsKey(FacebookMediationAdapter.KEY_SOCIAL_CONTEXT_ASSET)) {\n String socialContext = extras.getString(FacebookMediationAdapter.KEY_SOCIAL_CONTEXT_ASSET);\n // ...\n}\n```\n\nExample:\n```text\ncom.google.ads.mediation.facebook.FacebookAdapter\ncom.google.ads.mediation.facebook.FacebookMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.795Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":51,"estimatedTokens":354}}384{"id":"doc-integrate_liftoff_monetize_with_mediation_androi-1f5e49c9","source":"documentation","title":"Integrate Liftoff Monetize with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/liftoff-monetize","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:vungle:7.7.7.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:vungle:7.7.7.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nVunglePrivacySettings.setCCPAStatus(true);LiftoffMonetizeMediationSnippets.java\n```\n\nExample:\n```text\nVunglePrivacySettings.setCCPAStatus(true)LiftoffMonetizeMediationSnippets.kt\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(VungleConstants.KEY_USER_ID, \"myUserID\");\nextras.putInt(VungleConstants.KEY_ORIENTATION, 1);\n// Optional: Enables the back button on App Open ads immediately.\nextras.putBoolean(VungleConstants.KEY_BACK_BUTTON_IMMEDIATELY_ENABLED, true);\n\nAdRequest request =\n new AdRequest.Builder()\n .addNetworkExtrasBundle(VungleAdapter.class, extras) // Rewarded.\n .addNetworkExtrasBundle(VungleInterstitialAdapter.class, extras) // Interstitial.\n // App Open ads use VungleMediationAdapter\n .addNetworkExtrasBundle(VungleMediationAdapter.class, extras)\n .build();LiftoffMonetizeMediationSnippets.java\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putString(VungleConstants.KEY_USER_ID, \"myUserID\")\nextras.putInt(VungleConstants.KEY_ORIENTATION, 1)\n// Optional: Enables the back button on App Open ads immediately.\nextras.putBoolean(VungleConstants.KEY_BACK_BUTTON_IMMEDIATELY_ENABLED, true)\n\nval request =\n AdRequest.Builder()\n .addNetworkExtrasBundle(VungleAdapter::class.java, extras) // Rewarded.\n .addNetworkExtrasBundle(VungleInterstitialAdapter::class.java, extras) // Interstitial.\n // App Open ads use VungleMediationAdapter\n .addNetworkExtrasBundle(VungleMediationAdapter::class.java, extras)\n .build()LiftoffMonetizeMediationSnippets.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.796Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":71,"estimatedTokens":590}}385{"id":"doc-integrate_maio_with_mediation_android_google_for-317bb53c","source":"documentation","title":"Integrate maio with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/maio","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://imobile-maio.github.io/maven\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:maio:2.0.9.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:maio:2.0.9.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.797Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":40,"estimatedTokens":244}}386{"id":"doc-integrate_pubmatic_openwrap_beta_with_admob_medi-4f55c63a","source":"documentation","title":"Integrate PubMatic OpenWrap (Beta) with AdMob Mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/pubmatic","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://repo.pubmatic.com/artifactory/public-repos\")\n }\n }\n}\n```\n\nExample:\n```text\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:pubmatic:5.2.0.0\")\n}\n\nconfigurations {\n all {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n }\n}\n```\n\nExample:\n```text\ncom.pubmatic.sdk\ncom.google.ads.mediation.pubmatic\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.798Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":35,"estimatedTokens":169}}387{"id":"doc-integrate_ironsource_with_mediation_android_goog-f2e0249d","source":"documentation","title":"Integrate ironSource with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/ironsource","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://android-sdk.is.com/\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:ironsource:9.5.0.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:ironsource:9.5.0.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nLevelPlay.setMetaData(\"do_not_sell\", \"true\");IronSourceMediationSnippets.java\n```\n\nExample:\n```text\nLevelPlay.setMetaData(\"do_not_sell\", \"true\")IronSourceMediationSnippets.kt\n```\n\nExample:\n```text\ncom.google.ads.mediation.ironsource.IronSourceAdapter\ncom.google.ads.mediation.ironsource.IronSourceRewardedAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.800Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":56,"estimatedTokens":329}}388{"id":"doc-integrate_mytarget_with_mediation_android_google-02c71e18","source":"documentation","title":"Integrate myTarget with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/mediation/mytarget","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n implementation(\"com.google.ads.mediation:mytarget:5.51.2.0\")\n}\n\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n implementation 'com.google.ads.mediation:mytarget:5.51.2.0'\n}\n\nconfigurations.configureEach {\n exclude group: 'com.google.android.gms', module: 'play-services-ads'\n exclude group: 'com.google.android.gms', module: 'play-services-ads-lite'\n}\n```\n\nExample:\n```text\nMyTargetPrivacy.setUserConsent(true);MyTargetMediationSnippets.java\n```\n\nExample:\n```text\nMyTargetPrivacy.setUserConsent(true)MyTargetMediationSnippets.kt\n```\n\nExample:\n```text\nMyTargetPrivacy.setUserAgeRestricted(true);MyTargetMediationSnippets.java\n```\n\nExample:\n```text\nMyTargetPrivacy.setUserAgeRestricted(true)MyTargetMediationSnippets.kt\n```\n\nExample:\n```text\nMyTargetPrivacy.setCcpaUserConsent(true);MyTargetMediationSnippets.java\n```\n\nExample:\n```text\nMyTargetPrivacy.setCcpaUserConsent(true)MyTargetMediationSnippets.kt\n```\n\nExample:\n```text\ncom.google.ads.mediation.mytarget.MyTargetAdapter\ncom.google.ads.mediation.mytarget.MyTargetNativeAdapter\ncom.google.ads.mediation.mytarget.MyTargetRewardedAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.801Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":64,"estimatedTokens":385}}389{"id":"doc-disable_ad_inspector_android_google_for_develope-f49a7527","source":"documentation","title":"Disable ad inspector | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/ad-inspector/disable-ad-inspector","text":"Example:\n```text\nandroid {\n buildTypes {\n getByName(\"debug\") {\n manifestPlaceholders[\"disableAdInspector\"] = \"false\"\n }\n getByName(\"release\") {\n optimization {\n enable = false\n }\n manifestPlaceholders[\"disableAdInspector\"] = \"false\"\n }\n create(\"profile\") {\n initWith(getByName(\"release\"))\n manifestPlaceholders[\"disableAdInspector\"] = \"true\"\n }\n }\n}\n```\n\nExample:\n```text\nandroid {\n buildTypes {\n release {\n manifestPlaceholders = [disableAdInspector: \"false\"]\n }\n debug {\n manifestPlaceholders = [disableAdInspector: \"false\"]\n }\n create(\"profile\") {\n initWith(getByName(\"release\"))\n manifestPlaceholders = [disableAdInspector: \"true\"]\n }\n }\n}\n```\n\nExample:\n```text\n<application>\n<!-- Dynamically enable or disable ad inspector based on build type -->\n<meta-data\n android:name=\n \"com.google.android.libraries.ads.mobile.sdk.flag.DISABLE_AD_INSPECTOR\"\n android:value=\"${disableAdInspector}\" />\n</application>\n```\n\nExample:\n```text\nAd inspector is disabled in the AndroidManifest.xml.\n```\n\nExample:\n```text\nAd inspector cannot be opened because it is disabled in the AndroidManifest.xml.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.803Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":61,"estimatedTokens":334}}390{"id":"doc-launch_ad_inspector_android_google_for_developer-1de2628c","source":"documentation","title":"Launch ad inspector | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/ad-inspector/launch-ad-inspector","text":"Example:\n```text\nMobileAds.openAdInspector(context, new OnAdInspectorClosedListener() {\n public void onAdInspectorClosed(@Nullable AdInspectorError error) {\n // Error will be non-null if ad inspector closed due to an error.\n }\n});\n```\n\nExample:\n```text\nMobileAds.openAdInspector(context) { error ->\n // Error will be non-null if ad inspector closed due to an error.\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.803Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":17,"estimatedTokens":98}}391{"id":"doc-test_ad_units_android_google_for_developers-c57a6813","source":"documentation","title":"Test ad units | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/ad-inspector/test-ad-units","text":"Example:\n```text\nAd Unit has no applicable adapter for single ad source testing on network: AD_SOURCE_ADAPTER_CLASS_NAME\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.804Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":35}}392{"id":"doc-interstitial_ads_custom_events_android_google_fo-2c7ce546","source":"documentation","title":"Interstitial ads custom events | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/custom-events/interstitial","text":"Example:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationInterstitialAd;\nimport com.google.android.gms.ads.mediation.MediationInterstitialAdCallback;\n...\n\npublic class SampleCustomEvent extends Adapter {\n private SampleInterstitialCustomEventLoader interstitialLoader;\n @Override\n public void loadInterstitialAd(\n @NonNull MediationInterstitialAdConfiguration adConfiguration,\n @NonNull\n MediationAdLoadCallback<MediationInterstitialAd, MediationInterstitialAdCallback>\n callback) {\n interstitialLoader = new SampleInterstitialCustomEventLoader(adConfiguration, callback);\n interstitialLoader.loadAd();\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationInterstitialAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationInterstitialAd;\nimport com.google.android.gms.ads.mediation.MediationInterstitialAdCallback;\n...\n\npublic class SampleInterstitialCustomEventLoader extends SampleAdListener\n implements MediationInterstitialAd {\n\n /** A sample third-party SDK interstitial ad. */\n private SampleInterstitial sampleInterstitialAd;\n\n /** Configuration for requesting the interstitial ad from the third-party network. */\n private final MediationInterstitialAdConfiguration mediationInterstitialAdConfiguration;\n\n /** Callback for interstitial ad events. */\n private MediationInterstitialAdCallback interstitialAdCallback;\n\n /** Callback that fires on loading success or failure. */\n private final MediationAdLoadCallback<MediationInterstitialAd, MediationInterstitialAdCallback>\n mediationAdLoadCallback;\n\n /** Constructor. */\n public SampleInterstitialCustomEventLoader(\n @NonNull MediationInterstitialAdConfiguration mediationInterstitialAdConfiguration,\n @NonNull MediationAdLoadCallback<MediationInterstitialAd, MediationInterstitialAdCallback>\n mediationAdLoadCallback) {\n this.mediationInterstitialAdConfiguration = mediationInterstitialAdConfiguration;\n this.mediationAdLoadCallback = mediationAdLoadCallback;\n }\n\n /** Loads the interstitial ad from the third-party ad network. */\n public void loadAd() {\n // All custom events have a server parameter named \"parameter\" that returns\n // back the parameter entered into the UI when defining the custom event.\n Log.i(\"InterstitialCustomEvent\", \"Begin loading interstitial ad.\");\n String serverParameter = mediationInterstitialAdConfiguration.getServerParameters().getString(\n MediationConfiguration.CUSTOM_EVENT_SERVER_PARAMETER_FIELD);\n Log.d(\"InterstitialCustomEvent\", \"Received server parameter.\");\n\n sampleInterstitialAd =\n new SampleInterstitial(mediationInterstitialAdConfiguration.getContext());\n sampleInterstitialAd.setAdUnit(serverParameter);\n\n // Implement a SampleAdListener and forward callbacks to mediation.\n sampleInterstitialAd.setAdListener(this);\n\n // Make an ad request.\n Log.i(\"InterstitialCustomEvent\", \"start fetching interstitial ad.\");\n sampleInterstitialAd.fetchAd(\n SampleCustomEvent.createSampleRequest(mediationInterstitialAdConfiguration));\n }\n\npublic SampleAdRequest createSampleRequest(\n MediationAdConfiguration mediationAdConfiguration) {\n SampleAdRequest request = new SampleAdRequest();\n request.setTestMode(mediationAdConfiguration.isTestRequest());\n request.setKeywords(mediationAdConfiguration.getMediationExtras().keySet());\n return request;\n }\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdFetchSucceeded() {\n interstitialAdCallback = mediationAdLoadCallback.onSuccess(this);\n}\n\n@Override\npublic void onAdFetchFailed(SampleErrorCode errorCode) {\n mediationAdLoadCallback.onFailure(SampleCustomEventError.createSampleSdkError(errorCode));\n}\n```\n\nExample:\n```text\n@Override\npublic void showAd(@NonNull Context context) {\n sampleInterstitialAd.show();\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdFullScreen() {\n interstitialAdCallback.reportAdImpression();\n interstitialAdCallback.onAdOpened();\n}\n\n@Override\npublic void onAdClosed() {\n interstitialAdCallback.onAdClosed();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.805Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":129,"estimatedTokens":1137}}393{"id":"doc-native_ads_custom_events_android_google_for_deve-ac221a80","source":"documentation","title":"Native ads custom events | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/custom-events/native","text":"Example:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\n\nimport com.google.android.gms.ads.mediation.MediationNativeAdCallback;\n...\npublic class SampleCustomEvent extends Adapter {\n private SampleNativeCustomEventLoader nativeLoader;\n\n @Override\n public void loadNativeAd(\n @NonNull MediationNativeAdConfiguration adConfiguration,\n @NonNull MediationAdLoadCallback<UnifiedNativeAdMapper, MediationNativeAdCallback> callback) {\n nativeLoader = new SampleNativeCustomEventLoader(adConfiguration, callback);\n nativeLoader.loadAd();\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationNativeAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationNativeAdCallback;\n...\n\npublic class SampleNativeCustomEventLoader extends SampleNativeAdListener {\n /** Configuration for requesting the native ad from the third-party network. */\n private final MediationNativeAdConfiguration mediationNativeAdConfiguration;\n\n /** Callback that fires on loading success or failure. */\n private final MediationAdLoadCallback<UnifiedNativeAdMapper, MediationNativeAdCallback>\n mediationAdLoadCallback;\n\n /** Callback for native ad events. */\n private MediationNativeAdCallback nativeAdCallback;\n\n /** Constructor */\n public SampleNativeCustomEventLoader(\n @NonNull MediationNativeAdConfiguration mediationNativeAdConfiguration,\n @NonNull MediationAdLoadCallback<MediationNativeAd, MediationNativeAdCallback>\n mediationAdLoadCallback) {\n this.mediationNativeAdConfiguration = mediationNativeAdConfiguration;\n this.mediationAdLoadCallback = mediationAdLoadCallback;\n }\n\n /** Loads the native ad from the third-party ad network. */\n public void loadAd() {\n // Create one of the Sample SDK's ad loaders to request ads.\n Log.i(\"NativeCustomEvent\", \"Begin loading native ad.\");\n SampleNativeAdLoader loader =\n new SampleNativeAdLoader(mediationNativeAdConfiguration.getContext());\n\n // All custom events have a server parameter named \"parameter\" that returns\n // back the parameter entered into the UI when defining the custom event.\n String serverParameter = mediationNativeAdConfiguration\n .getServerParameters()\n .getString(MediationConfiguration\n .CUSTOM_EVENT_SERVER_PARAMETER_FIELD);\n Log.d(\"NativeCustomEvent\", \"Received server parameter.\");\n\n loader.setAdUnit(serverParameter);\n\n // Create a native request to give to the SampleNativeAdLoader.\n SampleNativeAdRequest request = new SampleNativeAdRequest();\n NativeAdOptions options = mediationNativeAdConfiguration.getNativeAdOptions();\n if (options != null) {\n // If the NativeAdOptions' shouldReturnUrlsForImageAssets is true, the adapter should\n // send just the URLs for the images.\n request.setShouldDownloadImages(!options.shouldReturnUrlsForImageAssets());\n\n request.setShouldDownloadMultipleImages(options.shouldRequestMultipleImages());\n switch (options.getMediaAspectRatio()) {\n case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_LANDSCAPE:\n request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_LANDSCAPE);\n break;\n case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_PORTRAIT:\n request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_PORTRAIT);\n break;\n case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_SQUARE:\n case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_ANY:\n case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_UNKNOWN:\n default:\n request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_ANY);\n }\n }\n\n loader.setNativeAdListener(this);\n\n // Begin a request.\n Log.i(\"NativeCustomEvent\", \"Start fetching native ad.\");\n loader.fetchAd(request);\n }\n}\n```\n\nExample:\n```text\n@Override\npublic void onNativeAdFetched(SampleNativeAd ad) {\n SampleUnifiedNativeAdMapper mapper = new SampleUnifiedNativeAdMapper(ad);\n mediationNativeAdCallback = mediationAdLoadCallback.onSuccess(mapper);\n}\n\n@Override\npublic void onAdFetchFailed(SampleErrorCode errorCode) {\n mediationAdLoadCallback.onFailure(SampleCustomEventError.createSampleSdkError(errorCode));\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.UnifiedNativeAdMapper;\nimport com.google.android.gms.ads.nativead.NativeAd;\n...\n\npublic class SampleUnifiedNativeAdMapper extends UnifiedNativeAdMapper {\n\n private final SampleNativeAd sampleAd;\n\n public SampleUnifiedNativeAdMapper(SampleNativeAd ad) {\n sampleAd = ad;\n setHeadline(sampleAd.getHeadline());\n setBody(sampleAd.getBody());\n setCallToAction(sampleAd.getCallToAction());\n setStarRating(sampleAd.getStarRating());\n setStore(sampleAd.getStoreName());\n setIcon(\n new SampleNativeMappedImage(\n ad.getIcon(), ad.getIconUri(), SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));\n setAdvertiser(ad.getAdvertiser());\n\n List<NativeAd.Image> imagesList = new ArrayList<NativeAd.Image>();\n imagesList.add(new SampleNativeMappedImage(ad.getImage(), ad.getImageUri(),\n SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));\n setImages(imagesList);\n\n if (sampleAd.getPrice() != null) {\n NumberFormat formatter = NumberFormat.getCurrencyInstance();\n String priceString = formatter.format(sampleAd.getPrice());\n setPrice(priceString);\n }\n\n Bundle extras = new Bundle();\n extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness());\n this.setExtras(extras);\n\n setOverrideClickHandling(false);\n setOverrideImpressionRecording(false);\n\n setAdChoicesContent(sampleAd.getInformationIcon());\n }\n\n @Override\n public void recordImpression() {\n sampleAd.recordImpression();\n }\n\n @Override\n public void handleClick(View view) {\n sampleAd.handleClick(view);\n }\n\n // The Sample SDK doesn't do its own impression/click tracking, instead relies on its\n // publishers calling the recordImpression and handleClick methods on its native ad object. So\n // there's no need to pass a reference to the View being used to display the native ad. If\n // your mediated network does need a reference to the view, the following method can be used\n // to provide one.\n\n @Override\n public void trackViews(View containerView, Map<String, View> clickableAssetViews,\n Map<String, View> nonClickableAssetViews) {\n super.trackViews(containerView, clickableAssetViews, nonClickableAssetViews);\n // If your ad network SDK does its own impression tracking, here is where you can track the\n // top level native ad view and its individual asset views.\n }\n\n @Override\n public void untrackView(View view) {\n super.untrackView(view);\n // Here you would remove any trackers from the View added in trackView.\n }\n}\n```\n\nExample:\n```text\nif (sampleAd.getPrice() != null) {\n NumberFormat formatter = NumberFormat.getCurrencyInstance();\n String priceString = formatter.format(sampleAd.getPrice());\n setPrice(priceString);\n}\n```\n\nExample:\n```text\npublic class SampleNativeMappedImage extends NativeAd.Image {\n\n private Drawable drawable;\n private Uri imageUri;\n private double scale;\n\n public SampleNativeMappedImage(Drawable drawable, Uri imageUri, double scale) {\n this.drawable = drawable;\n this.imageUri = imageUri;\n this.scale = scale;\n }\n\n @Override\n public Drawable getDrawable() {\n return drawable;\n }\n\n @Override\n public Uri getUri() {\n return imageUri;\n }\n\n @Override\n public double getScale() {\n return scale;\n }\n}\n```\n\nExample:\n```text\nsetIcon(new SampleNativeMappedImage(ad.getAppIcon(), ad.getAppIconUri(),\n SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness());\nthis.setExtras(extras);\n```\n\nExample:\n```text\npublic SampleNativeAdMapper(SampleNativeAd ad) {\n ...\n setAdChoicesContent(sampleAd.getInformationIcon());\n}\n```\n\nExample:\n```text\n@Override\npublic void recordImpression() {\n sampleAd.recordImpression();\n}\n\n@Override\npublic void handleClick(View view) {\n sampleAd.handleClick(view);\n}\n```\n\nExample:\n```text\nsetOverrideClickHandling(true);\nsetOverrideImpressionRecording(true);\n```\n\nExample:\n```text\n@Override\npublic void trackViews(View containerView,\n Map<String, View> clickableAssetViews,\n Map<String, View> nonClickableAssetViews) {\n sampleAd.setNativeAdViewForTracking(containerView);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.807Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":285,"estimatedTokens":2240}}394{"id":"doc-rewarded_ads_custom_events_android_google_for_de-64f54b50","source":"documentation","title":"Rewarded ads custom events | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/custom-events/rewarded","text":"Example:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationRewardedAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationRewardedAd;\nimport com.google.android.gms.ads.mediation.MediationRewardedAdCallback;\n...\n\npublic class SampleCustomEvent extends Adapter {\n\n private SampleNativeCustomEventLoader nativeLoader;\n\n @Override\n public void loadRewardedAd(\n @NonNull MediationRewardedAdConfiguration mediationRewardedAdConfiguration,\n @NonNull\n MediationAdLoadCallback<MediationRewardedAd, MediationRewardedAdCallback>\n mediationAdLoadCallback) {\n rewardedLoader =\n new SampleRewardedCustomEventLoader(\n mediationRewardedAdConfiguration, mediationAdLoadCallback);\n rewardedLoader.loadAd();\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationRewardedAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationRewardedAd;\nimport com.google.android.gms.ads.mediation.MediationRewardedAdCallback;\n...\n\npublic class SampleRewardedCustomEventLoader extends SampleRewardedAdListener\n implements MediationRewardedAd {\n\n /** Configuration for requesting the rewarded ad from the third-party network. */\n private final MediationRewardedAdConfiguration mediationRewardedAdConfiguration;\n\n /**\n * A {@link MediationAdLoadCallback} that handles any callback when a Sample\n * rewarded ad finishes loading.\n */\n private final MediationAdLoadCallback<MediationRewardedAd, MediationRewardedAdCallback>\n mediationAdLoadCallback;\n\n /** Callback for rewarded ad events. */\n private MediationRewardedAdCallback rewardedAdCallback;\n\n /** Constructor. */\n public SampleRewardedCustomEventLoader(\n @NonNull MediationRewardedAdConfiguration mediationRewardedAdConfiguration,\n @NonNull MediationAdLoadCallback<MediationRewardedAd, MediationRewardedAdCallback>\n mediationAdLoadCallback) {\n this.mediationRewardedAdConfiguration = mediationRewardedAdConfiguration;\n this.mediationAdLoadCallback = mediationAdLoadCallback;\n }\n\n /** Loads the rewarded ad from the third-party ad network. */\n public void loadAd() {\n // All custom events have a server parameter named \"parameter\" that returns\n // back the parameter entered into the AdMob UI when defining the custom event.\n Log.i(\"RewardedCustomEvent\", \"Begin loading rewarded ad.\");\n String serverParameter = mediationRewardedAdConfiguration\n .getServerParameters()\n .getString(MediationConfiguration\n .CUSTOM_EVENT_SERVER_PARAMETER_FIELD);\n Log.d(\"RewardedCustomEvent\", \"Received server parameter.\");\n SampleAdRequest request = createSampleRequest(mediationRewardedAdConfiguration);\n sampleRewardedAd = new SampleRewardedAd(serverParameter);\n sampleRewardedAd.setListener(this);\n Log.i(\"RewardedCustomEvent\", \"Start fetching rewarded ad.\");\n sampleRewardedAd.loadAd(request);\n }\n\n public SampleAdRequest createSampleRequest(\n MediationAdConfiguration mediationAdConfiguration) {\n SampleAdRequest request = new SampleAdRequest();\n request.setTestMode(mediationAdConfiguration.isTestRequest());\n request.setKeywords(mediationAdConfiguration.getMediationExtras().keySet());\n return request;\n }\n}\n```\n\nExample:\n```text\n@Override\npublic void onRewardedAdLoaded() {\n rewardedAdCallback = mediationAdLoadCallback.onSuccess(this);\n}\n\n@Override\npublic void onRewardedAdFailedToLoad(SampleErrorCode errorCode) {\n mediationAdLoadCallback.onFailure(SampleCustomEventError.createSampleSdkError(errorCode));\n}\n```\n\nExample:\n```text\n@Override\npublic void showAd(Context context) {\n if (!(context instanceof Activity)) {\n rewardedAdCallback.onAdFailedToShow(\n SampleCustomEventError.createCustomEventNoActivityContextError());\n return;\n }\n Activity activity = (Activity) context;\n\n if (!sampleRewardedAd.isAdAvailable()) {\n rewardedAdCallback.onAdFailedToShow(\n SampleCustomEventError.createCustomEventAdNotAvailableError());\n return;\n }\n sampleRewardedAd.showAd(activity);\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdRewarded(final String rewardType, final int amount) {\n RewardItem rewardItem =\n new RewardItem() {\n @Override\n public String getType() {\n return rewardType;\n }\n\n @Override\n public int getAmount() {\n return amount;\n }\n };\n rewardedAdCallback.onUserEarnedReward(rewardItem);\n}\n\n@Override\npublic void onAdClicked() {\n rewardedAdCallback.reportAdClicked();\n}\n\n@Override\npublic void onAdFullScreen() {\n rewardedAdCallback.onAdOpened();\n rewardedAdCallback.onVideoStart();\n rewardedAdCallback.reportAdImpression();\n}\n\n@Override\npublic void onAdClosed() {\n rewardedAdCallback.onAdClosed();\n}\n\n@Override\npublic void onAdCompleted() {\n rewardedAdCallback.onVideoComplete();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.808Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":169,"estimatedTokens":1325}}395{"id":"doc-banner_ads_custom_events_android_google_for_deve-0398f142","source":"documentation","title":"Banner ads custom events | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/custom-events/banner","text":"Example:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationBannerAd;\nimport com.google.android.gms.ads.mediation.MediationBannerAdCallback;\n...\n\npublic class SampleCustomEvent extends Adapter {\n private SampleBannerCustomEventLoader bannerLoader;\n @Override\n public void loadBannerAd(\n @NonNull MediationBannerAdConfiguration adConfiguration,\n @NonNull MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback> callback) {\n bannerLoader = new SampleBannerCustomEventLoader(adConfiguration, callback);\n bannerLoader.loadAd();\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationBannerAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationBannerAd;\nimport com.google.android.gms.ads.mediation.MediationBannerAdCallback;\n...\n\npublic class SampleBannerCustomEventLoader extends SampleAdListener implements MediationBannerAd {\n\n /** View to contain the sample banner ad. */\n private SampleAdView sampleAdView;\n\n /** Configuration for requesting the banner ad from the third-party network. */\n private final MediationBannerAdConfiguration mediationBannerAdConfiguration;\n\n /** Callback that fires on loading success or failure. */\n private final MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback>\n mediationAdLoadCallback;\n\n /** Callback for banner ad events. */\n private MediationBannerAdCallback bannerAdCallback;\n\n /** Constructor. */\n public SampleBannerCustomEventLoader(\n @NonNull MediationBannerAdConfiguration mediationBannerAdConfiguration,\n @NonNull MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback>\n mediationAdLoadCallback) {\n this.mediationBannerAdConfiguration = mediationBannerAdConfiguration;\n this.mediationAdLoadCallback = mediationAdLoadCallback;\n }\n\n /** Loads a banner ad from the third-party ad network. */\n public void loadAd() {\n // All custom events have a server parameter named \"parameter\" that returns\n // back the parameter entered into the UI when defining the custom event.\n Log.i(\"BannerCustomEvent\", \"Begin loading banner ad.\");\n String serverParameter =\n mediationBannerAdConfiguration.getServerParameters().getString(\n MediationConfiguration.CUSTOM_EVENT_SERVER_PARAMETER_FIELD);\n\n Log.d(\"BannerCustomEvent\", \"Received server parameter.\");\n\n Context context = mediationBannerAdConfiguration.getContext();\n sampleAdView = new SampleAdView(context);\n\n // Assumes that the serverParameter is the ad unit of the Sample Network.\n sampleAdView.setAdUnit(serverParameter);\n AdSize size = mediationBannerAdConfiguration.getAdSize();\n\n // Internally, smart banners use constants to represent their ad size, which\n // means a call to AdSize.getHeight could return a negative value. You can\n // accommodate this by using AdSize.getHeightInPixels and\n // AdSize.getWidthInPixels instead, and then adjusting to match the device's\n // display metrics.\n int widthInPixels = size.getWidthInPixels(context);\n int heightInPixels = size.getHeightInPixels(context);\n DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();\n int widthInDp = Math.round(widthInPixels / displayMetrics.density);\n int heightInDp = Math.round(heightInPixels / displayMetrics.density);\n\n sampleAdView.setSize(new SampleAdSize(widthInDp, heightInDp));\n sampleAdView.setAdListener(this);\n\n SampleAdRequest request = createSampleRequest(mediationBannerAdConfiguration);\n Log.i(\"BannerCustomEvent\", \"Start fetching banner ad.\");\n sampleAdView.fetchAd(request);\n }\n\n public SampleAdRequest createSampleRequest(\n MediationAdConfiguration mediationAdConfiguration) {\n SampleAdRequest request = new SampleAdRequest();\n request.setTestMode(mediationAdConfiguration.isTestRequest());\n request.setKeywords(mediationAdConfiguration.getMediationExtras().keySet());\n return request;\n }\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdFetchSucceeded() {\n bannerAdCallback = mediationAdLoadCallback.onSuccess(this);\n}\n\n@Override\npublic void onAdFetchFailed(SampleErrorCode errorCode) {\n mediationAdLoadCallback.onFailure(SampleCustomEventError.createSampleSdkError(errorCode));\n}\n```\n\nExample:\n```text\n@Override\n@NonNull\npublic View getView() {\n return sampleAdView;\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdFullScreen() {\n bannerAdCallback.onAdOpened();\n bannerAdCallback.reportAdClicked();\n}\n\n@Override\npublic void onAdClosed() {\n bannerAdCallback.onAdClosed();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.808Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":142,"estimatedTokens":1249}}396{"id":"doc-test_creative_types_android_google_for_developer-4524e71f","source":"documentation","title":"Test creative types | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/test-creative-types","text":"Example:\n```text\nval extras = Bundle()\nextras.putString(\"ft_ctype\", \"video_app_install\")\n\nval request = AdRequest\n .Builder(AD_UNIT_ID)\n .setGoogleExtrasBundle(extras)\n .build()\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(\"ft_ctype\", \"video_app_install\");\n\nAdRequest request = new AdRequest\n .Builder(AD_UNIT_ID)\n .setGoogleExtrasBundle(extras)\n .build();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.809Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":23,"estimatedTokens":101}}397{"id":"doc-set_up_custom_events_android_google_for_develope-21c431ed","source":"documentation","title":"Set up custom events | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/custom-events/setup","text":"Example:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.InitializationCompleteCallback;\nimport com.google.android.gms.ads.mediation.MediationConfiguration;\n\npublic class SampleAdNetworkCustomEvent extends Adapter {\n private static final String SAMPLE_AD_UNIT_KEY = \"parameter\";\n\n @Override\n public void initialize(Context context,\n InitializationCompleteCallback initializationCompleteCallback,\n List<MediationConfiguration> mediationConfigurations) {\n // This is where you will initialize the SDK that this custom\n // event is built for. Upon finishing the SDK initialization,\n // call the completion handler with success.\n initializationCompleteCallback.onInitializationSucceeded();\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent\n\nimport com.google.android.gms.ads.mediation.Adapter\nimport com.google.android.gms.ads.mediation.InitializationCompleteCallback\nimport com.google.android.gms.ads.mediation.MediationConfiguration\n\nclass SampleCustomEvent : Adapter() {\n private val SAMPLE_AD_UNIT_KEY = \"parameter\"\n\n override fun initialize(\n context: Context,\n initializationCompleteCallback: InitializationCompleteCallback,\n mediationConfigurations: List<MediationConfiguration>\n ) {\n // This is where you will initialize the SDK that this custom\n // event is built for. Upon finishing the SDK initialization,\n // call the completion handler with success.\n initializationCompleteCallback.onInitializationSucceeded()\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\npublic class SampleCustomEvent extends Adapter {\n\n @Override\n public VersionInfo getVersionInfo() {\n String versionString = new VersionInfo(1, 2, 3);\n String[] splits = versionString.split(\"\\\\.\");\n\n if (splits.length >= 4) {\n int major = Integer.parseInt(splits[0]);\n int minor = Integer.parseInt(splits[1]);\n int micro = Integer.parseInt(splits[2]) * 100 + Integer.parseInt(splits[3]);\n return new VersionInfo(major, minor, micro);\n }\n\n return new VersionInfo(0, 0, 0);\n }\n\n @Override\n public VersionInfo getSDKVersionInfo() {\n String versionString = SampleAdRequest.getSDKVersion();\n String[] splits = versionString.split(\"\\\\.\");\n\n if (splits.length >= 3) {\n int major = Integer.parseInt(splits[0]);\n int minor = Integer.parseInt(splits[1]);\n int micro = Integer.parseInt(splits[2]);\n return new VersionInfo(major, minor, micro);\n }\n\n return new VersionInfo(0, 0, 0);\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent\n\nclass SampleCustomEvent : Adapter() {\n override fun getVersionInfo(): VersionInfo {\n val versionString = VersionInfo(1,2,3).toString()\n val splits: List<String> = versionString.split(\"\\\\.\")\n\n if (splits.count() >= 4) {\n val major = splits[0].toInt()\n val minor = splits[1].toInt()\n val micro = (splits[2].toInt() * 100) + splits[3].toInt()\n return VersionInfo(major, minor, micro)\n }\n\n return VersionInfo(0, 0, 0)\n }\n\n override fun getSDKVersionInfo(): VersionInfo {\n val versionString = VersionInfo(1,2,3).toString()\n val splits: List<String> = versionString.split(\"\\\\.\")\n\n if (splits.count() >= 3) {\n val major = splits[0].toInt()\n val minor = splits[1].toInt()\n val micro = splits[2].toInt()\n return VersionInfo(major, minor, micro)\n }\n\n return VersionInfo(0, 0, 0)\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.810Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":121,"estimatedTokens":896}}398{"id":"doc-integrate_the_webview_api_for_ads_android_google-438977ff","source":"documentation","title":"Integrate the WebView API for Ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/browser/webview/api-for-ads","text":"Example:\n```text\nMobileAds.initialize(\n this@MainActivity,\n // Use this application ID to initialize the GMA Next-Gen SDK if\n // you don't have an AdMob application ID.\n InitializationConfig.Builder(InitializationConfig.WEBVIEW_APIS_FOR_ADS_APPLICATION_ID)\n .build(),\n ) {\n // Adapter initialization complete.\n }\n```\n\nExample:\n```text\nMobileAds.initialize(\n this,\n // Use this application ID to initialize the GMA Next-Gen SDK if\n // you don't have an AdMob application ID.\n new InitializationConfig.Builder(InitializationConfig.WEBVIEW_APIS_FOR_ADS_APPLICATION_ID)\n .build(),\n initializationStatus -> {\n // Adapter initialization is complete.\n });\n```\n\nExample:\n```text\nimport android.webkit.CookieManager\nimport android.webkit.WebView\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds\n\nclass MainActivity : AppCompatActivity() {\n lateinit var webView: WebView\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n webView = findViewById(R.id.webview)\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)\n // Let the web view use JavaScript.\n webView.settings.javaScriptEnabled = true\n // Let the web view access local storage.\n webView.settings.domStorageEnabled = true\n // Let HTML videos play automatically.\n webView.settings.mediaPlaybackRequiresUserGesture = false\n\n // Register the web view.\n MobileAds.registerWebView(webView)\n }\n}\n```\n\nExample:\n```text\nimport android.webkit.CookieManager;\nimport android.webkit.WebView;\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds;\n\npublic class MainActivity extends AppCompatActivity {\n private WebView webView;\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n webView = findViewById(R.id.webview);\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);\n // Let the web view use JavaScript.\n webView.getSettings().setJavaScriptEnabled(true);\n // Let the web view access local storage.\n webView.getSettings().setDomStorageEnabled(true);\n // Let HTML videos play automatically.\n webView.getSettings().setMediaPlaybackRequiresUserGesture(false);\n\n // Register the web view.\n MobileAds.registerWebView(webView);\n }\n}\n```\n\nExample:\n```text\nhttps://google.github.io/webview-ads/test/#api-for-ads-tests\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.810Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":91,"estimatedTokens":662}}399{"id":"doc-optimize_webview_click_behavior_android_google_f-cda59638","source":"documentation","title":"Optimize WebView click behavior | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/browser/webview/click-behavior","text":"Example:\n```text\ndependencies {\n implementation 'androidx.browser:browser:1.5.0'\n}\n```\n\nExample:\n```text\npublic class MainActivity extends AppCompatActivity {\n\n private WebView webView;\n\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n\n // ... Register the WebView.\n\n webView = new WebView(this);\n WebSettings webSettings = webView.getSettings();\n webSettings.setJavaScriptEnabled(true);\n webView.setWebViewClient(\n new WebViewClient() {\n // 1. Implement the web view click handler.\n @Override\n public boolean shouldOverrideUrlLoading(\n WebView view,\n WebResourceRequest request) {\n // 2. Determine whether to override the behavior of the URL.\n // If the target URL has no host and no scheme, return early.\n if (request.getUrl().getHost() == null && request.getUrl().getScheme() == null) {\n return false;\n }\n\n // Handle custom URL schemes such as market:// by attempting to\n // launch the corresponding application in a new intent.\n if (!request.getUrl().getScheme().equals(\"http\")\n && !request.getUrl().getScheme().equals(\"https\")) {\n Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());\n // If the URL cannot be opened, return early.\n try {\n MainActivity.this.startActivity(intent);\n } catch (ActivityNotFoundException exception) {\n Log.d(\"TAG\", \"Failed to load URL with scheme:\" + request.getUrl().getScheme());\n }\n return true;\n }\n\n String currentDomain;\n // If the current URL's host cannot be found, return early.\n try {\n currentDomain = new URI(view.getUrl()).toURL().getHost();\n } catch (URISyntaxException | MalformedURLException exception) {\n // Malformed URL.\n return false;\n }\n String targetDomain = request.getUrl().getHost();\n\n // If the current domain equals the target domain, the\n // assumption is the user is not navigating away from\n // the site. Reload the URL within the existing web view.\n if (currentDomain.equals(targetDomain)) {\n return false;\n }\n\n // 3. User is navigating away from the site, open the URL in\n // Custom Tabs to preserve the state of the web view.\n CustomTabsIntent intent = new CustomTabsIntent.Builder().build();\n intent.launchUrl(MainActivity.this, request.getUrl());\n return true;\n }\n });\n }\n}\n```\n\nExample:\n```text\nclass MainActivity : AppCompatActivity() {\n\n private lateinit var webView: WebView\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n\n // ... Register the WebView.\n\n webView.webViewClient = object : WebViewClient() {\n // 1. Implement the web view click handler.\n override fun shouldOverrideUrlLoading(\n view: WebView?,\n request: WebResourceRequest?\n ): Boolean {\n // 2. Determine whether to override the behavior of the URL.\n // If the target URL has no host and no scheme, return early.\n if (request?.url?.host == null && request.url.scheme == null) {\n return false\n }\n val currentDomain = URI(view?.url).toURL().host\n\n // Handle custom URL schemes such as market:// by attempting to\n // launch the corresponding application in a new intent.\n if (!request.url.scheme.equals(\"http\") &&\n !request.url.scheme.equals(\"https\")) {\n val intent = Intent(Intent.ACTION_VIEW, request.url)\n // If the URL cannot be opened, return early.\n try {\n this@MainActivity.startActivity(intent)\n } catch (exception: ActivityNotFoundException) {\n Log.d(\"TAG\", \"Failed to load URL with scheme: ${request.url.scheme}\")\n }\n return true\n }\n\n val targetDomain = request.url.host\n\n // If the current domain equals the target domain, the\n // assumption is the user is not navigating away from\n // the site. Reload the URL within the existing web view.\n if (currentDomain.equals(targetDomain)) {\n return false\n }\n\n // 3. User is navigating away from the site, open the URL in\n // Custom Tabs to preserve the state of the web view.\n val customTabsIntent = CustomTabsIntent.Builder().build()\n customTabsIntent.launchUrl(this@MainActivity, request.url)\n return true\n }\n }\n }\n}\n```\n\nExample:\n```text\nhttps://google.github.io/webview-ads/test/#click-behavior-tests\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.811Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":140,"estimatedTokens":1214}}400{"id":"doc-authorized_sellers_for_apps_app_ads_txt_android_-4665ec51","source":"documentation","title":"Authorized Sellers for Apps (app-ads.txt) | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/app-ads","text":"Example:\n```text\ngoogle.com, pub-00000000000000, DIRECT, f08c47fec0942fa0\n```\n\nExample:\n```text\nfirebase init\n```\n\nExample:\n```text\nfirebase deploy --only hosting\n```\n\nExample:\n```text\n\"hosting\": {\n ...\n \"redirects\": [\n {\n \"source\": \"/\",\n \"destination\": \"URL_TO_REDIRECT\",\n \"type\": 301\n }\n ]\n}\n```\n\nExample:\n```text\n\"hosting\": {\n ...\n \"redirects\": [\n {\n \"source\": \"/\",\n \"destination\": \"https://www.example.com\",\n \"type\": 301\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.812Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":44,"estimatedTokens":125}}401{"id":"doc-set_up_charles_proxy_for_gma_next_gen_sdk_on_and-b9c775a8","source":"documentation","title":"Set up Charles proxy for GMA Next-Gen SDK on Android N or Higher | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/charles","text":"Example:\n```text\n<network-security-config>\n <debug-overrides>\n <trust-anchors>\n <!-- Trust user added CAs while debuggable only -->\n <certificates src=\"user\" />\n </trust-anchors>\n </debug-overrides>\n</network-security-config>\n```\n\nExample:\n```text\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest ... >\n <application ...\n android:networkSecurityConfig=\"@xml/network_security_config\"\n ... >\n ...\n </application>\n</manifest>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.813Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":25,"estimatedTokens":131}}402{"id":"doc-set_up_webview_android_google_for_developers-bd81fc7c","source":"documentation","title":"Set up WebView | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/browser/webview","text":"Example:\n```text\nCookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);\n```\n\nExample:\n```text\nCookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)\n```\n\nExample:\n```text\nimport android.webkit.CookieManager;\nimport android.webkit.WebView;\n\npublic class MainActivity extends AppCompatActivity {\n private WebView webView;\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n webView = findViewById(R.id.webview);\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);\n // Let the web view use JavaScript.\n webView.getSettings().setJavaScriptEnabled(true);\n // Let the web view access local storage.\n webView.getSettings().setDomStorageEnabled(true);\n // Let HTML videos play automatically.\n webView.getSettings().setMediaPlaybackRequiresUserGesture(false);\n }\n}\n```\n\nExample:\n```text\nimport android.webkit.CookieManager\nimport android.webkit.WebView\n\nclass MainActivity : AppCompatActivity() {\n lateinit var webView: WebView\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n webView = findViewById(R.id.webview)\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)\n // Let the web view use JavaScript.\n webView.settings.javaScriptEnabled = true\n // Let the web view access local storage.\n webView.settings.domStorageEnabled = true\n // Let HTML videos play automatically.\n webView.settings.mediaPlaybackRequiresUserGesture = false\n }\n}\n```\n\nExample:\n```text\nimport android.webkit.CookieManager;\nimport android.webkit.WebView;\n\npublic class MainActivity extends AppCompatActivity {\n private WebView webView;\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n webView = findViewById(R.id.webview);\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);\n // Let the web view use JavaScript.\n webView.getSettings().setJavaScriptEnabled(true);\n // Let the web view access local storage.\n webView.getSettings().setDomStorageEnabled(true);\n // Let HTML videos play automatically.\n webView.getSettings().setMediaPlaybackRequiresUserGesture(false);\n\n // Load the URL for optimized web view performance.\n webView.loadUrl(\"https://google.github.io/webview-ads/test/\");\n }\n}\n```\n\nExample:\n```text\nimport android.webkit.CookieManager\nimport android.webkit.WebView\n\nclass MainActivity : AppCompatActivity() {\n lateinit var webView: WebView\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n webView = findViewById(R.id.webview)\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)\n // Let the web view use JavaScript.\n webView.settings.javaScriptEnabled = true\n // Let the web view access local storage.\n webView.settings.domStorageEnabled = true\n // Let HTML videos play automatically.\n webView.settings.mediaPlaybackRequiresUserGesture = false\n\n // Load the URL for optimized web view performance.\n webView.loadUrl(\"https://google.github.io/webview-ads/test/\")\n }\n}\n```\n\nExample:\n```text\nhttps://google.github.io/webview-ads/test/\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.814Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":124,"estimatedTokens":911}}403{"id":"doc-targeting_android_google_for_developers-70d26fbd","source":"documentation","title":"Targeting | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/targeting","text":"Example:\n```text\nval requestConfiguration = RequestConfiguration\n .Builder()\n // Set your targeting tags.\n .setTagForChildDirectedTreatment(RequestConfiguration.TagForChildDirectedTreatment.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE)\n .build()\n\nMobileAds.setRequestConfiguration(requestConfiguration)\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration = new RequestConfiguration\n .Builder()\n // Set your targeting tags.\n .setTagForChildDirectedTreatment(TagForChildDirectedTreatment.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE)\n .build();\n\nMobileAds.setRequestConfiguration(requestConfiguration);\n```\n\nExample:\n```text\nval requestConfiguration = RequestConfiguration\n .Builder()\n // Set your targeting tags.\n .setTagForChildDirectedTreatment(RequestConfiguration.TagForChildDirectedTreatment.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE)\n .build()\n\nCoroutineScope(Dispatchers.IO).launch {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this@MainActivity,\n InitializationConfig\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n .Builder(\"SAMPLE_APP_ID\")\n .setRequestConfiguration(requestConfiguration)\n .build()\n ) {\n // Adapter initialization is complete.\n }\n // Other methods on MobileAds can now be called.\n}\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration = new RequestConfiguration\n .Builder()\n // Set your targeting tags.\n .setTagForChildDirectedTreatment(TagForChildDirectedTreatment.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE)\n .build();\n\nnew Thread(\n () -> {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this,\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n new InitializationConfig\n .Builder(\"SAMPLE_APP_ID\")\n .setRequestConfiguration(requestConfiguration)\n .build(),\n initializationStatus -> {\n // Adapter initialization is complete.\n });\n // Other methods on MobileAds can now be called.\n })\n .start();\n```\n\nExample:\n```text\nval requestConfiguration =\n RequestConfiguration.Builder()\n // Indicate that ad requests should have child age treatment.\n .setAgeRestrictedTreatment(AgeRestrictedTreatment.CHILD)\n .build()\nMobileAds.setRequestConfiguration(requestConfiguration)RequestConfigurationSnippets.kt\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration =\n new RequestConfiguration.Builder()\n // Indicate that ad requests should have child age treatment.\n .setAgeRestrictedTreatment(AgeRestrictedTreatment.CHILD)\n .build();\nMobileAds.setRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.java\n```\n\nExample:\n```text\nval requestConfiguration = RequestConfiguration\n .Builder()\n .setTagForChildDirectedTreatment(RequestConfiguration.TagForChildDirectedTreatment.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE)\n .build()\n\nMobileAds.setRequestConfiguration(requestConfiguration)\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration = new RequestConfiguration\n .Builder()\n .setTagForChildDirectedTreatment(TagForChildDirectedTreatment.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE)\n .build();\n\nMobileAds.setRequestConfiguration(requestConfiguration);\n```\n\nExample:\n```text\nval requestConfiguration = RequestConfiguration\n .Builder()\n .setTagForUnderAgeOfConsent(RequestConfiguration.TagForUnderAgeOfConsent.TAG_FOR_UNDER_AGE_OF_CONSENT_TRUE)\n .build()\n\nMobileAds.setRequestConfiguration(requestConfiguration)\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration = new RequestConfiguration\n .Builder()\n .setTagForUnderAgeOfConsent(TagForUnderAgeOfConsent.TAG_FOR_UNDER_AGE_OF_CONSENT_TRUE)\n .build();\n\nMobileAds.setRequestConfiguration(requestConfiguration);\n```\n\nExample:\n```text\nval requestConfiguration = RequestConfiguration\n .Builder()\n .setMaxAdContentRating(RequestConfiguration.MaxAdContentRating.MAX_AD_CONTENT_RATING_G)\n .build()\n\nMobileAds.setRequestConfiguration(requestConfiguration)\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration = new RequestConfiguration\n .Builder()\n .setMaxAdContentRating(MaxAdContentRating.MAX_AD_CONTENT_RATING_G)\n .build();\n\nMobileAds.setRequestConfiguration(requestConfiguration);\n```\n\nExample:\n```text\nval requestConfiguration = RequestConfiguration\n .Builder()\n .setPublisherPrivacyPersonalizationState(RequestConfiguration.PublisherPrivacyPersonalizationState.DISABLED)\n .build()\n\nMobileAds.setRequestConfiguration(requestConfiguration)\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration = new RequestConfiguration\n .Builder()\n .setPublisherPrivacyPersonalizationState(RequestConfiguration.PublisherPrivacyPersonalizationState.DISABLED)\n .build();\n\nMobileAds.setRequestConfiguration(requestConfiguration);\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putString(\"collapsible\", \"bottom\")\nval adRequest =\n NativeAdRequest.Builder(\"AD_UNIT_ID\", listOf(NativeAd.NativeAdType.NATIVE))\n .setGoogleExtrasBundle(extras)\n .build()\nNativeAdLoader.load(adRequest, adCallback)\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(\"collapsible\", \"bottom\");\nNativeAdRequest adRequest =\n new NativeAdRequest.Builder(\"AD_UNIT_ID\", Arrays.asList(NativeAd.NativeAdType.NATIVE))\n .setGoogleExtrasBundle(extras)\n .build();\nNativeAdLoader.load(adRequest, adCallback);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.815Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":195,"estimatedTokens":1364}}404{"id":"doc-global_settings_android_google_for_developers-9543b025","source":"documentation","title":"Global settings | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/global-settings","text":"Example:\n```text\noverride fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n\n val backgroundScope = CoroutineScope(Dispatchers.IO)\n backgroundScope.launch {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this@MainActivity,\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n InitializationConfig.Builder(\"SAMPLE_APP_ID\").build()\n ) {}\n \n // Set app volume to be half of current device volume.\n MobileAds.setUserControlledAppVolume(0.5f)\n }\n}\n```\n\nExample:\n```text\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n\n new Thread(\n () -> {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this,\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n new InitializationConfig.Builder(\"SAMPLE_APP_ID\")\n .build(),\n initializationStatus -> {\n });\n \n // Set app volume to be half of current device volume.\n MobileAds.setUserControlledAppVolume(0.5f);\n })\n .start();\n}\n```\n\nExample:\n```text\nMobileAds.setUserMutedApp(true)\n```\n\nExample:\n```text\nMobileAds.setUserMutedApp(true);\n```\n\nExample:\n```text\nval sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context)\n// Set the value to 0 to enable limited ads.\nsharedPrefs.edit().putInt(\"gad_has_consent_for_cookies\", 0).apply()\n```\n\nExample:\n```text\nContext activity = getActivity();\nSharedPreferences sharedPreferences =\n PreferenceManager.getDefaultSharedPreferences(activity);\n// Set the value to 0 to enable limited ads.\nsharedPreferences.edit().putInt(\"gad_has_consent_for_cookies\", 0).apply();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.815Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":71,"estimatedTokens":462}}405{"id":"doc-retrieve_information_about_the_ad_response_andro-f07d50d8","source":"documentation","title":"Retrieve information about the ad response | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/response-info","text":"Example:\n```text\noverride fun onAdLoaded(interstitialAd: InterstitialAd)) {\n val responseInfo = interstitialAd.responseInfo\n Log.d(TAG, responseInfo.toString())\n}\n\noverride fun onAdFailedToLoad(adError: LoadAdError) {\n val responseInfo = adError.responseInfo\n Log.d(TAG, responseInfo.toString())\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdLoaded(@NonNull InterstitialAd interstitialAd) {\n ResponseInfo responseInfo = interstitialAd.getResponseInfo();\n Log.d(TAG, responseInfo.toString());\n}\n\n@Override\npublic void onAdFailedToLoad(LoadAdError loadAdError) {\n ResponseInfo responseInfo = loadAdError.getResponseInfo();\n Log.d(TAG, responseInfo.toString());\n}\n```\n\nExample:\n```text\n{\n \"Response ID\": \"COOllLGxlPoCFdAx4Aod-Q4A0g\",\n \"Mediation Adapter Class Name\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Adapter Responses\": [\n {\n \"Adapter\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Latency\": 328,\n \"Ad Source Name\": \"Reservation campaign\",\n \"Ad Source ID\": \"7068401028668408324\",\n \"Ad Source Instance Name\": \"[DO NOT EDIT] Publisher Test Interstitial\",\n \"Ad Source Instance ID\": \"4665218928925097\",\n \"Credentials\": {},\n \"Ad Error\": \"null\"\n }\n ],\n \"Loaded Adapter Response\": {\n \"Adapter\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Latency\": 328,\n \"Ad Source Name\": \"Reservation campaign\",\n \"Ad Source ID\": \"7068401028668408324\",\n \"Ad Source Instance Name\": \"[DO NOT EDIT] Publisher Test Interstitial\",\n \"Ad Source Instance ID\": \"4665218928925097\",\n \"Credentials\": {},\n \"Ad Error\": \"null\"\n },\n \"Response Extras\": {\n \"mediation_group_name\": \"Campaign\"\n }\n}\n```\n\nExample:\n```text\noverride fun onAdLoaded(interstitialAd: InterstitialAd) {\n val responseInfo = interstitialAd.responseInfo\n\n val responseId = responseInfo.responseId\n val adapterClassName = responseInfo.adapterClassName\n val adSourceResponses = responseInfo.adSourceResponses\n val loadedAdSourceResponse = responseInfo.loadedAdSourceResponse\n val extras = responseInfo.responseExtras\n val mediationGroupName = extras.getString(\"mediation_group_name\")\n val mediationABTestName = extras.getString(\"mediation_ab_test_name\")\n val mediationABTestVariant = extras.getString(\"mediation_ab_test_variant\")\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdLoaded(@NonNull InterstitialAd interstitialAd) {\n MyActivity.this.interstitialAd = interstitialAd;\n\n ResponseInfo responseInfo = interstitialAd.getResponseInfo();\n String responseId = responseInfo.getResponseId();\n String adapterClassName = responseInfo.getAdapterClassName();\n List<AdSourceResponseInfo> adSourceResponses = responseInfo.getAdSourceResponses();\n AdSourceResponseInfo loadedAdSourceResponse = responseInfo.getLoadedAdSourceResponse();\n Bundle extras = responseInfo.getResponseExtras();\n String mediationGroupName = extras.getString(\"mediation_group_name\");\n String mediationABTestName = extras.getString(\"mediation_ab_test_name\");\n String mediationABTestVariant = extras.getString(\"mediation_ab_test_variant\");\n}\n```\n\nExample:\n```text\n{\n \"Adapter\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Latency\": 328,\n \"Ad Source Name\": \"Reservation campaign\",\n \"Ad Source ID\": \"7068401028668408324\",\n \"Ad Source Instance Name\": \"[DO NOT EDIT] Publisher Test Interstitial\",\n \"Ad Source Instance ID\": \"4665218928925097\",\n \"Credentials\": {},\n \"Ad Error\": \"null\"\n}\n```\n\nExample:\n```text\noverride fun onAdLoaded(interstitialAd: InterstitialAds) {\n val loadedAdSourceResponseInfo = interstitialAd.responseInfo.loadedAdSourceResponse\n\n val adError = loadedAdSourceResponseInfo.adError\n val adSourceId = loadedAdSourceResponseInfo.id\n val adSourceInstanceId = loadedAdSourceResponseInfo.instanceId\n val adSourceInstanceName = loadedAdSourceResponseInfo.instanceName\n val adSourceName = loadedAdSourceResponseInfo.name\n val adapterClassName = loadedAdSourceResponseInfo.adapterClassName\n val credentials = loadedAdSourceResponseInfo.credentials\n val latencyMillis = loadedAdSourceResponseInfo.latencyMillis\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdLoaded(@NonNull InterstitialAd interstitialAd) {\n AdSourceResponseInfo loadedAdSourceResponseInfo =\n interstitialAd.getResponseInfo().getLoadedAdSourceResponse();\n\n AdError adError = loadedAdSourceResponseInfo.getAdError();\n String adSourceId = loadedAdSourceResponseInfo.getId();\n String adSourceInstanceId = loadedAdSourceResponseInfo.getInstanceId();\n String adSourceInstanceName = loadedAdSourceResponseInfo.getInstanceName();\n String adSourceName = loadedAdSourceResponseInfo.getName();\n String adapterClassName = loadedAdSourceResponseInfo.getAdapterClassName();\n Bundle credentials = loadedAdSourceResponseInfo.getCredentials();\n long latencyMillis = loadedAdSourceResponseInfo.getLatencyMillis();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.816Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":144,"estimatedTokens":1215}}406{"id":"doc-optimize_custom_tabs_android_google_for_develope-ef7d40e7","source":"documentation","title":"Optimize Custom Tabs | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/browser/custom-tabs","text":"Example:\n```text\nMobileAds.initialize(\n this@MainActivity,\n // Use this application ID to initialize the GMA Next-Gen SDK if\n // you don't have an AdMob application ID.\n InitializationConfig.Builder(InitializationConfig.WEBVIEW_APIS_FOR_ADS_APPLICATION_ID)\n .build(),\n ) {\n // Adapter initialization complete.\n }\n```\n\nExample:\n```text\nMobileAds.initialize(\n this,\n // Use this application ID to initialize the GMA Next-Gen SDK if\n // you don't have an AdMob application ID.\n new InitializationConfig.Builder(InitializationConfig.WEBVIEW_APIS_FOR_ADS_APPLICATION_ID)\n .build(),\n initializationStatus -> {\n // Adapter initialization is complete.\n });\n```\n\nExample:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds\n\nclass MainActivity : ComponentActivity() {\n private var customTabsClient: CustomTabsClient? = null\n private var customTabsSession: CustomTabsSession? = null\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n\n // Get the default browser package name, this will be null if\n // the default browser does not provide a CustomTabsService.\n val packageName = CustomTabsClient.getPackageName(applicationContext, null);\n if (packageName == null) {\n // Do nothing as service connection is not supported.\n return\n }\n\n CustomTabsClient.bindCustomTabsService(\n applicationContext,\n packageName,\n object : CustomTabsServiceConnection() {\n override fun onCustomTabsServiceConnected(\n name: ComponentName, client: CustomTabsClient,\n ) {\n customTabsClient = client\n\n // Warm up the browser process.\n customTabsClient?.warmup(0L)\n // Create a new browser session using the GMA Next-Gen SDK.\n customTabsSession = MobileAds.INSTANCE.registerCustomTabsSession(\n client,\n // Checks the \"Digital Asset Link\" to connect the postMessage channel.\n ORIGIN,\n // Optional parameter to receive the delegated callbacks.\n customTabsCallback\n )\n\n // Create a new browser session if the GMA Next-Gen SDK is\n // unable to create one.\n if (customTabsSession == null) {\n customTabsSession = client.newSession(customTabsCallback)\n }\n\n // Pass the custom tabs session into the intent.\n val customTabsIntent = CustomTabsIntent.Builder(customTabsSession).build()\n customTabsIntent.launchUrl(this@MainActivity,\n Uri.parse(\"YOUR_URL\"))\n }\n\n override fun onServiceDisconnected(componentName: ComponentName) {\n // Remove the custom tabs client and custom tabs session.\n customTabsClient = null\n customTabsSession = null\n }\n })\n }\n\n // Listen for events from the CustomTabsSession delegated by the GMA Next-Gen SDK.\n private val customTabsCallback: CustomTabsCallback = object : CustomTabsCallback() {\n @Synchronized\n override fun onNavigationEvent(navigationEvent: Int, extras: Bundle?) {\n // Called when a navigation event happens.\n }\n\n @Synchronized\n override fun onMessageChannelReady(extras: Bundle?) {\n // Called when the channel is ready for sending and receiving messages on both\n // ends.\n // This frequently happens, such as each time the SDK requests a\n // new channel.\n }\n\n @Synchronized\n override fun onPostMessage(message: String, extras: Bundle?) {\n // Called when a tab controlled by this CustomTabsSession has sent a postMessage.\n }\n\n override fun onRelationshipValidationResult(\n relation: Int, requestedOrigin: Uri, result: Boolean, extras: Bundle?\n ) {\n // Called when a relationship validation result is available.\n }\n\n override fun onActivityResized(height: Int, width: Int, extras: Bundle) {\n // Called when the tab is resized.\n }\n\n override fun extraCallback(callbackName: String, args: Bundle?) {\n\n }\n\n override fun extraCallbackWithResult(callbackName: String, args: Bundle?): Bundle? {\n return null\n }\n }\n\n companion object {\n // Replace this URL with an associated website.\n const val ORIGIN = \"https://www.google.com\"\n }\n}\n```\n\nExample:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds;\n\nclass MainActivity extends ComponentActivity {\n // Replace this URL with an associated website.\n private static final String ORIGIN = \"https://www.google.com\";\n private CustomTabsClient customTabsClient;\n private CustomTabsSession customTabsSession;\n\n @Override\n protected void onCreate(@Nullable Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n\n // Get the default browser package name, this will be null if\n // the default browser does not provide a CustomTabsService.\n String packageName = CustomTabsClient.getPackageName(getApplicationContext(), null);\n if (packageName == null) {\n // Do nothing as service connection is not supported.\n return;\n }\n\n CustomTabsClient.bindCustomTabsService(\n getApplicationContext(),\n packageName,\n new CustomTabsServiceConnection() {\n @Override\n public void onCustomTabsServiceConnected(@NonNull ComponentName name,\n @NonNull CustomTabsClient client) {\n customTabsClient = client;\n\n // Warm up the browser process.\n customTabsClient.warmup(0);\n // Create a new browser session using the GMA Next-Gen SDK.\n customTabsSession = MobileAds.INSTANCE.registerCustomTabsSession(\n client,\n // Checks the \"Digital Asset Link\" to connect the postMessage channel.\n ORIGIN,\n // Optional parameter to receive the delegated callbacks.\n customTabsCallback);\n\n // Create a new browser session if the GMA Next-Gen SDK is\n // unable to create one.\n if (customTabsSession == null) {\n customTabsSession = client.newSession(customTabsCallback);\n }\n\n // Pass the custom tabs session into the intent.\n CustomTabsIntent intent = new CustomTabsIntent.Builder(customTabsSession).build();\n intent.launchUrl(MainActivity.this, Uri.parse(\"YOUR_URL\"));\n }\n\n @Override\n public void onServiceDisconnected(ComponentName componentName) {\n // Remove the custom tabs client and custom tabs session.\n customTabsClient = null;\n customTabsSession = null;\n }\n }\n\n );\n }\n\n // Listen for events from the CustomTabsSession delegated by the GMA Next-Gen SDK.\n private final CustomTabsCallback customTabsCallback = new CustomTabsCallback() {\n @Override\n public void onNavigationEvent(int navigationEvent, @Nullable Bundle extras) {\n // Called when a navigation event happens.\n super.onNavigationEvent(navigationEvent, extras);\n }\n\n @Override\n public void onMessageChannelReady(@Nullable Bundle extras) {\n // Called when the channel is ready for sending and receiving messages on both\n // ends.\n // This frequently happens, such as each time the SDK requests a\n // new channel.\n super.onMessageChannelReady(extras);\n }\n\n @Override\n public void onPostMessage(@NonNull String message, @Nullable Bundle extras) {\n // Called when a tab controlled by this CustomTabsSession has sent a postMessage.\n super.onPostMessage(message, extras);\n }\n\n @Override\n public void onRelationshipValidationResult(int relation, @NonNull Uri requestedOrigin,\n boolean result, @Nullable Bundle extras) {\n // Called when a relationship validation result is available.\n super.onRelationshipValidationResult(relation, requestedOrigin, result, extras);\n }\n\n @Override\n public void onActivityResized(int height, int width, @NonNull Bundle extras) {\n // Called when the tab is resized.\n super.onActivityResized(height, width, extras);\n }\n\n @Override\n public void extraCallback(@NonNull String callbackName, @Nullable Bundle args) {\n super.extraCallback(callbackName, args);\n }\n\n @Nullable\n @Override\n public Bundle extraCallbackWithResult(@NonNull String callbackName, @Nullable Bundle args) {\n return super.extraCallbackWithResult(callbackName, args);\n }\n };\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.817Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":246,"estimatedTokens":2147}}407{"id":"doc-ad_load_errors_android_google_for_developers-08303d26","source":"documentation","title":"Ad load errors | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/ad-load-errors","text":"Example:\n```text\noverride fun onAdFailedToLoad(adError: LoadAdError) {\n // Gets the error code. See\n // https://developers.google.com/admob/android/early-access/nextgen/reference/com/google/android/libraries/ads/mobile/sdk/common/LoadAdError.ErrorCode\n // for a list of possible codes.\n val errorCode = adError.code\n // Gets an error message.\n // For example \"Account not approved yet\". See\n // https://support.google.com/admob/answer/9905175 for explanations of\n // common errors.\n val errorMessage = adError.message\n // Gets additional response information about the request. See\n // https://developers.google.com/admob/android/next-gen/response-info\n // for more information.\n val responseInfo = adError.responseInfo\n // All of this information is available using the error's toString() method.\n Log.d(\"Ads\", adError.toString())\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Gets the error code. See\n // https://developers.google.com/admob/android/early-access/nextgen/reference/com/google/android/libraries/ads/mobile/sdk/common/LoadAdError.ErrorCode\n // for a list of possible codes.\n ErrorCode errorCode = adError.getCode();\n // Gets an error message.\n // For example \"Account not approved yet\". See\n // https://support.google.com/admob/answer/9905175 for explanations of\n // common errors.\n String errorMessage = adError.getMessage();\n // Gets additional response information about the request. See\n // https://developers.google.com/admob/android/next-gen/response-info\n // for more information.\n ResponseInfo responseInfo = adError.getResponseInfo();\n // All of this information is available using the error's toString() method.\n Log.d(\"Ads\", adError.toString());\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.817Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":44,"estimatedTokens":444}}408{"id":"doc-validate_server_side_verification_ssv_callbacks_-5b6b0042","source":"documentation","title":"Validate server-side verification (SSV) callbacks | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/ssv","text":"Example:\n```text\nRewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()\n .fetchVerifyingPublicKeysWith(\n RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)\n .build();\nString rewardUrl = ...;\nverifier.verify(rewardUrl);\n```\n\nExample:\n```text\nRewardedAd.load(\n AdRequest.Builder(\"AD_UNIT_ID\").build(),\n object : AdLoadCallback<RewardedAd> {\n override fun onAdLoaded(ad: RewardedAd) {\n // Rewarded ad loaded.\n rewardedAd = ad;\n rewardedAd.setServerSideVerificationOptions(\n ServerSideVerificationOptions(\"userId\", \"customData\")\n )\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // Rewarded ad failed to load.\n rewardedAd = null\n }\n },\n )\n```\n\nExample:\n```text\nRewardedAd.load(\n new AdRequest.Builder(\"AD_UNIT_ID\").build(),\n new AdLoadCallback<RewardedAd>() {\n @Override\n public void onAdLoaded(@NonNull RewardedAd ad) {\n // Rewarded ad loaded.\n rewardedAd = ad;\n rewardedAd.setServerSideVerificationOptions(\n new ServerSideVerificationOptions(\"userId\", \"customData\")\n );\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Rewarded ad failed to load.\n rewardedAd = null;\n }\n }\n );\n```\n\nExample:\n```text\n{\n \"keys\": [\n {\n keyId: 1916455855,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...YTPcw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFkwEwYHKoZIzj0CAQYI...ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw==\"\n },\n {\n keyId: 3901585526,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...aDUsw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFYwEAYHKoZIzj0CAQYF...4akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw==\"\n },\n ],\n}\n```\n\nExample:\n```text\nString url = ...;\nNetHttpTransport httpTransport = new NetHttpTransport.Builder().build();\nHttpRequest httpRequest =\n httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(url));\nHttpResponse httpResponse = httpRequest.execute();\nif (httpResponse.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) {\n throw new IOException(\"Unexpected status code = \" + httpResponse.getStatusCode());\n}\nString data;\nInputStream contentStream = httpResponse.getContent();\ntry {\n InputStreamReader reader = new InputStreamReader(contentStream, UTF_8);\n data = readerToString(reader);\n} finally {\n contentStream.close();\n}\n```\n\nExample:\n```text\nprivate static Map<Integer, ECPublicKey> parsePublicKeysJson(String publicKeysJson)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = new HashMap<>();\n try {\n JSONArray keys = new JSONObject(publicKeysJson).getJSONArray(\"keys\");\n for (int i = 0; i < keys.length(); i++) {\n JSONObject key = keys.getJSONObject(i);\n publicKeys.put(\n key.getInt(\"keyId\"),\n EllipticCurves.getEcPublicKey(Base64.decode(key.getString(\"base64\"))));\n }\n } catch (JSONException e) {\n throw new GeneralSecurityException(\"failed to extract trusted signing public keys\", e);\n }\n if (publicKeys.isEmpty()) {\n throw new GeneralSecurityException(\"No trusted keys are available.\");\n }\n return publicKeys;\n}\n```\n\nExample:\n```devsite-click-to-copy\nhttps://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins\n×tamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887\n```\n\nExample:\n```text\npublic static final String SIGNATURE_PARAM_NAME = \"signature=\";\n...\nURI uri;\ntry {\n uri = new URI(rewardUrl);\n} catch (URISyntaxException ex) {\n throw new GeneralSecurityException(ex);\n}\nString queryString = uri.getQuery();\nint i = queryString.indexOf(SIGNATURE_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a signature query parameter\");\n}\nbyte[] queryParamContentData =\n queryString\n .substring(0, i - 1)\n // i - 1 instead of i because of & in the query string\n .getBytes(Charset.forName(\"UTF-8\"));\n```\n\nExample:\n```text\npublic static final String KEY_ID_PARAM_NAME = \"key_id=\";\n...\nString sigAndKeyId = queryString.substring(i);\ni = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a key_id query parameter\");\n}\nString sig =\n sigAndKeyId.substring(\n SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */);\nint keyId = Integer.valueOf(sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length()));\n```\n\nExample:\n```text\nprivate void verify(final byte[] dataToVerify, int keyId, final byte[] signature)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = parsePublicKeysJson();\n if (publicKeys.containsKey(keyId)) {\n foundKeyId = true;\n ECPublicKey publicKey = publicKeys.get(keyId);\n EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, HashType.SHA256, EcdsaEncoding.DER);\n verifier.verify(signature, dataToVerify);\n } else {\n throw new GeneralSecurityException(\"cannot find verifying key with key ID: \" + keyId);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.818Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":175,"estimatedTokens":1270}}409{"id":"doc-mobileads_android_google_for_developers-8fdb8b05","source":"documentation","title":"MobileAds | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/reference/kotlin/com/google/android/libraries/ads/mobile/sdk/MobileAds","text":"Example:\n```text\nclass MobileAds\n```\n\nExample:\n```text\nsuspend fun generateSignal(request: SignalRequest): SignalGenerationResult\n```\n\nExample:\n```text\nfun generateSignal( request: SignalRequest, callback: SignalGenerationCallback): Unit\n```\n\nExample:\n```text\nfun getInitializationStatus(): InitializationStatus\n```\n\nExample:\n```text\nfun getRequestConfiguration(): RequestConfiguration\n```\n\nExample:\n```text\nfun getVersion(): VersionInfo\n```\n\nExample:\n```text\n@WorkerThread@RequiresPermission(value = \"android.permission.INTERNET\")fun initialize(context: Context, initializationConfig: InitializationConfig): Unit\n```\n\nExample:\n```text\n@WorkerThread@RequiresPermission(value = \"android.permission.INTERNET\")fun initialize( context: Context, initializationConfig: InitializationConfig, listener: OnAdapterInitializationCompleteListener?): Unit\n```\n\nExample:\n```text\n@ExperimentalApifun initializeAdapters( adapterInitializationConfig: AdapterInitializationConfig): Unit\n```\n\nExample:\n```text\n@ExperimentalApifun initializeAdapters( adapterInitializationConfig: AdapterInitializationConfig, listener: OnAdapterInitializationCompleteListener?): Unit\n```\n\nExample:\n```text\nfun openAdInspector(listener: OnAdInspectorClosedListener): Unit\n```\n\nExample:\n```text\nfun openDebugMenu(activity: Activity, adUnitId: String): Unit\n```\n\nExample:\n```text\nfun putPublisherFirstPartyIdEnabled(enabled: Boolean): Boolean\n```\n\nExample:\n```text\nfun registerCustomTabsSession( client: CustomTabsClient, origin: String, callback: CustomTabsCallback?): CustomTabsSession?\n```\n\nExample:\n```text\nfun registerWebView(webView: WebView): Unit\n```\n\nExample:\n```text\nfun setRequestConfiguration(requestConfiguration: RequestConfiguration): Unit\n```\n\nExample:\n```text\nfun setUserControlledAppVolume(volume: Float): Unit\n```\n\nExample:\n```text\nfun setUserMutedApp(muted: Boolean): Unit\n```\n\nExample:\n```text\nvolatile val isInitialized: Boolean\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.820Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":19,"totalLines":96,"estimatedTokens":491}}410{"id":"doc-requestconfiguration_tagforchilddirectedtreatmen-4059007c","source":"documentation","title":"RequestConfiguration.TagForChildDirectedTreatment | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/reference/kotlin/com/google/android/gms/ads/RequestConfiguration.TagForChildDirectedTreatment","text":"Example:\n```text\n@Retention(value = AnnotationRetention.SOURCE)@IntDef(value = [-1, 0, 1])annotation RequestConfiguration.TagForChildDirectedTreatment\n```\n\nExample:\n```text\nTagForChildDirectedTreatment()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.821Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":56}}411{"id":"doc-migrate_sdk_versions_unity_google_for_developers-754e109b","source":"documentation","title":"Migrate SDK versions | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/migration","text":"Example:\n```text\n#if UNITY_ANDROID\nconst string adUnitId = \"ca-app-pub-3940256099942544/1033173712\";\n#elif UNITY_IPHONE\nconst string adUnitId = \"ca-app-pub-3940256099942544/4411468910\";\n#else\nconst string adUnitId = \"unexpected_platform\";\n#endif\n\nprivate InterstitialAd _interstitialAd;\n\nprivate void LoadAd()\n{\n // Load an interstitial ad\n InterstitialAd.Load(adUnitId, new AdRequest(),\n (InterstitialAd ad, LoadAdError loadAdError) =>\n {\n if (loadAdError != null)\n {\n Debug.Log(\"Interstitial ad failed to load with error: \" +\n loadAdError.GetMessage());\n return;\n }\n else if (ad == null)\n {\n Debug.Log(\"Interstitial ad failed to load.\");\n return;\n }\n\n Debug.Log(\"Interstitial ad loaded.\");\n _interstitialAd = ad;\n });\n}\n```\n\nExample:\n```text\n#if UNITY_ANDROID\nconst string adUnitId = \"ca-app-pub-3940256099942544/1033173712\";\n#elif UNITY_IPHONE\nconst string adUnitId = \"ca-app-pub-3940256099942544/4411468910\";\n#else\nconst string adUnitId = \"unexpected_platform\";\n#endif\n\nprivate InterstitialAd _interstitialAd;\n\nprivate void LoadInterstitialAd()\n{\n // Initialize an InterstitialAd.\n _interstitialAd = new InterstitialAd(adUnitId);\n // Called when an ad request has successfully loaded.\n _interstitialAd.OnAdLoaded += HandleOnAdLoaded;\n // Called when an ad request has failed to load.\n _interstitialAd.OnAdFailedToLoad += HandleOnAdFailedToLoad;\n // Create an empty ad request.\n AdRequest request = new AdRequest.Builder().Build();\n // Load the interstitial with the request.\n _interstitialAd.LoadAd(request);\n}\n\nprivate void HandleOnAdLoaded(object sender, EventArgs args)\n{\n Debug.Log(\"Interstitial ad loaded.\");\n}\n\nprivate void HandleOnAdFailedToLoad(object sender, AdFailedToLoadEventArgs args)\n{\n if (args != null)\n {\n Debug.Log(\"Interstitial ad failed to load with error: \" +\n args.LoadAdError.GetMessage());\n }\n}\n```\n\nExample:\n```text\n// These ad units are configured to always serve test ads.\n#if UNITY_ANDROID\nconst string adUnitId = \"ca-app-pub-3940256099942544/5224354917\";\n#elif UNITY_IPHONE\nconst string adUnitId = \"ca-app-pub-3940256099942544/1712485313\";\n#else\nconst string adUnitId = \"unused\";\n#endif\n\nprivate RewardedAd _rewardedAd;\n\nprivate void LoadRewardedAd()\n{\n // Load a rewarded ad\n RewardedAd.Load(adUnitId, new AdRequest(),\n (Rewarded ad, LoadAdError loadError) =>\n {\n if (loadError != null)\n {\n Debug.Log(\"Rewarded ad failed to load with error: \" +\n loadError.GetMessage());\n return;\n }\n else if (ad == null)\n {\n Debug.Log(\"Rewarded ad failed to load.\");\n return;\n }\n\n Debug.Log(\"Rewarded ad loaded.\");\n _rewardedAd = ad;\n });\n}\n```\n\nExample:\n```text\n// These ad units are configured to always serve test ads.\n#if UNITY_ANDROID\nconst string adUnitId = \"ca-app-pub-3940256099942544/5224354917\";\n#elif UNITY_IPHONE\nconst string adUnitId = \"ca-app-pub-3940256099942544/1712485313\";\n#else\nconst string adUnitId = \"unused\";\n#endif\n\nprivate RewardedAd _rewardedAd;\n\nprivate void LoadRewardedAd()\n{\n // Initialize an InterstitialAd.\n _rewardedAd = new RewardedAd(adUnitId);\n // Called when an ad request has successfully loaded.\n _rewardedAd.OnAdLoaded += HandleOnAdLoaded;\n // Called when an ad request has failed to load.\n _rewardedAd.OnAdFailedToLoad += HandleOnAdFailedToLoad;\n // Create an empty ad request.\n AdRequest request = new AdRequest.Builder().Build();\n // Load the interstitial with the request.\n _rewardedAd.LoadAd(request);\n}\n\nprivate void HandleOnAdLoaded(object sender, EventArgs args)\n{\n Debug.Log(\"Rewarded ad loaded.\");\n}\n\nprivate void HandleOnAdFailedToLoad(object sender, AdFailedToLoadEventArgs args)\n{\n if (args != null)\n {\n Debug.Log(\"Rewarded ad failed to load with error: \" +\n args.LoadAdError.GetMessage());\n }\n}\n```\n\nExample:\n```text\nprivate InterstitialAd _interstitalAd;\n\npublic void ShowInterstitialAd()\n{\n if (_interstitalAd != null && _interstitalAd.CanShowAd())\n {\n _interstitalAd.Show();\n }\n else\n {\n Debug.Log(\"Interstitial ad cannot be shown.\");\n }\n}\n```\n\nExample:\n```text\nprivate InterstitialAd _interstitalAd;\n\npublic void ShowInterstitialAd()\n{\n if (_interstitalAd != null && _interstitalAd.IsLoaded())\n {\n _interstitalAd.Show();\n }\n else\n {\n Debug.Log(\"Interstitial ad is not ready yet.\");\n }\n}\n```\n\nExample:\n```text\nprivate RewardedAd _rewardedAd;\n\npublic void ShowRewardedAd()\n{\n if (_rewardedAd != null && _rewardedAd.CanShowAd())\n {\n _rewardedAd.Show((Reward reward) =>\n {\n Debug.Log(\"Rewarded ad granted a reward: \" +\n reward.Amount);\n });\n }\n else\n {\n Debug.Log(\"Rewarded ad cannot be shown.\");\n }\n}\n```\n\nExample:\n```text\nprivate RewardedAd _rewardedAd;\n\npublic void ShowRewardedAd()\n{\n if (_rewardedAd != null && _rewardedAd.CanShowAd())\n {\n _rewardedAd.OnUserEarnedReward += HandleUserEarnedReward;\n _rewardedAd.Show();\n }\n else\n {\n Debug.Log(\"Rewarded ad is not ready yet.\");\n }\n}\npublic void HandleUserEarnedReward(object sender, Reward reward)\n{\n Debug.Log(\"Rewarded ad granted a reward: \" +\n reward.Amount);\n}\n```\n\nExample:\n```text\nprivate BannerView _bannerView;\n\npublic void ConfigureBanner()\n{\n _bannerView.OnAdPaid += (AdValue value) =>\n {\n AdValue value = value;\n };\n}\n```\n\nExample:\n```text\nprivate BannerView _bannerView;\n\npublic void ConfigureBanner()\n{\n _bannerView.OnPaidEvent += (object sender, AdValueEventArg arg) =>\n {\n AdValue value = arg.Value;\n };\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.822Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":261,"estimatedTokens":1508}}412{"id":"doc-impression_level_ad_revenue_android_google_for_d-449508fb","source":"documentation","title":"Impression-level ad revenue | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/impression-level-ad-revenue","text":"Example:\n```text\nad.adEventCallback =\n object : RewardedAdEventCallback {\n override fun onAdPaid(adValue: AdValue) {\n // Send the impression-level ad revenue information to your\n // preferred analytics server directly within this callback.\n\n // Extract the impression-level ad revenue data.\n val valueMicros = adValue.valueMicros\n val currencyCode = adValue.currencyCode\n val precisionType = adValue.precisionType\n\n val loadedAdSourceResponseInfo = ad.getResponseInfo().loadedAdSourceResponseInfo\n val adSourceName = loadedAdSourceResponseInfo?.name\n val adSourceId = loadedAdSourceResponseInfo?.id\n val adSourceInstanceName = loadedAdSourceResponseInfo?.instanceName\n val adSourceInstanceId = loadedAdSourceResponseInfo?.instanceId\n val extras = ad.getResponseInfo().responseExtras\n val mediationGroupName = extras.getString(\"mediation_group_name\")\n val mediationABTestName = extras.getString(\"mediation_ab_test_name\")\n val mediationABTestVariant = extras.getString(\"mediation_ab_test_variant\")\n }\n }ImpressionLevelAdRevenueSnippets.kt\n```\n\nExample:\n```text\nad.setAdEventCallback(\n new RewardedAdEventCallback() {\n @Override\n public void onAdPaid(@NonNull AdValue value) {\n // Send the impression-level ad revenue information to your preferred\n // analytics server directly within this callback.\n\n // Extract the impression-level ad revenue data.\n long valueMicros = value.getValueMicros();\n String currencyCode = value.getCurrencyCode();\n PrecisionType precisionType = value.getPrecisionType();\n\n AdSourceResponseInfo loadedAdSourceResponseInfo =\n ad.getResponseInfo().getLoadedAdSourceResponseInfo();\n String adSourceName = loadedAdSourceResponseInfo.getName();\n String adSourceId = loadedAdSourceResponseInfo.getId();\n String adSourceInstanceName = loadedAdSourceResponseInfo.getInstanceName();\n String adSourceInstanceId = loadedAdSourceResponseInfo.getInstanceId();\n\n Bundle extras = ad.getResponseInfo().getResponseExtras();\n String mediationGroupName = extras.getString(\"mediation_group_name\");\n String mediationABTestName = extras.getString(\"mediation_ab_test_name\");\n String mediationABTestVariant = extras.getString(\"mediation_ab_test_variant\");\n }\n });ImpressionLevelAdRevenueSnippets.java\n```\n\nExample:\n```text\nprivate fun getUniqueAdSourceName(loadedAdapterResponseInfo: AdSourceResponseInfo): String {\n var adSourceName = loadedAdapterResponseInfo.name\n if (adSourceName == \"Custom Event\") {\n if (\n loadedAdapterResponseInfo.adapterClassName ==\n \"com.google.ads.mediation.sample.customevent.SampleCustomEvent\"\n ) {\n adSourceName = \"Sample Ad Network (Custom Event)\"\n }\n }\n return adSourceName\n}ImpressionLevelAdRevenueSnippets.kt\n```\n\nExample:\n```text\nprivate String getUniqueAdSourceName(@NonNull AdSourceResponseInfo loadedAdapterResponseInfo) {\n String adSourceName = loadedAdapterResponseInfo.getName();\n if (adSourceName.equals(\"Custom Event\")) {\n if (loadedAdapterResponseInfo\n .getAdapterClassName()\n .equals(\"com.google.ads.mediation.sample.customevent.SampleCustomEvent\")) {\n adSourceName = \"Sample Ad Network (Custom Event)\";\n }\n }\n return adSourceName;\n}ImpressionLevelAdRevenueSnippets.java\n```\n\nExample:\n```text\nrewardedAd.adEventCallback =\n object : RewardedAdEventCallback {\n override fun onAdPaid(value: AdValue) {\n // Send ad revenue info to Adjust.\n val adRevenue = AdjustAdRevenue(\"admob_sdk\")\n adRevenue.setRevenue(value.valueMicros / 1000000.0, value.currencyCode)\n val loadedAdSourceResponseInfo = rewardedAd.getResponseInfo().loadedAdSourceResponseInfo\n loadedAdSourceResponseInfo?.let { adRevenue.setAdRevenueNetwork(it.name) }\n Adjust.trackAdRevenue(adRevenue)\n }\n }\n```\n\nExample:\n```text\nrewardedAd.setAdEventCallback(\n new RewardedAdEventCallback() {\n @Override\n public void onAdPaid(@NonNull AdValue value) {\n // Send ad revenue info to Adjust.\n AdjustAdRevenue adRevenue = new AdjustAdRevenue(\"admob_sdk\");\n adRevenue.setRevenue(value.getValueMicros() / 1000000.0, value.getCurrencyCode());\n if (rewardedAd.getResponseInfo().getLoadedAdSourceResponseInfo() != null) {\n adRevenue.setAdRevenueNetwork(\n rewardedAd.getResponseInfo().getLoadedAdSourceResponseInfo().getName());\n }\n Adjust.trackAdRevenue(adRevenue);\n }\n });\n```\n\nExample:\n```text\nrewardedAd.adEventCallback =\n object : RewardedAdEventCallback {\n override fun onAdPaid(value: AdValue) {\n val valueMicros = value.valueMicros\n val currencyCode = value.currencyCode\n\n val adRevenueData =\n AFAdRevenueData(\n \"AdMob Mediation\", // monetizationNetwork\n MediationNetwork.GOOGLE_ADMOB, // mediationNetwork\n currencyCode, // currencyIso4217Code\n valueMicros.toDouble(), // revenue\n )\n\n val additionalParameters: MutableMap<String?, Any?> = HashMap()\n additionalParameters[COUNTRY] = \"US\"\n additionalParameters[AD_UNIT] = AD_UNIT_ID\n additionalParameters[AD_TYPE] = AdFormat.REWARDED\n\n appsflyer.logAdRevenue(adRevenueData, additionalParameters)\n }\n }\n```\n\nExample:\n```text\nrewardedAd.setAdEventCallback(\n new RewardedAdEventCallback() {\n @Override\n public void onAdPaid(@NonNull AdValue value) {\n long valueMicros = value.getValueMicros();\n String currencyCode = value.getCurrencyCode();\n\n AFAdRevenueData adRevenueData =\n new AFAdRevenueData(\n \"AdMob Mediation\", // monetizationNetwork\n MediationNetwork.GOOGLE_ADMOB, // mediationNetwork\n currencyCode, // currencyIso4217Code\n (double) valueMicros // revenue\n );\n\n Map<String, Object> additionalParameters = new HashMap<>();\n additionalParameters.put(COUNTRY, \"US\");\n additionalParameters.put(AD_UNIT, AD_UNIT_ID);\n additionalParameters.put(AD_TYPE, AdFormat.REWARDED);\n\n AppsFlyerLib.getInstance().logAdRevenue(adRevenueData, additionalParameters);\n }\n });\n```\n\nExample:\n```text\nrewardedAd.adEventCallback =\n object : RewardedAdEventCallback {\n override fun onAdPaid(value: AdValue) {\n // Convert revenue from micros to standard units.\n val revenue = value.valueMicros / 1000000.0\n val currency = value.currencyCode\n\n // Validate ad revenue data before sending.\n if (revenue > 0 && currency.isNotEmpty()) {\n val adData = SingularAdData(\"AdMob\", currency, revenue)\n Singular.adRevenue(adData)\n }\n }\n }\n```\n\nExample:\n```text\nrewardedAd.setAdEventCallback(\n new RewardedAdEventCallback() {\n @Override\n public void onAdPaid(@NonNull AdValue value) {\n // Convert revenue from micros to standard units.\n double revenue = value.getValueMicros() / 1000000.0;\n String currency = value.getCurrencyCode();\n\n // Validate ad revenue data before sending.\n if (revenue > 0 && !currency.isEmpty()) {\n SingularAdData adData = new SingularAdData(\"AdMob\", currency, revenue);\n Singular.adRevenue(adData);\n }\n }\n });\n```\n\nExample:\n```text\nrewardedAd.adEventCallback =\n object : RewardedAdEventCallback {\n override fun onAdPaid(value: AdValue) {\n val responseInfo = rewardedAd.getResponseInfo()\n\n // Extract the impression-level ad revenue data.\n val valueMicros = value.valueMicros\n val currencyCode = value.currencyCode\n val precisionType = value.precisionType\n\n val json = JSONObject()\n try {\n json.put(\"ad_unit_id\", AD_UNIT_ID)\n json.put(\"currency_code\", currencyCode)\n json.put(\"response_id\", responseInfo.responseId)\n json.put(\"value_micros\", valueMicros)\n responseInfo.loadedAdSourceResponseInfo?.let {\n json.put(\"mediation_adapter_class_name\", it.adapterClassName)\n }\n json.put(\"precision_type\", precisionType)\n\n tenjinInstance.eventAdImpressionAdMob(json)\n } catch (_: JSONException) {\n // Handle error.\n }\n }\n }\n```\n\nExample:\n```text\nrewardedAd.setAdEventCallback(\n new RewardedAdEventCallback() {\n @Override\n public void onAdPaid(@NonNull AdValue value) {\n ResponseInfo responseInfo = rewardedAd.getResponseInfo();\n\n // Extract the impression-level ad revenue data.\n long valueMicros = value.getValueMicros();\n String currencyCode = value.getCurrencyCode();\n PrecisionType precisionType = value.getPrecisionType();\n\n JSONObject json = new JSONObject();\n try {\n json.put(\"ad_unit_id\", AD_UNIT_ID);\n json.put(\"currency_code\", currencyCode);\n json.put(\"response_id\", responseInfo.getResponseId());\n json.put(\"value_micros\", valueMicros);\n if (responseInfo.getLoadedAdSourceResponseInfo() != null) {\n json.put(\n \"mediation_adapter_class_name\",\n responseInfo.getLoadedAdSourceResponseInfo().getAdapterClassName());\n }\n json.put(\"precision_type\", precisionType);\n\n tenjinInstance.eventAdImpressionAdMob(json);\n } catch (JSONException e) {\n // Handle error.\n }\n }\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.823Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":275,"estimatedTokens":2361}}413{"id":"doc-resolve_ios_mediation_runtime_errors_unity_googl-631cfdbf","source":"documentation","title":"Resolve iOS mediation runtime errors | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/ios-mediation-errors","text":"Example:\n```text\n<dependencies>\n <iosPods>\n <!-- AppLovin adapter dependencies. -->\n <iosPod name=\"AppLovinSDK\" addToAllTargets=\"true\"/>\n <!-- InMobi adapter dependencies. -->\n <iosPod name=\"InMobiSDK\" addToAllTargets=\"true\"/>\n <!-- maio adapter dependencies. -->\n <iosPod name=\"MaioSDK-v2\" addToAllTargets=\"true\"/>\n </iosPods>\n</dependencies>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.824Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":15,"estimatedTokens":96}}414{"id":"doc-install_gma_next_gen_sdk_unity_google_for_develo-8b774c98","source":"documentation","title":"Install GMA Next-Gen SDK | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/android-next","text":"Example:\n```text\n<androidPackage spec=\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\">\n <repositories>\n <repository>https://maven.google.com/</repository>\n </repositories>\n</androidPackage>\n```\n\nExample:\n```text\n<dependencies>\n<androidPackages>\n <androidPackage spec=\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\">\n <repositories>\n <repository>https://maven.google.com/</repository>\n </repositories>\n </androidPackage>\n <androidPackage spec=\"androidx.constraintlayout:constraintlayout:2.1.4\">\n <repositories>\n <repository>https://maven.google.com/</repository>\n </repositories>\n </androidPackage>\n <androidPackage spec=\"androidx.lifecycle:lifecycle-process:2.6.2\">\n <repositories>\n <repository>https://maven.google.com/</repository>\n </repositories>\n </androidPackage>\n</androidPackages>\n\n<iosPods>\n <iosPod name=\"Google-Mobile-Ads-SDK\" version=\"~> 12.11.0\">\n <sources>\n <source>https://github.com/CocoaPods/Specs</source>\n </sources>\n </iosPod>\n</iosPods>\n</dependencies>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.825Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":41,"estimatedTokens":274}}415{"id":"doc-requestconfiguration_tagforunderageofconsent_and-81b8be7a","source":"documentation","title":"RequestConfiguration.TagForUnderAgeOfConsent | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/reference/kotlin/com/google/android/gms/ads/RequestConfiguration.TagForUnderAgeOfConsent","text":"Example:\n```text\n@Retention(value = AnnotationRetention.SOURCE)@IntDef(value = [-1, 0, 1])annotation RequestConfiguration.TagForUnderAgeOfConsent\n```\n\nExample:\n```text\nTagForUnderAgeOfConsent()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.825Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":53}}416{"id":"doc-set_up_banner_ads_unity_google_for_developers-e5bf1c3f","source":"documentation","title":"Set up banner ads | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/banner","text":"Example:\n```text\nusing GoogleMobileAds;\nusing GoogleMobileAds.Api;\n\npublic class GoogleMobileAdsDemoScript : MonoBehaviour\n{\n public void Start()\n {\n // Initialize Google Mobile Ads Unity Plugin.\n MobileAds.Initialize((InitializationStatus initStatus) =>\n {\n // This callback is called once the MobileAds SDK is initialized.\n });\n }\n}\n```\n\nExample:\n```text\n// Create a 320x50 banner at top of the screen.\nbannerView = new BannerView(\"AD_UNIT_ID\", AdSize.Banner, AdPosition.Top);BannerViewSnippets.cs\n```\n\nExample:\n```text\n// Create a 320x50 banner views at coordinate (0,50) on screen.\nbannerView = new BannerView(\"AD_UNIT_ID\", AdSize.Banner, 0, 50);BannerViewSnippets.cs\n```\n\nExample:\n```text\n// Create a 250x250 banner at the bottom of the screen.\nAdSize adSize = new AdSize(250, 250);\nbannerView = new BannerView(\"AD_UNIT_ID\", adSize, AdPosition.Bottom);BannerViewSnippets.cs\n```\n\nExample:\n```text\n// Send a request to load an ad into the banner view.\nbannerView.LoadAd(new AdRequest());BannerViewSnippets.cs\n```\n\nExample:\n```text\nbannerView.OnBannerAdLoaded += () =>\n{\n // Raised when an ad is loaded into the banner view.\n};\nbannerView.OnBannerAdLoadFailed += (LoadAdError error) =>\n{\n // Raised when an ad fails to load into the banner view.\n};\nbannerView.OnAdPaid += (AdValue adValue) =>\n{\n // Raised when the ad is estimated to have earned money.\n};\nbannerView.OnAdImpressionRecorded += () =>\n{\n // Raised when an impression is recorded for an ad.\n};\nbannerView.OnAdClicked += () =>\n{\n // Raised when a click is recorded for an ad.\n};\nbannerView.OnAdFullScreenContentOpened += () =>\n{\n // Raised when an ad opened full screen content.\n};\nbannerView.OnAdFullScreenContentClosed += () =>\n{\n // Raised when the ad closed full screen content.\n};BannerViewSnippets.cs\n```\n\nExample:\n```text\nif (bannerView != null)\n{\n // Always destroy the banner view when no longer needed.\n bannerView.Destroy();\n bannerView = null;\n}BannerViewSnippets.cs\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.826Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":86,"estimatedTokens":510}}417{"id":"doc-loopback_ip_address_flow_migration_guide_authori-ae98c034","source":"documentation","title":"Loopback IP Address flow Migration Guide | Authorization Resources | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/resources/loopback-migration","text":"Example:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth?\nredirect_uri=http://localhost:3000&\nresponse_type=code&\nscope=<SCOPES>&\nstate=<STATE>&\nclient_id=<CLIENT_ID>\n```\n\nExample:\n```text\nList requestedScopes = Arrays.asList(DriveScopes.DRIVE_APPDATA);\n AuthorizationRequest authorizationRequest = AuthorizationRequest.builder().setRequestedScopes(requestedScopes).build();\n Identity.getAuthorizationClient(activity)\n .authorize(authorizationRequest)\n .addOnSuccessListener(\n authorizationResult -> {\n if (authorizationResult.hasResolution()) {\n // Access needs to be granted by the user\n PendingIntent pendingIntent = authorizationResult.getPendingIntent();\n try {\n startIntentSenderForResult(pendingIntent.getIntentSender(),\n REQUEST_AUTHORIZE, null, 0, 0, 0, null);\n } catch (IntentSender.SendIntentException e) {\n Log.e(TAG, \"Couldn't start Authorization UI: \" + e.getLocalizedMessage());\n }\n } else {\n // Access already granted, continue with user action\n saveToDriveAppFolder(authorizationResult);\n }\n })\n .addOnFailureListener(e -> Log.e(TAG, \"Failed to authorize\", e));\n```\n\nExample:\n```devsite-click-to-copy\nList requestedScopes = Arrays.asList(DriveScopes.DRIVE_APPDATA);\n AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()\n .requestOfflineAccess(webClientId)\n .setRequestedScopes(requestedScopes)\n .build();\n Identity.getAuthorizationClient(activity)\n .authorize(authorizationRequest)\n .addOnSuccessListener(\n authorizationResult -> {\n if (authorizationResult.hasResolution()) {\n // Access needs to be granted by the user\n PendingIntent pendingIntent = authorizationResult.getPendingIntent();\n try {\n startIntentSenderForResult(pendingIntent.getIntentSender(),\n REQUEST_AUTHORIZE, null, 0, 0, 0, null);\n } catch (IntentSender.SendIntentException e) {\n Log.e(TAG, \"Couldn't start Authorization UI: \" + e.getLocalizedMessage());\n }\n } else {\n String authCode = authorizationResult.getServerAuthCode();\n }\n })\n .addOnFailureListener(e -> Log.e(TAG, \"Failed to authorize\", e));\n```\n\nExample:\n```devsite-click-to-copy\nuser.authentication.do { authentication, error in\n guard error == nil else { return }\n guard let authentication = authentication else { return }\n \n // Get the access token to attach it to a REST or gRPC request.\n let accessToken = authentication.accessToken\n \n // Or, get an object that conforms to GTMFetcherAuthorizationProtocol for\n // use with GTMAppAuth and the Google APIs client library.\n let authorizer = authentication.fetcherAuthorizer()\n}\n```\n\nExample:\n```devsite-click-to-copy\nGIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in\n guard error == nil else { return }\n guard let user = user else { return }\n \n // request a one-time authorization code that your server exchanges for\n // an access token and refresh token\n let authCode = user.serverAuthCode\n}\n```\n\nExample:\n```devsite-click-to-copy\nwindow.onload = function() {\n document.querySelector('button').addEventListener('click', function() {\n\n \n // retrieve access token\n chrome.identity.getAuthToken({interactive: true}, function(token) {\n \n // ..........\n\n\n // the example below shows how to use a retrieved access token with an appropriate scope\n // to call the Google People API contactGroups.get endpoint\n\n fetch(\n 'https://people.googleapis.com/v1/contactGroups/all?maxMembers=20&key=API_KEY',\n init)\n .then((response) => response.json())\n .then(function(data) {\n console.log(data)\n });\n });\n });\n};\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.827Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":117,"estimatedTokens":1022}}418{"id":"doc-enable_test_ads_unity_google_for_developers-13d03e62","source":"documentation","title":"Enable test ads | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/test-ads","text":"Example:\n```text\n...\nprivate void RequestBanner()\n{\n #if UNITY_ANDROID\n string adUnitId = \"ca-app-pub-3940256099942544/6300978111\";\n #elif UNITY_IPHONE\n string adUnitId = \"ca-app-pub-3940256099942544/2934735716\";\n #else\n string adUnitId = \"unexpected_platform\";\n #endif\n\n // Create a 320x50 banner at the top of the screen.\n bannerView = new BannerView(adUnitId, AdSize.Banner, AdPosition.Top);\n // Create an empty ad request.\n AdRequest request = new AdRequest();\n // Load the banner with the request.\n bannerView.LoadAd(request);\n}\n```\n\nExample:\n```text\nI/Ads: Use\n RequestConfiguration.Builder\n .setTestDeviceIds(Arrays.asList(\"33BE2250B43518CCDA7DE426D04EE231\"))\n to get test ads on this device.\n```\n\nExample:\n```text\n<Google> To get test ads on this device, set:\n GADMobileAds.sharedInstance.requestConfiguration.testDeviceIdentifiers =\n @[ @\"2077ef9a63d2b398840261c8221a0c9b\" ];\n```\n\nExample:\n```text\nList<string> testDeviceIds = new List<string>();\ntestDeviceIds.Add(\"TEST_DEVICE_ID\");\n\nRequestConfiguration requestConfiguration = new RequestConfiguration\n{\n TestDeviceIds = testDeviceIds\n};RequestConfigurationSnippets.cs\n```\n\nExample:\n```text\nMobileAds.SetRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.cs\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.827Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":54,"estimatedTokens":330}}419{"id":"doc-app_open_ads_unity_google_for_developers-6d04aa75","source":"documentation","title":"App open ads | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/app-open","text":"Example:\n```text\nusing System;\nusing UnityEngine;\nusing GoogleMobileAds.Api;\nusing GoogleMobileAds.Common;\n\n/// <summary>\n/// Demonstrates how to use the Google Mobile Ads app open ad format.\n/// </summary>\n[AddComponentMenu(\"GoogleMobileAds/Samples/AppOpenAdController\")]\npublic class AppOpenAdController : MonoBehaviour\n{\n\n // These ad units are configured to always serve test ads.\n#if UNITY_ANDROID\n private string _adUnitId = \"ca-app-pub-3940256099942544/9257395921\";\n#elif UNITY_IPHONE\n string _adUnitId = \"ca-app-pub-3940256099942544/5575463023\";\n#else\n private string _adUnitId = \"unused\";\n#endif\n\n public bool IsAdAvailable\n {\n get\n {\n return _appOpenAd != null;\n }\n }\n\n public void Start()\n {\n // Initialize Google Mobile Ads Unity Plugin.\n MobileAds.Initialize((InitializationStatus initStatus) =>\n {\n // This callback is called once the MobileAds SDK is initialized.\n });\n }\n\n /// <summary>\n /// Loads the app open ad.\n /// </summary>\n public void LoadAppOpenAd()\n {\n }\n\n /// <summary>\n /// Shows the app open ad.\n /// </summary>\n public void ShowAppOpenAd()\n {\n }\n}\n```\n\nExample:\n```text\n// These ad units are configured to always serve test ads.\n#if UNITY_ANDROID\n private string _adUnitId = \"ca-app-pub-3940256099942544/9257395921\";\n#elif UNITY_IPHONE\n string _adUnitId = \"ca-app-pub-3940256099942544/5575463023\";\n#else\n private string _adUnitId = \"unused\";\n#endif\n\n private AppOpenAd appOpenAd;\n\n /// <summary>\n /// Loads the app open ad.\n /// </summary>\n public void LoadAppOpenAd()\n {\n // Clean up the old ad before loading a new one.\n if (appOpenAd != null)\n {\n appOpenAd.Destroy();\n appOpenAd = null;\n }\n\n Debug.Log(\"Loading the app open ad.\");\n\n // Create our request used to load the ad.\n var adRequest = new AdRequest();\n\n // send the request to load the ad.\n AppOpenAd.Load(_adUnitId, adRequest,\n (AppOpenAd ad, LoadAdError error) =>\n {\n // if error is not null, the load request failed.\n if (error != null || ad == null)\n {\n Debug.LogError(\"app open ad failed to load an ad \" +\n \"with error : \" + error);\n return;\n }\n\n Debug.Log(\"App open ad loaded with response : \"\n + ad.GetResponseInfo());\n\n appOpenAd = ad;\n RegisterEventHandlers(ad);\n });\n }\n```\n\nExample:\n```text\nprivate void RegisterEventHandlers(AppOpenAd ad)\n{\n // Raised when the ad is estimated to have earned money.\n ad.OnAdPaid += (AdValue adValue) =>\n {\n Debug.Log(String.Format(\"App open ad paid {0} {1}.\",\n adValue.Value,\n adValue.CurrencyCode));\n };\n // Raised when an impression is recorded for an ad.\n ad.OnAdImpressionRecorded += () =>\n {\n Debug.Log(\"App open ad recorded an impression.\");\n };\n // Raised when a click is recorded for an ad.\n ad.OnAdClicked += () =>\n {\n Debug.Log(\"App open ad was clicked.\");\n };\n // Raised when an ad opened full screen content.\n ad.OnAdFullScreenContentOpened += () =>\n {\n Debug.Log(\"App open ad full screen content opened.\");\n };\n // Raised when the ad closed full screen content.\n ad.OnAdFullScreenContentClosed += () =>\n {\n Debug.Log(\"App open ad full screen content closed.\");\n };\n // Raised when the ad failed to open full screen content.\n ad.OnAdFullScreenContentFailed += (AdError error) =>\n {\n Debug.LogError(\"App open ad failed to open full screen content \" +\n \"with error : \" + error);\n };\n}\n```\n\nExample:\n```text\n// send the request to load the ad.\nAppOpenAd.Load(_adUnitId, adRequest,\n (AppOpenAd ad, LoadAdError error) =>\n {\n // If the operation failed, an error is returned.\n if (error != null || ad == null)\n {\n Debug.LogError(\"App open ad failed to load an ad with error : \" +\n error);\n return;\n }\n\n // If the operation completed successfully, no error is returned.\n Debug.Log(\"App open ad loaded with response : \" + ad.GetResponseInfo());\n\n // App open ads can be preloaded for up to 4 hours.\n _expireTime = DateTime.Now + TimeSpan.FromHours(4);\n\n _appOpenAd = ad;\n });\n```\n\nExample:\n```text\npublic bool IsAdAvailable\n{\n get\n {\n return _appOpenAd != null\n && _appOpenAd.IsLoaded()\n && DateTime.Now < _expireTime;\n }\n}\n```\n\nExample:\n```text\nprivate void Awake()\n{\n // Use the AppStateEventNotifier to listen to application open/close events.\n // This is used to launch the loaded ad when we open the app.\n AppStateEventNotifier.AppStateChanged += OnAppStateChanged;\n}\n\nprivate void OnDestroy()\n{\n // Always unlisten to events when complete.\n AppStateEventNotifier.AppStateChanged -= OnAppStateChanged;\n}\n```\n\nExample:\n```text\nprivate void OnAppStateChanged(AppState state)\n{\n Debug.Log(\"App State changed to : \"+ state);\n\n // if the app is Foregrounded and the ad is available, show it.\n if (state == AppState.Foreground)\n {\n if (IsAdAvailable)\n {\n ShowAppOpenAd();\n }\n }\n}\n```\n\nExample:\n```text\n/// <summary>\n/// Shows the app open ad.\n/// </summary>\npublic void ShowAppOpenAd()\n{\n if (appOpenAd != null && appOpenAd.CanShowAd())\n {\n Debug.Log(\"Showing app open ad.\");\n appOpenAd.Show();\n }\n else\n {\n Debug.LogError(\"App open ad is not ready yet.\");\n }\n}\n```\n\nExample:\n```text\nappOpenAd.Destroy();\n```\n\nExample:\n```text\nprivate void RegisterReloadHandler(AppOpenAd ad)\n{\n ...\n // Raised when the ad closed full screen content.\n ad.OnAdFullScreenContentClosed += ()\n {\n Debug.Log(\"App open ad full screen content closed.\");\n\n // Reload the ad so that we can show another as soon as possible.\n LoadAppOpenAd();\n };\n // Raised when the ad failed to open full screen content.\n ad.OnAdFullScreenContentFailed += (AdError error) =>\n {\n Debug.LogError(\"App open ad failed to open full screen content \" +\n \"with error : \" + error);\n\n // Reload the ad so that we can show another as soon as possible.\n LoadAppOpenAd();\n };\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.828Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":267,"estimatedTokens":1630}}420{"id":"doc-out_of_band_oob_flow_migration_guide_authorizati-406eac1e","source":"documentation","title":"Out-Of-Band (OOB) flow Migration Guide | Authorization Resources | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/resources/oob-migration","text":"Example:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth?\nresponse_type=code&\nscope=<SCOPES>&\nstate=<STATE>&\nredirect_uri=urn:ietf:wg:oauth:2.0:oob&\nclient_id=<CLIENT_ID>\n```\n\nExample:\n```text\nList requestedScopes = Arrays.asList(DriveScopes.DRIVE_APPDATA);\n AuthorizationRequest authorizationRequest = AuthorizationRequest.builder().setRequestedScopes(requestedScopes).build();\n Identity.getAuthorizationClient(activity)\n .authorize(authorizationRequest)\n .addOnSuccessListener(\n authorizationResult -> {\n if (authorizationResult.hasResolution()) {\n // Access needs to be granted by the user\n PendingIntent pendingIntent = authorizationResult.getPendingIntent();\n try {\n startIntentSenderForResult(pendingIntent.getIntentSender(),\n REQUEST_AUTHORIZE, null, 0, 0, 0, null);\n } catch (IntentSender.SendIntentException e) {\n Log.e(TAG, \"Couldn't start Authorization UI: \" + e.getLocalizedMessage());\n }\n } else {\n // Access already granted, continue with user action\n saveToDriveAppFolder(authorizationResult);\n }\n })\n .addOnFailureListener(e -> Log.e(TAG, \"Failed to authorize\", e));\n```\n\nExample:\n```devsite-click-to-copy\nList requestedScopes = Arrays.asList(DriveScopes.DRIVE_APPDATA);\n AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()\n .requestOfflineAccess(webClientId)\n .setRequestedScopes(requestedScopes)\n .build();\n Identity.getAuthorizationClient(activity)\n .authorize(authorizationRequest)\n .addOnSuccessListener(\n authorizationResult -> {\n if (authorizationResult.hasResolution()) {\n // Access needs to be granted by the user\n PendingIntent pendingIntent = authorizationResult.getPendingIntent();\n try {\n startIntentSenderForResult(pendingIntent.getIntentSender(),\n REQUEST_AUTHORIZE, null, 0, 0, 0, null);\n } catch (IntentSender.SendIntentException e) {\n Log.e(TAG, \"Couldn't start Authorization UI: \" + e.getLocalizedMessage());\n }\n } else {\n String authCode = authorizationResult.getServerAuthCode();\n }\n })\n .addOnFailureListener(e -> Log.e(TAG, \"Failed to authorize\", e));\n```\n\nExample:\n```devsite-click-to-copy\nuser.authentication.do { authentication, error in\n guard error == nil else { return }\n guard let authentication = authentication else { return }\n \n // Get the access token to attach it to a REST or gRPC request.\n let accessToken = authentication.accessToken\n \n // Or, get an object that conforms to GTMFetcherAuthorizationProtocol for\n // use with GTMAppAuth and the Google APIs client library.\n let authorizer = authentication.fetcherAuthorizer()\n}\n```\n\nExample:\n```devsite-click-to-copy\nGIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in\n guard error == nil else { return }\n guard let user = user else { return }\n \n // request a one-time authorization code that your server exchanges for\n // an access token and refresh token\n let authCode = user.serverAuthCode\n}\n```\n\nExample:\n```devsite-click-to-copy\nwindow.onload = function() {\n document.querySelector('button').addEventListener('click', function() {\n\n \n // retrieve access token\n chrome.identity.getAuthToken({interactive: true}, function(token) {\n \n // ..........\n\n\n // the example below shows how to use a retrieved access token with an appropriate scope\n // to call the Google People API contactGroups.get endpoint\n\n fetch(\n 'https://people.googleapis.com/v1/contactGroups/all?maxMembers=20&key=API_KEY',\n init)\n .then((response) => response.json())\n .then(function(data) {\n console.log(data)\n });\n });\n });\n};\n```\n\nExample:\n```devsite-click-to-copy\nasync function main() {\n const server = http.createServer(async function (req, res) {\n\n if (req.url.startsWith('/oauth2callback')) {\n let q = url.parse(req.url, true).query;\n\n if (q.error) {\n console.log('Error:' + q.error);\n } else {\n \n // Get access and refresh tokens (if access_type is offline)\n let { tokens } = await oauth2Client.getToken(q.code);\n oauth2Client.setCredentials(tokens);\n\n // Example of using Google Drive API to list filenames in user's Drive.\n const drive = google.drive('v3');\n drive.files.list({\n auth: oauth2Client,\n pageSize: 10,\n fields: 'nextPageToken, files(id, name)',\n }, (err1, res1) => {\n // TODO(developer): Handle response / error.\n });\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\n// initTokenClient() initializes a new token client with your\n// web app's client ID and the scope you need access to\n\nconst client = google.accounts.oauth2.initTokenClient({\n client_id: 'YOUR_GOOGLE_CLIENT_ID',\n scope: 'https://www.googleapis.com/auth/calendar.readonly',\n \n // callback function to handle the token response\n callback: (tokenResponse) => {\n if (tokenResponse && tokenResponse.access_token) { \n gapi.client.setApiKey('YOUR_API_KEY');\n gapi.client.load('calendar', 'v3', listUpcomingEvents);\n }\n },\n});\n\nfunction listUpcomingEvents() {\n gapi.client.calendar.events.list(...);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.829Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":170,"estimatedTokens":1390}}421{"id":"doc-dpop_adoption_guide_authorization_resources_goog-848b3113","source":"documentation","title":"DPoP Adoption Guide | Authorization Resources | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/resources/dpop-adoption","text":"Example:\n```text\n$ curl -G \"https://accounts.google.com/o/oauth2/v2/auth\" \\\n --data-urlencode \"client_id=YOUR_CLIENT_ID.apps.googleusercontent.com\" \\\n --data-urlencode \"redirect_uri=http://127.0.0.1:8080\" \\\n --data-urlencode \"response_type=code\" \\\n --data-urlencode \"scope=calendar.readonly\" \\\n --data-urlencode \"state=AI1Bvapj7E5SDmtW4gohcA\" \\\n --data-urlencode \"code_challenge=PO4pPROl-31Wy9fVZ7uTW9Ga6CrjrSKsf4AAtx_JNM8\" \\\n --data-urlencode \"code_challenge_method=S256\" \\\n --data-urlencode \"nonce=PrMfmSNAvJFPQ7GnlEKUaw\" \\\n --data-urlencode \"access_type=offline\" \\\n --data-urlencode \"prompt=consent\"\n```\n\nExample:\n```text\n{\n \"typ\": \"dpop+jwt\",\n \"alg\": \"ES256\",\n \"jwk\": {\n \"kty\": \"EC\",\n \"crv\": \"P-256\",\n \"x\": \"VC91y9ZYdfSWaDv8JaI6gx5ifOw2rn3YdqkAB51Uu6E\",\n \"y\": \"ikPjOtea4k7fWPVrRYwaA4Ww6iVY3pOOICotHwwGV3o\"\n }\n}\n```\n\nExample:\n```text\n{\n \"jti\": \"o29CN8LIY0l_N8iy5-ilon1guad9NFQHFOdXTzrBNck\",\n \"htm\": \"POST\",\n \"htu\": \"https://oauth2.googleapis.com/token\",\n \"iat\": 1784822025\n}\n```\n\nExample:\n```text\n$ curl -X POST https://oauth2.googleapis.com/token \\\n -H \"Content-Type: application/x-www-form-urlencoded\" \\\n -H \"DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6\\\n IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiVkM5MXk5WllkZlNXYUR2OEphSTZneDVpZ\\\n k93MnJuM1lkcWtBQjUxVXU2RSIsInkiOiJpa1BqT3RlYTRrN2ZXUFZyUll3YUE0V3\\\n c2aVZZM3BPT0lDb3RId3dHVjNvIn19.eyJqdGkiOiJvMjlDTjhMSVkwbF9OOGl5NS\\\n 1pbG9uMWd1YWQ5TkZRSEZPZFhUenJCTmNrIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh\\\n 0dHBzOi8vb2F1dGgyLmdvb2dsZWFwaXMuY29tL3Rva2VuIiwiaWF0IjoxNzg0ODIy\\\n MDI1fQ.OSdQCmqTng_uZmGK5UXf8hcEMtoOu7ucmYtl5mx4901RXnj6fJRJQmIeTq\\\n fhprRBTG_RSJv2fPcWDqvQbDW7YA\" \\\n --data-urlencode \"grant_type=authorization_code\" \\\n --data-urlencode \"code=4/0AXEQxIDNpLD-qpSIvjHb2Hl10uS_2sk2GBRpO8UJQ78YZF3hZ9LB9kTA1xYLD4xisi4C5w\" \\\n --data-urlencode \"redirect_uri=http://127.0.0.1:8080\" \\\n --data-urlencode \"client_id=YOUR_CLIENT_ID.apps.googleusercontent.com\" \\\n --data-urlencode \"client_secret=YOUR_CLIENT_SECRET\" \\\n --data-urlencode \"code_verifier=q8ZztyVv7HH8E2M-SEL8WaB-7CPs68rejN5UZ9OdYgo\"\n```\n\nExample:\n```text\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nDPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI\n\n{\n \"access_token\": \"ya29.a0ARGnu0aebRL97B91dmvm14gTug5wpItFf9MVWq12Hja6yv09A_qxa4T73_z2gFbf32qR4RXispQ7vnOzv6gn0APLQrF51LVa6AOqCVPH2Tupocv8y0JHu4ByEbvgXEEhiHEU8Xa9_w3i-PKBPsKWiLi210RCZdqJjLXkcRrGnoPPjbGPzOPtm6KCJjPrNHG16caOWecaCgYKASESARASFQHGX2MiBn7ihbbk_n-buCbOfl2TDA0206\",\n \"expires_in\": 3599,\n \"refresh_token\": \"1//06dUPZ9FIBQm3CgYIARAAGAYSNwF-L9IrJwuIEKUA_zbBPU-xoCDGM0QrDu7-jv7cMQZ0kARPUK9WhwfFFfbOVEgXDQKmFh4w9GM\",\n \"scope\": \"https://www.googleapis.com/auth/calendar.readonly\",\n \"token_type\": \"Bearer\"\n}\n```\n\nExample:\n```text\n{\n \"jti\": \"o29CN8ZIY0l_K8iy5-ilon1gwad9NF6HFOdXTzrBNck\",\n \"htm\": \"POST\",\n \"htu\": \"https://oauth2.googleapis.com/token\",\n \"nonce\": \"AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI\",\n \"iat\": 1784822025\n}\n```\n\nExample:\n```text\n$ curl -X POST https://oauth2.googleapis.com/token \\\n -H \"Content-Type: application/x-www-form-urlencoded\" \\\n -H \"DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6\\\n IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiVkM5MXk5WllkZlNXYUR2OEphSTZneDVpZ\\\n k93MnJuM1lkcWtBQjUxVXU2RSIsInkiOiJpa1BqT3RlYTRrN2ZXUFZyUll3YUE0V3\\\n c2aVZZM3BPT0lDb3RId3dHVjNvIn19.eyJqdGkiOiJvMjlDTjhaSVkwbF9LOGl5NS\\\n 1pbG9uMWd1YWQ5TkY2SEZPZFhUenJCTmNrIiwiaHRtIjoiUE9TVCIsImh0dSI6Imh\\\n 0dHBzOi8vb2F1dGgyLmdvb2dsZWFwaXMuY29tL3Rva2VuIiwibm9uY2UiOiJBTjNY\\\n d0pqWnNqbmIwWnVXa1JsZWs4UVU3d1ktWmhmLTVJUDZ0TzB0T1J6MEtndERUMUJvO\\\n EZYLXc0bnozcjVsbmVwSSIsImlhdCI6MTc4NDgyMjAyNX0.MEQCIDm09AXo2c9sov\\\n GrTUkrbEB_k9mra_Dkji-CQ9mSZVP1AiBxbiqkCE7Dt9RKyUT_3kj7q1vCvVggwnW\\\n JNX3P3vO1mw\" \\\n --data-urlencode \"grant_type=refresh_token\" \\\n --data-urlencode \"refresh_token=1//06dUPZ9FIBQm3CgYIARAAGAYSNwF-L9IrJwuIEKUA_zbBPU-xoCDGM0QrDu7-jv7cMQZ0kARPUK9WhwfFFfbOVEgXDQKmFh4w9GM\" \\\n --data-urlencode \"client_id=YOUR_CLIENT_ID.apps.googleusercontent.com\" \\\n --data-urlencode \"client_secret=YOUR_CLIENT_SECRET\"\n```\n\nExample:\n```text\nHTTP/1.1 400 Bad Request\nContent-Type: application/json; charset=utf-8\nDPoP-Nonce: AO4t07Kf85RJXmltUhiAiELLPPrJ4zOi66zWxU1uDZbhRcahFBYvT0WlcjSSXULXknSA\n\n{\n \"error\": \"use_dpop_nonce\",\n \"error_description\": \"New DPoP nonce issued due to invalid or expired challenge.\"\n}\n```\n\nExample:\n```text\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nDPoP-Nonce: AO4t07IXuovyCbtLEr6VVFZQ_Kb78MMOXTt6-CyZpsJeF62HZ3P_EW55XbWqYcU76Jg=\n\n{\n \"access_token\": \"ya29.a0ARGnu0bDj9BAQYVbF5hi3vw-brBUZBZu1bnInk1hS7gueqEb6QPqUjDGb0MMj9A0QX5FRrJo3FDw-DEDtvVbRUdeCgjwsL_LVVFXz-p-MUyiFyRoufI4KC0Go9aq5cEjD_BWvOJLMSIY6_EnwnhqDgk0XxvzaaAxDnv8PXJAGev_UotcfApstqi0NCxbfi-6Kgull9QaCgYKAUQSARASFQHGX2MiZpMjRS6z4S0RjOkNxn2o1Q0206\",\n \"expires_in\": 3599,\n \"scope\": \"https://www.googleapis.com/auth/calendar.readonly\",\n \"token_type\": \"Bearer\",\n \"challenge\": \"AO4t07IXuovyCbtLEr6VVFZQ_Kb78MMOXTt6-CyZpsJeF62HZ3P_EW55XbWqYcU76Jg\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.830Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":133,"estimatedTokens":1286}}422{"id":"doc-use_collapsible_banners_unity_google_for_develop-3e9c6971","source":"documentation","title":"Use collapsible banners | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/banner/collapsible","text":"Example:\n```text\nprivate void LoadBannerAd()\n{\n var bannerView = new BannerView(_adUnitId, AdSize.Banner, AdPosition.Bottom);\n\n var adRequest = new AdRequest();\n\n // Create an extra parameter that aligns the bottom of\n // the expanded ad to the bottom of the bannerView.\n adRequest.Extras.Add(\"collapsible\", \"bottom\");\n\n bannerView.LoadAd(adRequest);\n}\n```\n\nExample:\n```text\n_bannerView.OnBannerAdLoaded += () =>\n {\n Debug.Log(_bannerView.IsCollapsible()\n ? \"Banner is collapsible.\"\n : \"Banner is not collapsible.\");\n };\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.830Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":27,"estimatedTokens":143}}423{"id":"doc-rewarded_interstitial_ads_beta_unity_google_for_-38ef5622","source":"documentation","title":"Rewarded interstitial ads (beta) | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/rewarded-interstitial","text":"Example:\n```text\nusing GoogleMobileAds;\nusing GoogleMobileAds.Api;\n\npublic class GoogleMobileAdsDemoScript : MonoBehaviour\n{\n public void Start()\n {\n // Initialize Google Mobile Ads Unity Plugin.\n MobileAds.Initialize((InitializationStatus initStatus) =>\n {\n // This callback is called once the MobileAds SDK is initialized.\n });\n }\n}\n```\n\nExample:\n```text\n// These ad units are configured to always serve test ads.\n#if UNITY_ANDROID\n private string _adUnitId = \"ca-app-pub-3940256099942544/5354046379\";\n#elif UNITY_IPHONE\n private string _adUnitId = \"ca-app-pub-3940256099942544/6978759866\";\n#else\n private string _adUnitId = \"unused\";\n#endif\n\n private RewardedInterstitialAd _rewardedInterstitialAd;\n\n /// <summary>\n /// Loads the rewarded interstitial ad.\n /// </summary>\n public void LoadRewardedInterstitialAd()\n {\n // Clean up the old ad before loading a new one.\n if (_rewardedInterstitialAd != null)\n {\n _rewardedInterstitialAd.Destroy();\n _rewardedInterstitialAd = null;\n }\n\n Debug.Log(\"Loading the rewarded interstitial ad.\");\n\n // create our request used to load the ad.\n var adRequest = new AdRequest();\n adRequest.Keywords.Add(\"unity-admob-sample\");\n\n // send the request to load the ad.\n RewardedInterstitialAd.Load(_adUnitId, adRequest,\n (RewardedInterstitialAd ad, LoadAdError error) =>\n {\n // if error is not null, the load request failed.\n if (error != null || ad == null)\n {\n Debug.LogError(\"rewarded interstitial ad failed to load an ad \" +\n \"with error : \" + error);\n return;\n }\n\n Debug.Log(\"Rewarded interstitial ad loaded with response : \"\n + ad.GetResponseInfo());\n\n _rewardedInterstitialAd = ad;\n });\n }\n```\n\nExample:\n```text\n// send the request to load the ad.\nRewardedInterstitialAd.Load(_adUnitId,\n adRequest,\n (RewardedInterstitialAd ad, LoadAdError error) =>\n {\n // If the operation failed, an error is returned.\n if (error != null || ad == null)\n {\n Debug.LogError(\"Rewarded interstitial ad failed to load an ad \" +\n \" with error : \" + error);\n return;\n }\n\n // If the operation completed successfully, no error is returned.\n Debug.Log(\"Rewarded interstitial ad loaded with response : \" +\n ad.GetResponseInfo());\n \n // Create and pass the SSV options to the rewarded ad.\n var options = new ServerSideVerificationOptions\n .Builder()\n .SetCustomData(\"SAMPLE_CUSTOM_DATA_STRING\")\n .Build()\n ad.SetServerSideVerificationOptions(options);\n \n});\n```\n\nExample:\n```text\npublic void ShowRewardedInterstitialAd()\n{\n const string rewardMsg =\n \"Rewarded interstitial ad rewarded the user. Type: {0}, amount: {1}.\";\n\n if (rewardedInterstitialAd != null && rewardedInterstitialAd.CanShowAd())\n {\n rewardedInterstitialAd.Show((Reward reward) =>\n {\n // TODO: Reward the user.\n Debug.Log(String.Format(rewardMsg, reward.Type, reward.Amount));\n });\n }\n}\n```\n\nExample:\n```text\nprivate void RegisterEventHandlers(RewardedInterstitialAd ad)\n{\n // Raised when the ad is estimated to have earned money.\n ad.OnAdPaid += (AdValue adValue) =>\n {\n Debug.Log(String.Format(\"Rewarded interstitial ad paid {0} {1}.\",\n adValue.Value,\n adValue.CurrencyCode));\n };\n // Raised when an impression is recorded for an ad.\n ad.OnAdImpressionRecorded += () =>\n {\n Debug.Log(\"Rewarded interstitial ad recorded an impression.\");\n };\n // Raised when a click is recorded for an ad.\n ad.OnAdClicked += () =>\n {\n Debug.Log(\"Rewarded interstitial ad was clicked.\");\n };\n // Raised when an ad opened full screen content.\n ad.OnAdFullScreenContentOpened += () =>\n {\n Debug.Log(\"Rewarded interstitial ad full screen content opened.\");\n };\n // Raised when the ad closed full screen content.\n ad.OnAdFullScreenContentClosed += () =>\n {\n Debug.Log(\"Rewarded interstitial ad full screen content closed.\");\n };\n // Raised when the ad failed to open full screen content.\n ad.OnAdFullScreenContentFailed += (AdError error) =>\n {\n Debug.LogError(\"Rewarded interstitial ad failed to open \" +\n \"full screen content with error : \" + error);\n };\n}\n```\n\nExample:\n```text\n_rewardedInterstitialAd.Destroy();\n```\n\nExample:\n```text\nprivate void RegisterReloadHandler(RewardedInterstitialAd ad)\n{\n // Raised when the ad closed full screen content.\n ad.OnAdFullScreenContentClosed += ()\n {\n Debug.Log(\"Rewarded interstitial ad full screen content closed.\");\n\n // Reload the ad so that we can show another as soon as possible.\n LoadRewardedInterstitialAd();\n };\n // Raised when the ad failed to open full screen content.\n ad.OnAdFullScreenContentFailed += (AdError error) =>\n {\n Debug.LogError(\"Rewarded interstitial ad failed to open \" +\n \"full screen content with error : \" + error);\n\n // Reload the ad so that we can show another as soon as possible.\n LoadRewardedInterstitialAd();\n };\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.832Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":186,"estimatedTokens":1397}}424{"id":"doc-using_oauth_2_0_for_server_to_server_application-5c5525c6","source":"documentation","title":"Using OAuth 2.0 for Server to Server Applications | Authorization | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/service-account","text":"Example:\n```text\nimport com.google.auth.oauth2.GoogleCredentials;\nimport com.google.api.services.sqladmin.SQLAdminScopes;\n\n// ...\n\nGoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(\"ServiceAccountKey.json\"))\n .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN));\n```\n\nExample:\n```devsite-click-to-copy\nGoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(\"ServiceAccountKey.json\"))\n .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN))\n .createDelegated(\"workspace-user@example.com\");\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\n\nSCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']\nSERVICE_ACCOUNT_FILE = '/path/to/ServiceAccountKey.json'\n\ncredentials = service_account.Credentials.from_service_account_file(\n SERVICE_ACCOUNT_FILE, scopes=SCOPES)\n```\n\nExample:\n```text\ndelegated_credentials = credentials.with_subject('user@example.org')\n```\n\nExample:\n```text\n{Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature}\n```\n\nExample:\n```text\n{Base64url encoded header}.{Base64url encoded claim set}\n```\n\nExample:\n```text\n{\"alg\":\"RS256\",\"typ\":\"JWT\", \"kid\":\"370ab79b4513eb9bad7c9bd16a95cb76b5b2a56a\"}\n```\n\nExample:\n```text\neyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsICJraWQiOiIzNzBhYjc5YjQ1MTNlYjliYWQ3YzliZDE2YTk1Y2I3NmI1YjJhNTZhIn0=\n```\n\nExample:\n```text\n{\n \"iss\": \"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com\",\n \"scope\": \"https://www.googleapis.com/auth/devstorage.read_only\",\n \"aud\": \"https://oauth2.googleapis.com/token\",\n \"exp\": 1328554385,\n \"iat\": 1328550785\n}\n```\n\nExample:\n```text\n{\n \"iss\": \"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com\",\n \"sub\": \"some.user@example.com\",\n \"scope\": \"https://www.googleapis.com/auth/prediction\",\n \"aud\": \"https://oauth2.googleapis.com/token\",\n \"exp\": 1328554385,\n \"iat\": 1328550785\n}\n```\n\nExample:\n```text\n{\n \"iss\": \"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com\",\n \"scope\": \"https://www.googleapis.com/auth/prediction\",\n \"aud\": \"https://oauth2.googleapis.com/token\",\n \"exp\": 1328554385,\n \"iat\": 1328550785\n}\n```\n\nExample:\n```text\n{Base64url encoded header}.\n{Base64url encoded claim set}.\n{Base64url encoded signature}\n```\n\nExample:\n```text\n{\"alg\":\"RS256\",\"typ\":\"JWT\"}.\n{\n\"iss\":\"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com\",\n\"scope\":\"https://www.googleapis.com/auth/prediction\",\n\"aud\":\"https://oauth2.googleapis.com/token\",\n\"exp\":1328554385,\n\"iat\":1328550785\n}.\n[signature bytes]\n```\n\nExample:\n```text\neyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL29hdXRoMi92NC90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ.UFUt59SUM2_AW4cRU8Y0BYVQsNTo4n7AFsNrqOpYiICDu37vVt-tw38UKzjmUKtcRsLLjrR3gFW3dNDMx_pL9DVjgVHDdYirtrCekUHOYoa1CMR66nxep5q5cBQ4y4u2kIgSvChCTc9pmLLNoIem-ruCecAJYgI9Ks7pTnW1gkOKs0x3YpiLpzplVHAkkHztaXiJdtpBcY1OXyo6jTQCa3Lk2Q3va1dPkh_d--GU2M5flgd8xNBPYw4vxyt0mP59XZlHMpztZt0soSgObf7G3GXArreF_6tpbFsS3z2t5zkEiHuWJXpzcYr5zWTRPDEHsejeBSG8EgpLDce2380ROQ\n```\n\nExample:\n```text\nhttps://oauth2.googleapis.com/token\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.googleapis.com\nContent-Type: application/x-www-form-urlencoded\n\ngrant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.ixOUGehweEVX_UKXv5BbbwVEdcz6AYS-6uQV6fGorGKrHf3LIJnyREw9evE-gs2bmMaQI5_UbabvI4k-mQE4kBqtmSpTzxYBL1TCd7Kv5nTZoUC1CmwmWCFqT9RE6D7XSgPUh_jF1qskLa2w0rxMSjwruNKbysgRNctZPln7cqQ\n```\n\nExample:\n```text\ncurl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.RZVpzWygMLuL-n3GwjW1_yhQhrqDacyvaXkuf8HcJl8EtXYjGjMaW5oiM5cgAaIorrqgYlp4DPF_GuncFqg9uDZrx7pMmCZ_yHfxhSCXru3gbXrZvAIicNQZMFxrEEn4REVuq7DjkTMyCMGCY1dpMa8aWfTQFt3Eh7smLchaZsU\n' https://oauth2.googleapis.com/token\n```\n\nExample:\n```text\n{\n \"access_token\": \"1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M\",\n \"scope\": \"https://www.googleapis.com/auth/prediction\"\n \"token_type\": \"Bearer\",\n \"expires_in\": 3600\n}\n```\n\nExample:\n```text\nSQLAdmin sqladmin =\n new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credentials).build();\n```\n\nExample:\n```text\nSQLAdmin.Instances.List instances =\n sqladmin.instances().list(\"exciting-example-123\").execute();\n```\n\nExample:\n```text\nimport googleapiclient.discovery\n\nsqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta3', credentials=credentials)\n```\n\nExample:\n```text\nresponse = sqladmin.instances().list(project='exciting-example-123').execute()\n```\n\nExample:\n```text\nGET /drive/v2/files HTTP/1.1\nHost: www.googleapis.com\nAuthorization: Bearer access_token\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v2/files?access_token=access_token\n```\n\nExample:\n```text\ncurl -H \"Authorization: Bearer access_token\" https://www.googleapis.com/drive/v2/files\n```\n\nExample:\n```text\ncurl https://www.googleapis.com/drive/v2/files?access_token=access_token\n```\n\nExample:\n```text\n{\n \"alg\": \"RS256\",\n \"typ\": \"JWT\",\n \"kid\": \"abcdef1234567890\"\n}\n.\n{\n \"iss\": \"123456-compute@developer.gserviceaccount.com\",\n \"sub\": \"123456-compute@developer.gserviceaccount.com\",\n \"aud\": \"https://firestore.googleapis.com/\",\n \"iat\": 1511900000,\n \"exp\": 1511903600\n}\n```\n\nExample:\n```text\nimport com.google.auth.oauth2.ServiceAccountCredentials;\n...\nGoogleCredentials credentials =\n GoogleCredentials.fromStream(new FileInputStream(\"MyProject-1234.json\"));\nPrivateKey privateKey = ((ServiceAccountCredentials) credentials).getPrivateKey();\nString privateKeyId = ((ServiceAccountCredentials) credentials).getPrivateKeyId();\n\nlong now = System.currentTimeMillis();\n\ntry {\n Algorithm algorithm = Algorithm.RSA256(null, privateKey);\n String signedJwt = JWT.create()\n .withKeyId(privateKeyId)\n .withIssuer(\"123456-compute@developer.gserviceaccount.com\")\n .withSubject(\"123456-compute@developer.gserviceaccount.com\")\n .withAudience(\"https://firestore.googleapis.com/\")\n .withIssuedAt(new Date(now))\n .withExpiresAt(new Date(now + 3600 * 1000L))\n .sign(algorithm);\n} catch ...\n```\n\nExample:\n```text\niat = time.time()\nexp = iat + 3600\npayload = {'iss': '123456-compute@developer.gserviceaccount.com',\n 'sub': '123456-compute@developer.gserviceaccount.com',\n 'aud': 'https://firestore.googleapis.com/',\n 'iat': iat,\n 'exp': exp}\nadditional_headers = {'kid': PRIVATE_KEY_ID_FROM_JSON}\nsigned_jwt = jwt.encode(payload, PRIVATE_KEY_FROM_JSON, headers=additional_headers,\n algorithm='RS256')\n```\n\nExample:\n```text\nGET /v1/projects/abc/databases/123/indexes HTTP/1.1\nAuthorization: Bearer SIGNED_JWT\nHost: firestore.googleapis.com\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.834Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":30,"totalLines":252,"estimatedTokens":1904}}425{"id":"doc-set_up_admob_mediation_unity_google_for_develope-e8e3bf4c","source":"documentation","title":"Set up AdMob Mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation","text":"Example:\n```text\nMobileAds.Initialize((InitializationStatus initializationStatus) =>\n{\n Dictionary<string, AdapterStatus> map = initializationStatus.getAdapterStatusMap();\n foreach (KeyValuePair<string, AdapterStatus> keyValuePair in map)\n {\n string className = keyValuePair.Key;\n AdapterStatus status = keyValuePair.Value;\n switch (status.InitializationState)\n {\n case AdapterState.NotReady:\n // The adapter initialization did not complete.\n Debug.Log($\"Adapter: {className} is not ready.\");\n break;\n case AdapterState.Ready:\n // The adapter was successfully initialized.\n Debug.Log($\"Adapter: {className} is initialized.\");\n break;\n }\n }\n});MediationSnippets.cs\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.834Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":25,"estimatedTokens":210}}426{"id":"doc-how_to_handle_granular_permissions_authorization-933e3c55","source":"documentation","title":"How to handle granular permissions | Authorization Resources | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions","text":"Example:\n```devsite-click-to-copy\nconst client = google.accounts.oauth2.initTokenClient({\n client_id: 'YOUR_CLIENT_ID',\n scope: 'https://www.googleapis.com/auth/calendar.readonly \\\n https://www.googleapis.com/auth/contacts.readonly',\n callback: (response) => {\n ...\n },\n});\n```\n\nExample:\n```devsite-click-to-copy\nimport google.oauth2.credentials\nimport google_auth_oauthlib.flow\n\n# Use the client_secret.json file to identify the application requesting\n# authorization. The client ID (from that file) and access scopes are required.\nflow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(\n 'client_secret.json',\n scopes=['https://www.googleapis.com/auth/calendar.readonly',\n 'https://www.googleapis.com/auth/contacts.readonly'])\n```\n\nExample:\n```devsite-click-to-copy\nconst {google} = require('googleapis');\n\n/**\n * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI\n * from the client_secret.json file. To get these credentials for your application, visit\n * https://console.cloud.google.com/apis/credentials.\n */\nconst oauth2Client = new google.auth.OAuth2(\n YOUR_CLIENT_ID,\n YOUR_CLIENT_SECRET,\n YOUR_REDIRECT_URL\n);\n\n// Access scopes for read-only Calendar and Contacts.\nconst scopes = [\n 'https://www.googleapis.com/auth/calendar.readonly',\n 'https://www.googleapis.com/auth/contacts.readonly']\n];\n\n// Generate a url that asks permissions\nconst authorizationUrl = oauth2Client.generateAuthUrl({\n // 'online' (default) or 'offline' (gets refresh_token)\n access_type: 'offline',\n /** Pass in the scopes array defined above.\n * Alternatively, if only one scope is needed, you can pass a scope URL as a string */\n scope: scopes,\n // Enable incremental authorization. Recommended as best practices.\n include_granted_scopes: true\n});\n```\n\nExample:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth?\naccess_type=offline&\nscope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20openid%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly&\ninclude_granted_scopes=true&\nresponse_type=code&\nredirect_uri=YOUR_REDIRECT_URL&\nclient_id=YOUR_CLIENT_ID\n```\n\nExample:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth?\naccess_type=offline&\nscope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.file&\ninclude_granted_scopes=true&\nresponse_type=code&\nredirect_uri=YOUR_REDIRECT_URL&\nclient_id=YOUR_CLIENT_ID\n```\n\nExample:\n```devsite-click-to-copy\n{\n \"name\": \"Example Chrome extension application\",\n ...\n \"permissions\": [\n \"identity\"\n ],\n \"oauth2\" : {\n \"client_id\": \"YOUR_CLIENT_ID\",\n \"scopes\":[\"https://www.googleapis.com/auth/calendar.readonly\",\n \"https://www.googleapis.com/auth/contacts.readonly\"]\n }\n}\n```\n\nExample:\n```text\n...\ndocument.querySelector('button').addEventListener('click', function () {\n chrome.identity.getAuthToken({ interactive: true },\n function (token) {\n if (token === undefined) {\n // User didn't authorize both scopes.\n // Updating the UX and application accordingly\n ...\n } else {\n // User authorized both or one of the scopes.\n // It neglects to check which scopes users granted and assumes users granted all scopes.\n\n // Calling the APIs, etc.\n ...\n }\n });\n});\n```\n\nExample:\n```text\n...\ndocument.querySelector('button').addEventListener('click', function () {\n chrome.identity.getAuthToken({ interactive: true, enableGranularPermissions: true },\n function (token, grantedScopes) {\n if (token === undefined) {\n // User didn't authorize any scope.\n // Updating the UX and application accordingly\n ...\n } else {\n // User authorized the request. Now, check which scopes were granted.\n if (grantedScopes.includes('https://www.googleapis.com/auth/calendar.readonly'))\n {\n // User authorized Calendar read permission.\n // Calling the APIs, etc.\n ...\n }\n else\n {\n // User didn't authorize Calendar read permission.\n // Update UX and application accordingly\n ...\n }\n\n if (grantedScopes.includes('https://www.googleapis.com/auth/contacts.readonly'))\n {\n // User authorized Contacts read permission.\n // Calling the APIs, etc.\n ...\n }\n else\n {\n // User didn't authorize Contacts read permission.\n // Update UX and application accordingly\n ...\n }\n }\n });\n});\n```\n\nExample:\n```text\n...\nconst oauth2Client = new google.auth.OAuth2(\n YOUR_CLIENT_ID,\n YOUR_CLIENT_SECRET,\n YOUR_REDIRECT_URL\n);\n\n// Access scopes for two non-Sign-In scopes - Google Calendar and Contacts\nconst scopes = [\n 'https://www.googleapis.com/auth/contacts.readonly',\n 'https://www.googleapis.com/auth/calendar.readonly'\n];\n\n// Generate a url that asks permissions for the Google Calendar and Contacts scopes\nconst authorizationUrl = oauth2Client.generateAuthUrl({\n // 'online' (default) or 'offline' (gets refresh_token)\n access_type: 'offline',\n // Pass in the scopes array defined above\n scope: scopes,\n // Enable incremental authorization. Recommended as best practices.\n include_granted_scopes: true\n});\n\nasync function main() {\n const server = http.createServer(async function (req, res) {\n // Example on redirecting user to Google OAuth 2.0 server.\n if (req.url == '/') {\n res.writeHead(301, { \"Location\": authorizationUrl });\n }\n // Receive the callback from Google OAuth 2.0 server.\n if (req.url.startsWith('/oauth2callback')) {\n // Handle the Google OAuth 2.0 server response\n let q = url.parse(req.url, true).query;\n\n if (q.error) {\n // User didn't authorize both scopes.\n // Updating the UX and application accordingly\n ...\n } else {\n // User authorized both or one of the scopes.\n // It neglects to check which scopes users granted and assumes users granted all scopes.\n\n // Get access and refresh tokens (if access_type is offline)\n let { tokens } = await oauth2Client.getToken(q.code);\n // Calling the APIs, etc.\n ...\n }\n }\n res.end();\n }).listen(80);\n}\n```\n\nExample:\n```text\n...\nconst oauth2Client = new google.auth.OAuth2(\n YOUR_CLIENT_ID,\n YOUR_CLIENT_SECRET,\n YOUR_REDIRECT_URL\n);\n\n// Access scopes for two non-Sign-In scopes - Google Calendar and Contacts\nconst scopes = [\n 'https://www.googleapis.com/auth/contacts.readonly',\n 'https://www.googleapis.com/auth/calendar.readonly'\n];\n\n// Generate a url that asks permissions for the Google Calendar and Contacts scopes\nconst authorizationUrl = oauth2Client.generateAuthUrl({\n // 'online' (default) or 'offline' (gets refresh_token)\n access_type: 'offline',\n // Pass in the scopes array defined above\n scope: scopes,\n // Enable incremental authorization. Recommended as best practices.\n include_granted_scopes: true,\n // Set to true to enable more granular permissions for Google OAuth 2.0 client IDs created before 2019.\n // No effect for newer Google OAuth 2.0 client IDs, since more granular permissions is always enabled for them.\n enable_granular_consent: true\n});\n\nasync function main() {\n const server = http.createServer(async function (req, res) {\n // Redirect users to Google OAuth 2.0 server.\n if (req.url == '/') {\n res.writeHead(301, { \"Location\": authorizationUrl });\n }\n // Receive the callback from Google OAuth 2.0 server.\n if (req.url.startsWith('/oauth2callback')) {\n // Handle the Google OAuth 2.0 server response\n let q = url.parse(req.url, true).query;\n\n if (q.error) {\n // User didn't authorize both scopes.\n // Updating the UX and application accordingly\n ...\n } else {\n // Get access and refresh tokens (if access_type is offline)\n let { tokens } = await oauth2Client.getToken(q.code);\n oauth2Client.setCredentials(tokens);\n\n // User authorized the request. Now, check which scopes were granted.\n if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly'))\n {\n // User authorized Calendar read permission.\n // Calling the APIs, etc.\n ...\n }\n else\n {\n // User didn't authorize Calendar read permission.\n // Calling the APIs, etc.\n ...\n }\n\n // Check which scopes user granted the permission to application\n if (tokens.scope.includes('https://www.googleapis.com/auth/contacts.readonly'))\n {\n // User authorized Contacts read permission.\n // Calling the APIs, etc.\n ...\n }\n else\n {\n // User didn't authorize Contacts read permission.\n // Update UX and application accordingly\n ...\n }\n }\n }\n res.end();\n }).listen(80);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.836Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":297,"estimatedTokens":2311}}427{"id":"doc-smart_banners_unity_google_for_developers-61676615","source":"documentation","title":"Smart Banners | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/banner/smart","text":"Example:\n```text\n// Create a Smart Banner at the top of the screen.\nBannerView bannerView = new BannerView(adUnitId, AdSize.SmartBanner, AdPosition.Top);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.836Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":7,"estimatedTokens":43}}428{"id":"doc-native_overlay_ads_unity_google_for_developers-b93203b0","source":"documentation","title":"Native overlay ads | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/native-overlay","text":"Example:\n```text\n// These ad units are configured to always serve test ads.\n#if UNITY_ANDROID\n private string _adUnitId = \"ca-app-pub-3940256099942544/2247696110\";\n#elif UNITY_IPHONE\n private string _adUnitId = \"ca-app-pub-3940256099942544/3986624511\";\n#else\n private string _adUnitId = \"unused\";\n#endif\n\n\nprivate NativeOverlayAd _nativeOverlayAd;\n\n/// <summary>\n/// Loads the ad.\n/// </summary>\npublic void LoadAd()\n{\n // Clean up the old ad before loading a new one.\n if (_nativeOverlayAd != null)\n {\n DestroyAd();\n }\n\n Debug.Log(\"Loading native overlay ad.\");\n\n // Create a request used to load the ad.\n var adRequest = new AdRequest();\n\n // Optional: Define native ad options.\n var options = new NativeAdOptions\n {\n AdChoicesPosition = AdChoicesPlacement.TopRightCorner,\n MediaAspectRatio = NativeMediaAspectRatio.Any,\n };\n\n // Send the request to load the ad.\n NativeOverlayAd.Load(_adUnitId, adRequest, options,\n (NativeOverlayAd ad, LoadAdError error) =>\n {\n if (error != null)\n {\n Debug.LogError(\"Native Overlay ad failed to load an ad \" +\n \" with error: \" + error);\n return;\n }\n\n // The ad should always be non-null if the error is null, but\n // double-check to avoid a crash.\n if (ad == null)\n {\n Debug.LogError(\"Unexpected error: Native Overlay ad load event \" +\n \" fired with null ad and null error.\");\n return;\n }\n\n // The operation completed successfully.\n Debug.Log(\"Native Overlay ad loaded with response : \" +\n ad.GetResponseInfo());\n _nativeOverlayAd = ad;\n\n // Register to ad events to extend functionality.\n RegisterEventHandlers(ad);\n });\n}\n```\n\nExample:\n```text\n/// <summary>\n/// Renders the ad.\n/// </summary>\npublic void RenderAd()\n{\n if (_nativeOverlayAd != null)\n {\n Debug.Log(\"Rendering Native Overlay ad.\");\n\n // Define a native template style with a custom style.\n var style = new NativeTemplateStyle\n {\n TemplateID = NativeTemplateID.Medium,\n MainBackgroundColor = Color.red,\n CallToActionText = new NativeTemplateTextStyles\n {\n BackgroundColor = Color.green,\n FontColor = Color.white,\n FontSize = 9,\n Style = NativeTemplateFontStyle.Bold\n }\n };\n\n // Renders a native overlay ad at the default size\n // and anchored to the bottom of the screne.\n _nativeOverlayAd.RenderTemplate(style, AdPosition.Bottom);\n }\n}\n```\n\nExample:\n```text\n/// <summary>\n/// Shows the ad.\n/// </summary>\npublic void ShowAd()\n{\n if (_nativeOverlayAd != null)\n {\n Debug.Log(\"Showing Native Overlay ad.\");\n _nativeOverlayAd.Show();\n }\n}\n```\n\nExample:\n```text\n/// <summary>\n/// Hides the ad.\n/// </summary>\npublic void HideAd()\n{\n if (_nativeOverlayAd != null)\n {\n Debug.Log(\"Hiding Native Overlay ad.\");\n _nativeOverlayAd.Hide();\n }\n}\n```\n\nExample:\n```text\n/// <summary>\n/// Destroys the native overlay ad.\n/// </summary>\npublic void DestroyAd()\n{\n if (_nativeOverlayAd != null)\n {\n Debug.Log(\"Destroying native overlay ad.\");\n _nativeOverlayAd.Destroy();\n _nativeOverlayAd = null;\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.837Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":147,"estimatedTokens":861}}429{"id":"doc-integrate_bidmachine_with_mediation_unity_google-22830cc1","source":"documentation","title":"Integrate BidMachine with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/bidmachine","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.bidmachine\n```\n\nExample:\n```text\nio.bidmachine\ncom.google.ads.mediation.bidmachine\n```\n\nExample:\n```text\nGADMediationAdapterBidMachine\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.838Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":17,"estimatedTokens":54}}430{"id":"doc-interstitial_ads_unity_google_for_developers-80baffe3","source":"documentation","title":"Interstitial ads | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/interstitial","text":"Example:\n```text\nusing GoogleMobileAds;\nusing GoogleMobileAds.Api;\n\npublic class GoogleMobileAdsDemoScript : MonoBehaviour\n{\n public void Start()\n {\n // Initialize Google Mobile Ads Unity Plugin.\n MobileAds.Initialize((InitializationStatus initStatus) =>\n {\n // This callback is called once the MobileAds SDK is initialized.\n });\n }\n}\n```\n\nExample:\n```text\n// Create our request used to load the ad.\nvar adRequest = new AdRequest();\n\n// Send the request to load the ad.\nInterstitialAd.Load(\"AD_UNIT_ID\", adRequest, (InterstitialAd ad, LoadAdError error) =>\n{\n if (error != null)\n {\n // The ad failed to load.\n return;\n }\n // The ad loaded successfully.\n});InterstitialAdSnippets.cs\n```\n\nExample:\n```text\nif (interstitialAd != null && interstitialAd.CanShowAd())\n{\n interstitialAd.Show();\n}InterstitialAdSnippets.cs\n```\n\nExample:\n```text\ninterstitialAd.OnAdPaid += (AdValue adValue) =>\n{\n // Raised when the ad is estimated to have earned money.\n};\ninterstitialAd.OnAdImpressionRecorded += () =>\n{\n // Raised when an impression is recorded for an ad.\n};\ninterstitialAd.OnAdClicked += () =>\n{\n // Raised when a click is recorded for an ad.\n};\ninterstitialAd.OnAdFullScreenContentOpened += () =>\n{\n // Raised when the ad opened full screen content.\n};\ninterstitialAd.OnAdFullScreenContentClosed += () =>\n{\n // Raised when the ad closed full screen content.\n};\ninterstitialAd.OnAdFullScreenContentFailed += (AdError error) =>\n{\n // Raised when the ad failed to open full screen content.\n};InterstitialAdSnippets.cs\n```\n\nExample:\n```text\nif (interstitialAd != null)\n{\n interstitialAd.Destroy();\n}InterstitialAdSnippets.cs\n```\n\nExample:\n```text\ninterstitialAd.OnAdFullScreenContentClosed += () =>\n{\n // Reload the ad so that we can show another as soon as possible.\n var adRequest = new AdRequest();\n InterstitialAd.Load(\"AD_UNIT_ID\", adRequest, (InterstitialAd ad, LoadAdError error) =>\n {\n // Handle ad loading here.\n });\n};InterstitialAdSnippets.cs\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.838Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":93,"estimatedTokens":521}}431{"id":"doc-integrate_anchored_adaptive_banners_unity_google-7a6e7219","source":"documentation","title":"Integrate anchored adaptive banners | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/banner/anchored-adaptive","text":"Example:\n```text\n// Get the device safe width in density-independent pixels.\nint deviceWidth = MobileAds.Utils.GetDeviceSafeWidth();\n\n// Define the anchored adaptive ad size.\nAdSize adaptiveSize =\n AdSize.GetCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(deviceWidth);\n\n// Create an anchored adaptive banner view.\nbannerView = new BannerView(\"ANCHORED_ADAPTIVE_AD_UNIT_ID\", adaptiveSize, AdPosition.Bottom);BannerViewSnippets.cs\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.839Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":14,"estimatedTokens":115}}432{"id":"doc-integrate_chartboost_with_mediation_unity_google-fd17136f","source":"documentation","title":"Integrate Chartboost with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/chartboost","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.chartboost\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.Chartboost;\n// ...\n\nChartboost.AddDataUseConsent(CBCCPADataUseConsent.OptInSale);\n```\n\nExample:\n```text\n<uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n```\n\nExample:\n```text\ncom.google.ads.mediation.chartboost.ChartboostAdapter\ncom.google.ads.mediation.chartboost.ChartboostMediationAdapter\n```\n\nExample:\n```text\nGADMAdapterChartboost\nGADMediationAdapterChartboost\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.840Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":31,"estimatedTokens":134}}433{"id":"doc-rewarded_ads_unity_google_for_developers-22d3d9fc","source":"documentation","title":"Rewarded ads | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/rewarded","text":"Example:\n```text\nusing GoogleMobileAds;\nusing GoogleMobileAds.Api;\n\npublic class GoogleMobileAdsDemoScript : MonoBehaviour\n{\n public void Start()\n {\n // Initialize Google Mobile Ads Unity Plugin.\n MobileAds.Initialize((InitializationStatus initStatus) =>\n {\n // This callback is called once the MobileAds SDK is initialized.\n });\n }\n}\n```\n\nExample:\n```text\n// Create our request used to load the ad.\nvar adRequest = new AdRequest();\n\n// Send the request to load the ad.\nRewardedAd.Load(\"AD_UNIT_ID\", adRequest, (RewardedAd ad, LoadAdError error) =>\n{\n if (error != null)\n {\n // The ad failed to load.\n return;\n }\n // The ad loaded successfully.\n});RewardedAdSnippets.cs\n```\n\nExample:\n```text\n// Create and pass the SSV options to the rewarded ad.\nvar options = new ServerSideVerificationOptions\n{\n CustomData = \"\"SAMPLE_CUSTOM_DATA_STRING\"\"\n};\n\nrewardedAd.SetServerSideVerificationOptions(options);RewardedAdSnippets.cs\n```\n\nExample:\n```text\nif (rewardedAd != null && rewardedAd.CanShowAd())\n{\n rewardedAd.Show((Reward reward) =>\n {\n // The ad was showen and the user earned a reward.\n });\n}RewardedAdSnippets.cs\n```\n\nExample:\n```text\nrewardedAd.OnAdPaid += (AdValue adValue) =>\n{\n // Raised when the ad is estimated to have earned money.\n};\nrewardedAd.OnAdImpressionRecorded += () =>\n{\n // Raised when an impression is recorded for an ad.\n};\nrewardedAd.OnAdClicked += () =>\n{\n // Raised when a click is recorded for an ad.\n};\nrewardedAd.OnAdFullScreenContentOpened += () =>\n{\n // Raised when the ad opened full screen content.\n};\nrewardedAd.OnAdFullScreenContentClosed += () =>\n{\n // Raised when the ad closed full screen content.\n};\nrewardedAd.OnAdFullScreenContentFailed += (AdError error) =>\n{\n // Raised when the ad failed to open full screen content.\n};RewardedAdSnippets.cs\n```\n\nExample:\n```text\nif (rewardedAd != null)\n{\n rewardedAd.Destroy();\n}RewardedAdSnippets.cs\n```\n\nExample:\n```text\nrewardedAd.OnAdFullScreenContentClosed += () =>\n{\n // Reload the ad so that we can show another as soon as possible.\n var adRequest = new AdRequest();\n RewardedAd.Load(\"AD_UNIT_ID\", adRequest, (RewardedAd ad, LoadAdError error) =>\n {\n // Handle ad loading here.\n });\n};RewardedAdSnippets.cs\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.840Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":107,"estimatedTokens":586}}434{"id":"doc-get_started_unity_google_for_developers-25b003e7","source":"documentation","title":"Get started | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/native","text":"Example:\n```text\nprivate void RequestNativeAd() {\n AdLoader adLoader = new AdLoader.Builder(INSERT_AD_UNIT_HERE)\n .ForNativeAd()\n .Build();\n}\n```\n\nExample:\n```text\nadLoader.LoadAd(new AdRequest.Builder().Build());\n```\n\nExample:\n```text\nprivate void RequestNativeAd() {\n AdLoader adLoader = new AdLoader.Builder(INSERT_AD_UNIT_HERE)\n .ForNativeAd()\n .Build();\n adLoader.OnNativeAdLoaded += this.HandleNativeAdLoaded;\n adLoader.OnAdFailedToLoad += this.HandleAdFailedToLoad;\n adLoader.LoadAd(new AdRequest.Builder().Build());\n}\n```\n\nExample:\n```text\nprivate void RequestNativeAd() {\n ...\n adLoader.OnAdFailedToLoad += this.HandleNativeAdFailedToLoad;\n}\n\nprivate void HandleNativeAdFailedToLoad(object sender, AdFailedToLoadEventArgs args) {\n Debug.Log(\"Native ad failed to load: \" + args.Message);\n}\n```\n\nExample:\n```text\nprivate NativeAd nativeAd;\n...\nprivate void HandleNativeAdLoaded(object sender, NativeAdEventArgs args) {\n Debug.Log(\"Native ad loaded.\");\n this.nativeAd = args.nativeAd;\n}\n```\n\nExample:\n```text\nprivate bool nativeAdLoaded;\nprivate NativeAd nativeAd;\n\nvoid Update() {\n ...\n\n if (this.nativeAdLoaded) {\n this.nativeAdLoaded = false;\n // Get Texture2D for the icon asset of native ad.\n Texture2D iconTexture = this.nativeAd.GetIconTexture();\n\n // Get string for headline asset of native ad.\n string headline = this.nativeAd.GetHeadlineText();\n }\n}\n\nprivate void HandleNativeAdLoaded(object sender, NativeAdEventArgs args) {\n Debug.Log(\"Native ad loaded.\");\n this.nativeAd = args.nativeAd;\n this.nativeAdLoaded = true;\n}\n```\n\nExample:\n```text\nif (!this.nativeAd.RegisterIconImageGameObject(icon))\n{\n // Handle failure to register the icon ad asset.\n}\n```\n\nExample:\n```text\n// Create GameObject that will display the headline ad asset.\nGameObject headline = new GameObject();\nheadline.AddComponent<TextMesh>();\nheadline.GetComponent<TextMesh>().characterSize = 0.5 f;\nheadline.GetComponent<TextMesh>().anchor = TextAnchor.MiddleCenter;\nheadline.GetComponent<TextMesh>().color = Color.black;\n\n// Get string of the headline asset.\nstring headlineText = this.nativeAd.GetHeadlineText();\nheadline.GetComponent<TextMesh>().text = headlineText;\n\n// Add box collider to the GameObject which will automatically scale.\nheadline.AddComponent<BoxCollider>();\n```\n\nExample:\n```text\nprivate GameObject icon;\nprivate bool nativeAdLoaded;\nprivate NativeAd nativeAd;\n...\nvoid Update() {\n ...\n\n if (this.nativeAdLoaded) {\n this.nativeAdLoaded = false;\n // Get Texture2D for icon asset of native ad.\n Texture2D iconTexture = this.nativeAd.GetIconTexture();\n\n icon = GameObject.CreatePrimitive(PrimitiveType.Quad);\n icon.transform.position = new Vector3(1, 1, 1);\n icon.transform.localScale = new Vector3(1, 1, 1);\n icon.GetComponent<Renderer>().material.mainTexture = iconTexture;\n\n // Register GameObject that will display icon asset of native ad.\n if (!this.nativeAd.RegisterIconImageGameObject(icon))\n {\n // Handle failure to register ad asset.\n }\n }\n}\n...\n\nprivate void HandleNativeAdLoaded(object sender, NativeAdEventArgs args) {\n Debug.Log(\"Native ad loaded.\");\n this.nativeAd = args.nativeAd;\n this.nativeAdLoaded = true;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.841Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":134,"estimatedTokens":842}}435{"id":"doc-integrate_bigo_ads_sdk_with_mediation_unity_goog-f1a0c7ba","source":"documentation","title":"Integrate BIGO Ads SDK with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/bigo","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.bigo\n```\n\nExample:\n```text\nusing GoogleMobileAds.Mediation.Bigo.Api;\n// ...\n\nBigo.SetCcpaConsent(true);\n```\n\nExample:\n```text\nsg.bigo.ads\ncom.google.ads.mediation.bigo.BigoMediationAdapter\n```\n\nExample:\n```text\nGADMediationAdapterBigo\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.842Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":25,"estimatedTokens":79}}436{"id":"doc-integrate_i_mobile_with_mediation_unity_google_f-9ae81ebd","source":"documentation","title":"Integrate i-mobile with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/imobile","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.imobile\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.844Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":22}}437{"id":"doc-integrate_applovin_with_mediation_unity_google_f-07450758","source":"documentation","title":"Integrate AppLovin with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/applovin","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.applovin\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.AppLovin;\n// ...\n\nAppLovin.SetHasUserConsent(true);\n```\n\nExample:\n```text\nAppLovin.SetIsAgeRestrictedUser(true);\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.AppLovin;\n// ...\n\nAppLovin.SetDoNotSell(true);\n```\n\nExample:\n```text\ncom.google.ads.mediation.applovin.mediation.ApplovinAdapter\ncom.google.ads.mediation.applovin.AppLovinMediationAdapter\n```\n\nExample:\n```text\nGADMAdapterAppLovin\nGADMAdapterAppLovinRewardBasedVideoAd\nGADMediationAdapterAppLovin\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.845Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":40,"estimatedTokens":153}}438{"id":"doc-integrate_moloco_with_mediation_unity_google_for-38886ff3","source":"documentation","title":"Integrate Moloco with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/moloco","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.moloco\n```\n\nExample:\n```text\ncom.moloco.sdk\ncom.google.ads.mediation.moloco.MolocoMediationAdapter\n```\n\nExample:\n```text\nMolocoSDK.MolocoError\nGADMediationAdapterMoloco\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.846Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":18,"estimatedTokens":62}}439{"id":"doc-integrate_pubmatic_openwrap_beta_with_mediation_-626df83f","source":"documentation","title":"Integrate PubMatic OpenWrap (Beta) with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/pubmatic","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.pubmatic\n```\n\nExample:\n```text\ncom.pubmatic.sdk\ncom.google.ads.mediation.pubmatic\n```\n\nExample:\n```text\nGADMediationAdapterPubMatic\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.847Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":17,"estimatedTokens":53}}440{"id":"doc-integrate_ironsource_ads_with_mediation_unity_go-8fbc4389","source":"documentation","title":"Integrate ironSource Ads with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/ironsource","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.ironsource\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.IronSource;\n// ...\n\nIronSource.SetMetaData(\"do_not_sell\", \"true\");\n```\n\nExample:\n```text\ncom.google.ads.mediation.ironsource.IronSourceAdapter\ncom.google.ads.mediation.ironsource.IronSourceRewardedAdapter\n```\n\nExample:\n```text\nGADMAdapterIronSource\nGADMAdapterIronSourceRewarded\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.849Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":26,"estimatedTokens":107}}441{"id":"doc-using_oauth_2_0_for_web_server_applications_auth-1ceca6d8","source":"documentation","title":"Using OAuth 2.0 for Web Server Applications | Authorization | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/web-server","text":"Example:\n```text\ncomposer require google/apiclient:^2.15.0\n```\n\nExample:\n```text\npip install --upgrade google-api-python-client\n```\n\nExample:\n```text\npip install --upgrade google-auth google-auth-oauthlib google-auth-httplib2\n```\n\nExample:\n```text\npip install --upgrade flask\n```\n\nExample:\n```text\npip install --upgrade requests\n```\n\nExample:\n```text\ngem install googleauth\n```\n\nExample:\n```text\ngem install google-apis-drive_v3 google-apis-calendar_v3\n```\n\nExample:\n```text\ngem install sinatra\n```\n\nExample:\n```text\nnpm install googleapis crypto express express-session\n```\n\nExample:\n```text\nuse Google\\Client;\n\n$client = new Client();\n\n// Required, call the setAuthConfig function to load authorization credentials from\n// client_secret.json file.\n$client->setAuthConfig('client_secret.json');\n\n// Required, to set the scope value, call the addScope function\n$client->addScope([Google\\Service\\Drive::DRIVE_METADATA_READONLY, Google\\Service\\Calendar::CALENDAR_READONLY]);\n\n// Required, call the setRedirectUri function to specify a valid redirect URI for the\n// provided client_id\n$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');\n\n// Recommended, offline access will give you both an access and refresh token so that\n// your app can refresh the access token without user interaction.\n$client->setAccessType('offline');\n\n// Recommended, call the setState function. Using a state value can increase your assurance that\n// an incoming connection is the result of an authentication request.\n$client->setState($sample_passthrough_value);\n\n// Optional, if your application knows which user is trying to authenticate, it can use this\n// parameter to provide a hint to the Google Authentication Server.\n$client->setLoginHint('hint@example.com');\n\n// Optional, call the setPrompt function to set \"consent\" will prompt the user for consent\n$client->setPrompt('consent');\n\n// Optional, call the setIncludeGrantedScopes function with true to enable incremental\n// authorization\n$client->setIncludeGrantedScopes(true);\n```\n\nExample:\n```text\nimport google.oauth2.credentials\nimport google_auth_oauthlib.flow\n\n# Required, call the from_client_secrets_file method to retrieve the client ID from a\n# client_secret.json file. The client ID (from that file) and access scopes are required. (You can\n# also use the from_client_config method, which passes the client configuration as it originally\n# appeared in a client secrets file but doesn't access the file itself.)\nflow = google_auth_oauthlib.flow.Flow.from_client_secrets_file('client_secret.json',\n scopes=['https://www.googleapis.com/auth/drive.metadata.readonly',\n 'https://www.googleapis.com/auth/calendar.readonly'])\n\n# Required, indicate where the API server will redirect the user after the user completes\n# the authorization flow. The redirect URI is required. The value must exactly\n# match one of the authorized redirect URIs for the OAuth 2.0 client, which you\n# configured in the API Console. If this value doesn't match an authorized URI,\n# you will get a 'redirect_uri_mismatch' error.\nflow.redirect_uri = 'https://www.example.com/oauth2callback'\n\n# Generate URL for request to Google's OAuth 2.0 server.\n# Use kwargs to set optional request parameters.\nauthorization_url, state = flow.authorization_url(\n # Recommended, enable offline access so that you can refresh an access token without\n # re-prompting the user for permission. Recommended for web server apps.\n access_type='offline',\n # Optional, enable incremental authorization. Recommended as a best practice.\n include_granted_scopes='true',\n # Optional, if your application knows which user is trying to authenticate, it can use this\n # parameter to provide a hint to the Google Authentication Server.\n login_hint='hint@example.com',\n # Optional, set prompt to 'consent' will prompt the user for consent\n prompt='consent')\n```\n\nExample:\n```text\nrequire 'googleauth'\nrequire 'googleauth/web_user_authorizer'\nrequire 'googleauth/stores/redis_token_store'\n\nrequire 'google/apis/drive_v3'\nrequire 'google/apis/calendar_v3'\n\n# Required, call the from_file method to retrieve the client ID from a\n# client_secret.json file.\nclient_id = Google::Auth::ClientId.from_file('/path/to/client_secret.json')\n\n# Required, scope value \n# Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.\nscope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY',\n 'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY']\n\n# Required, Authorizers require a storage instance to manage long term persistence of\n# access and refresh tokens.\ntoken_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)\n\n# Required, indicate where the API server will redirect the user after the user completes\n# the authorization flow. The redirect URI is required. The value must exactly\n# match one of the authorized redirect URIs for the OAuth 2.0 client, which you\n# configured in the API Console. If this value doesn't match an authorized URI,\n# you will get a 'redirect_uri_mismatch' error.\ncallback_uri = '/oauth2callback'\n\n# To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI\n# from the client_secret.json file. To get these credentials for your application, visit\n# https://console.cloud.google.com/apis/credentials.\nauthorizer = Google::Auth::WebUserAuthorizer.new(client_id, scope,\n token_store, callback_uri)\n```\n\nExample:\n```text\nconst {google} = require('googleapis');\nconst crypto = require('crypto');\nconst express = require('express');\nconst session = require('express-session');\n\n/**\n * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI\n * from the client_secret.json file. To get these credentials for your application, visit\n * https://console.cloud.google.com/apis/credentials.\n */\nconst oauth2Client = new google.auth.OAuth2(\n YOUR_CLIENT_ID,\n YOUR_CLIENT_SECRET,\n YOUR_REDIRECT_URL\n);\n\n// Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.\nconst scopes = [\n 'https://www.googleapis.com/auth/drive.metadata.readonly',\n 'https://www.googleapis.com/auth/calendar.readonly'\n];\n\n// Generate a secure random state value.\nconst state = crypto.randomBytes(32).toString('hex');\n\n// Store state in the session\nreq.session.state = state;\n\n// Generate a url that asks permissions for the Drive activity and Google Calendar scope\nconst authorizationUrl = oauth2Client.generateAuthUrl({\n // 'online' (default) or 'offline' (gets refresh_token)\n access_type: 'offline',\n /** Pass in the scopes array defined above.\n * Alternatively, if only one scope is needed, you can pass a scope URL as a string */\n scope: scopes,\n // Enable incremental authorization. Recommended as a best practice.\n include_granted_scopes: true,\n // Include the state parameter to reduce the risk of CSRF attacks.\n state: state\n});\n```\n\nExample:\n```text\n$auth_url = $client->createAuthUrl();\n```\n\nExample:\n```text\nheader('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));\n```\n\nExample:\n```text\nreturn flask.redirect(authorization_url)\n```\n\nExample:\n```text\nauth_uri = authorizer.get_authorization_url(request: request)\n```\n\nExample:\n```text\nres.redirect(authorizationUrl);\n```\n\nExample:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth?\n scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&\n access_type=offline&\n include_granted_scopes=true&\n response_type=code&\n state=state_parameter_passthrough_value&\n redirect_uri=https%3A//developers.google.com/oauthplayground&\n client_id=client_id\n```\n\nExample:\n```text\nhttps://oauth2.example.com/auth?error=access_denied\n```\n\nExample:\n```text\nhttps://oauth2.example.com/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7\n```\n\nExample:\n```text\n$access_token = $client->fetchAccessTokenWithAuthCode($_GET['code']);\n```\n\nExample:\n```text\nstate = flask.session['state']\nflow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(\n 'client_secret.json',\n scopes=['https://www.googleapis.com/auth/drive.metadata.readonly'],\n state=state)\nflow.redirect_uri = flask.url_for('oauth2callback', _external=True)\n\nauthorization_response = flask.request.url\nflow.fetch_token(authorization_response=authorization_response)\n\n# Store the credentials in browser session storage, but for security: client_id, client_secret,\n# and token_uri are instead stored only on the backend server.\ncredentials = flow.credentials\nflask.session['credentials'] = {\n 'token': credentials.token,\n 'refresh_token': credentials.refresh_token,\n 'granted_scopes': credentials.granted_scopes}\n```\n\nExample:\n```text\ntarget_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)\n redirect target_url\n```\n\nExample:\n```text\nconst url = require('url');\n\n// Receive the callback from Google's OAuth 2.0 server.\napp.get('/oauth2callback', async (req, res) => {\n let q = url.parse(req.url, true).query;\n\n if (q.error) { // An error response e.g. error=access_denied\n console.log('Error:' + q.error);\n } else if (q.state !== req.session.state) { //check state value\n console.log('State mismatch. Possible CSRF attack');\n res.end('State mismatch. Possible CSRF attack');\n } else { // Get access and refresh tokens (if access_type is offline)\n\n let { tokens } = await oauth2Client.getToken(q.code);\n oauth2Client.setCredentials(tokens);\n});\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.googleapis.com\nContent-Type: application/x-www-form-urlencoded\nDPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik\\\n VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR\\\n nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE\\\n QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj\\\n oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia\\\n WF0IjoxNTYyMjYyNjE2fQ.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg\\\n 4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg\n\ncode=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&\nclient_id=your_client_id&\nredirect_uri=https%3A//developers.google.com/oauthplayground&\ngrant_type=authorization_code\n```\n\nExample:\n```text\nopenssl ecparam -name prime256v1 -genkey -noout -out dpop_private.pem\nopenssl ec -in dpop_private.pem -pubout -out dpop_public.pem\n```\n\nExample:\n```text\n{\n \"typ\":\"dpop+jwt\",\n \"alg\":\"ES256\",\n \"jwk\": {\n \"kty\":\"EC\",\n \"x\":\"YOUR_PUBLIC_KEY_X\",\n \"y\":\"YOUR_PUBLIC_KEY_Y\",\n \"crv\":\"P-256\"\n }\n}\n```\n\nExample:\n```text\n{\n \"jti\":\"JTI_VALUE\",\n \"htm\":\"POST\",\n \"htu\":\"https://oauth2.googleapis.com/token\",\n \"iat\":YOUR_JWT_ISSUED_TIME,\n \"nonce\":\"SERVER_PROVIDED_NONCE\"\n}\n```\n\nExample:\n```text\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nDPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI\n\n{\n \"access_token\": \"1/fFAGRNJru1FTz70BzhT3Zg\",\n \"expires_in\": 3920,\n \"token_type\": \"Bearer\",\n \"scope\": \"https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly\",\n \"refresh_token\": \"1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI\"\n}\n```\n\nExample:\n```text\n// Space-separated string of granted scopes if it exists, otherwise null.\n$granted_scopes = $client->getOAuth2Service()->getGrantedScope();\n\n// Determine which scopes user granted and build a dictionary\n$granted_scopes_dict = [\n 'Drive' => str_contains($granted_scopes, Google\\Service\\Drive::DRIVE_METADATA_READONLY),\n 'Calendar' => str_contains($granted_scopes, Google\\Service\\Calendar::CALENDAR_READONLY)\n];\n```\n\nExample:\n```text\ncredentials = flow.credentials\nflask.session['credentials'] = {\n 'token': credentials.token,\n 'refresh_token': credentials.refresh_token,\n 'granted_scopes': credentials.granted_scopes}\n```\n\nExample:\n```text\ndef check_granted_scopes(credentials):\n features = {}\n if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['granted_scopes']:\n features['drive'] = True\n else:\n features['drive'] = False\n\n if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']:\n features['calendar'] = True\n else:\n features['calendar'] = False\n\n return features\n```\n\nExample:\n```text\n# User authorized the request. Now, check which scopes were granted.\nif credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY)\n # User authorized read-only Drive activity permission.\n # Calling the APIs, etc\nelse\n # User didn't authorize read-only Drive activity permission.\n # Update UX and application accordingly\nend\n\n# Check if user authorized Calendar read permission.\nif credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY)\n # User authorized Calendar read permission.\n # Calling the APIs, etc.\nelse\n # User didn't authorize Calendar read permission.\n # Update UX and application accordingly\nend\n```\n\nExample:\n```text\n// User authorized the request. Now, check which scopes were granted.\nif (tokens.scope.includes('https://www.googleapis.com/auth/drive.metadata.readonly'))\n{\n // User authorized read-only Drive activity permission.\n // Calling the APIs, etc.\n}\nelse\n{\n // User didn't authorize read-only Drive activity permission.\n // Update UX and application accordingly\n}\n\n// Check if user authorized Calendar read permission.\nif (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly'))\n{\n // User authorized Calendar read permission.\n // Calling the APIs, etc.\n}\nelse\n{\n // User didn't authorize Calendar read permission.\n // Update UX and application accordingly\n}\n```\n\nExample:\n```text\n{\n \"access_token\": \"1/fFAGRNJru1FTz70BzhT3Zg\",\n \"expires_in\": 3920,\n \"token_type\": \"Bearer\",\n \"scope\": \"https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly\",\n \"refresh_token\": \"1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI\"\n }\n```\n\nExample:\n```text\n$client->setAccessToken($access_token);\n```\n\nExample:\n```text\n$drive = new Google\\Service\\Drive($client);\n```\n\nExample:\n```text\n$files = $drive->files->listFiles(array());\n```\n\nExample:\n```text\nfrom googleapiclient.discovery import build\n\ndrive = build('drive', 'v2', credentials=credentials)\n```\n\nExample:\n```text\nfiles = drive.files().list().execute()\n```\n\nExample:\n```text\ndrive = Google::Apis::DriveV3::DriveService.new\n```\n\nExample:\n```text\ndrive.authorization = credentials\n```\n\nExample:\n```text\nfiles = drive.list_files\n```\n\nExample:\n```text\nfiles = drive.list_files(options: { authorization: credentials })\n```\n\nExample:\n```text\nconst { google } = require('googleapis');\n\n// Example of using Google Drive API to list filenames in user's Drive.\nconst drive = google.drive('v3');\ndrive.files.list({\n auth: oauth2Client,\n pageSize: 10,\n fields: 'nextPageToken, files(id, name)',\n}, (err1, res1) => {\n if (err1) return console.log('The API returned an error: ' + err1);\n const files = res1.data.files;\n if (files.length) {\n console.log('Files:');\n files.map((file) => {\n console.log(`${file.name} (${file.id})`);\n });\n } else {\n console.log('No files found.');\n }\n});\n```\n\nExample:\n```text\nGET /drive/v2/files HTTP/1.1\nHost: www.googleapis.com\nAuthorization: Bearer access_token\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v2/files?access_token=access_token\n```\n\nExample:\n```text\ncurl -H \"Authorization: Bearer access_token\" https://www.googleapis.com/drive/v2/files\n```\n\nExample:\n```text\ncurl https://www.googleapis.com/drive/v2/files?access_token=access_token\n```\n\nExample:\n```text\nmkdir ~/php-oauth2-example\ncd ~/php-oauth2-example\n```\n\nExample:\n```text\nphp -S localhost:8080 ~/php-oauth2-example\n```\n\nExample:\n```text\n<?php\nrequire_once __DIR__.'/vendor/autoload.php';\n\nsession_start();\n\n$client = new Google\\Client();\n$client->setAuthConfig('client_secret.json');\n\n// User granted permission as an access token is in the session.\nif (isset($_SESSION['access_token']) && $_SESSION['access_token'])\n{\n $client->setAccessToken($_SESSION['access_token']);\n \n // Check if user granted Drive permission\n if ($_SESSION['granted_scopes_dict']['Drive']) {\n echo \"Drive feature is enabled.\";\n echo \"</br>\";\n $drive = new Drive($client);\n $files = array();\n $response = $drive->files->listFiles(array());\n foreach ($response->files as $file) {\n echo \"File: \" . $file->name . \" (\" . $file->id . \")\";\n echo \"</br>\";\n }\n } else {\n echo \"Drive feature is NOT enabled.\";\n echo \"</br>\";\n }\n\n // Check if user granted Calendar permission\n if ($_SESSION['granted_scopes_dict']['Calendar']) {\n echo \"Calendar feature is enabled.\";\n echo \"</br>\";\n } else {\n echo \"Calendar feature is NOT enabled.\";\n echo \"</br>\";\n }\n}\nelse\n{\n // Redirect users to outh2call.php which redirects users to Google OAuth 2.0\n $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php';\n header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));\n}\n?>\n```\n\nExample:\n```text\n<?php\nrequire_once __DIR__.'/vendor/autoload.php';\n\nsession_start();\n\n$client = new Google\\Client();\n\n// Required, call the setAuthConfig function to load authorization credentials from\n// client_secret.json file.\n$client->setAuthConfigFile('client_secret.json');\n$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST']. $_SERVER['PHP_SELF']);\n\n// Required, to set the scope value, call the addScope function.\n$client->addScope([Google\\Service\\Drive::DRIVE_METADATA_READONLY, Google\\Service\\Calendar::CALENDAR_READONLY]);\n\n// Enable incremental authorization. Recommended as a best practice.\n$client->setIncludeGrantedScopes(true);\n\n// Recommended, offline access will give you both an access and refresh token so that\n// your app can refresh the access token without user interaction.\n$client->setAccessType(\"offline\");\n\n// Generate a URL for authorization as it doesn't contain code and error\nif (!isset($_GET['code']) && !isset($_GET['error']))\n{\n // Generate and set state value\n $state = bin2hex(random_bytes(16));\n $client->setState($state);\n $_SESSION['state'] = $state;\n\n // Generate a url that asks permissions.\n $auth_url = $client->createAuthUrl();\n header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));\n}\n\n// User authorized the request and authorization code is returned to exchange access and\n// refresh tokens.\nif (isset($_GET['code']))\n{\n // Check the state value\n if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['state']) {\n die('State mismatch. Possible CSRF attack.');\n }\n\n // Get access and refresh tokens (if access_type is offline)\n $token = $client->fetchAccessTokenWithAuthCode($_GET['code']);\n\n /** Save access and refresh token to the session variables.\n * ACTION ITEM: In a production app, you likely want to save the\n * refresh token in a secure persistent storage instead. */\n $_SESSION['access_token'] = $token;\n $_SESSION['refresh_token'] = $client->getRefreshToken();\n \n // Space-separated string of granted scopes if it exists, otherwise null.\n $granted_scopes = $client->getOAuth2Service()->getGrantedScope();\n\n // Determine which scopes user granted and build a dictionary\n $granted_scopes_dict = [\n 'Drive' => str_contains($granted_scopes, Google\\Service\\Drive::DRIVE_METADATA_READONLY),\n 'Calendar' => str_contains($granted_scopes, Google\\Service\\Calendar::CALENDAR_READONLY)\n ];\n $_SESSION['granted_scopes_dict'] = $granted_scopes_dict;\n \n $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/';\n header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));\n}\n\n// An error response e.g. error=access_denied\nif (isset($_GET['error']))\n{\n echo \"Error: \". $_GET['error'];\n}\n?>\n```\n\nExample:\n```text\n# -*- coding: utf-8 -*-\n\nimport os\nimport flask\nimport json\nimport requests\n\nimport google.oauth2.credentials\nimport google_auth_oauthlib.flow\nimport googleapiclient.discovery\n\n# This variable specifies the name of a file that contains the OAuth 2.0\n# information for this application, including its client_id and client_secret.\nCLIENT_SECRETS_FILE = \"client_secret.json\"\n\n# The OAuth 2.0 access scope allows for access to the\n# authenticated user's account and requires requests to use an SSL connection.\nSCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly',\n 'https://www.googleapis.com/auth/calendar.readonly']\nAPI_SERVICE_NAME = 'drive'\nAPI_VERSION = 'v2'\n\napp = flask.Flask(__name__)\n# Note: A secret key is included in the sample so that it works.\n# If you use this code in your application, replace this with a truly secret\n# key. See https://flask.palletsprojects.com/quickstart/#sessions.\napp.secret_key = 'REPLACE ME - this value is here as a placeholder.'\n\n@app.route('/')\ndef index():\n return print_index_table()\n\n@app.route('/drive')\ndef drive_api_request():\n if 'credentials' not in flask.session:\n return flask.redirect('authorize')\n\n features = flask.session['features']\n\n if features['drive']:\n # Load client secrets from the server-side file.\n with open(CLIENT_SECRETS_FILE, 'r') as f:\n client_config = json.load(f)['web']\n\n # Load user-specific credentials from browser session storage.\n session_credentials = flask.session['credentials']\n\n # Reconstruct the credentials object.\n credentials = google.oauth2.credentials.Credentials(\n refresh_token=session_credentials.get('refresh_token'),\n scopes=session_credentials.get('granted_scopes'),\n token=session_credentials.get('token'),\n client_id=client_config.get('client_id'),\n client_secret=client_config.get('client_secret'),\n token_uri=client_config.get('token_uri'))\n\n drive = googleapiclient.discovery.build(\n API_SERVICE_NAME, API_VERSION, credentials=credentials)\n\n files = drive.files().list().execute()\n\n # Save credentials back to session in case access token was refreshed.\n flask.session['credentials'] = credentials_to_dict(credentials)\n\n return flask.jsonify(**files)\n else:\n # User didn't authorize read-only Drive activity permission.\n return '<p>Drive feature is not enabled.</p>'\n\n@app.route('/calendar')\ndef calendar_api_request():\n if 'credentials' not in flask.session:\n return flask.redirect('authorize')\n\n features = flask.session['features']\n\n if features['calendar']:\n # User authorized Calendar read permission.\n # Calling the APIs, etc.\n return ('<p>User granted the Google Calendar read permission. '+\n 'This sample code does not include code to call Calendar</p>')\n else:\n # User didn't authorize Calendar read permission.\n # Update UX and application accordingly\n return '<p>Calendar feature is not enabled.</p>'\n\n@app.route('/authorize')\ndef authorize():\n # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps.\n flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(\n CLIENT_SECRETS_FILE, scopes=SCOPES)\n\n # The URI created here must exactly match one of the authorized redirect URIs\n # for the OAuth 2.0 client, which you configured in the API Console. If this\n # value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch'\n # error.\n flow.redirect_uri = flask.url_for('oauth2callback', _external=True)\n\n authorization_url, state = flow.authorization_url(\n # Enable offline access so that you can refresh an access token without\n # re-prompting the user for permission. Recommended for web server apps.\n access_type='offline',\n # Enable incremental authorization. Recommended as a best practice.\n include_granted_scopes='true')\n\n # Store the state so the callback can verify the auth server response.\n flask.session['state'] = state\n\n return flask.redirect(authorization_url)\n\n@app.route('/oauth2callback')\ndef oauth2callback():\n # Specify the state when creating the flow in the callback so that it can\n # verified in the authorization server response.\n state = flask.session['state']\n\n flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(\n CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)\n flow.redirect_uri = flask.url_for('oauth2callback', _external=True)\n\n # Use the authorization server's response to fetch the OAuth 2.0 tokens.\n authorization_response = flask.request.url\n flow.fetch_token(authorization_response=authorization_response)\n\n # Store credentials in the session.\n # ACTION ITEM: In a production app, you likely want to save these\n # credentials in a persistent database instead.\n credentials = flow.credentials\n \n credentials = credentials_to_dict(credentials)\n flask.session['credentials'] = credentials\n\n # Check which scopes user granted\n features = check_granted_scopes(credentials)\n flask.session['features'] = features\n return flask.redirect('/')\n \n@app.route('/revoke')\ndef revoke():\n if 'credentials' not in flask.session:\n return ('You need to <a href=\"/authorize\">authorize</a> before ' +\n 'testing the code to revoke credentials.')\n\n # Load client secrets from the server-side file.\n with open(CLIENT_SECRETS_FILE, 'r') as f:\n client_config = json.load(f)['web']\n\n # Load user-specific credentials from the session.\n session_credentials = flask.session['credentials']\n\n # Reconstruct the credentials object.\n credentials = google.oauth2.credentials.Credentials(\n refresh_token=session_credentials.get('refresh_token'),\n scopes=session_credentials.get('granted_scopes'),\n token=session_credentials.get('token'),\n client_id=client_config.get('client_id'),\n client_secret=client_config.get('client_secret'),\n token_uri=client_config.get('token_uri'))\n\n revoke = requests.post('https://oauth2.googleapis.com/revoke',\n params={'token': credentials.token},\n headers = {'content-type': 'application/x-www-form-urlencoded'})\n\n status_code = getattr(revoke, 'status_code')\n if status_code == 200:\n # Clear the user's session credentials after successful revocation\n if 'credentials' in flask.session:\n del flask.session['credentials']\n del flask.session['features']\n return('Credentials successfully revoked.' + print_index_table())\n else:\n return('An error occurred.' + print_index_table())\n\n@app.route('/clear')\ndef clear_credentials():\n if 'credentials' in flask.session:\n del flask.session['credentials']\n return ('Credentials have been cleared.<br><br>' +\n print_index_table())\n\ndef credentials_to_dict(credentials):\n return {'token': credentials.token,\n 'refresh_token': credentials.refresh_token,\n 'granted_scopes': credentials.granted_scopes}\n\ndef check_granted_scopes(credentials):\n features = {}\n if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['granted_scopes']:\n features['drive'] = True\n else:\n features['drive'] = False\n\n if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']:\n features['calendar'] = True\n else:\n features['calendar'] = False\n\n return features\n\ndef print_index_table():\n return ('<table>' + \n '<tr><td><a href=\"/authorize\">Test the auth flow directly</a></td>' +\n '<td>Go directly to the authorization flow. If there are stored ' +\n ' credentials, you still might not be prompted to reauthorize ' +\n ' the application.</td></tr>' +\n '<tr><td><a href=\"/drive\">Call Drive API directly</a></td>' +\n '<td> Use stored credentials to call the API, you still might not be prompted to reauthorize ' +\n ' the application.</td></tr>' +\n '<tr><td><a href=\"/calendar\">Call Calendar API directly</a></td>' +\n '<td> Use stored credentials to call the API, you still might not be prompted to reauthorize ' +\n ' the application.</td></tr>' + \n '<tr><td><a href=\"/revoke\">Revoke current credentials</a></td>' +\n '<td>Revoke the access token associated with the current user ' +\n ' session. After revoking credentials, if you go to the test ' +\n ' page, you should see an <code>invalid_grant</code> error.' +\n '</td></tr>' +\n '<tr><td><a href=\"/clear\">Clear Flask session credentials</a></td>' +\n '<td>Clear the access token currently stored in the user session. ' +\n ' After clearing the token, if you <a href=\"/authorize\">authorize</a> ' +\n ' again, you should go back to the auth flow.' +\n '</td></tr></table>')\n\nif __name__ == '__main__':\n # When running locally, disable OAuthlib's HTTPs verification.\n # ACTION ITEM for developers:\n # When running in production *do not* leave this option enabled.\n os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'\n\n # This disables the requested scopes and granted scopes check.\n # If users only grant partial request, the warning would not be thrown.\n os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1'\n\n # Specify a hostname and port that are set as a valid redirect URI\n # for your API project in the Google API Console.\n app.run('localhost', 8080, debug=True)\n```\n\nExample:\n```text\nrequire 'googleauth'\nrequire 'googleauth/web_user_authorizer'\nrequire 'googleauth/stores/redis_token_store'\n\nrequire 'google/apis/drive_v3'\nrequire 'google/apis/calendar_v3'\n\nrequire 'sinatra'\n\nconfigure do\n enable :sessions\n\n # Required, call the from_file method to retrieve the client ID from a\n # client_secret.json file.\n set :client_id, Google::Auth::ClientId.from_file('/path/to/client_secret.json')\n\n # Required, scope value\n # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.\n scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY',\n 'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY']\n\n # Required, Authorizers require a storage instance to manage long term persistence of\n # access and refresh tokens.\n set :token_store, Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)\n\n # Required, indicate where the API server will redirect the user after the user completes\n # the authorization flow. The redirect URI is required. The value must exactly\n # match one of the authorized redirect URIs for the OAuth 2.0 client, which you\n # configured in the API Console. If this value doesn't match an authorized URI,\n # you will get a 'redirect_uri_mismatch' error.\n set :callback_uri, '/oauth2callback'\n\n # To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI\n # from the client_secret.json file. To get these credentials for your application, visit\n # https://console.cloud.google.com/apis/credentials.\n set :authorizer, Google::Auth::WebUserAuthorizer.new(settings.client_id, settings.scope,\n settings.token_store, callback_uri: settings.callback_uri)\nend\n\nget '/' do\n # NOTE: Assumes the user is already authenticated to the app\n user_id = request.session['user_id']\n\n # Fetch stored credentials for the user from the given request session.\n # nil if none present\n credentials = settings.authorizer.get_credentials(user_id, request)\n\n if credentials.nil?\n # Generate a url that asks the user to authorize requested scope(s).\n # Then, redirect user to the url.\n redirect settings.authorizer.get_authorization_url(request: request)\n end\n \n # User authorized the request. Now, check which scopes were granted.\n if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY)\n # User authorized read-only Drive activity permission.\n # Example of using Google Drive API to list filenames in user's Drive.\n drive = Google::Apis::DriveV3::DriveService.new\n files = drive.list_files(options: { authorization: credentials })\n \"<pre>#{JSON.pretty_generate(files.to_h)}</pre>\"\n else\n # User didn't authorize read-only Drive activity permission.\n # Update UX and application accordingly\n end\n\n # Check if user authorized Calendar read permission.\n if credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY)\n # User authorized Calendar read permission.\n # Calling the APIs, etc.\n else\n # User didn't authorize Calendar read permission.\n # Update UX and application accordingly\n end\nend\n\n# Receive the callback from Google's OAuth 2.0 server.\nget '/oauth2callback' do\n # Handle the result of the oauth callback. Defers the exchange of the code by\n # temporarily stashing the results in the user's session.\n target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)\n redirect target_url\nend\n```\n\nExample:\n```text\nmkdir ~/nodejs-oauth2-example\ncd ~/nodejs-oauth2-example\n```\n\nExample:\n```text\nnpm install googleapis\n```\n\nExample:\n```text\nnode .\\main.js\n```\n\nExample:\n```text\nconst http = require('http');\nconst https = require('https');\nconst url = require('url');\nconst { google } = require('googleapis');\nconst crypto = require('crypto');\nconst express = require('express');\nconst session = require('express-session');\n\n/**\n * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI.\n * To get these credentials for your application, visit\n * https://console.cloud.google.com/apis/credentials.\n */\nconst oauth2Client = new google.auth.OAuth2(\n YOUR_CLIENT_ID,\n YOUR_CLIENT_SECRET,\n YOUR_REDIRECT_URL\n);\n\n// Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.\nconst scopes = [\n 'https://www.googleapis.com/auth/drive.metadata.readonly',\n 'https://www.googleapis.com/auth/calendar.readonly'\n];\n\n/* Global variable that stores user credential in this code example.\n * ACTION ITEM for developers:\n * Store user's refresh token in your data store if\n * incorporating this code into your real app.\n * For more information on handling refresh tokens,\n * see https://github.com/googleapis/google-api-nodejs-client#handling-refresh-tokens\n */\nlet userCredential = null;\n\nasync function main() {\n const app = express();\n\n app.use(session({\n secret: 'your_secure_secret_key', // Replace with a strong secret\n resave: false,\n saveUninitialized: false,\n }));\n\n // Example on redirecting user to Google's OAuth 2.0 server.\n app.get('/', async (req, res) => {\n // Generate a secure random state value.\n const state = crypto.randomBytes(32).toString('hex');\n // Store state in the session\n req.session.state = state;\n\n // Generate a url that asks permissions for the Drive activity and Google Calendar scope\n const authorizationUrl = oauth2Client.generateAuthUrl({\n // 'online' (default) or 'offline' (gets refresh_token)\n access_type: 'offline',\n /** Pass in the scopes array defined above.\n * Alternatively, if only one scope is needed, you can pass a scope URL as a string */\n scope: scopes,\n // Enable incremental authorization. Recommended as a best practice.\n include_granted_scopes: true,\n // Include the state parameter to reduce the risk of CSRF attacks.\n state: state\n });\n\n res.redirect(authorizationUrl);\n });\n\n // Receive the callback from Google's OAuth 2.0 server.\n app.get('/oauth2callback', async (req, res) => {\n // Handle the OAuth 2.0 server response\n let q = url.parse(req.url, true).query;\n\n if (q.error) { // An error response e.g. error=access_denied\n console.log('Error:' + q.error);\n } else if (q.state !== req.session.state) { //check state value\n console.log('State mismatch. Possible CSRF attack');\n res.end('State mismatch. Possible CSRF attack');\n } else { // Get access and refresh tokens (if access_type is offline)\n let { tokens } = await oauth2Client.getToken(q.code);\n oauth2Client.setCredentials(tokens);\n\n /** Save credential to the global variable in case access token was refreshed.\n * ACTION ITEM: In a production app, you likely want to save the refresh token\n * in a secure persistent database instead. */\n userCredential = tokens;\n \n // User authorized the request. Now, check which scopes were granted.\n if (tokens.scope.includes('https://www.googleapis.com/auth/drive.metadata.readonly'))\n {\n // User authorized read-only Drive activity permission.\n // Example of using Google Drive API to list filenames in user's Drive.\n const drive = google.drive('v3');\n drive.files.list({\n auth: oauth2Client,\n pageSize: 10,\n fields: 'nextPageToken, files(id, name)',\n }, (err1, res1) => {\n if (err1) return console.log('The API returned an error: ' + err1);\n const files = res1.data.files;\n if (files.length) {\n console.log('Files:');\n files.map((file) => {\n console.log(`${file.name} (${file.id})`);\n });\n } else {\n console.log('No files found.');\n }\n });\n }\n else\n {\n // User didn't authorize read-only Drive activity permission.\n // Update UX and application accordingly\n }\n\n // Check if user authorized Calendar read permission.\n if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly'))\n {\n // User authorized Calendar read permission.\n // Calling the APIs, etc.\n }\n else\n {\n // User didn't authorize Calendar read permission.\n // Update UX and application accordingly\n }\n }\n });\n\n // Example on revoking a token\n app.get('/revoke', async (req, res) => {\n // Build the string for the POST request\n let postData = \"token=\" + userCredential.access_token;\n\n // Options for POST request to Google's OAuth 2.0 server to revoke a token\n let postOptions = {\n host: 'oauth2.googleapis.com',\n port: '443',\n path: '/revoke',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Content-Length': Buffer.byteLength(postData)\n }\n };\n\n // Set up the request\n const postReq = https.request(postOptions, function (res) {\n res.setEncoding('utf8');\n res.on('data', d => {\n console.log('Response: ' + d);\n });\n });\n\n postReq.on('error', error => {\n console.log(error)\n });\n\n // Post the request with data\n postReq.write(postData);\n postReq.end();\n });\n\n\n const server = http.createServer(app);\n server.listen(8080);\n}\nmain().catch(console.error);\n```\n\nExample:\n```text\nimport json\nimport flask\nimport requests\n\napp = flask.Flask(__name__)\n\n# To get these credentials (CLIENT_ID CLIENT_SECRET) and for your application, visit\n# https://console.cloud.google.com/apis/credentials.\nCLIENT_ID = '123456789.apps.googleusercontent.com'\nCLIENT_SECRET = 'abc123' # Read from a file or environmental variable in a real app\n\n# Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar.\nSCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly'\n\n# Indicate where the API server will redirect the user after the user completes\n# the authorization flow. The redirect URI is required. The value must exactly\n# match one of the authorized redirect URIs for the OAuth 2.0 client, which you\n# configured in the API Console. If this value doesn't match an authorized URI,\n# you will get a 'redirect_uri_mismatch' error.\nREDIRECT_URI = 'http://example.com/oauth2callback'\n\n@app.route('/')\ndef index():\n if 'credentials' not in flask.session:\n return flask.redirect(flask.url_for('oauth2callback'))\n\n credentials = json.loads(flask.session['credentials'])\n\n if credentials['expires_in'] <= 0:\n return flask.redirect(flask.url_for('oauth2callback'))\n else: \n # User authorized the request. Now, check which scopes were granted.\n if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['scope']:\n # User authorized read-only Drive activity permission.\n # Example of using Google Drive API to list filenames in user's Drive.\n headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])}\n req_uri = 'https://www.googleapis.com/drive/v2/files'\n r = requests.get(req_uri, headers=headers).text\n else:\n # User didn't authorize read-only Drive activity permission.\n # Update UX and application accordingly\n r = 'User did not authorize Drive permission.'\n\n # Check if user authorized Calendar read permission.\n if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['scope']:\n # User authorized Calendar read permission.\n # Calling the APIs, etc.\n r += 'User authorized Calendar permission.'\n else:\n # User didn't authorize Calendar read permission.\n # Update UX and application accordingly\n r += 'User did not authorize Calendar permission.'\n\n return r\n\n@app.route('/oauth2callback')\ndef oauth2callback():\n if 'code' not in flask.request.args:\n state = str(uuid.uuid4())\n flask.session['state'] = state\n # Generate a url that asks permissions for the Drive activity\n # and Google Calendar scope. Then, redirect user to the url.\n auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code'\n '&client_id={}&redirect_uri={}&scope={}&state={}').format(CLIENT_ID, REDIRECT_URI,\n SCOPE, state)\n return flask.redirect(auth_uri)\n else:\n if 'state' not in flask.request.args or flask.request.args['state'] != flask.session['state']:\n return 'State mismatch. Possible CSRF attack.', 400\n\n auth_code = flask.request.args.get('code')\n data = {'code': auth_code,\n 'client_id': CLIENT_ID,\n 'client_secret': CLIENT_SECRET,\n 'redirect_uri': REDIRECT_URI,\n 'grant_type': 'authorization_code'}\n\n # Exchange authorization code for access and refresh tokens (if access_type is offline)\n r = requests.post('https://oauth2.googleapis.com/token', data=data)\n flask.session['credentials'] = r.text\n return flask.redirect(flask.url_for('index'))\n\nif __name__ == '__main__':\n import uuid\n app.secret_key = str(uuid.uuid4())\n app.debug = False\n app.run()\n```\n\nExample:\n```text\n$client->setIncludeGrantedScopes(true);\n```\n\nExample:\n```devsite-click-to-copy\nauthorization_url, state = flow.authorization_url(\n # Enable offline access so that you can refresh an access token without\n # re-prompting the user for permission. Recommended for web server apps.\n access_type='offline',\n # Enable incremental authorization. Recommended as a best practice.\n include_granted_scopes='true')\n```\n\nExample:\n```devsite-click-to-copy\nauth_client.update!(\n :additional_parameters => {\"include_granted_scopes\" => \"true\"}\n)\n```\n\nExample:\n```devsite-click-to-copy\nconst authorizationUrl = oauth2Client.generateAuthUrl({\n // 'online' (default) or 'offline' (gets refresh_token)\n access_type: 'offline',\n /** Pass in the scopes array defined above.\n * Alternatively, if only one scope is needed, you can pass a scope URL as a string */\n scope: scopes,\n // Enable incremental authorization. Recommended as a best practice.\n include_granted_scopes: true\n});\n```\n\nExample:\n```text\nGET https://accounts.google.com/o/oauth2/v2/auth?\n client_id=your_client_id&\n response_type=code&\n state=state_parameter_passthrough_value&\n scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&\n redirect_uri=https%3A//developers.google.com/oauthplayground&\n prompt=consent&\n include_granted_scopes=true\n```\n\nExample:\n```text\n$client->setAccessType(\"offline\");\n```\n\nExample:\n```text\nauth_client.update!(\n :additional_parameters => {\"access_type\" => \"offline\"}\n)\n```\n\nExample:\n```text\noauth2Client.on('tokens', (tokens) => {\n if (tokens.refresh_token) {\n // store the refresh_token in your secure persistent database\n console.log(tokens.refresh_token);\n }\n console.log(tokens.access_token);\n});\n```\n\nExample:\n```text\noauth2Client.setCredentials({\n refresh_token: `STORED_REFRESH_TOKEN`\n});\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.googleapis.com\nContent-Type: application/x-www-form-urlencoded\nDPoP: DPOP_PROOF_JWT\n\nclient_id=your_client_id&\nrefresh_token=refresh_token&\ngrant_type=refresh_token\n```\n\nExample:\n```text\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nDPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI\n\n{\n \"access_token\": \"1/fFAGRNJru1FTz70BzhT3Zg\",\n \"expires_in\": 3920,\n \"scope\": \"https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly\",\n \"token_type\": \"Bearer\"\n}\n```\n\nExample:\n```text\n$client->revokeToken();\n```\n\nExample:\n```text\nrequests.post('https://oauth2.googleapis.com/revoke',\n params={'token': credentials.token},\n headers = {'content-type': 'application/x-www-form-urlencoded'})\n```\n\nExample:\n```text\nuri = URI('https://oauth2.googleapis.com/revoke')\nresponse = Net::HTTP.post_form(uri, 'token' => auth_client.access_token)\n```\n\nExample:\n```text\nconst https = require('https');\n\n// Build the string for the POST request\nlet postData = \"token=\" + userCredential.access_token;\n\n// Options for POST request to Google's OAuth 2.0 server to revoke a token\nlet postOptions = {\n host: 'oauth2.googleapis.com',\n port: '443',\n path: '/revoke',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'Content-Length': Buffer.byteLength(postData)\n }\n};\n\n// Set up the request\nconst postReq = https.request(postOptions, function (res) {\n res.setEncoding('utf8');\n res.on('data', d => {\n console.log('Response: ' + d);\n });\n});\n\npostReq.on('error', error => {\n console.log(error)\n});\n\n// Post the request with data\npostReq.write(postData);\npostReq.end();\n```\n\nExample:\n```text\ncurl -d -X -POST --header \"Content-type:application/x-www-form-urlencoded\" \\\n https://oauth2.googleapis.com/revoke?token={token}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.854Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":77,"totalLines":1455,"estimatedTokens":11536}}442{"id":"doc-rewarded_interstitial_ads_beta_android_google_fo-8bd35c95","source":"documentation","title":"Rewarded interstitial ads (beta) | Android | Google for Developers","url":"https://developers.google.com/admob/android/rewarded-interstitial","text":"Example:\n```text\nrewardedInterstitialAd.setFullScreenContentCallback(\n new FullScreenContentCallback() {\n @Override\n public void onAdDismissedFullScreenContent() {\n // Called when fullscreen content is dismissed.\n Log.d(TAG, \"The ad was dismissed.\");\n // Make sure to set your reference to null so you don't\n // show it a second time.\n rewardedInterstitialAd = null;\n if (googleMobileAdsConsentManager.canRequestAds()) {\n loadRewardedInterstitialAd();\n }\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(AdError adError) {\n // Called when fullscreen content failed to show.\n Log.d(TAG, \"The ad failed to show.\");\n // Make sure to set your reference to null so you don't\n // show it a second time.\n rewardedInterstitialAd = null;\n }\n\n @Override\n public void onAdShowedFullScreenContent() {\n // Called when fullscreen content is shown.\n Log.d(TAG, \"The ad was shown.\");\n }\n\n @Override\n public void onAdImpression() {\n // Called when an impression is recorded for an ad.\n Log.d(TAG, \"The ad recorded an impression.\");\n }\n\n @Override\n public void onAdClicked() {\n // Called when ad is clicked.\n Log.d(TAG, \"The ad was clicked.\");\n }\n });MainActivity.java\n```\n\nExample:\n```text\nrewardedInterstitialAd?.fullScreenContentCallback =\n object : FullScreenContentCallback() {\n override fun onAdDismissedFullScreenContent() {\n // Called when fullscreen content is dismissed.\n Log.d(TAG, \"Ad was dismissed.\")\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n rewardedInterstitialAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(adError: AdError) {\n // Called when fullscreen content failed to show.\n Log.d(TAG, \"Ad failed to show.\")\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n rewardedInterstitialAd = null\n }\n\n override fun onAdShowedFullScreenContent() {\n // Called when fullscreen content is shown.\n Log.d(TAG, \"Ad showed fullscreen content.\")\n }\n\n override fun onAdImpression() {\n // Called when an impression is recorded for an ad.\n Log.d(TAG, \"Ad recorded an impression.\")\n }\n\n override fun onAdClicked() {\n // Called when an ad is clicked.\n Log.d(TAG, \"Ad was clicked.\")\n }\n }MainActivity.kt\n```\n\nExample:\n```text\nrewardedInterstitialAd.show(\n MainActivity.this,\n new OnUserEarnedRewardListener() {\n @Override\n public void onUserEarnedReward(@NonNull RewardItem rewardItem) {\n Log.d(TAG, \"The user earned the reward.\");\n // Handle the reward.\n int rewardAmount = rewardItem.getAmount();\n String rewardType = rewardItem.getType();\n }\n });MainActivity.java\n```\n\nExample:\n```text\nrewardedInterstitialAd?.show(this) { rewardItem ->\n Log.d(TAG, \"User earned the reward.\")\n // Handle the reward.\n val rewardAmount = rewardItem.amount\n val rewardType = rewardItem.type\n}MainActivity.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.856Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":108,"estimatedTokens":799}}443{"id":"doc-launch_ad_inspector_unity_google_for_developers-94ed543a","source":"documentation","title":"Launch ad inspector | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/ad-inspector/launch-ad-inspector","text":"Example:\n```text\npublic void OnButtonClick() {\n MobileAds.OpenAdInspector((AdInspectorError error) =>\n {\n // Error will be set if there was an issue and the inspector was not displayed.\n });\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.857Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":11,"estimatedTokens":54}}444{"id":"doc-integrate_adcolony_with_mediation_deprecated_and-8eb1ea76","source":"documentation","title":"Integrate AdColony with mediation (Deprecated) | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/adcolony","text":"Example:\n```text\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:adcolony:4.8.0.2\")\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.857Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":9,"estimatedTokens":47}}445{"id":"doc-integrate_applovin_with_mediation_android_google-3a300bd3","source":"documentation","title":"Integrate AppLovin with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/applovin","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:applovin:13.6.4.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:applovin:13.6.4.0'\n}\n```\n\nExample:\n```text\nAppLovinPrivacySettings.setHasUserConsent(true);AppLovinMediationSnippets.java\n```\n\nExample:\n```text\nAppLovinPrivacySettings.setHasUserConsent(true)AppLovinMediationSnippets.kt\n```\n\nExample:\n```text\nAppLovinPrivacySettings.setDoNotSell(true);AppLovinMediationSnippets.java\n```\n\nExample:\n```text\nAppLovinPrivacySettings.setDoNotSell(true)AppLovinMediationSnippets.kt\n```\n\nExample:\n```text\nBundle extras = new AppLovinExtras.Builder().setMuteAudio(true).build();\nAdRequest request =\n new AdRequest.Builder().addNetworkExtrasBundle(ApplovinAdapter.class, extras).build();AppLovinMediationSnippets.java\n```\n\nExample:\n```text\nval extras = AppLovinExtras.Builder().setMuteAudio(true).build()\nval request =\n AdRequest.Builder().addNetworkExtrasBundle(ApplovinAdapter::class.java, extras).build()AppLovinMediationSnippets.kt\n```\n\nExample:\n```text\ncom.google.ads.mediation.applovin.ApplovinAdapter\ncom.google.ads.mediation.applovin.AppLovinMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.859Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":57,"estimatedTokens":342}}446{"id":"doc-integrate_inmobi_with_mediation_android_google_f-fd3cae49","source":"documentation","title":"Integrate InMobi with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/inmobi","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:inmobi:11.4.0.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:inmobi:11.4.0.0'\n}\n```\n\nExample:\n```text\n<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(InMobiNetworkKeys.AGE_GROUP, InMobiNetworkValues.BETWEEN_35_AND_44);\nextras.putString(InMobiNetworkKeys.AREA_CODE, AREA_CODE_VALUE);\n\nAdRequest request =\n new AdRequest.Builder().addNetworkExtrasBundle(InMobiAdapter.class, extras).build();InMobiMediationSnippets.java\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putString(InMobiNetworkKeys.AGE_GROUP, InMobiNetworkValues.BETWEEN_35_AND_44)\nextras.putString(InMobiNetworkKeys.AREA_CODE, AREA_CODE_VALUE)\n\nval request =\n AdRequest.Builder().addNetworkExtrasBundle(InMobiAdapter::class.java, extras).build()InMobiMediationSnippets.kt\n```\n\nExample:\n```text\ncom.google.ads.mediation.inmobi.InMobiAdapter\ncom.google.ads.mediation.inmobi.InMobiMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.861Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":50,"estimatedTokens":354}}447{"id":"doc-rewarded_ads_android_google_for_developers-a13da4ac","source":"documentation","title":"Rewarded ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/rewarded","text":"Example:\n```text\nRewardedAd.load(\n this,\n \"AD_UNIT_ID\",\n new AdRequest.Builder().build(),\n new RewardedAdLoadCallback() {\n @Override\n public void onAdLoaded(@NonNull RewardedAd rewardedAd) {\n Log.d(TAG, \"Ad was loaded.\");\n MainActivity.this.rewardedAd = rewardedAd;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {\n Log.d(TAG, loadAdError.getMessage());\n rewardedAd = null;\n }\n });MainActivity.java\n```\n\nExample:\n```text\nRewardedAd.load(\n this,\n \"AD_UNIT_ID\",\n AdRequest.Builder().build(),\n object : RewardedAdLoadCallback() {\n override fun onAdLoaded(ad: RewardedAd) {\n Log.d(TAG, \"Ad was loaded.\")\n rewardedAd = ad\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n Log.d(TAG, adError.message)\n rewardedAd = null\n }\n },\n)MainActivity.kt\n```\n\nExample:\n```text\nrewardedAd.setFullScreenContentCallback(\n new FullScreenContentCallback() {\n @Override\n public void onAdDismissedFullScreenContent() {\n // Called when fullscreen content is dismissed.\n Log.d(TAG, \"Ad was dismissed.\");\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n rewardedAd = null;\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(AdError adError) {\n // Called when fullscreen content failed to show.\n Log.d(TAG, \"Ad failed to show.\");\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n rewardedAd = null;\n }\n\n @Override\n public void onAdShowedFullScreenContent() {\n // Called when fullscreen content is shown.\n Log.d(TAG, \"Ad showed fullscreen content.\");\n }\n\n @Override\n public void onAdImpression() {\n // Called when an impression is recorded for an ad.\n Log.d(TAG, \"Ad recorded an impression.\");\n }\n\n @Override\n public void onAdClicked() {\n // Called when an ad is clicked.\n Log.d(TAG, \"Ad was clicked.\");\n }\n });MainActivity.java\n```\n\nExample:\n```text\nrewardedAd?.fullScreenContentCallback =\n object : FullScreenContentCallback() {\n override fun onAdDismissedFullScreenContent() {\n // Called when fullscreen content is dismissed.\n Log.d(TAG, \"Ad was dismissed.\")\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n rewardedAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(adError: AdError) {\n // Called when fullscreen content failed to show.\n Log.d(TAG, \"Ad failed to show.\")\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n rewardedAd = null\n }\n\n override fun onAdShowedFullScreenContent() {\n // Called when fullscreen content is shown.\n Log.d(TAG, \"Ad showed fullscreen content.\")\n }\n\n override fun onAdImpression() {\n // Called when an impression is recorded for an ad.\n Log.d(TAG, \"Ad recorded an impression.\")\n }\n\n override fun onAdClicked() {\n // Called when an ad is clicked.\n Log.d(TAG, \"Ad was clicked.\")\n }\n }MainActivity.kt\n```\n\nExample:\n```text\nrewardedAd.show(\n MainActivity.this,\n new OnUserEarnedRewardListener() {\n @Override\n public void onUserEarnedReward(@NonNull RewardItem rewardItem) {\n Log.d(TAG, \"User earned the reward.\");\n // Handle the reward.\n }\n });MainActivity.java\n```\n\nExample:\n```text\nrewardedAd?.show(\n this,\n OnUserEarnedRewardListener { rewardItem ->\n Log.d(TAG, \"User earned the reward.\")\n // Handle the reward.\n val rewardAmount = rewardItem.amount\n val rewardType = rewardItem.type\n },\n)MainActivity.kt\n```\n\nExample:\n```text\nRewardedAd.load(\n context,\n AD_UNIT_ID,\n new AdRequest.Builder().build(),\n new RewardedAdLoadCallback() {\n @Override\n public void onAdLoaded(RewardedAd ad) {\n rewardedAd = ad;\n ServerSideVerificationOptions options =\n new ServerSideVerificationOptions.Builder()\n .setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\")\n .build();\n rewardedAd.setServerSideVerificationOptions(options);\n }\n });RewardedAdSnippets.java\n```\n\nExample:\n```text\nRewardedAd.load(\n context,\n AD_UNIT_ID,\n AdRequest.Builder().build(),\n object : RewardedAdLoadCallback() {\n override fun onAdLoaded(ad: RewardedAd) {\n rewardedAd = ad\n val options =\n ServerSideVerificationOptions.Builder().setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\").build()\n rewardedAd?.setServerSideVerificationOptions(options)\n }\n },\n)RewardedAdSnippets.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.861Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":183,"estimatedTokens":1193}}448{"id":"doc-integrate_bidmachine_with_mediation_android_goog-931cbe6f","source":"documentation","title":"Integrate BidMachine with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/bidmachine","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:bidmachine:3.7.1.1\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:bidmachine:3.7.1.1'\n}\n```\n\nExample:\n```text\nio.bidmachine\ncom.google.ads.mediation.bidmachine\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.863Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":23,"estimatedTokens":117}}449{"id":"doc-integrate_i_mobile_with_mediation_android_google-cb723af7","source":"documentation","title":"Integrate i-mobile with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/imobile","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://imobile.github.io/adnw-sdk-android\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:imobile:2.3.2.4\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:imobile:2.3.2.4'\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.864Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":30,"estimatedTokens":145}}450{"id":"doc-set_up_admob_mediation_android_google_for_develo-8eb78a4e","source":"documentation","title":"Set up AdMob Mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation","text":"Example:\n```text\npublic void initialize(Context context) {\n new Thread(\n () ->\n // Initialize the Google Mobile Ads SDK on a background thread.\n MobileAds.initialize(context, this::logAdapterStatus))\n .start();\n}\n\nprivate void logAdapterStatus(InitializationStatus initializationStatus) {\n // Check each adapter's initialization status.\n Map<String, AdapterStatus> statusMap = initializationStatus.getAdapterStatusMap();\n for (Map.Entry<String, AdapterStatus> entry : statusMap.entrySet()) {\n String adapterClass = entry.getKey();\n AdapterStatus status = entry.getValue();\n Log.d(\n TAG,\n String.format(\n \"Adapter name: %s, Description: %s, Latency: %d\",\n adapterClass, status.getDescription(), status.getLatency()));\n }\n}MediationSnippets.java\n```\n\nExample:\n```text\nfun initialize(context: Context) {\n CoroutineScope(Dispatchers.IO).launch {\n // Initialize the Google Mobile Ads SDK on a background thread.\n MobileAds.initialize(context, ::logAdapterStatus)\n }\n}\n\nprivate fun logAdapterStatus(initializationStatus: InitializationStatus) {\n // Check each adapter's initialization status.\n for ((adapterClass, status) in initializationStatus.adapterStatusMap) {\n Log.d(\n TAG,\n \"Adapter: $adapterClass, Status: ${status.description}, Latency: ${status.latency}ms\",\n )\n }\n}\nMediationSnippets.kt\n```\n\nExample:\n```text\nResponseInfo responseInfo = ad.getResponseInfo();\nString adapterClassName = null;\nif (responseInfo != null) {\n adapterClassName = responseInfo.getMediationAdapterClassName();\n}\nLog.d(TAG, \"Adapter class name: \" + adapterClassName);ResponseInfoSnippets.java\n```\n\nExample:\n```text\nLog.d(TAG, \"Adapter class name:\" + ad.responseInfo?.mediationAdapterClassName)ResponseInfoSnippets.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.864Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":62,"estimatedTokens":458}}451{"id":"doc-integrate_nend_with_mediation_deprecated_android-58d672f8","source":"documentation","title":"Integrate nend with mediation (Deprecated) | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/nend","text":"Example:\n```text\nrepositories {\n google()\n maven {\n url = uri(\"https://fan-adn.github.io/nendSDK-Android-lib/library\")\n }\n}\n\n// ...\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:nend:10.0.0.1\")\n}\n// ...\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.865Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":18,"estimatedTokens":80}}452{"id":"doc-integrate_vpon_with_mediation_android_google_for-5760b5aa","source":"documentation","title":"Integrate Vpon with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/vpon","text":"Example:\n```text\ndependencies {\n implementation(fileTree(mapOf(\"dir\" to \"libs\", \"include\" to listOf(\"*.aar\", \"*.jar\"))))\n // ...\n}\n```\n\nExample:\n```text\ndependencies {\n implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'])\n // ...\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.865Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":17,"estimatedTokens":69}}453{"id":"doc-integrate_dt_exchange_with_mediation_android_goo-d0877264","source":"documentation","title":"Integrate DT Exchange with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/dt-exchange","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:fyber:8.4.7.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:fyber:8.4.7.0'\n}\n```\n\nExample:\n```text\nInneractiveAdManager.setUSPrivacyString(US_PRIVACY_STRING);DTExchangeMediationSnippets.java\n```\n\nExample:\n```text\nInneractiveAdManager.setUSPrivacyString(US_PRIVACY_STRING)DTExchangeMediationSnippets.kt\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putInt(InneractiveMediationDefs.KEY_AGE, 10);\nextras.putBoolean(FyberMediationAdapter.KEY_MUTE_VIDEO, false);\n\nAdRequest request =\n new AdRequest.Builder().addNetworkExtrasBundle(FyberMediationAdapter.class, extras).build();DTExchangeMediationSnippets.java\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putInt(InneractiveMediationDefs.KEY_AGE, 10)\nextras.putBoolean(FyberMediationAdapter.KEY_MUTE_VIDEO, false)\n\nval request =\n AdRequest.Builder().addNetworkExtrasBundle(FyberMediationAdapter::class.java, extras).build()DTExchangeMediationSnippets.kt\n```\n\nExample:\n```text\ncom.google.ads.mediation.fyber.FyberMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.867Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":52,"estimatedTokens":326}}454{"id":"doc-set_advanced_native_features_android_google_for_-3673c9c8","source":"documentation","title":"Set advanced native features | Android | Google for Developers","url":"https://developers.google.com/admob/android/native/options","text":"Example:\n```text\nNativeAdOptions nativeAdOptions =\n new NativeAdOptions.Builder()\n .setMediaAspectRatio(NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_LANDSCAPE)\n .build();\n\nAdLoader loader =\n new AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build();NativeAdOptionsSnippets.java\n```\n\nExample:\n```text\nval nativeAdOptions =\n NativeAdOptions.Builder()\n .setMediaAspectRatio(NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_LANDSCAPE)\n .build()\n\nval loader = AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build()NativeAdOptionsSnippets.kt\n```\n\nExample:\n```text\nNativeAdOptions nativeAdOptions =\n new NativeAdOptions.Builder().setReturnUrlsForImageAssets(true).build();\n\nAdLoader loader =\n new AdLoader.Builder(context, \"AD_UNIT_ID\")\n .withNativeAdOptions(nativeAdOptions)\n .forNativeAd(\n nativeAd -> {\n List<Uri> imageUris = new ArrayList<>();\n for (Image image : nativeAd.getImages()) {\n imageUris.add(image.getUri());\n }\n })\n .build();NativeAdOptionsSnippets.java\n```\n\nExample:\n```text\nval nativeAdOptions = NativeAdOptions.Builder().setReturnUrlsForImageAssets(true).build()\n\nval loader =\n AdLoader.Builder(context, \"AD_UNIT_ID\")\n .withNativeAdOptions(nativeAdOptions)\n .forNativeAd { nativeAd ->\n val imageUris = nativeAd.images.mapNotNull { it.uri }\n }\n .build()NativeAdOptionsSnippets.kt\n```\n\nExample:\n```text\nNativeAdOptions nativeAdOptions =\n new NativeAdOptions.Builder().setRequestMultipleImages(true).build();\n\nAdLoader loader =\n new AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build();NativeAdOptionsSnippets.java\n```\n\nExample:\n```text\nval nativeAdOptions = NativeAdOptions.Builder().setRequestMultipleImages(true).build()\n\nval loader = AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build()NativeAdOptionsSnippets.kt\n```\n\nExample:\n```text\nNativeAdOptions nativeAdOptions =\n new NativeAdOptions.Builder()\n .setAdChoicesPlacement(NativeAdOptions.ADCHOICES_BOTTOM_RIGHT)\n .build();\n\nAdLoader loader =\n new AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build();NativeAdOptionsSnippets.java\n```\n\nExample:\n```text\nval nativeAdOptions =\n NativeAdOptions.Builder()\n .setAdChoicesPlacement(NativeAdOptions.ADCHOICES_BOTTOM_RIGHT)\n .build()\n\nval loader = AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build()NativeAdOptionsSnippets.kt\n```\n\nExample:\n```text\nNativeAdView nativeAdView = new NativeAdView(context);\nAdChoicesView adChoicesView = new AdChoicesView(context);\nnativeAdView.setAdChoicesView(adChoicesView);NativeAdOptionsSnippets.java\n```\n\nExample:\n```text\nval nativeAdView = NativeAdView(context)\nval adChoicesView = AdChoicesView(context)\nnativeAdView.adChoicesView = adChoicesViewNativeAdOptionsSnippets.kt\n```\n\nExample:\n```text\nVideoOptions videoOptions = new VideoOptions.Builder().setStartMuted(false).build();\n\nNativeAdOptions nativeAdOptions =\n new NativeAdOptions.Builder().setVideoOptions(videoOptions).build();\n\nAdLoader loader =\n new AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build();NativeAdOptionsSnippets.java\n```\n\nExample:\n```text\nval videoOptions = VideoOptions.Builder().setStartMuted(false).build()\n\nval nativeAdOptions = NativeAdOptions.Builder().setVideoOptions(videoOptions).build()\n\nval loader = AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build()NativeAdOptionsSnippets.kt\n```\n\nExample:\n```text\nVideoOptions videoOptions = new VideoOptions.Builder().setCustomControlsRequested(true).build();\n\nNativeAdOptions nativeAdOptions =\n new NativeAdOptions.Builder().setVideoOptions(videoOptions).build();\n\nAdLoader loader =\n new AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build();NativeAdOptionsSnippets.java\n```\n\nExample:\n```text\nval videoOptions = VideoOptions.Builder().setCustomControlsRequested(true).build()\n\nval nativeAdOptions = NativeAdOptions.Builder().setVideoOptions(videoOptions).build()\n\nval loader = AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build()NativeAdOptionsSnippets.kt\n```\n\nExample:\n```text\nMediaContent mediaContent = nativeAd.getMediaContent();\nif (mediaContent != null) {\n VideoController videoController = mediaContent.getVideoController();\n boolean canShowCustomControls = videoController.isCustomControlsEnabled();\n}NativeAdOptionsSnippets.java\n```\n\nExample:\n```text\nval mediaContent = nativeAd.mediaContent\nif (mediaContent != null) {\n val videoController = mediaContent.videoController\n val canShowCustomControls = videoController.isCustomControlsEnabled\n}NativeAdOptionsSnippets.kt\n```\n\nExample:\n```text\nNativeAdOptions adOptions =\n new NativeAdOptions.Builder()\n .enableCustomClickGestureDirection(\n NativeAdOptions.SWIPE_GESTURE_DIRECTION_RIGHT, /* tapsAllowed= */ true)\n .build();\n\n// ca-app-pub-3940256099942544/2247696110 is a sample ad unit ID that has custom click\n// gestures enabled.\nAdLoader.Builder builder =\n new AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(adOptions);NativeAdOptionsSnippets.java\n```\n\nExample:\n```text\nval adOptions =\n NativeAdOptions.Builder()\n .enableCustomClickGestureDirection(NativeAdOptions.SWIPE_GESTURE_DIRECTION_RIGHT, true)\n .build()\n\nval builder = AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(adOptions)NativeAdOptionsSnippets.kt\n```\n\nExample:\n```text\nAdLoader adLoader =\n new AdLoader.Builder(context, AD_UNIT_ID)\n .withAdListener(\n new AdListener() {\n // Called when a swipe gesture click is recorded.\n @Override\n public void onAdSwipeGestureClicked() {\n // Called when a swipe gesture click is recorded.\n Log.d(TAG, \"A swipe gesture click has occurred.\");\n }\n\n @Override\n public void onAdClicked() {\n // Called when a swipe gesture click or a tap click is recorded, as\n // configured in NativeAdOptions.\n Log.d(TAG, \"A swipe gesture click or a tap click has occurred.\");\n }\n })\n .build();NativeAdOptionsSnippets.java\n```\n\nExample:\n```text\nval adLoader =\n AdLoader.Builder(context, AD_UNIT_ID)\n .withAdListener(\n object : AdListener() {\n override fun onAdSwipeGestureClicked() {\n // Called when a swipe gesture click is recorded.\n Log.d(TAG, \"A swipe gesture click has occurred.\")\n }\n\n override fun onAdClicked() {\n // Called when a swipe gesture click or a tap click is recorded, as\n // configured in NativeAdOptions.\n Log.d(TAG, \"A swipe gesture click or a tap click has occurred.\")\n }\n }\n )\n .build()NativeAdOptionsSnippets.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.868Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":20,"totalLines":230,"estimatedTokens":1756}}455{"id":"doc-respond_to_video_events_android_google_for_devel-077ae26b","source":"documentation","title":"Respond to video events | Android | Google for Developers","url":"https://developers.google.com/admob/android/native/video-ads","text":"Example:\n```text\nif (nativeAd.getMediaContent() != null) {\n MediaContent mediaContent = nativeAd.getMediaContent();\n float mediaAspectRatio = mediaContent.getAspectRatio();\n if (mediaContent.hasVideoContent()) {\n float duration = mediaContent.getDuration();\n }\n}NativeVideoAdsSnippets.java\n```\n\nExample:\n```text\nnativeAd.mediaContent?.let { mediaContent ->\n val mediaAspectRatio: Float = mediaContent.aspectRatio\n if (mediaContent.hasVideoContent()) {\n val duration: Float = mediaContent.duration\n }\n}NativeVideoAdsSnippets.kt\n```\n\nExample:\n```text\nif (nativeAd.getMediaContent() != null) {\n VideoController videoController = nativeAd.getMediaContent().getVideoController();\n if (videoController != null) {\n videoController.setVideoLifecycleCallbacks(\n new VideoController.VideoLifecycleCallbacks() {\n @Override\n public void onVideoStart() {\n Log.d(TAG, \"Video started.\");\n }\n\n @Override\n public void onVideoPlay() {\n Log.d(TAG, \"Video played.\");\n }\n\n @Override\n public void onVideoPause() {\n Log.d(TAG, \"Video paused.\");\n }\n\n @Override\n public void onVideoEnd() {\n Log.d(TAG, \"Video ended.\");\n }\n\n @Override\n public void onVideoMute(boolean isMuted) {\n Log.d(TAG, \"Video isMuted: \" + isMuted + \".\");\n }\n });\n }\n}NativeVideoAdsSnippets.java\n```\n\nExample:\n```text\nval videoLifecycleCallbacks =\n object : VideoController.VideoLifecycleCallbacks() {\n override fun onVideoStart() {\n Log.d(TAG, \"Video started.\")\n }\n\n override fun onVideoPlay() {\n Log.d(TAG, \"Video played.\")\n }\n\n override fun onVideoPause() {\n Log.d(TAG, \"Video paused.\")\n }\n\n override fun onVideoEnd() {\n Log.d(TAG, \"Video ended.\")\n }\n\n override fun onVideoMute(isMuted: Boolean) {\n Log.d(TAG, \"Video isMuted: $isMuted.\")\n }\n }\nnativeAd.mediaContent?.videoController?.videoLifecycleCallbacks = videoLifecycleCallbacksNativeVideoAdsSnippets.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.868Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":85,"estimatedTokens":526}}456{"id":"doc-integrate_mintegral_with_mediation_android_googl-f40e50cd","source":"documentation","title":"Integrate Mintegral with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/mintegral","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:mintegral:17.1.71.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:mintegral:17.1.71.0'\n}\n```\n\nExample:\n```text\nMBridgeSDK sdk = MBridgeSDKFactory.getMBridgeSDK();\nsdk.setConsentStatus(context, MBridgeConstans.IS_SWITCH_ON);MintegralMediationSnippets.java\n```\n\nExample:\n```text\nval sdk = MBridgeSDKFactory.getMBridgeSDK()\nsdk.setConsentStatus(context, MBridgeConstans.IS_SWITCH_ON)MintegralMediationSnippets.kt\n```\n\nExample:\n```text\nMBridgeSDK mBridgeSDK = MBridgeSDKFactory.getMBridgeSDK();\nmBridgeSDK.setDoNotTrackStatus(false);MintegralMediationSnippets.java\n```\n\nExample:\n```text\nval sdk = MBridgeSDKFactory.getMBridgeSDK()\nsdk.setDoNotTrackStatus(false)MintegralMediationSnippets.kt\n```\n\nExample:\n```text\ncom.mbridge.msdk\ncom.google.ads.mediation.mintegral.MintegralMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.870Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":60,"estimatedTokens":330}}457{"id":"doc-integrate_tapjoy_with_mediation_deprecated_andro-b9ea6453","source":"documentation","title":"Integrate Tapjoy with mediation (Deprecated) | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/tapjoy","text":"Example:\n```text\nrepositories {\n google()\n maven {\n url 'https://sdk.tapjoy.com/'\n }\n}\n\n// ...\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:tapjoy:13.2.1.0'\n}\n// ...\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.870Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":18,"estimatedTokens":71}}458{"id":"doc-integrate_moloco_with_mediation_android_google_f-cc4b6c6d","source":"documentation","title":"Integrate Moloco with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/moloco","text":"Example:\n```text\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:moloco:4.11.0.0\")\n}\n```\n\nExample:\n```text\nval privacySettings = PrivacySettings(isUserConsent = true)\nMolocoPrivacy.setPrivacy(privacySettings)MolocoMediationSnippets.kt\n```\n\nExample:\n```text\nPrivacySettings privacySettings =\n new PrivacySettings(\n /* isUserConsent= */ true, /* isAgeRestrictedUser= */ false, /* isDoNotSell= */ false);\nMolocoPrivacy.setPrivacy(privacySettings);MolocoMediationSnippets.java\n```\n\nExample:\n```text\nval privacySettings = PrivacySettings(isDoNotSell = true)\nMolocoPrivacy.setPrivacy(privacySettings)MolocoMediationSnippets.kt\n```\n\nExample:\n```text\ncom.moloco.sdk\ncom.google.ads.mediation.moloco.MolocoMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.871Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":35,"estimatedTokens":206}}459{"id":"doc-integrate_meta_audience_network_with_bidding_and-a5d43b1e","source":"documentation","title":"Integrate Meta Audience Network with bidding | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/meta","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:facebook:6.22.0.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:facebook:6.22.0.0'\n}\n```\n\nExample:\n```text\nBundle extras = nativeAd.getExtras();\nif (extras.containsKey(FacebookMediationAdapter.KEY_SOCIAL_CONTEXT_ASSET)) {\n String socialContext = extras.getString(FacebookMediationAdapter.KEY_SOCIAL_CONTEXT_ASSET);\n // ...\n}MetaMediationSnippets.java\n```\n\nExample:\n```text\nval extras = nativeAd.getExtras()\nif (extras.containsKey(FacebookMediationAdapter.KEY_SOCIAL_CONTEXT_ASSET)) {\n val socialContext = extras.getString(FacebookMediationAdapter.KEY_SOCIAL_CONTEXT_ASSET)\n // ...\n}MetaMediationSnippets.kt\n```\n\nExample:\n```text\ncom.google.ads.mediation.facebook.FacebookAdapter\ncom.google.ads.mediation.facebook.FacebookMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.873Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":41,"estimatedTokens":263}}460{"id":"doc-integrate_maio_with_mediation_android_google_for-9de29800","source":"documentation","title":"Integrate maio with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/maio","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://imobile-maio.github.io/maven\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:maio:2.0.9.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:maio:2.0.9.0'\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.874Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":30,"estimatedTokens":142}}461{"id":"doc-integrate_mytarget_with_mediation_android_google-6aa91b3c","source":"documentation","title":"Integrate myTarget with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/mytarget","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:mytarget:5.51.2.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:mytarget:5.51.2.0'\n}\n```\n\nExample:\n```text\nMyTargetPrivacy.setUserConsent(true);MyTargetMediationSnippets.java\n```\n\nExample:\n```text\nMyTargetPrivacy.setUserConsent(true)MyTargetMediationSnippets.kt\n```\n\nExample:\n```text\nMyTargetPrivacy.setUserAgeRestricted(true);MyTargetMediationSnippets.java\n```\n\nExample:\n```text\nMyTargetPrivacy.setUserAgeRestricted(true)MyTargetMediationSnippets.kt\n```\n\nExample:\n```text\nMyTargetPrivacy.setCcpaUserConsent(true);MyTargetMediationSnippets.java\n```\n\nExample:\n```text\nMyTargetPrivacy.setCcpaUserConsent(true)MyTargetMediationSnippets.kt\n```\n\nExample:\n```text\ncom.google.ads.mediation.mytarget.MyTargetAdapter\ncom.google.ads.mediation.mytarget.MyTargetNativeAdapter\ncom.google.ads.mediation.mytarget.MyTargetRewardedAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.875Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":54,"estimatedTokens":283}}462{"id":"doc-integrate_liftoff_monetize_with_mediation_androi-00dd2f10","source":"documentation","title":"Integrate Liftoff Monetize with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/liftoff-monetize","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:vungle:7.7.7.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:vungle:7.7.7.0'\n}\n```\n\nExample:\n```text\nVunglePrivacySettings.setCCPAStatus(true);LiftoffMonetizeMediationSnippets.java\n```\n\nExample:\n```text\nVunglePrivacySettings.setCCPAStatus(true)LiftoffMonetizeMediationSnippets.kt\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(VungleConstants.KEY_USER_ID, \"myUserID\");\nextras.putInt(VungleConstants.KEY_ORIENTATION, 1);\n// Optional: Enables the back button on App Open ads immediately.\nextras.putBoolean(VungleConstants.KEY_BACK_BUTTON_IMMEDIATELY_ENABLED, true);\n\nAdRequest request =\n new AdRequest.Builder()\n .addNetworkExtrasBundle(VungleAdapter.class, extras) // Rewarded.\n .addNetworkExtrasBundle(VungleInterstitialAdapter.class, extras) // Interstitial.\n // App Open ads use VungleMediationAdapter\n .addNetworkExtrasBundle(VungleMediationAdapter.class, extras)\n .build();LiftoffMonetizeMediationSnippets.java\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putString(VungleConstants.KEY_USER_ID, \"myUserID\")\nextras.putInt(VungleConstants.KEY_ORIENTATION, 1)\n// Optional: Enables the back button on App Open ads immediately.\nextras.putBoolean(VungleConstants.KEY_BACK_BUTTON_IMMEDIATELY_ENABLED, true)\n\nval request =\n AdRequest.Builder()\n .addNetworkExtrasBundle(VungleAdapter::class.java, extras) // Rewarded.\n .addNetworkExtrasBundle(VungleInterstitialAdapter::class.java, extras) // Interstitial.\n // App Open ads use VungleMediationAdapter\n .addNetworkExtrasBundle(VungleMediationAdapter::class.java, extras)\n .build()LiftoffMonetizeMediationSnippets.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.877Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":61,"estimatedTokens":488}}463{"id":"doc-integrate_pangle_with_mediation_android_google_f-a7473e30","source":"documentation","title":"Integrate Pangle with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/pangle","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://artifact.bytedance.com/repository/pangle/\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:pangle:8.2.0.4.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:pangle:8.2.0.4.0'\n}\n```\n\nExample:\n```text\nPangleMediationAdapter.setPAConsent(PAGConstant.PAGPAConsentType.PAG_PA_CONSENT_TYPE_CONSENT);PangleMediationSnippets.java\n```\n\nExample:\n```text\nPangleMediationAdapter.setPAConsent(PAGConstant.PAGPAConsentType.PAG_PA_CONSENT_TYPE_CONSENT)PangleMediationSnippets.kt\n```\n\nExample:\n```text\ncom.pangle.ads\ncom.google.ads.mediation.pangle.PangleMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.879Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":46,"estimatedTokens":242}}464{"id":"doc-integrate_ly_ads_network_with_mediation_android_-da033d42","source":"documentation","title":"Integrate LY Ads Network with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/line","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:line:3.1.1.1\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:line:3.1.1.1'\n}\n```\n\nExample:\n```text\nLineMediationAdapter.Companion.setTestMode(true);LineMediationSnippets.java\n```\n\nExample:\n```text\nLineMediationAdapter.setTestMode(true)LineMediationSnippets.kt\n```\n\nExample:\n```text\nLineExtras lineExtras = new LineExtras(/* enableAdSound: */ true);\nBundle extras = lineExtras.build();\n\nAdRequest request =\n new AdRequest.Builder().addNetworkExtrasBundle(LineMediationAdapter.class, extras).build();LineMediationSnippets.java\n```\n\nExample:\n```text\nval lineExtras = LineExtras(enableAdSound = true)\nval extras = lineExtras.build()\n\nval request =\n AdRequest.Builder().addNetworkExtrasBundle(LineMediationAdapter::class.java, extras).build()LineMediationSnippets.kt\n```\n\nExample:\n```text\ncom.line.ads\ncom.google.ads.mediation.line.LineMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.882Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":51,"estimatedTokens":290}}465{"id":"doc-integrate_zucks_with_mediation_android_google_fo-c538c01e","source":"documentation","title":"Integrate Zucks with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/zucks","text":"Example:\n```text\ndependencies {\n implementation(fileTree(mapOf(\"dir\" to \"libs\", \"include\" to listOf(\"*.aar\", \"*.jar\"))))\n // ...\n}\n```\n\nExample:\n```text\ndependencies {\n implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'])\n // ...\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.883Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":17,"estimatedTokens":69}}466{"id":"doc-integrate_pubmatic_openwrap_beta_with_mediation_-2d07cd36","source":"documentation","title":"Integrate PubMatic OpenWrap (Beta) with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/pubmatic","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://repo.pubmatic.com/artifactory/public-repos\")\n }\n }\n}\n```\n\nExample:\n```text\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:pubmatic:5.2.0.0\")\n}\n```\n\nExample:\n```text\ncom.pubmatic.sdk\ncom.google.ads.mediation.pubmatic\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.884Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":28,"estimatedTokens":114}}467{"id":"doc-launch_ad_inspector_android_google_for_developer-792eb4e1","source":"documentation","title":"Launch ad inspector | Android | Google for Developers","url":"https://developers.google.com/admob/android/ad-inspector/launch-ad-inspector","text":"Example:\n```text\nMobileAds.openAdInspector(\n context,\n new OnAdInspectorClosedListener() {\n public void onAdInspectorClosed(@Nullable AdInspectorError error) {\n // Error will be non-null if ad inspector closed due to an error.\n }\n });AdInspectorSnippets.java\n```\n\nExample:\n```text\nMobileAds.openAdInspector(context) { error ->\n // Error will be non-null if ad inspector closed due to an error.\n}AdInspectorSnippets.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.885Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":19,"estimatedTokens":116}}468{"id":"doc-test_creative_types_android_google_for_developer-202f6faa","source":"documentation","title":"Test creative types | Android | Google for Developers","url":"https://developers.google.com/admob/android/test-creative-types","text":"Example:\n```text\nval extras = Bundle()\nextras.putString(\"ft_ctype\", \"video_app_install\")\n\nval request = AdRequest\n .Builder()\n .addNetworkExtrasBundle(AdMobAdapter::class.java, extras)\n .build()\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(\"ft_ctype\", \"video_app_install\");\n\nAdRequest request = new AdRequest\n .Builder()\n .addNetworkExtrasBundle(AdMobAdapter.class, extras)\n .build();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.885Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":23,"estimatedTokens":108}}469{"id":"doc-integrate_unity_ads_with_mediation_android_googl-1c1332ed","source":"documentation","title":"Integrate Unity Ads with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/unity","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.unity3d.ads:unity-ads:4.19.0\")\n implementation(\"com.google.ads.mediation:unity:4.19.0.1\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.unity3d.ads:unity-ads:4.19.0'\n implementation 'com.google.ads.mediation:unity:4.19.0.1'\n}\n```\n\nExample:\n```text\nMetaData gdprMetaData = new MetaData(this);\ngdprMetaData.set(\"gdpr.consent\", true);\ngdprMetaData.commit();UnityAdsMediationSnippets.java\n```\n\nExample:\n```text\nval gdprMetaData = MetaData(this)\ngdprMetaData[\"gdpr.consent\"] = true\ngdprMetaData.commit()UnityAdsMediationSnippets.kt\n```\n\nExample:\n```text\nMetaData ccpaMetaData = new MetaData(this);\nccpaMetaData.set(\"privacy.consent\", true);\nccpaMetaData.commit();UnityAdsMediationSnippets.java\n```\n\nExample:\n```text\nval ccpaMetaData = MetaData(this)\nccpaMetaData[\"privacy.consent\"] = true\nccpaMetaData.commit()UnityAdsMediationSnippets.kt\n```\n\nExample:\n```text\ncom.google.ads.mediation.unity.UnityAdapter\ncom.google.ads.mediation.unity.UnityMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.887Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":53,"estimatedTokens":306}}470{"id":"doc-integrate_yahoo_with_mediation_deprecated_androi-11099557","source":"documentation","title":"Integrate Yahoo with mediation (Deprecated) | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/yahoo","text":"Example:\n```text\nrepositories {\n google()\n maven {\n url 'https://artifactory.yahooinc.com/artifactory/maven/'\n }\n}\n\n// ...\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:yahoo:1.4.1.1'\n}\n// ...\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.887Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":18,"estimatedTokens":77}}471{"id":"doc-integrate_ironsource_ads_with_mediation_android_-d553c688","source":"documentation","title":"Integrate ironSource Ads with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/ironsource","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://android-sdk.is.com/\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:ironsource:9.5.0.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:ironsource:9.5.0.0'\n}\n```\n\nExample:\n```text\nLevelPlay.setMetaData(\"do_not_sell\", \"true\");IronSourceMediationSnippets.java\n```\n\nExample:\n```text\nLevelPlay.setMetaData(\"do_not_sell\", \"true\")IronSourceMediationSnippets.kt\n```\n\nExample:\n```text\ncom.google.ads.mediation.ironsource.IronSourceAdapter\ncom.google.ads.mediation.ironsource.IronSourceRewardedAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.890Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":46,"estimatedTokens":226}}472{"id":"doc-interstitial_ads_custom_events_android_google_fo-4a1a1f67","source":"documentation","title":"Interstitial ads custom events | Android | Google for Developers","url":"https://developers.google.com/admob/android/custom-events/interstitial","text":"Example:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationInterstitialAd;\nimport com.google.android.gms.ads.mediation.MediationInterstitialAdCallback;\n...\n\npublic class SampleCustomEvent extends Adapter {\n private SampleInterstitialCustomEventLoader interstitialLoader;\n @Override\n public void loadInterstitialAd(\n @NonNull MediationInterstitialAdConfiguration adConfiguration,\n @NonNull\n MediationAdLoadCallback<MediationInterstitialAd, MediationInterstitialAdCallback>\n callback) {\n interstitialLoader = new SampleInterstitialCustomEventLoader(adConfiguration, callback);\n interstitialLoader.loadAd();\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationInterstitialAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationInterstitialAd;\nimport com.google.android.gms.ads.mediation.MediationInterstitialAdCallback;\n...\n\npublic class SampleInterstitialCustomEventLoader extends SampleAdListener\n implements MediationInterstitialAd {\n\n /** A sample third-party SDK interstitial ad. */\n private SampleInterstitial sampleInterstitialAd;\n\n /** Configuration for requesting the interstitial ad from the third-party network. */\n private final MediationInterstitialAdConfiguration mediationInterstitialAdConfiguration;\n\n /** Callback for interstitial ad events. */\n private MediationInterstitialAdCallback interstitialAdCallback;\n\n /** Callback that fires on loading success or failure. */\n private final MediationAdLoadCallback<MediationInterstitialAd, MediationInterstitialAdCallback>\n mediationAdLoadCallback;\n\n /** Constructor. */\n public SampleInterstitialCustomEventLoader(\n @NonNull MediationInterstitialAdConfiguration mediationInterstitialAdConfiguration,\n @NonNull MediationAdLoadCallback<MediationInterstitialAd, MediationInterstitialAdCallback>\n mediationAdLoadCallback) {\n this.mediationInterstitialAdConfiguration = mediationInterstitialAdConfiguration;\n this.mediationAdLoadCallback = mediationAdLoadCallback;\n }\n\n /** Loads the interstitial ad from the third-party ad network. */\n public void loadAd() {\n // All custom events have a server parameter named \"parameter\" that returns\n // back the parameter entered into the UI when defining the custom event.\n Log.i(\"InterstitialCustomEvent\", \"Begin loading interstitial ad.\");\n String serverParameter = mediationInterstitialAdConfiguration.getServerParameters().getString(\n MediationConfiguration.CUSTOM_EVENT_SERVER_PARAMETER_FIELD);\n Log.d(\"InterstitialCustomEvent\", \"Received server parameter.\");\n\n sampleInterstitialAd =\n new SampleInterstitial(mediationInterstitialAdConfiguration.getContext());\n sampleInterstitialAd.setAdUnit(serverParameter);\n\n // Implement a SampleAdListener and forward callbacks to mediation.\n sampleInterstitialAd.setAdListener(this);\n\n // Make an ad request.\n Log.i(\"InterstitialCustomEvent\", \"start fetching interstitial ad.\");\n sampleInterstitialAd.fetchAd(\n SampleCustomEvent.createSampleRequest(mediationInterstitialAdConfiguration));\n }\n\npublic SampleAdRequest createSampleRequest(\n MediationAdConfiguration mediationAdConfiguration) {\n SampleAdRequest request = new SampleAdRequest();\n request.setTestMode(mediationAdConfiguration.isTestRequest());\n request.setKeywords(mediationAdConfiguration.getMediationExtras().keySet());\n return request;\n }\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdFetchSucceeded() {\n interstitialAdCallback = mediationAdLoadCallback.onSuccess(this);\n}\n\n@Override\npublic void onAdFetchFailed(SampleErrorCode errorCode) {\n mediationAdLoadCallback.onFailure(SampleCustomEventError.createSampleSdkError(errorCode));\n}\n```\n\nExample:\n```text\n@Override\npublic void showAd(@NonNull Context context) {\n sampleInterstitialAd.show();\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdFullScreen() {\n interstitialAdCallback.reportAdImpression();\n interstitialAdCallback.onAdOpened();\n}\n\n@Override\npublic void onAdClosed() {\n interstitialAdCallback.onAdClosed();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.891Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":129,"estimatedTokens":1137}}473{"id":"doc-set_up_custom_events_android_google_for_develope-d0cc4841","source":"documentation","title":"Set up custom events | Android | Google for Developers","url":"https://developers.google.com/admob/android/custom-events/setup","text":"Example:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.InitializationCompleteCallback;\nimport com.google.android.gms.ads.mediation.MediationConfiguration;\n\npublic class SampleAdNetworkCustomEvent extends Adapter {\n private static final String SAMPLE_AD_UNIT_KEY = \"parameter\";\n\n @Override\n public void initialize(Context context,\n InitializationCompleteCallback initializationCompleteCallback,\n List<MediationConfiguration> mediationConfigurations) {\n // This is where you will initialize the SDK that this custom\n // event is built for. Upon finishing the SDK initialization,\n // call the completion handler with success.\n initializationCompleteCallback.onInitializationSucceeded();\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent\n\nimport com.google.android.gms.ads.mediation.Adapter\nimport com.google.android.gms.ads.mediation.InitializationCompleteCallback\nimport com.google.android.gms.ads.mediation.MediationConfiguration\n\nclass SampleCustomEvent : Adapter() {\n private val SAMPLE_AD_UNIT_KEY = \"parameter\"\n\n override fun initialize(\n context: Context,\n initializationCompleteCallback: InitializationCompleteCallback,\n mediationConfigurations: List<MediationConfiguration>\n ) {\n // This is where you will initialize the SDK that this custom\n // event is built for. Upon finishing the SDK initialization,\n // call the completion handler with success.\n initializationCompleteCallback.onInitializationSucceeded()\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\npublic class SampleCustomEvent extends Adapter {\n\n @Override\n public VersionInfo getVersionInfo() {\n String versionString = new VersionInfo(1, 2, 3);\n String[] splits = versionString.split(\"\\\\.\");\n\n if (splits.length >= 4) {\n int major = Integer.parseInt(splits[0]);\n int minor = Integer.parseInt(splits[1]);\n int micro = Integer.parseInt(splits[2]) * 100 + Integer.parseInt(splits[3]);\n return new VersionInfo(major, minor, micro);\n }\n\n return new VersionInfo(0, 0, 0);\n }\n\n @Override\n public VersionInfo getSDKVersionInfo() {\n String versionString = SampleAdRequest.getSDKVersion();\n String[] splits = versionString.split(\"\\\\.\");\n\n if (splits.length >= 3) {\n int major = Integer.parseInt(splits[0]);\n int minor = Integer.parseInt(splits[1]);\n int micro = Integer.parseInt(splits[2]);\n return new VersionInfo(major, minor, micro);\n }\n\n return new VersionInfo(0, 0, 0);\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent\n\nclass SampleCustomEvent : Adapter() {\n override fun getVersionInfo(): VersionInfo {\n val versionString = VersionInfo(1,2,3).toString()\n val splits: List<String> = versionString.split(\"\\\\.\")\n\n if (splits.count() >= 4) {\n val major = splits[0].toInt()\n val minor = splits[1].toInt()\n val micro = (splits[2].toInt() * 100) + splits[3].toInt()\n return VersionInfo(major, minor, micro)\n }\n\n return VersionInfo(0, 0, 0)\n }\n\n override fun getSDKVersionInfo(): VersionInfo {\n val versionString = VersionInfo(1,2,3).toString()\n val splits: List<String> = versionString.split(\"\\\\.\")\n\n if (splits.count() >= 3) {\n val major = splits[0].toInt()\n val minor = splits[1].toInt()\n val micro = splits[2].toInt()\n return VersionInfo(major, minor, micro)\n }\n\n return VersionInfo(0, 0, 0)\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.891Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":121,"estimatedTokens":896}}474{"id":"doc-test_ad_units_android_google_for_developers-52c109b1","source":"documentation","title":"Test ad units | Android | Google for Developers","url":"https://developers.google.com/admob/android/ad-inspector/test-ad-units","text":"Example:\n```text\nAd Unit has no applicable adapter for single ad source testing on network: AD_SOURCE_ADAPTER_CLASS_NAME\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.893Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":35}}475{"id":"doc-banner_ads_custom_events_android_google_for_deve-4a0cd497","source":"documentation","title":"Banner ads custom events | Android | Google for Developers","url":"https://developers.google.com/admob/android/custom-events/banner","text":"Example:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationBannerAd;\nimport com.google.android.gms.ads.mediation.MediationBannerAdCallback;\n...\n\npublic class SampleCustomEvent extends Adapter {\n private SampleBannerCustomEventLoader bannerLoader;\n @Override\n public void loadBannerAd(\n @NonNull MediationBannerAdConfiguration adConfiguration,\n @NonNull MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback> callback) {\n bannerLoader = new SampleBannerCustomEventLoader(adConfiguration, callback);\n bannerLoader.loadAd();\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationBannerAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationBannerAd;\nimport com.google.android.gms.ads.mediation.MediationBannerAdCallback;\n...\n\npublic class SampleBannerCustomEventLoader extends SampleAdListener implements MediationBannerAd {\n\n /** View to contain the sample banner ad. */\n private SampleAdView sampleAdView;\n\n /** Configuration for requesting the banner ad from the third-party network. */\n private final MediationBannerAdConfiguration mediationBannerAdConfiguration;\n\n /** Callback that fires on loading success or failure. */\n private final MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback>\n mediationAdLoadCallback;\n\n /** Callback for banner ad events. */\n private MediationBannerAdCallback bannerAdCallback;\n\n /** Constructor. */\n public SampleBannerCustomEventLoader(\n @NonNull MediationBannerAdConfiguration mediationBannerAdConfiguration,\n @NonNull MediationAdLoadCallback<MediationBannerAd, MediationBannerAdCallback>\n mediationAdLoadCallback) {\n this.mediationBannerAdConfiguration = mediationBannerAdConfiguration;\n this.mediationAdLoadCallback = mediationAdLoadCallback;\n }\n\n /** Loads a banner ad from the third-party ad network. */\n public void loadAd() {\n // All custom events have a server parameter named \"parameter\" that returns\n // back the parameter entered into the UI when defining the custom event.\n Log.i(\"BannerCustomEvent\", \"Begin loading banner ad.\");\n String serverParameter =\n mediationBannerAdConfiguration.getServerParameters().getString(\n MediationConfiguration.CUSTOM_EVENT_SERVER_PARAMETER_FIELD);\n\n Log.d(\"BannerCustomEvent\", \"Received server parameter.\");\n\n Context context = mediationBannerAdConfiguration.getContext();\n sampleAdView = new SampleAdView(context);\n\n // Assumes that the serverParameter is the ad unit of the Sample Network.\n sampleAdView.setAdUnit(serverParameter);\n AdSize size = mediationBannerAdConfiguration.getAdSize();\n\n // Internally, smart banners use constants to represent their ad size, which\n // means a call to AdSize.getHeight could return a negative value. You can\n // accommodate this by using AdSize.getHeightInPixels and\n // AdSize.getWidthInPixels instead, and then adjusting to match the device's\n // display metrics.\n int widthInPixels = size.getWidthInPixels(context);\n int heightInPixels = size.getHeightInPixels(context);\n DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();\n int widthInDp = Math.round(widthInPixels / displayMetrics.density);\n int heightInDp = Math.round(heightInPixels / displayMetrics.density);\n\n sampleAdView.setSize(new SampleAdSize(widthInDp, heightInDp));\n sampleAdView.setAdListener(this);\n\n SampleAdRequest request = createSampleRequest(mediationBannerAdConfiguration);\n Log.i(\"BannerCustomEvent\", \"Start fetching banner ad.\");\n sampleAdView.fetchAd(request);\n }\n\n public SampleAdRequest createSampleRequest(\n MediationAdConfiguration mediationAdConfiguration) {\n SampleAdRequest request = new SampleAdRequest();\n request.setTestMode(mediationAdConfiguration.isTestRequest());\n request.setKeywords(mediationAdConfiguration.getMediationExtras().keySet());\n return request;\n }\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdFetchSucceeded() {\n bannerAdCallback = mediationAdLoadCallback.onSuccess(this);\n}\n\n@Override\npublic void onAdFetchFailed(SampleErrorCode errorCode) {\n mediationAdLoadCallback.onFailure(SampleCustomEventError.createSampleSdkError(errorCode));\n}\n```\n\nExample:\n```text\n@Override\n@NonNull\npublic View getView() {\n return sampleAdView;\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdFullScreen() {\n bannerAdCallback.onAdOpened();\n bannerAdCallback.reportAdClicked();\n}\n\n@Override\npublic void onAdClosed() {\n bannerAdCallback.onAdClosed();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.893Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":142,"estimatedTokens":1249}}476{"id":"doc-ad_load_errors_android_google_for_developers-06fb26d9","source":"documentation","title":"Ad load errors | Android | Google for Developers","url":"https://developers.google.com/admob/android/ad-load-errors","text":"Example:\n```text\nfun onAdFailedToLoad(error: LoadAdError)\n```\n\nExample:\n```text\npublic void onAdFailedToLoad(LoadAdError adError);\n```\n\nExample:\n```text\noverride fun onAdFailedToLoad(error: LoadAdError) {\n // Gets the domain from which the error came.\n val errorDomain = error.domain\n // Gets the error code. See\n // https://developers.google.com/admob/android/reference/com/google/android/gms/ads/AdRequest#constant-summary\n // for a list of possible codes.\n val errorCode = error.code\n // Gets an error message.\n // For example \"Account not approved yet\". See\n // https://support.google.com/admob/answer/9905175 for explanations of\n // common errors.\n val errorMessage = error.message\n // Gets additional response information about the request. See\n // https://developers.google.com/admob/android/response-info\n // information.\n val responseInfo = error.responseInfo\n // Gets the cause of the error, if available.\n val cause = error.cause\n // All of this information is available using the error's toString() method.\n Log.d(\"Ads\", error.toString())\n}BannerSnippets.kt\n```\n\nExample:\n```text\n@Override\npublic void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Gets the domain from which the error came.\n String errorDomain = adError.getDomain();\n // Gets the error code. See\n // https://developers.google.com/admob/android/reference/com/google/android/gms/ads/AdRequest#constant-summary\n // for a list of possible codes.\n int errorCode = adError.getCode();\n // Gets an error message.\n // For example \"Account not approved yet\". See\n // https://support.google.com/admob/answer/9905175 for explanations of\n // common errors.\n String errorMessage = adError.getMessage();\n // Gets additional response information about the request. See\n // https://developers.google.com/admob/android/response-info\n // information.\n ResponseInfo responseInfo = adError.getResponseInfo();\n // Gets the cause of the error, if available.\n AdError cause = adError.getCause();\n // All of this information is available using the error's toString() method.\n Log.d(\"Ads\", adError.toString());\n}BannerSnippets.java\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.894Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":62,"estimatedTokens":537}}477{"id":"doc-network_tracing_android_google_for_developers-e20a72f0","source":"documentation","title":"Network tracing | Android | Google for Developers","url":"https://developers.google.com/admob/android/network-tracing","text":"Example:\n```text\nadb logcat '*:S' Ads:I Ads-cont:I | tee logs.txt\n```\n\nExample:\n```text\nI/Ads ( 4660): GMA Debug BEGIN\nI/Ads ( 4660): GMA Debug CONTENT {\"timestamp\":1510679993741,...}\nI/Ads ( 4660): GMA Debug FINISH\n```\n\nExample:\n```text\n{\n \"timestamp\": 1510679994904,\n \"event\": \"onNetworkRequest\",\n \"components\": [\n \"ad_request_cf5ab185-3c3f-4f01-9f56-33da2ae110f2\",\n \"network_request_6553bc32-1d44-4f18-9dd0-5c183abbeb90\"\n ],\n \"params\": {\n \"firstline\": {\n \"uri\": \"http://googleads.g.doubleclick.net/pagead/ads?carrier=....\",\n \"verb\": \"GET\"\n },\n \"headers\": [\n {\n \"name\": \"User-Agent\",\n \"value\": \"Mozilla/5.0 (Linux; Android 5.0.2;...\"\n }\n ]\n }\n}\n```\n\nExample:\n```text\n{\n \"timestamp\": 1510679995295,\n \"event\": \"onNetworkResponse\",\n \"components\": [\n \"ad_request_cf5ab185-3c3f-4f01-9f56-33da2ae110f2\",\n \"network_request_6553bc32-1d44-4f18-9dd0-5c183abbeb90\"\n ],\n \"params\": {\n \"firstline\": {\n \"code\": 200\n },\n \"headers\": [\n {\n \"name\": null,\n \"value\": \"HTTP/1.1 200 OK\"\n },\n {\n \"name\": \"X-Google-DOS-Service-Trace\",\n \"value\": \"main:pagead\"\n },\n {\n \"name\": \"Content-Type\",\n \"value\": \"text/html; charset=UTF-8\"\n },\n ...\n ]\n }\n}\n```\n\nExample:\n```text\n{\n \"timestamp\": 1510679995375,\n \"event\": \"onNetworkResponseBody\",\n \"components\": [\n \"ad_request_cf5ab185-3c3f-4f01-9f56-33da2ae110f2\",\n \"network_request_6553bc32-1d44-4f18-9dd0-5c183abbeb90\"\n ],\n \"params\": {\n \"bodydigest\": \"B2520049D02F3C70A12AD1BC0D1B58A4\",\n \"bodylength\": 122395\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.895Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":85,"estimatedTokens":411}}478{"id":"doc-rewarded_ads_custom_events_android_google_for_de-96be7f9a","source":"documentation","title":"Rewarded ads custom events | Android | Google for Developers","url":"https://developers.google.com/admob/android/custom-events/rewarded","text":"Example:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationRewardedAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationRewardedAd;\nimport com.google.android.gms.ads.mediation.MediationRewardedAdCallback;\n...\n\npublic class SampleCustomEvent extends Adapter {\n\n private SampleNativeCustomEventLoader nativeLoader;\n\n @Override\n public void loadRewardedAd(\n @NonNull MediationRewardedAdConfiguration mediationRewardedAdConfiguration,\n @NonNull\n MediationAdLoadCallback<MediationRewardedAd, MediationRewardedAdCallback>\n mediationAdLoadCallback) {\n rewardedLoader =\n new SampleRewardedCustomEventLoader(\n mediationRewardedAdConfiguration, mediationAdLoadCallback);\n rewardedLoader.loadAd();\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationRewardedAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationRewardedAd;\nimport com.google.android.gms.ads.mediation.MediationRewardedAdCallback;\n...\n\npublic class SampleRewardedCustomEventLoader extends SampleRewardedAdListener\n implements MediationRewardedAd {\n\n /** Configuration for requesting the rewarded ad from the third-party network. */\n private final MediationRewardedAdConfiguration mediationRewardedAdConfiguration;\n\n /**\n * A {@link MediationAdLoadCallback} that handles any callback when a Sample\n * rewarded ad finishes loading.\n */\n private final MediationAdLoadCallback<MediationRewardedAd, MediationRewardedAdCallback>\n mediationAdLoadCallback;\n\n /** Callback for rewarded ad events. */\n private MediationRewardedAdCallback rewardedAdCallback;\n\n /** Constructor. */\n public SampleRewardedCustomEventLoader(\n @NonNull MediationRewardedAdConfiguration mediationRewardedAdConfiguration,\n @NonNull MediationAdLoadCallback<MediationRewardedAd, MediationRewardedAdCallback>\n mediationAdLoadCallback) {\n this.mediationRewardedAdConfiguration = mediationRewardedAdConfiguration;\n this.mediationAdLoadCallback = mediationAdLoadCallback;\n }\n\n /** Loads the rewarded ad from the third-party ad network. */\n public void loadAd() {\n // All custom events have a server parameter named \"parameter\" that returns\n // back the parameter entered into the AdMob UI when defining the custom event.\n Log.i(\"RewardedCustomEvent\", \"Begin loading rewarded ad.\");\n String serverParameter = mediationRewardedAdConfiguration\n .getServerParameters()\n .getString(MediationConfiguration\n .CUSTOM_EVENT_SERVER_PARAMETER_FIELD);\n Log.d(\"RewardedCustomEvent\", \"Received server parameter.\");\n SampleAdRequest request = createSampleRequest(mediationRewardedAdConfiguration);\n sampleRewardedAd = new SampleRewardedAd(serverParameter);\n sampleRewardedAd.setListener(this);\n Log.i(\"RewardedCustomEvent\", \"Start fetching rewarded ad.\");\n sampleRewardedAd.loadAd(request);\n }\n\n public SampleAdRequest createSampleRequest(\n MediationAdConfiguration mediationAdConfiguration) {\n SampleAdRequest request = new SampleAdRequest();\n request.setTestMode(mediationAdConfiguration.isTestRequest());\n request.setKeywords(mediationAdConfiguration.getMediationExtras().keySet());\n return request;\n }\n}\n```\n\nExample:\n```text\n@Override\npublic void onRewardedAdLoaded() {\n rewardedAdCallback = mediationAdLoadCallback.onSuccess(this);\n}\n\n@Override\npublic void onRewardedAdFailedToLoad(SampleErrorCode errorCode) {\n mediationAdLoadCallback.onFailure(SampleCustomEventError.createSampleSdkError(errorCode));\n}\n```\n\nExample:\n```text\n@Override\npublic void showAd(Context context) {\n if (!(context instanceof Activity)) {\n rewardedAdCallback.onAdFailedToShow(\n SampleCustomEventError.createCustomEventNoActivityContextError());\n return;\n }\n Activity activity = (Activity) context;\n\n if (!sampleRewardedAd.isAdAvailable()) {\n rewardedAdCallback.onAdFailedToShow(\n SampleCustomEventError.createCustomEventAdNotAvailableError());\n return;\n }\n sampleRewardedAd.showAd(activity);\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdRewarded(final String rewardType, final int amount) {\n RewardItem rewardItem =\n new RewardItem() {\n @Override\n public String getType() {\n return rewardType;\n }\n\n @Override\n public int getAmount() {\n return amount;\n }\n };\n rewardedAdCallback.onUserEarnedReward(rewardItem);\n}\n\n@Override\npublic void onAdClicked() {\n rewardedAdCallback.reportAdClicked();\n}\n\n@Override\npublic void onAdFullScreen() {\n rewardedAdCallback.onAdOpened();\n rewardedAdCallback.onVideoStart();\n rewardedAdCallback.reportAdImpression();\n}\n\n@Override\npublic void onAdClosed() {\n rewardedAdCallback.onAdClosed();\n}\n\n@Override\npublic void onAdCompleted() {\n rewardedAdCallback.onVideoComplete();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.896Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":169,"estimatedTokens":1325}}479{"id":"doc-retrieve_information_about_the_ad_response_andro-36629cfc","source":"documentation","title":"Retrieve information about the ad response | Android | Google for Developers","url":"https://developers.google.com/admob/android/response-info","text":"Example:\n```text\noverride fun onAdLoaded() {\n val responseInfo = adView.responseInfo\n Log.d(TAG, responseInfo.toString())\n}\n\noverride fun onAdFailedToLoad(adError: LoadAdError) {\n val responseInfo = adError.responseInfo\n Log.d(TAG, responseInfo.toString())\n}ResponseInfoSnippets.kt\n```\n\nExample:\n```text\n@Override\npublic void onAdLoaded() {\n ResponseInfo responseInfo = adView.getResponseInfo();\n Log.d(TAG, responseInfo.toString());\n}\n\n@Override\npublic void onAdFailedToLoad(LoadAdError adError) {\n ResponseInfo responseInfo = adError.getResponseInfo();\n Log.d(TAG, responseInfo.toString());\n}ResponseInfoSnippets.java\n```\n\nExample:\n```text\n{\n \"Response ID\": \"COOllLGxlPoCFdAx4Aod-Q4A0g\",\n \"Mediation Adapter Class Name\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Adapter Responses\": [\n {\n \"Adapter\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Latency\": 328,\n \"Ad Source Name\": \"Reservation campaign\",\n \"Ad Source ID\": \"7068401028668408324\",\n \"Ad Source Instance Name\": \"[DO NOT EDIT] Publisher Test Interstitial\",\n \"Ad Source Instance ID\": \"4665218928925097\",\n \"Credentials\": {},\n \"Ad Error\": \"null\"\n }\n ],\n \"Loaded Adapter Response\": {\n \"Adapter\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Latency\": 328,\n \"Ad Source Name\": \"Reservation campaign\",\n \"Ad Source ID\": \"7068401028668408324\",\n \"Ad Source Instance Name\": \"[DO NOT EDIT] Publisher Test Interstitial\",\n \"Ad Source Instance ID\": \"4665218928925097\",\n \"Credentials\": {},\n \"Ad Error\": \"null\"\n },\n \"Response Extras\": {\n \"mediation_group_name\": \"Campaign\"\n }\n}\n```\n\nExample:\n```text\noverride fun onAdLoaded() {\n val responseInfo = adView.responseInfo\n\n val responseId = responseInfo?.responseId\n val mediationAdapterClassName = responseInfo?.mediationAdapterClassName\n val adapterResponses = responseInfo?.adapterResponses\n val loadedAdapterResponseInfo = responseInfo?.loadedAdapterResponseInfo\n val extras = responseInfo?.responseExtras\n val mediationGroupName = extras?.getString(\"mediation_group_name\")\n val mediationABTestName = extras?.getString(\"mediation_ab_test_name\")\n val mediationABTestVariant = extras?.getString(\"mediation_ab_test_variant\")\n}ResponseInfoSnippets.kt\n```\n\nExample:\n```text\n@Override\npublic void onAdLoaded() {\n ResponseInfo responseInfo = adView.getResponseInfo();\n\n String responseId = responseInfo.getResponseId();\n String mediationAdapterClassName = responseInfo.getMediationAdapterClassName();\n List<AdapterResponseInfo> adapterResponses = responseInfo.getAdapterResponses();\n AdapterResponseInfo loadedAdapterResponseInfo =\n responseInfo.getLoadedAdapterResponseInfo();\n Bundle extras = responseInfo.getResponseExtras();\n String mediationGroupName = extras.getString(\"mediation_group_name\");\n String mediationABTestName = extras.getString(\"mediation_ab_test_name\");\n String mediationABTestVariant = extras.getString(\"mediation_ab_test_variant\");\n}ResponseInfoSnippets.java\n```\n\nExample:\n```text\n{\n \"Adapter\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Latency\": 328,\n \"Ad Source Name\": \"Reservation campaign\",\n \"Ad Source ID\": \"7068401028668408324\",\n \"Ad Source Instance Name\": \"[DO NOT EDIT] Publisher Test Interstitial\",\n \"Ad Source Instance ID\": \"4665218928925097\",\n \"Credentials\": {},\n \"Ad Error\": \"null\"\n}\n```\n\nExample:\n```text\noverride fun onAdLoaded() {\n val loadedAdapterResponseInfo = adView.responseInfo?.loadedAdapterResponseInfo\n\n val adError = loadedAdapterResponseInfo?.adError\n val adSourceId = loadedAdapterResponseInfo?.adSourceId\n val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId\n val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName\n val adSourceName = loadedAdapterResponseInfo?.adSourceName\n val adapterClassName = loadedAdapterResponseInfo?.adapterClassName\n val credentials = loadedAdapterResponseInfo?.credentials\n val latencyMillis = loadedAdapterResponseInfo?.latencyMillis\n}ResponseInfoSnippets.kt\n```\n\nExample:\n```text\n@Override\npublic void onAdLoaded() {\n AdapterResponseInfo loadedAdapterResponseInfo =\n adView.getResponseInfo().getLoadedAdapterResponseInfo();\n\n AdError adError = loadedAdapterResponseInfo.getAdError();\n String adSourceId = loadedAdapterResponseInfo.getAdSourceId();\n String adSourceInstanceId = loadedAdapterResponseInfo.getAdSourceInstanceId();\n String adSourceInstanceName = loadedAdapterResponseInfo.getAdSourceInstanceName();\n String adSourceName = loadedAdapterResponseInfo.getAdSourceName();\n String adapterClassName = loadedAdapterResponseInfo.getAdapterClassName();\n Bundle credentials = loadedAdapterResponseInfo.getCredentials();\n long latencyMillis = loadedAdapterResponseInfo.getLatencyMillis();\n}ResponseInfoSnippets.java\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.897Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":144,"estimatedTokens":1201}}480{"id":"doc-global_settings_android_google_for_developers-775b0a5e","source":"documentation","title":"Global settings | Android | Google for Developers","url":"https://developers.google.com/admob/android/global-settings","text":"Example:\n```text\noverride fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n\n val backgroundScope = CoroutineScope(Dispatchers.IO)\n backgroundScope.launch {\n // Initialize Google Mobile Ads SDK (Legacy) on a background thread.\n MobileAds.initialize(this@MainActivity) {}\n \n // Set app volume to be half of current device volume.\n MobileAds.setAppVolume(0.5f)\n }\n}\n```\n\nExample:\n```text\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n\n new Thread(\n () -> {\n // Initialize Google Mobile Ads SDK (Legacy) on a background thread.\n MobileAds.initialize(this, initializationStatus -> {});\n \n // Set app volume to be half of current device volume.\n MobileAds.setAppVolume(0.5f);\n })\n .start();\n}\n```\n\nExample:\n```text\nMobileAds.setAppMuted(true)\n```\n\nExample:\n```text\nMobileAds.setAppMuted(true);\n```\n\nExample:\n```text\nval sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context)\n// Set the value to 0 to enable limited ads.\nsharedPrefs.edit().putInt(\"gad_has_consent_for_cookies\", 0).apply()\n```\n\nExample:\n```text\nContext activity = getActivity();\nSharedPreferences sharedPreferences =\n PreferenceManager.getDefaultSharedPreferences(activity);\n// Set the value to 0 to enable limited ads.\nsharedPreferences.edit().putInt(\"gad_has_consent_for_cookies\", 0).apply();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.897Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":61,"estimatedTokens":368}}481{"id":"doc-native_ads_custom_events_android_google_for_deve-941089c1","source":"documentation","title":"Native ads custom events | Android | Google for Developers","url":"https://developers.google.com/admob/android/custom-events/native","text":"Example:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\n\nimport com.google.android.gms.ads.mediation.MediationNativeAdCallback;\n...\npublic class SampleCustomEvent extends Adapter {\n private SampleNativeCustomEventLoader nativeLoader;\n\n @Override\n public void loadNativeAd(\n @NonNull MediationNativeAdConfiguration adConfiguration,\n @NonNull MediationAdLoadCallback<UnifiedNativeAdMapper, MediationNativeAdCallback> callback) {\n nativeLoader = new SampleNativeCustomEventLoader(adConfiguration, callback);\n nativeLoader.loadAd();\n }\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.Adapter;\nimport com.google.android.gms.ads.mediation.MediationNativeAdConfiguration;\nimport com.google.android.gms.ads.mediation.MediationAdLoadCallback;\nimport com.google.android.gms.ads.mediation.MediationNativeAdCallback;\n...\n\npublic class SampleNativeCustomEventLoader extends SampleNativeAdListener {\n /** Configuration for requesting the native ad from the third-party network. */\n private final MediationNativeAdConfiguration mediationNativeAdConfiguration;\n\n /** Callback that fires on loading success or failure. */\n private final MediationAdLoadCallback<UnifiedNativeAdMapper, MediationNativeAdCallback>\n mediationAdLoadCallback;\n\n /** Callback for native ad events. */\n private MediationNativeAdCallback nativeAdCallback;\n\n /** Constructor */\n public SampleNativeCustomEventLoader(\n @NonNull MediationNativeAdConfiguration mediationNativeAdConfiguration,\n @NonNull MediationAdLoadCallback<MediationNativeAd, MediationNativeAdCallback>\n mediationAdLoadCallback) {\n this.mediationNativeAdConfiguration = mediationNativeAdConfiguration;\n this.mediationAdLoadCallback = mediationAdLoadCallback;\n }\n\n /** Loads the native ad from the third-party ad network. */\n public void loadAd() {\n // Create one of the Sample SDK's ad loaders to request ads.\n Log.i(\"NativeCustomEvent\", \"Begin loading native ad.\");\n SampleNativeAdLoader loader =\n new SampleNativeAdLoader(mediationNativeAdConfiguration.getContext());\n\n // All custom events have a server parameter named \"parameter\" that returns\n // back the parameter entered into the UI when defining the custom event.\n String serverParameter = mediationNativeAdConfiguration\n .getServerParameters()\n .getString(MediationConfiguration\n .CUSTOM_EVENT_SERVER_PARAMETER_FIELD);\n Log.d(\"NativeCustomEvent\", \"Received server parameter.\");\n\n loader.setAdUnit(serverParameter);\n\n // Create a native request to give to the SampleNativeAdLoader.\n SampleNativeAdRequest request = new SampleNativeAdRequest();\n NativeAdOptions options = mediationNativeAdConfiguration.getNativeAdOptions();\n if (options != null) {\n // If the NativeAdOptions' shouldReturnUrlsForImageAssets is true, the adapter should\n // send just the URLs for the images.\n request.setShouldDownloadImages(!options.shouldReturnUrlsForImageAssets());\n\n request.setShouldDownloadMultipleImages(options.shouldRequestMultipleImages());\n switch (options.getMediaAspectRatio()) {\n case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_LANDSCAPE:\n request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_LANDSCAPE);\n break;\n case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_PORTRAIT:\n request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_PORTRAIT);\n break;\n case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_SQUARE:\n case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_ANY:\n case NativeAdOptions.NATIVE_MEDIA_ASPECT_RATIO_UNKNOWN:\n default:\n request.setPreferredImageOrientation(SampleNativeAdRequest.IMAGE_ORIENTATION_ANY);\n }\n }\n\n loader.setNativeAdListener(this);\n\n // Begin a request.\n Log.i(\"NativeCustomEvent\", \"Start fetching native ad.\");\n loader.fetchAd(request);\n }\n}\n```\n\nExample:\n```text\n@Override\npublic void onNativeAdFetched(SampleNativeAd ad) {\n SampleUnifiedNativeAdMapper mapper = new SampleUnifiedNativeAdMapper(ad);\n mediationNativeAdCallback = mediationAdLoadCallback.onSuccess(mapper);\n}\n\n@Override\npublic void onAdFetchFailed(SampleErrorCode errorCode) {\n mediationAdLoadCallback.onFailure(SampleCustomEventError.createSampleSdkError(errorCode));\n}\n```\n\nExample:\n```text\npackage com.google.ads.mediation.sample.customevent;\n\nimport com.google.android.gms.ads.mediation.UnifiedNativeAdMapper;\nimport com.google.android.gms.ads.nativead.NativeAd;\n...\n\npublic class SampleUnifiedNativeAdMapper extends UnifiedNativeAdMapper {\n\n private final SampleNativeAd sampleAd;\n\n public SampleUnifiedNativeAdMapper(SampleNativeAd ad) {\n sampleAd = ad;\n setHeadline(sampleAd.getHeadline());\n setBody(sampleAd.getBody());\n setCallToAction(sampleAd.getCallToAction());\n setStarRating(sampleAd.getStarRating());\n setStore(sampleAd.getStoreName());\n setIcon(\n new SampleNativeMappedImage(\n ad.getIcon(), ad.getIconUri(), SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));\n setAdvertiser(ad.getAdvertiser());\n\n List<NativeAd.Image> imagesList = new ArrayList<NativeAd.Image>();\n imagesList.add(new SampleNativeMappedImage(ad.getImage(), ad.getImageUri(),\n SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));\n setImages(imagesList);\n\n if (sampleAd.getPrice() != null) {\n NumberFormat formatter = NumberFormat.getCurrencyInstance();\n String priceString = formatter.format(sampleAd.getPrice());\n setPrice(priceString);\n }\n\n Bundle extras = new Bundle();\n extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness());\n this.setExtras(extras);\n\n setOverrideClickHandling(false);\n setOverrideImpressionRecording(false);\n\n setAdChoicesContent(sampleAd.getInformationIcon());\n }\n\n @Override\n public void recordImpression() {\n sampleAd.recordImpression();\n }\n\n @Override\n public void handleClick(View view) {\n sampleAd.handleClick(view);\n }\n\n // The Sample SDK doesn't do its own impression/click tracking, instead relies on its\n // publishers calling the recordImpression and handleClick methods on its native ad object. So\n // there's no need to pass a reference to the View being used to display the native ad. If\n // your mediated network does need a reference to the view, the following method can be used\n // to provide one.\n\n @Override\n public void trackViews(View containerView, Map<String, View> clickableAssetViews,\n Map<String, View> nonClickableAssetViews) {\n super.trackViews(containerView, clickableAssetViews, nonClickableAssetViews);\n // If your ad network SDK does its own impression tracking, here is where you can track the\n // top level native ad view and its individual asset views.\n }\n\n @Override\n public void untrackView(View view) {\n super.untrackView(view);\n // Here you would remove any trackers from the View added in trackView.\n }\n}\n```\n\nExample:\n```text\nif (sampleAd.getPrice() != null) {\n NumberFormat formatter = NumberFormat.getCurrencyInstance();\n String priceString = formatter.format(sampleAd.getPrice());\n setPrice(priceString);\n}\n```\n\nExample:\n```text\npublic class SampleNativeMappedImage extends NativeAd.Image {\n\n private Drawable drawable;\n private Uri imageUri;\n private double scale;\n\n public SampleNativeMappedImage(Drawable drawable, Uri imageUri, double scale) {\n this.drawable = drawable;\n this.imageUri = imageUri;\n this.scale = scale;\n }\n\n @Override\n public Drawable getDrawable() {\n return drawable;\n }\n\n @Override\n public Uri getUri() {\n return imageUri;\n }\n\n @Override\n public double getScale() {\n return scale;\n }\n}\n```\n\nExample:\n```text\nsetIcon(new SampleNativeMappedImage(ad.getAppIcon(), ad.getAppIconUri(),\n SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness());\nthis.setExtras(extras);\n```\n\nExample:\n```text\npublic SampleNativeAdMapper(SampleNativeAd ad) {\n ...\n setAdChoicesContent(sampleAd.getInformationIcon());\n}\n```\n\nExample:\n```text\n@Override\npublic void recordImpression() {\n sampleAd.recordImpression();\n}\n\n@Override\npublic void handleClick(View view) {\n sampleAd.handleClick(view);\n}\n```\n\nExample:\n```text\nsetOverrideClickHandling(true);\nsetOverrideImpressionRecording(true);\n```\n\nExample:\n```text\n@Override\npublic void trackViews(View containerView,\n Map<String, View> clickableAssetViews,\n Map<String, View> nonClickableAssetViews) {\n sampleAd.setNativeAdViewForTracking(containerView);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.899Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":285,"estimatedTokens":2240}}482{"id":"doc-integrate_bigo_ads_sdk_with_mediation_android_go-a923e29b","source":"documentation","title":"Integrate BIGO Ads SDK with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/bigo","text":"Example:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:bigo:5.10.1.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:bigo:5.10.1.0'\n}\n```\n\nExample:\n```text\nBigoAdSdk.setUserConsent(context, ConsentOptions.CCPA, true);BigoMediationSnippets.java\n```\n\nExample:\n```text\nBigoAdSdk.setUserConsent(context, ConsentOptions.CCPA, true)BigoMediationSnippets.kt\n```\n\nExample:\n```text\nsg.bigo.ads\ncom.google.ads.mediation.bigo.BigoMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.899Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":33,"estimatedTokens":172}}483{"id":"doc-load_a_single_app_open_ad_android_google_for_dev-24125b7b","source":"documentation","title":"Load a single app open ad | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/app-open/single-load","text":"Example:\n```text\n// Load ads after you initialize MobileAds.\nAppOpenAd.load(\n AdRequest.Builder(adUnitId).build(),\n object : AdLoadCallback<AppOpenAd> {\n override fun onAdLoaded(ad: AppOpenAd) {\n // App open ad loaded.\n appOpenAd = ad\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // App open ad failed to load.\n Log.e(TAG, \"App open ad failed to load: ${adError.message}\")\n appOpenAd = null\n }\n },\n)\n```\n\nExample:\n```text\n// Load ads after you initialize MobileAds.\nAppOpenAd.load(\n new AdRequest.Builder(adUnitId).build(),\n new AdLoadCallback<AppOpenAd>() {\n @Override\n public void onAdLoaded(AppOpenAd ad) {\n // App open ad loaded.\n appOpenAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(LoadAdError adError) {\n // App open ad failed to load.\n Log.e(TAG, \"App open ad failed to load: \" + adError.getMessage());\n appOpenAd = null;\n }\n });\n```\n\nExample:\n```text\nprivate fun showAd(appOpenAd: AppOpenAd, activity: Activity) {\n // Show the ad.\n appOpenAd.show(activity)\n}\n```\n\nExample:\n```text\nprivate void showAd(AppOpenAd appOpenAd, Activity activity) {\n // Show the ad.\n appOpenAd.show(activity);\n}\n```\n\nExample:\n```text\nprivate fun listenToAdEvents() {\n // Listen for ad events.\n val ad = appOpenAd\n if (ad == null) {\n Log.e(TAG, \"App open ad is not ready yet.\")\n return\n }\n\n ad.adEventCallback =\n object : AppOpenAdEventCallback {\n override fun onAdShowedFullScreenContent() {\n // App open ad did show.\n }\n\n override fun onAdDismissedFullScreenContent() {\n // App open ad did dismiss.\n appOpenAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(\n fullScreenContentError: FullScreenContentError\n ) {\n // App open ad failed to show.\n Log.e(TAG, \"App open ad failed to show: ${fullScreenContentError.message}\")\n }\n\n override fun onAdImpression() {\n // App open ad did record an impression.\n }\n\n override fun onAdClicked() {\n // App open ad did record a click.\n }\n }\n}\n```\n\nExample:\n```text\nprivate void listenToAdEvents() {\n // Listen for ad events.\n if (appOpenAd == null) {\n Log.e(TAG, \"App open ad is not ready yet.\");\n return;\n }\n\n appOpenAd.setAdEventCallback(\n new AppOpenAdEventCallback() {\n @Override\n public void onAdShowedFullScreenContent() {\n // App open ad did show.\n }\n\n @Override\n public void onAdDismissedFullScreenContent() {\n // App open ad did dismiss.\n appOpenAd = null;\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(\n @NonNull FullScreenContentError fullScreenContentError) {\n // App open ad failed to show.\n Log.e(TAG, \"App open ad failed to show: \" + fullScreenContentError.getMessage());\n }\n\n @Override\n public void onAdImpression() {\n // App open ad did record an impression.\n }\n\n @Override\n public void onAdClicked() {\n // App open ad did record a click.\n }\n });\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.900Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":139,"estimatedTokens":800}}484{"id":"doc-migrate_rewarded_interstitial_ads_android_google-680e4874","source":"documentation","title":"Migrate rewarded interstitial ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/migration/migrate-rewarded-interstitial","text":"Example:\n```text\nRewardedInterstitialAd.load(\n this@RewardedInterstitialActivity,\n \"AD_UNIT_ID\",\n AdRequest.Builder().build(),\n object : RewardedInterstitialAdLoadCallback() {\n override fun onAdLoaded(ad: RewardedInterstitialAd) {\n // Called when an ad has loaded.\n ad.fullScreenContentCallback =\n object : FullScreenContentCallback() {\n }\n rewardedInterstitialAd = ad\n }\n\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // Called when ad fails to load.\n }\n }\n)\n```\n\nExample:\n```text\nRewardedInterstitialAd.load(\n this,\n \"AD_UNIT_ID\",\n new AdRequest.Builder().build(),\n new RewardedInterstitialAdLoadCallback() {\n @Override\n public void onAdLoaded(@NonNull RewardedInterstitialAd ad) {\n // Called when an ad has loaded.\n ad.setFullScreenContentCallback(new FullScreenContentCallback() {});\n rewardedInterstitialAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {\n // Called when ad fails to load.\n }\n }\n);\n```\n\nExample:\n```text\nRewardedInterstitialAd.load(\n AdRequest.Builder(\"AD_UNIT_ID\").build(),\n object : AdLoadCallback<RewardedInterstitialAd> {\n override fun onAdLoaded(ad: RewardedInterstitialAd) {\n // Called when an ad has loaded.\n ad.adEventCallback =\n object : RewardedInterstitialAdEventCallback {\n }\n rewardedInterstitialAd = ad\n }\n\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // Called when ad fails to load.\n }\n }\n)\n```\n\nExample:\n```text\nRewardedInterstitialAd.load(\n new AdRequest.Builder(\"AD_UNIT_ID\").build(),\n new AdLoadCallback<RewardedInterstitialAd>() {\n @Override\n public void onAdLoaded(@NonNull RewardedInterstitialAd ad) {\n // Called when an ad has loaded.\n ad.setAdEventCallback(new RewardedInterstitialAdEventCallback() {});\n rewardedInterstitialAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Called when ad fails to load.\n }\n });\n```\n\nExample:\n```text\nrewardedInterstitialAd?.show(\n this@RewardedInterstitialActivity,\n object : OnUserEarnedRewardListener {\n override fun onUserEarnedReward(rewardItem: RewardItem) {\n // User earned the reward.\n val rewardAmount = rewardItem.amount\n val rewardType = rewardItem.type\n }\n }\n)\n```\n\nExample:\n```text\nrewardedInterstitialAd.show(\n this,\n new OnUserEarnedRewardListener() {\n @Override\n public void onUserEarnedReward(@NonNull RewardItem rewardItem) {\n // User earned the reward.\n int rewardAmount = rewardItem.getAmount();\n String rewardType = rewardItem.getType();\n }\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.901Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":112,"estimatedTokens":684}}485{"id":"doc-use_agent_skills_android_google_for_developers-89a5b0ea","source":"documentation","title":"Use agent skills | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/agent-skills","text":"Example:\n```text\nnpx skills add google/skills/skills/ads\n```\n\nExample:\n```text\nnpx skills update --all\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.901Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":30}}486{"id":"doc-collapsible_banner_ads_android_google_for_develo-1391e701","source":"documentation","title":"Collapsible banner ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/banner/collapsible","text":"Example:\n```text\nprivate fun loadBannerAd() {\n // ...\n\n // Create an extra parameter that aligns the bottom of the expanded ad to\n // the bottom of the bannerView.\n val extras = Bundle()\n extras.putString(\"collapsible\", \"bottom\")\n\n val bannerAdRequest = BannerAdRequest.Builder(\"AD_UNIT_ID\", adSize)\n .setGoogleExtrasBundle(extras)\n .build()\n\n BannerAd.load(\n bannerAdRequest,\n object : AdLoadCallback<BannerAd> {\n override fun onAdLoaded(ad: BannerAd) {\n // ...\n }\n\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // ...\n }\n },\n )\n}\n```\n\nExample:\n```text\nprivate void loadBannerAd() {\n // ...\n\n Bundle extras = new Bundle();\n extras.putString(\"collapsible\", \"bottom\");\n\n BannerAdRequest bannerAdRequest = new BannerAdRequest.Builder(\"AD_UNIT_ID\", adSize)\n .setGoogleExtrasBundle(extras)\n .build();\n\n BannerAd.load(\n bannerAdRequest,\n new AdLoadCallback<BannerAd>() {\n @Override\n public void onAdLoaded(@NonNull BannerAd ad) {\n // ...\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // ...\n }\n });\n}\n```\n\nExample:\n```text\noverride fun onAdLoaded(ad: BannerAd) {\n // ...\n Log.i(\n TAG,\n \"The last loaded banner is ${if (ad.isCollapsible()) \"\" else \"not \"}collapsible.\"\n )\n}\n```\n\nExample:\n```text\n@Override\npublic void onAdLoaded(@NonNull BannerAd ad) {\n // ...\n Log.i(TAG, String.format(\"The last loaded banner is %scollapsible.\",\n ad.isCollapsible() ? \"\" : \"not \"));\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.902Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":79,"estimatedTokens":398}}487{"id":"doc-migrate_with_ai_tools_beta_android_google_for_de-947fed55","source":"documentation","title":"Migrate with AI tools (beta) | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/migration/migrate-with-ai-tools","text":"Example:\n```text\nnpx skills add google/skills --skill google-mobile-ads-android-migrate-to-next-gen\n```\n\nExample:\n```text\nnpx skills update --all\n```\n\nExample:\n```text\nMigrate the files in my project from Google Mobile Ads SDK (Legacy) to GMA Next-Gen SDK\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.902Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":16,"estimatedTokens":69}}488{"id":"doc-test_creative_types_ios_google_for_developers-520cadb5","source":"documentation","title":"Test creative types | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/test-creative-types","text":"Example:\n```text\nlet extras = Extras()\nextras.additionalParameters = [\"ft_ctype\": \"video_app_install\"]\n\nlet request = Request()\nrequest.register(extras)\n```\n\nExample:\n```text\nGADExtras *extras = [[GADExtras alloc] init];\nextras.additionalParameters = @{@\"ft_ctype\" : @\"video_app_install\"};\n\nGADRequest *request = [GADRequest request];\n[request registerAdNetworkExtras:extras];\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.902Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":19,"estimatedTokens":99}}489{"id":"doc-global_settings_ios_google_for_developers-63278128","source":"documentation","title":"Global settings | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/global-settings","text":"Example:\n```text\nfunc viewDidLoad() {\n super.viewDidLoad()\n // Set app volume to be half of the current device volume.\n MobileAds.shared.applicationVolume = 0.5\n ...\n}\n```\n\nExample:\n```text\n- (void)viewDidLoad {\n [super viewDidLoad];\n // Set app volume to be half of the current device volume.\n GADMobileAds.sharedInstance.applicationVolume = 0.5;\n ...\n}\n```\n\nExample:\n```text\nMobileAds.shared.applicationMuted = true\n```\n\nExample:\n```text\nGADMobileAds.sharedInstance.applicationMuted = YES;\n```\n\nExample:\n```text\nfunc setUp() {\n MobileAds.shared.audioVideoManager.delegate = self\n MobileAds.shared.audioVideoManager.audioSessionIsApplicationManaged = false\n}\n\n// MARK: - GADAudioVideoManagerDelegate\nfunc audioVideoManagerWillPlayAudio(_ audioVideoManager: GADAudioVideoManager) {\n // The Google Mobile Ads SDK is notifying your app that it will play audio. You\n // could optionally pause music depending on your apps design.\n MyAppObject.shared.pauseAllMusic()\n}\n\nfunc audioVideoManagerDidStopPlayingAudio(_ audioVideoManager: GADAudioVideoManager) {\n // The Google Mobile Ads SDK is notifying your app that it has stopped playing\n // audio. Depending on your design, you could resume music here.\n MyAppObject.shared.resumeAllMusic()\n}\n```\n\nExample:\n```text\n- (void)setUp {\n GADMobileAds.sharedInstance.audioVideoManager.delegate = self;\n GADMobileAds.sharedInstance.audioVideoManager.audioSessionIsApplicationManaged = NO;\n}\n\n#pragma mark - GADAudioVideoManagerDelegate\n\n- (void)audioVideoManagerWillPlayAudio:(GADAudioVideoManager *)audioVideoManager {\n // Google Mobile Ads SDK is notifying your app that it will play audio. You\n // could optionally pause music depending on your apps design.\n [MyAppObject.sharedInstance pauseAllMusic];\n}\n\n- (void)audioVideoManagerDidStopPlayingAudio:(GADAudioVideoManager *)audioVideoManager {\n // Google Mobile Ads SDK is notifying your app that it has stopped playing\n // audio. Depending on your design, you could resume music here.\n [MyAppObject.sharedInstance resumeAllMusic];\n}\n```\n\nExample:\n```text\nimport GoogleMobileAds\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n func application(_ application: UIApplication,\n didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {\n\n MobileAds.shared.disableSDKCrashReporting()\n return true\n }\n}\n```\n\nExample:\n```text\n@import GoogleMobileAds;\n\n@implementation AppDelegate\n\n- (BOOL)application:(UIApplication *)application\n didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n\n [GADMobileAds disableSDKCrashReporting];\n return YES;\n}\n\n@end\n```\n\nExample:\n```text\nUserDefaults.standard.set(0, forKey: \"gad_has_consent_for_cookies\")\n```\n\nExample:\n```text\nNSUserDefaults.standardUserDefaults().setObject(Int(0),\n forKey: \"gad_has_consent_for_cookies\");\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.904Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":117,"estimatedTokens":721}}490{"id":"doc-impression_level_ad_revenue_ios_google_for_devel-5671af4d","source":"documentation","title":"Impression-level ad revenue | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/impression-level-ad-revenue","text":"Example:\n```text\nrewardedAd?.paidEventHandler = { adValue in\n // TODO: Send the impression-level ad revenue information to your preferred\n // analytics server directly within this callback.\n\n // Extract the impression-level ad revenue data.\n let value = adValue.value\n let currencyCode = adValue.currencyCode\n let precision = adValue.precision\n\n print(\n \"Ad paid event. Value: \\(value) \\(currencyCode), with precision: \\(precision).\"\n )\n}ImpressionLevelAdRevenueSnippets.swift\n```\n\nExample:\n```text\nrewardedAd.paidEventHandler = ^(GADAdValue *_Nonnull adValue) {\n // TODO: Send the impression-level ad revenue information to your preferred\n // analytics server directly within this callback.\n\n // Extract the impression-level ad revenue data.\n NSDecimalNumber *value = adValue.value;\n NSString *currencyCode = adValue.currencyCode;\n GADAdValuePrecision precision = adValue.precision;\n\n NSLog(@\"Ad paid event. Value: %@ %@, with precision: %ld.\", value, currencyCode,\n (long)precision);\n};ImpressionLevelAdRevenueSnippets.m\n```\n\nExample:\n```text\nfunc uniqueAdSourceName(for loadedAdNetworkResponseInfo: AdNetworkResponseInfo) -> String {\n var adSourceName: String = loadedAdNetworkResponseInfo.adSourceName ?? \"\"\n if adSourceName == \"Custom Event\" {\n if loadedAdNetworkResponseInfo.adNetworkClassName\n == \"MediationExample.SampleCustomEventSwift\"\n {\n adSourceName = \"Sample Ad Network (Custom Event)\"\n }\n }\n return adSourceName\n}ResponseInfoSnippets.swift\n```\n\nExample:\n```text\n- (NSString *)uniqueAdSourceNameForAdNetworkResponseInfo:\n (GADAdNetworkResponseInfo *)loadedAdNetworkResponseInfo {\n NSString *adSourceName = loadedAdNetworkResponseInfo.adSourceName;\n if ([adSourceName isEqualToString:@\"Custom Event\"]) {\n if ([loadedAdNetworkResponseInfo.adNetworkClassName isEqualToString:@\"SampleCustomEvent\"]) {\n adSourceName = @\"Sample Ad Network (Custom Event)\";\n }\n }\n return adSourceName;\n}ResponseInfoSnippets.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.905Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":63,"estimatedTokens":500}}491{"id":"doc-log_ad_response_id_to_crashlytics_ios_google_for-675b4df8","source":"documentation","title":"Log ad response ID to Crashlytics | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/crashlytics","text":"Example:\n```devsite-click-to-copy\nsource 'https://github.com/CocoaPods/Specs.git'\n\nplatform :ios, '8.0'\n\ntarget 'BannerExample' do\n use_frameworks!\n pod 'Google-Mobile-Ads-SDK'\n pod 'Firebase/Crashlytics'\n pod 'Firebase/Analytics'\nend\n```\n\nExample:\n```text\npod install --repo-update\n```\n\nExample:\n```devsite-click-to-copy\nimport UIKit\n\n// Import the Firebase library\nimport FirebaseCore\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n var window: UIWindow?\n\n func application(_ application: UIApplication,\n didFinishLaunchingWithOptions launchOptions:\n [UIApplication.LaunchOptionsKey: Any]?) -> Bool {\n // Configure an instance of Firebase\n FirebaseApp.configure()\n return true\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\n@import AppDelegate.h;\n\n// Import the Firebase library\n@import FirebaseCore;\n\n@interface AppDelegate ()\n\n@end\n\n@implementation AppDelegate\n\n‐ (BOOL)application:(UIApplication *)application\n didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n // Override point for customization after application launch.\n\n // Initialize Firebase\n [FIRApp configure];\n return YES;\n}\n```\n\nExample:\n```devsite-click-to-copy\noverride func viewDidLoad() {\n super.viewDidLoad()\n bannerView.delegate = self\n bannerView.adUnitID = \"ca-app-pub-3940256099942544/2934735716\"\n bannerView.rootViewController = self\n bannerView.load(Request())\n let button = UIButton(type: .roundedRect)\n button.frame = CGRect(x: 20, y: 50, width: 100, height: 30)\n button.setTitle(\"Crash\", for: [])\n button.addTarget(self, action: #selector(self.crashButtonTapped(_:)),\n for: .touchUpInside)\n view.addSubview(button)\n }\n```\n\nExample:\n```text\n@IBAction func crashButtonTapped(_ sender: AnyObject) {\n fatalError(\"Test Crash Happened\")\n }\n```\n\nExample:\n```devsite-click-to-copy\n‐ (void)viewDidLoad {\n [super viewDidLoad];\n\n /// ...\n\n UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];\n button.frame = CGRectMake(20, 50, 100, 30);\n [button setTitle:@\"Crash\" forState:UIControlStateNormal];\n [button addTarget:self action:@selector(crashButtonTapped:)\n forControlEvents:UIControlEventTouchUpInside];\n [self.view addSubview:button];\n}\n```\n\nExample:\n```text\n‐ (IBAction)crashButtonTapped:(id)sender {\n assert(NO);\n}\n```\n\nExample:\n```devsite-click-to-copy\nimport GoogleMobileAds\nimport UIKit\n\nclass ViewController: UIViewController, BannerViewDelegate {\n\n /// The banner view.\n @IBOutlet weak var bannerView: BannerView!\n\n override func viewDidLoad() {\n super.viewDidLoad()\n ...\n bannerView.delegate = self\n ...\n }\n\n /// Tells the delegate an ad request loaded an ad.\n func adViewDidReceiveAd(_ bannerView: BannerView) {\n if let responseInfo = bannerView.responseInfo,\n responseId = responseInfo.responseId {\n print(\"adViewDidReceiveAd from network:\n \\(responseInfo.adNetworkClassName), response Id='\\(responseId)'\")\n Crashlytics.sharedInstance().setCustomValue(responseId,\n forKey: \"banner_ad_response_id\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\n@import GoogleMobileAds;\n@interface ViewController ()\n\n@property(nonatomic, strong) GADBannerView *bannerView;\n\n@end\n\n@implementation ViewController\n\n‐ (void)viewDidLoad {\n [super viewDidLoad];\n\n // In this case, we instantiate the banner with desired ad size.\n self.bannerView = [[GADBannerView alloc]\n initWithAdSize:GADAdSizeBanner];\n\n [self addBannerViewToView:self.bannerView];\n}\n\n‐ (void)addBannerViewToView:(UIView *)bannerView {\n bannerView.translatesAutoresizingMaskIntoConstraints = NO;\n [self.view addSubview:bannerView];\n [self.view addConstraints:@[\n [NSLayoutConstraint constraintWithItem:bannerView\n attribute:NSLayoutAttributeBottom\n relatedBy:NSLayoutRelationEqual\n toItem:self.bottomLayoutGuide\n attribute:NSLayoutAttributeTop\n multiplier:1\n constant:0],\n [NSLayoutConstraint constraintWithItem:bannerView\n attribute:NSLayoutAttributeCenterX\n relatedBy:NSLayoutRelationEqual\n toItem:self.view\n kattribute:NSLayoutAttributeCenterX\n multiplier:1\n constant:0]\n ]];\n}\n\n- (void)adViewDidReceiveAd:(GADBannerView *)bannerView {\n NSString *adResponseId = bannerView.responseInfo.responseId;\n if (adResponseId) {\n NSLog(@\"adViewDidReceiveAd from network: %@ with response Id: %@\",\n bannerView.responseInfo.adNetworkClassName, adResponseId);\n [[FIRCrashlytics crashlytics] setCustomValue:adResponseId\n forKey:@\"banner_ad_response_id\"];\n }\n}\n\n@end\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.906Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":197,"estimatedTokens":1284}}492{"id":"doc-googlemobileads_framework_reference_ios_google_f-2fda9bea","source":"documentation","title":"GoogleMobileAds Framework Reference | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/api/reference/Classes/GADMobileAds","text":"Example:\n```objective_c\n@interface GADMobileAds : NSObject\n```\n\nExample:\n```swift\nclass var shared: MobileAds { get }\n```\n\nExample:\n```objective_c\n@property (class, nonatomic, readonly, nonnull) GADMobileAds *sharedInstance;\n```\n\nExample:\n```swift\nvar versionNumber: VersionNumber { get }\n```\n\nExample:\n```objective_c\n@property (nonatomic, readonly) GADVersionNumber versionNumber;\n```\n\nExample:\n```swift\nvar applicationVolume: Float { get set }\n```\n\nExample:\n```objective_c\n@property (nonatomic) float applicationVolume;\n```\n\nExample:\n```swift\nvar isApplicationMuted: Bool { get set }\n```\n\nExample:\n```objective_c\n@property (nonatomic, assign, unsafe_unretained, readwrite,\n getter=isApplicationMuted) BOOL applicationMuted;\n```\n\nExample:\n```swift\nvar audioVideoManager: AudioVideoManager { get }\n```\n\nExample:\n```objective_c\n@property (nonatomic, strong, readonly, nonnull) GADAudioVideoManager *audioVideoManager;\n```\n\nExample:\n```swift\nvar requestConfiguration: RequestConfiguration { get }\n```\n\nExample:\n```objective_c\n@property (nonatomic, strong, readonly, nonnull) GADRequestConfiguration *requestConfiguration;\n```\n\nExample:\n```swift\nvar initializationStatus: InitializationStatus { get }\n```\n\nExample:\n```objective_c\n@property (nonatomic, readonly, nonnull) GADInitializationStatus *initializationStatus;\n```\n\nExample:\n```swift\nfunc isSDKVersionAtLeast(major: Int, minor: Int, patch: Int) -> Bool\n```\n\nExample:\n```objective_c\n- (BOOL)isSDKVersionAtLeastMajor:(NSInteger)major\n minor:(NSInteger)minor\n patch:(NSInteger)patch;\n```\n\nExample:\n```swift\nfunc start() async -> InitializationStatus\n```\n\nExample:\n```objective_c\n- (void)startWithCompletionHandler:\n (nullable GADInitializationCompletionHandler)completionHandler;\n```\n\nExample:\n```swift\nfunc disableSDKCrashReporting()\n```\n\nExample:\n```objective_c\n- (void)disableSDKCrashReporting;\n```\n\nExample:\n```swift\nfunc disableMediationInitialization()\n```\n\nExample:\n```objective_c\n- (void)disableMediationInitialization;\n```\n\nExample:\n```swift\nfunc presentAdInspector(from viewController: UIViewController?) async throws\n```\n\nExample:\n```objective_c\n- (void)presentAdInspectorFromViewController:\n (nullable UIViewController *)viewController\n completionHandler:\n (nullable GADAdInspectorCompletionHandler)\n completionHandler;\n```\n\nExample:\n```swift\nfunc register(_ webView: WKWebView)\n```\n\nExample:\n```objective_c\n- (void)registerWebView:(nonnull WKWebView *)webView;\n```\n\nExample:\n```swift\nclass func generateSignal(_ request: SignalRequest) async throws -> Signal\n```\n\nExample:\n```objective_c\n+ (void)generateSignal:(nonnull GADSignalRequest *)request\n completionHandler:(nonnull GADSignalCompletionHandler)completionHandler;\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.907Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":29,"totalLines":155,"estimatedTokens":718}}493{"id":"doc-retrieve_information_about_the_ad_response_ios_g-c1134b3d","source":"documentation","title":"Retrieve information about the ad response | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/response-info","text":"Example:\n```text\nfileprivate func loadInterstitial() {\n InterstitialAd.load(\n with: \"ca-app-pub-3940256099942544/4411468910\", request: request\n ) { (ad, error) in\n if let error = error {\n let responseInfo = (error as NSError).userInfo[GADErrorUserInfoKeyResponseInfo] as? ResponseInfo\n print(\"\\(String(describing: responseInfo))\")\n return\n }\n let responseInfo = ad?.responseInfo\n print(\"\\(String(describing: responseInfo))\")\n }\n}\n```\n\nExample:\n```text\n- (void)loadInterstitial {\n [GADInterstitialAd\n loadWithAdUnitID:@\"ca-app-pub-3940256099942544/4411468910\"\n request:request\n completionHandler:^(GADInterstitialAd *ad, NSError *error) {\n if (error) {\n GADResponseInfo *responseInfo = error.userInfo[GADErrorUserInfoKeyResponseInfo];\n NSLog(@\"%@\", responseInfo.description);\n return;\n }\n GADResponseInfo *responseInfo = ad.responseInfo;\n NSLog(@\"%@\", responseInfo.description);\n }];\n}\n```\n\nExample:\n```text\n** Response Info **\n Response ID: CLz5r-KMtfoCFQvv7QodfGAMHw\n Network: GADMAdapterGoogleAdMobAds\n\n ** Loaded Adapter Response **\n Network: GADMAdapterGoogleAdMobAds\n Ad Source Name:Reservation campaign\n Ad Source ID:7068401028668408324\n Ad Source Instance Name:[DO NOT EDIT] Publisher Test Interstitial\n Ad Source Instance ID:[DO NOT EDIT] Publisher Test Interstitial\n AdUnitMapping:\n{\n}\n Error: (null)\n Latency: 0.357\n\n ** Extras Dictionary **\n {\n \"mediation_group_name\" = Campaign;\n }\n\n ** Mediation line items **\n Entry (1)\n Network: GADMAdapterGoogleAdMobAds\n Ad Source Name:Reservation campaign\n Ad Source ID:7068401028668408324\n Ad Source Instance Name:[DO NOT EDIT] Publisher Test Interstitial\n Ad Source Instance ID:[DO NOT EDIT] Publisher Test Interstitial\n AdUnitMapping:\n{\n}\n Error: (null)\n Latency: 0.357\n```\n\nExample:\n```text\nfileprivate func loadInterstitial() {\n InterstitialAd.load(\n with: \"ca-app-pub-3940256099942544/4411468910\", request: request\n ) { (ad, error) in\n let responseInfo = ad?.responseInfo\n\n let responseIdentifier = responseInfo?.responseIdentifier\n let adNetworkClassName = responseInfo?.adNetworkClassName\n let adNetworkInfoArray = responseInfo?.adNetworkInfoArray\n let loadedAdNetworkResponseInfo = responseInfo?.loadedAdNetworkResponseInfo\n let mediationGroupName = responseInfo?.extrasDictionary[\"mediation_group_name\"]\n let mediationABTestName = responseInfo?.extrasDictionary[\"mediation_ab_test_name\"]\n let mediationABTestVariant = responseInfo?.extrasDictionary[\"mediation_ab_test_variant\"]\n }\n}\n```\n\nExample:\n```text\n- (void)loadInterstitial {\n [GADInterstitialAd\n loadWithAdUnitID:@\"ca-app-pub-3940256099942544/4411468910\"\n request:request\n completionHandler:^(GADInterstitialAd *ad, NSError *error) {\n GADResponseInfo *responseInfo = ad.responseInfo;\n\n NSString *responseIdentifier = responseInfo.responseIdentifier;\n NSString *adNetworkClassName = responseInfo.adNetworkClassName;\n NSArray *adNetworkInfoArray = responseInfo.adNetworkInfoArray;\n GADAdNetworkResponseInfo *loadedAdNetworkResponseInfo = responseInfo.loadedAdNetworkResponseInfo;\n NSString *mediationGroupName = responseInfo.extrasDictionary[@\"mediation_group_name\"];\n NSString *mediationABTestName = responseInfo.extrasDictionary[@\"mediation_ab_test_name\"];\n NSString *mediationABTestVariant = responseInfo.extrasDictionary[@\"mediation_ab_test_variant\"];\n }];\n}\n```\n\nExample:\n```text\nNetwork: GADMAdapterGoogleAdMobAds\n Ad Source Name:Reservation campaign\n Ad Source ID:7068401028668408324\n Ad Source Instance Name:[DO NOT EDIT] Publisher Test Interstitial\n Ad Source Instance ID:[DO NOT EDIT] Publisher Test Interstitial\n AdUnitMapping:\n{\n}\n Error: (null)\n Latency: 0.277\n```\n\nExample:\n```text\nfileprivate func loadInterstitial() {\n InterstitialAd.load(\n with: \"ca-app-pub-3940256099942544/4411468910\", request: request\n ) { (ad, error) in\n let responseInfo = ad?.responseInfo\n let loadedAdNetworkResponseInfo = responseInfo?.loadedAdNetworkResponseInfo\n\n let adNetworkError = loadedAdNetworkResponseInfo?.error\n let adSourceId = loadedAdNetworkResponseInfo?.adSourceID\n let adSourceInstanceId = loadedAdNetworkResponseInfo?.adSourceInstanceID\n let adSourceInstanceName = loadedAdNetworkResponseInfo?.adSourceInstanceName\n let adSourceName = loadedAdNetworkResponseInfo?.adSourceName\n let adNetworkClassName = loadedAdNetworkResponseInfo?.adNetworkClassName\n let adUnitMapping = loadedAdNetworkResponseInfo?.adUnitMapping\n let latency = loadedAdNetworkResponseInfo?.latency\n }\n}\n```\n\nExample:\n```text\n- (void)loadInterstitial {\n [GADInterstitialAd\n loadWithAdUnitID:@\"ca-app-pub-3940256099942544/4411468910\"\n request:request\n completionHandler:^(GADInterstitialAd *ad, NSError *error) {\n GADResponseInfo *responseInfo = ad.responseInfo;\n GADAdNetworkResponseInfo *loadedAdNetworkResponseInfo = responseInfo.loadedAdNetworkResponseInfo;\n\n NSError *adNetworkError = loadedAdNetworkResponseInfo.error;\n NSString *adSourceId = loadedAdNetworkResponseInfo.adSourceID;\n NSString *adSourceInstanceId = loadedAdNetworkResponseInfo.adSourceInstanceID;\n NSString *adSourceInstanceName = loadedAdNetworkResponseInfo.adSourceInstanceName;\n NSString *adSourceName = loadedAdNetworkResponseInfo.adSourceName;\n NSString *adNetworkClassName = loadedAdNetworkResponseInfo.adNetworkClassName;\n NSDictionary *adUnitMapping = loadedAdNetworkResponseInfo.adUnitMapping;\n NSTimeInterval latency = loadedAdNetworkResponseInfo.latency;\n }];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.908Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":169,"estimatedTokens":1413}}494{"id":"doc-ad_preloading_beta_ios_google_for_developers-0549d9ab","source":"documentation","title":"Ad preloading (beta) | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/ad-preloading","text":"Example:\n```text\n// Start the preloading initialization process.\nlet request = Request()\nlet interstitialConfig = PreloadConfigurationV2(\n adUnitID: adUnitID, request: request)\nInterstitialAdPreloader.shared.preload(\n for: adUnitID, configuration: interstitialConfig, delegate: self)AdPreloaderSnippets.swift\n```\n\nExample:\n```text\n// Start the preloading initialization process.\nGADRequest *request = [GADRequest request];\nGADPreloadConfigurationV2 *interstitialConfig =\n [[GADPreloadConfigurationV2 alloc] initWithAdUnitID:adUnitID\n request:request];\n\n[GADInterstitialAdPreloader.sharedInstance preloadForPreloadID:adUnitID\n configuration:interstitialConfig\n delegate:self];AdPreloaderSnippets.m\n```\n\nExample:\n```text\nprivate func showInterstitialAd(adUnitID: String) {\n // Verify that the preloaded ad is available before polling.\n guard isInterstitialAvailable(adUnitID: adUnitID) else {\n print(\"Preloaded interstitial ad is not available.\")\n return\n }\n\n // Polling returns the next available ad and loads another ad in the background.\n let ad = InterstitialAdPreloader.shared.ad(with: adUnitID)\n\n // Interact with the ad object as needed.\n print(\"Interstitial ad response info: \\(String(describing: ad?.responseInfo))\")\n ad?.paidEventHandler = { (value: AdValue) in\n print(\"Interstitial ad paid event: \\(value.value), \\(value.currencyCode)\")\n }\n\n ad?.fullScreenContentDelegate = self\n ad?.present(from: self)\n}AdPreloaderSnippets.swift\n```\n\nExample:\n```text\n- (void)showInterstitialAdWithAdUnitID:(nonnull NSString *)adUnitID {\n // Verify that the preloaded ad is available before polling.\n if (![self isInterstitialAvailableWithAdUnitID:adUnitID]) {\n NSLog(@\"Preloaded interstitial ad is not available.\");\n return;\n }\n\n // Getting the preloaded ad loads another ad in the background.\n GADInterstitialAd *ad =\n [GADInterstitialAdPreloader.sharedInstance adWithPreloadID:adUnitID];\n\n // Interact with the ad object as needed.\n NSLog(@\"Interstitial ad response info: %@\", ad.responseInfo);\n ad.paidEventHandler = ^(GADAdValue *_Nonnull value) {\n NSLog(@\"Interstitial ad paid event: %@ %@ \", value.value, value.currencyCode);\n };\n ad.fullScreenContentDelegate = self;\n [ad presentFromRootViewController:self];\n}AdPreloaderSnippets.m\n```\n\nExample:\n```text\nprivate func getInterstitialAdResponseInfo(preloadID: String) {\n // Get the response info for the preloaded ad.\n if let responseInfo = InterstitialAdPreloader.shared.responseInfo(\n with: preloadID)\n {\n print(\"Ad response ID: \\(responseInfo.responseIdentifier ?? \"\")\")\n }\n}AdPreloaderSnippets.swift\n```\n\nExample:\n```text\n- (void)getInterstitialAdResponseInfoWithPreloadID:(nonnull NSString *)preloadID {\n // Get the response info for the preloaded ad.\n GADResponseInfo *responseInfo =\n [GADInterstitialAdPreloader.sharedInstance\n adResponseInfoWithPreloadID:preloadID];\n if (responseInfo) {\n NSLog(@\"Ad response ID: %@\", responseInfo.responseIdentifier);\n }\n}AdPreloaderSnippets.m\n```\n\nExample:\n```text\nprivate func isInterstitialAvailable(adUnitID: String) -> Bool {\n // Verify that an ad is available before polling.\n return InterstitialAdPreloader.shared.isAdAvailable(with: adUnitID)\n}AdPreloaderSnippets.swift\n```\n\nExample:\n```text\n- (BOOL)isInterstitialAvailableWithAdUnitID:(nonnull NSString *)adUnitID {\n // Verify that an ad is available before polling.\n return [GADInterstitialAdPreloader.sharedInstance isAdAvailableWithPreloadID:adUnitID];\n}AdPreloaderSnippets.m\n```\n\nExample:\n```text\nfunc adAvailable(forPreloadID preloadID: String, responseInfo: ResponseInfo) {\n // This callback indicates that an ad is available for the specified configuration.\n // No action is required here, but updating the UI can be useful in some cases.\n print(\"Ad preloaded successfully for ad preload ID: \\(preloadID)\")\n}\n\nfunc adsExhausted(forPreloadID preloadID: String) {\n // This callback indicates that all the ads for the specified configuration have been\n // consumed and no ads are available to show. No action is required here, but updating\n // the UI can be useful in some cases.\n // Don't call InterstitialAdPreloader.shared.preload or\n // InterstitialAdPreloader.shared.ad from adsExhausted.\n print(\"Ad exhausted for ad preload ID: \\(preloadID)\")\n}\n\nfunc adFailedToPreload(forPreloadID preloadID: String, error: Error) {\n print(\n \"Ad failed to load with ad preload ID: \\(preloadID), Error: \\(error.localizedDescription)\"\n )\n}AdPreloaderSnippets.swift\n```\n\nExample:\n```text\n- (void)adAvailableForPreloadID:(nonnull NSString *)preloadID\n responseInfo:(nonnull GADResponseInfo *)responseInfo {\n // This callback indicates that an ad is available for the specified configuration.\n // No action is required here, but updating the UI can be useful in some cases.\n NSLog(@\"Ad preloaded successfully for ad unit ID: %@\", preloadID);\n}\n\n- (void)adsExhaustedForPreloadID:(nonnull NSString *)preloadID {\n // This callback indicates that all the ads for the specified configuration have been\n // consumed and no ads are available to show. No action is required here, but updating\n // the UI can be useful in some cases.\n // Don't call [GAD<Format>AdPreloader preloadForPreloadID:] or\n // [GAD<Format>AdPreloader adWithPreloadID:] from adsExhaustedForPreloadID.\n NSLog(@\"Ad exhausted for ad preload ID: %@\", preloadID);\n}\n\n- (void)adFailedToPreloadForPreloadID:(nonnull NSString *)preloadID\n error:(nonnull NSError *)error {\n NSLog(@\"Ad failed to load with ad preload ID: %@, Error: %@\", preloadID,\n error.localizedDescription);\n}AdPreloaderSnippets.m\n```\n\nExample:\n```text\nlet preloadConfig = PreloadConfigurationV2(adUnitID: adUnitID)\n// Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\npreloadConfig.bufferSize = 2AdPreloaderSnippets.swift\n```\n\nExample:\n```text\nGADPreloadConfigurationV2 *preloadConfig =\n [[GADPreloadConfigurationV2 alloc] initWithAdUnitID:adUnitID];\n// Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\npreloadConfig.bufferSize = 2;AdPreloaderSnippets.m\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.909Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":175,"estimatedTokens":1576}}495{"id":"doc-integrate_the_webview_api_for_ads_ios_google_for-415414db","source":"documentation","title":"Integrate the WebView API for Ads | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/browser/webview/api-for-ads","text":"Example:\n```text\n<!-- Indicate Google Mobile Ads SDK usage is only for web view APIs for ads -->\n<key>GADIntegrationManager</key>\n<string>webview</string>\n```\n\nExample:\n```text\nimport WebKit\n\nclass ViewController: UIViewController {\n\n var webView: WKWebView!\n\n override func viewDidLoad() {\n super.viewDidLoad()\n\n // Initialize a WKWebViewConfiguration object.\n let webViewConfiguration = WKWebViewConfiguration()\n // Let HTML videos with a \"playsinline\" attribute play inline.\n webViewConfiguration.allowsInlineMediaPlayback = true\n // Let HTML videos with an \"autoplay\" attribute play automatically.\n webViewConfiguration.mediaTypesRequiringUserActionForPlayback = []\n\n // Initialize the WKWebView with your WKWebViewConfiguration object.\n webView = WKWebView(frame: view.frame, configuration: webViewConfiguration)\n view.addSubview(webView)\n\n // Register the web view.\n MobileAds.shared.register(webView)\n }\n}\n```\n\nExample:\n```text\n@import WebKit;\n\n#import \"ViewController.h\"\n\n@interface ViewController ()\n\n@property(nonatomic, strong) WKWebView *webView;\n\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n\n // Initialize a WKWebViewConfiguration object.\n WKWebViewConfiguration *webViewConfiguration = [[WKWebViewConfiguration alloc] init];\n // Let HTML videos with a \"playsinline\" attribute play inline.\n webViewConfiguration.allowsInlineMediaPlayback = YES;\n // Let HTML videos with an \"autoplay\" attribute play automatically.\n webViewConfiguration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;\n\n // Initialize the WKWebView with your WKWebViewConfiguration object.\n self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:webViewConfiguration];\n [self.view addSubview:self.webView];\n\n // Register the web view.\n [GADMobileAds.sharedInstance registerWebView:self.webView];\n}\n```\n\nExample:\n```text\nhttps://google.github.io/webview-ads/test/#api-for-ads-tests\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.910Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":74,"estimatedTokens":503}}496{"id":"doc-migrate_app_open_ads_android_google_for_develope-547d7ca1","source":"documentation","title":"Migrate app open ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/migration/migrate-app-open","text":"Example:\n```text\nAppOpenAd.load(\n this@AppOpenActivity,\n \"AD_UNIT_ID\",\n AdRequest.Builder().build(),\n object : AppOpenAdLoadCallback() {\n override fun onAdLoaded(ad: AppOpenAd) {\n // Called when an ad has loaded.\n ad.fullScreenContentCallback =\n object : FullScreenContentCallback() {\n }\n appOpenAd = ad\n }\n\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // Called when ad fails to load.\n }\n }\n)\n```\n\nExample:\n```text\nAppOpenAd.load(\n this,\n \"AD_UNIT_ID\",\n new AdRequest.Builder().build(),\n new AppOpenAdLoadCallback() {\n @Override\n public void onAdLoaded(@NonNull AppOpenAd ad) {\n // Called when an ad has loaded.\n ad.setFullScreenContentCallback(new FullScreenContentCallback() {});\n appOpenAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {\n // Called when ad fails to load.\n }\n }\n);\n```\n\nExample:\n```text\nAppOpenAd.load(\n AdRequest.Builder(\"AD_UNIT_ID\").build(),\n object : AdLoadCallback<AppOpenAd> {\n override fun onAdLoaded(ad: AppOpenAd) {\n // Called when an ad has loaded.\n ad.adEventCallback =\n object : AppOpenAdEventCallback {\n }\n appOpenAd = ad\n }\n\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // Called when ad fails to load.\n }\n }\n)\n```\n\nExample:\n```text\nAppOpenAd.load(\n new AdRequest.Builder(\"AD_UNIT_ID\").build(),\n new AdLoadCallback<AppOpenAd>() {\n @Override\n public void onAdLoaded(@NonNull AppOpenAd ad) {\n // Called when an ad has loaded.\n ad.setAdEventCallback(new AppOpenAdEventCallback() {});\n appOpenAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Called when ad fails to load.\n }\n });\n```\n\nExample:\n```text\nappOpenAd?.show(this@AppOpenActivity)\n```\n\nExample:\n```text\nappOpenAd.show(this);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.910Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":94,"estimatedTokens":488}}497{"id":"doc-migrate_to_gma_next_gen_sdk_android_google_for_d-7641776c","source":"documentation","title":"Migrate to GMA Next-Gen SDK | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/migration","text":"Example:\n```devsite-click-to-copy\ndependencies {\n // ...\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n // ...\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n // ...\n // Comment out/remove play-services-ads.\n // implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n // ...\n // Comment out/remove play-services-ads.\n // implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk:1.3.1'\n}\n```\n\nExample:\n```text\nconfigurations.configureEach {\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads\")\n exclude(group = \"com.google.android.gms\", module = \"play-services-ads-lite\")\n}\n```\n\nExample:\n```text\nconfigurations.configureEach {\n exclude group: \"com.google.android.gms\", module: \"play-services-ads\"\n exclude group: \"com.google.android.gms\", module: \"play-services-ads-lite\"\n}\n```\n\nExample:\n```devsite-click-to-copy\n<manifest>\n <application>\n <!-- Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713 -->\n <meta-data\n android:name=\"com.google.android.gms.ads.APPLICATION_ID\"\n android:value=\"SAMPLE_APP_ID\"/>\n </application>\n</manifest>\n```\n\nExample:\n```text\n// Initialize the Google Mobile Ads SDK.\nval initConfig = InitializationConfig.Builder(\"SAMPLE_APP_ID\").build()\nMobileAds.initialize(this@MainActivity, initConfig) {}\n```\n\nExample:\n```text\n// Initialize GMA Next-Gen SDK.\nInitializationConfig initConfig =\n new InitializationConfig.Builder(\"SAMPLE_APP_ID\").build();\nMobileAds.initialize(this, initConfig, initializationStatus -> {});\n```\n\nExample:\n```text\nimport com.google.android.gms.ads.MobileAds\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\n\nclass MainActivity : AppCompatActivity() {\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n\n val backgroundScope = CoroutineScope(Dispatchers.IO)\n backgroundScope.launch {\n // Initialize the Google Mobile Ads SDK on a background thread.\n MobileAds.initialize(this@MainActivity) {}\n }\n }\n}\n```\n\nExample:\n```text\nimport com.google.android.gms.ads.MobileAds;\nimport com.google.android.gms.ads.initialization.InitializationStatus;\nimport com.google.android.gms.ads.initialization.OnInitializationCompleteListener;\n\npublic class MainActivity extends AppCompatActivity {\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n\n new Thread(\n () -> {\n // Initialize the Google Mobile Ads SDK on a background thread.\n MobileAds.initialize(this, initializationStatus -> {});\n })\n .start();\n }\n}\n```\n\nExample:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds\nimport com.google.android.libraries.ads.mobile.sdk.initialization.InitializationConfig\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\n\nclass MainActivity : AppCompatActivity() {\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n\n val backgroundScope = CoroutineScope(Dispatchers.IO)\n backgroundScope.launch {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this@MainActivity,\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n InitializationConfig.Builder(\"SAMPLE_APP_ID\").build()\n ) {\n // Adapter initialization is complete.\n }\n // SDK initialization is complete. If you don't want to wait for bidding adapters to finish\n // initializing, start loading ads now.\n }\n }\n}\n```\n\nExample:\n```text\nimport com.google.android.libraries.ads.mobile.sdk.MobileAds;\nimport com.google.android.libraries.ads.mobile.sdk.initialization.InitializationConfig;\n\npublic class MainActivity extends AppCompatActivity {\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n\n new Thread(\n () -> {\n // Initialize GMA Next-Gen SDK on a background thread.\n MobileAds.initialize(\n this,\n // Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713\n new InitializationConfig.Builder(\"SAMPLE_APP_ID\")\n .build(),\n initializationStatus -> {\n // Adapter initialization is complete.\n });\n // SDK initialization is complete. If you don't want to wait for bidding adapters to\n // finish initializing, start loading ads now.\n })\n .start();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.912Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":181,"estimatedTokens":1298}}498{"id":"doc-handle_callbacks_from_background_thread_android_-fe378297","source":"documentation","title":"Handle callbacks from background thread | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/migration/handle-callbacks","text":"Example:\n```text\nadView.loadAd(\n adRequest,\n object : AdLoadCallback<BannerAd> {\n override fun onAdLoaded(ad: BannerAd) {\n // Show a toast on the UI thread.\n runOnUiThread {\n Toast.makeText(activity, \"Ad loaded.\", Toast.LENGTH_SHORT).show()\n }\n }\n },\n)\n```\n\nExample:\n```text\nadView.loadAd(\n adRequest,\n new AdLoadCallback<BannerAd>() {\n @Override\n public void onAdLoaded(@NonNull BannerAd ad) {\n // Show a toast on the UI thread.\n runOnUiThread(() ->\n Toast.makeText(activity, \"Ad loaded.\", Toast.LENGTH_SHORT).show()\n );\n }\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.912Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":31,"estimatedTokens":164}}499{"id":"doc-migrate_ad_requests_android_google_for_developer-50c8619b","source":"documentation","title":"Migrate ad requests | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/migration/migrate-ad-requests","text":"Example:\n```text\nval adRequest = AdRequest.Builder().build()\n\nInterstitialAd.load(\n this, \"AD_UNIT_ID\", adRequest,\n object : InterstitialAdLoadCallback() {\n }\n)\n```\n\nExample:\n```text\nAdRequest adRequest = new AdRequest.Builder().build();\n\nInterstitialAd.load(\n this, \"AD_UNIT_ID\", adRequest,\n new InterstitialAdLoadCallback() {\n }\n);\n```\n\nExample:\n```text\nval adRequest = AdRequest.Builder(\"AD_UNIT_ID\").build()\n\nInterstitialAd.load(adRequest, object : AdLoadCallback<InterstitialAd> {})\n```\n\nExample:\n```text\nAdRequest adRequest = new AdRequest.Builder(\"AD_UNIT_ID\").build();\n\nInterstitialAd.load(adRequest, new AdLoadCallback<InterstitialAd>() {});\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putInt(\"npa\", 1)\nval request = AdRequest.Builder()\n .addNetworkExtrasBundle(AdMobAdapter::class.java, extras)\n .build()\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putInt(\"npa\", 1);\nAdRequest request = new AdRequest.Builder()\n .addNetworkExtrasBundle(AdMobAdapter.class, extras)\n .build();\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putInt(\"npa\", 1)\nval request = AdRequest.Builder(\"AD_UNIT_ID\")\n .setGoogleExtrasBundle(extras)\n .build()\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putInt(\"npa\", 1);\nAdRequest request = new AdRequest.Builder(\"AD_UNIT_ID\")\n .setGoogleExtrasBundle(extras)\n .build();\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putString(\"exampleKey\", \"exampleValue\")\n\nval request = AdRequest.Builder()\n .addNetworkExtrasBundle(SampleAdapter::class, extras)\n .build()\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(\"exampleKey\", \"exampleValue\");\n\nAdRequest request = new AdRequest.Builder()\n .addNetworkExtrasBundle(SampleAdapter.class, extras)\n .build();\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putString(\"exampleKey\", \"exampleValue\")\n\nval request = AdRequest.Builder(\"AD_UNIT_ID\")\n .putAdSourceExtrasBundle(SampleAdapter::class.java, extras)\n .build()\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(\"exampleKey\", \"exampleValue\");\n\nAdRequest request = new AdRequest.Builder(\"AD_UNIT_ID\")\n .putAdSourceExtrasBundle(SampleAdapter.class, extras)\n .build();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.913Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":113,"estimatedTokens":561}}500{"id":"doc-release_notes_android_google_for_developers-2b4c8533","source":"documentation","title":"Release Notes | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/rel-notes","text":"Example:\n```text\nval config = InitializationConfig.Builder()\n .setExtras(bundleOf(\"force_use_cronet\" to true))\n .build()\nMobileAds.initialize(this, config)\n```\n\nExample:\n```text\nandroid {\n compileOptions {\n sourceCompatibility JavaVersion.VERSION_11\n targetCompatibility JavaVersion.VERSION_11\n }\n kotlinOptions {\n jvmTarget = '11'\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.914Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":22,"estimatedTokens":93}}501{"id":"doc-set_up_web_view_ios_google_for_developers-35a54ebe","source":"documentation","title":"Set up web view | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/browser/webview","text":"Example:\n```text\nimport WebKit\n\nclass ViewController: UIViewController {\n\n var webView: WKWebView!\n\n override func viewDidLoad() {\n super.viewDidLoad()\n\n // Initialize a WKWebViewConfiguration object.\n let webViewConfiguration = WKWebViewConfiguration()\n // Let HTML videos with a \"playsinline\" attribute play inline.\n webViewConfiguration.allowsInlineMediaPlayback = true\n // Let HTML videos with an \"autoplay\" attribute play automatically.\n webViewConfiguration.mediaTypesRequiringUserActionForPlayback = []\n\n // Initialize the WKWebView with your WKWebViewConfiguration object.\n webView = WKWebView(frame: view.frame, configuration: webViewConfiguration)\n view.addSubview(webView)\n }\n}\n```\n\nExample:\n```text\n@import WebKit;\n\n#import \"ViewController.h\"\n\n@interface ViewController ()\n\n@property(nonatomic, strong) WKWebView *webView;\n\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n\n // Initialize a WKWebViewConfiguration object.\n WKWebViewConfiguration *webViewConfiguration = [[WKWebViewConfiguration alloc] init];\n // Let HTML videos with a \"playsinline\" attribute play inline.\n webViewConfiguration.allowsInlineMediaPlayback = YES;\n // Let HTML videos with an \"autoplay\" attribute play automatically.\n webViewConfiguration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;\n\n // Initialize the WKWebView with your WKWebViewConfiguration object.\n self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:webViewConfiguration];\n [self.view addSubview:self.webView];\n}\n```\n\nExample:\n```text\nimport WebKit\n\nvar webview: WKWebview!\n\nclass ViewController: UIViewController {\n override func viewDidLoad() {\n super.viewDidLoad()\n\n // Initialize a WKWebViewConfiguration object.\n let webViewConfiguration = WKWebViewConfiguration()\n // Let HTML videos with a \"playsinline\" attribute play inline.\n webViewConfiguration.allowsInlineMediaPlayback = true\n // Let HTML videos with an \"autoplay\" attribute play automatically.\n webViewConfiguration.mediaTypesRequiringUserActionForPlayback = []\n\n // Initialize the WKWebView with your WKWebViewConfiguration object.\n webView = WKWebView(frame: view.frame, configuration: webViewConfiguration)\n view.addSubview(webView)\n\n // Load the URL for optimized web view performance.\n guard let url = URL(string: \"https://google.github.io/webview-ads/test/\") else { return }\n let request = URLRequest(url: url)\n webView.load(request)\n }\n}\n```\n\nExample:\n```text\n@import WebKit;\n\n#import \"ViewController.h\"\n\n@interface ViewController ()\n\n@property(nonatomic, strong) WKWebView *webView;\n\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n\n // Initialize a WKWebViewConfiguration object.\n WKWebViewConfiguration *webViewConfiguration = [[WKWebViewConfiguration alloc] init];\n // Let HTML videos with a \"playsinline\" attribute play inline.\n webViewConfiguration.allowsInlineMediaPlayback = YES;\n // Let HTML videos with an \"autoplay\" attribute play automatically.\n webViewConfiguration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;\n\n // Initialize the WKWebView with your WKWebViewConfiguration object.\n self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:webViewConfiguration];\n [self.view addSubview:self.webview];\n\n // Load the URL for optimized web view performance.\n NSURL *url = [NSURL URLWithString:@\"https://google.github.io/webview-ads/test/\"];\n NSURLRequest *request = [NSURLRequest requestWithURL:url];\n [webView loadRequest:request];\n}\n```\n\nExample:\n```text\nhttps://google.github.io/webview-ads/test/\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.915Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":125,"estimatedTokens":929}}502{"id":"doc-load_a_single_interstitial_ad_android_google_for-457813cc","source":"documentation","title":"Load a single interstitial ad | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/interstitial/single-load","text":"Example:\n```text\n// Load ads after you initialize MobileAds.\nInterstitialAd.load(\n AdRequest.Builder(adUnitId).build(),\n object : AdLoadCallback<InterstitialAd> {\n override fun onAdLoaded(ad: InterstitialAd) {\n // Interstitial ad loaded.\n interstitialAd = ad\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // Interstitial ad failed to load.\n Log.e(TAG, \"Interstitial ad failed to load: ${adError.message}\")\n interstitialAd = null\n }\n },\n)\n```\n\nExample:\n```text\n// Load ads after you initialize MobileAds.\nInterstitialAd.load(\n new AdRequest.Builder(adUnitId).build(),\n new AdLoadCallback<InterstitialAd>() {\n @Override\n public void onAdLoaded(@NonNull InterstitialAd ad) {\n // Interstitial ad loaded.\n interstitialAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Interstitial ad failed to load.\n Log.e(TAG, \"Interstitial ad failed to load: \" + adError.getMessage());\n interstitialAd = null;\n }\n });\n```\n\nExample:\n```text\nprivate fun showAd(interstitialAd: InterstitialAd, activity: Activity) {\n // Show the ad.\n interstitialAd.show(activity)\n}\n```\n\nExample:\n```text\nprivate void showAd(InterstitialAd interstitialAd, Activity activity) {\n // Show the ad.\n interstitialAd.show(activity);\n}\n```\n\nExample:\n```text\n// Listen for ad events.\nval ad = interstitialAd\nif (ad == null) {\n Log.e(TAG, \"Interstitial ad is not ready yet.\")\n return\n}\n\nad.adEventCallback =\n object : InterstitialAdEventCallback {\n override fun onAdShowedFullScreenContent() {\n // Interstitial ad did show.\n }\n\n override fun onAdDismissedFullScreenContent() {\n // Interstitial ad did dismiss.\n interstitialAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(\n fullScreenContentError: FullScreenContentError\n ) {\n // Interstitial ad failed to show.\n Log.e(TAG, \"Interstitial ad failed to show: ${fullScreenContentError.message}\")\n }\n\n override fun onAdImpression() {\n // Interstitial ad did record an impression.\n }\n\n override fun onAdClicked() {\n // Interstitial ad did record a click.\n }\n }\n```\n\nExample:\n```text\n// Listen for ad events.\nif (interstitialAd == null) {\n Log.e(TAG, \"Interstitial ad is not ready yet.\");\n return;\n}\n\ninterstitialAd.setAdEventCallback(\n new InterstitialAdEventCallback() {\n @Override\n public void onAdShowedFullScreenContent() {\n // Interstitial ad did show.\n }\n\n @Override\n public void onAdDismissedFullScreenContent() {\n // Interstitial ad did dismiss.\n interstitialAd = null;\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(\n @NonNull FullScreenContentError fullScreenContentError) {\n // Interstitial ad failed to show.\n Log.e(TAG, \"Interstitial ad failed to show: \" + fullScreenContentError.getMessage());\n }\n\n @Override\n public void onAdImpression() {\n // Interstitial ad did record an impression.\n }\n\n @Override\n public void onAdClicked() {\n // Interstitial ad did record a click.\n }\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.916Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":135,"estimatedTokens":806}}503{"id":"doc-fixed_size_banner_ads_android_google_for_develop-0b643e0c","source":"documentation","title":"Fixed size banner ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/banner/fixed-size","text":"Example:\n```text\nW/Ads: Not enough space to show ad. Needs 320x50 dp, but only has 288x495 dp.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.917Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":28}}504{"id":"doc-optimize_click_behavior_ios_google_for_developer-277142e1","source":"documentation","title":"Optimize click behavior | iOS | Google for Developers","url":"https://developers.google.com/admob/ios/browser/webview/click-behavior","text":"Example:\n```text\nimport GoogleMobileAds\nimport SafariServices\nimport WebKit\n\nclass ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {\n\n override func viewDidLoad() {\n super.viewDidLoad()\n\n // ... Register the WKWebView.\n\n // 1. Set the WKUIDelegate on your WKWebView instance.\n webView.uiDelegate = self;\n // 2. Set the WKNavigationDelegate on your WKWebView instance.\n webView.navigationDelegate = self\n }\n\n // Implement the WKUIDelegate method.\n func webView(\n _ webView: WKWebView,\n createWebViewWith configuration: WKWebViewConfiguration,\n for navigationAction: WKNavigationAction,\n windowFeatures: WKWindowFeatures) -> WKWebView? {\n // 3. Determine whether to optimize the behavior of the click URL.\n if didHandleClickBehavior(\n currentURL: webView.url,\n navigationAction: navigationAction) {\n print(\"URL opened in SFSafariViewController.\")\n }\n\n return nil\n }\n\n // Implement the WKNavigationDelegate method.\n func webView(\n _ webView: WKWebView,\n decidePolicyFor navigationAction: WKNavigationAction,\n decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)\n {\n // 3. Determine whether to optimize the behavior of the click URL.\n if didHandleClickBehavior(\n currentURL: webView.url,\n navigationAction: navigationAction) {\n return decisionHandler(.cancel)\n }\n\n decisionHandler(.allow)\n }\n\n // Implement a helper method to handle click behavior.\n func didHandleClickBehavior(\n currentURL: URL,\n navigationAction: WKNavigationAction) -> Bool {\n guard let targetURL = navigationAction.request.url else {\n return false\n }\n\n // Handle custom URL schemes such as itms-apps:// by attempting to\n // launch the corresponding application.\n if navigationAction.navigationType == .linkActivated {\n if let scheme = targetURL.scheme, ![\"http\", \"https\"].contains(scheme) {\n UIApplication.shared.open(targetURL, options: [:], completionHandler: nil)\n return true\n }\n }\n\n guard let currentDomain = currentURL.host,\n let targetDomain = targetURL.host else {\n return false\n }\n\n // Check if the navigationType is a link with an href attribute or\n // if the target of the navigation is a new window.\n if (navigationAction.navigationType == .linkActivated ||\n navigationAction.targetFrame == nil) &&\n // If the current domain does not equal the target domain,\n // the assumption is the user is navigating away from the site.\n currentDomain != targetDomain {\n // 4. Open the URL in a SFSafariViewController.\n let safariViewController = SFSafariViewController(url: targetURL)\n present(safariViewController, animated: true)\n return true\n }\n\n return false\n }\n}\n```\n\nExample:\n```text\n@import GoogleMobileAds;\n@import SafariServices;\n@import WebKit;\n\n@interface ViewController () <WKNavigationDelegate, WKUIDelegate>\n\n@property(nonatomic, strong) WKWebView *webView;\n\n@end\n\n@implementation ViewController\n\n- (void)viewDidLoad {\n [super viewDidLoad];\n\n // ... Register the WKWebView.\n\n // 1. Set the WKUIDelegate on your WKWebView instance.\n self.webView.uiDelegate = self;\n // 2. Set the WKNavigationDelegate on your WKWebView instance.\n self.webView.navigationDelegate = self;\n}\n\n// Implement the WKUIDelegate method.\n- (WKWebView *)webView:(WKWebView *)webView\n createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration\n forNavigationAction:(WKNavigationAction *)navigationAction\n windowFeatures:(WKWindowFeatures *)windowFeatures {\n // 3. Determine whether to optimize the behavior of the click URL.\n if ([self didHandleClickBehaviorForCurrentURL: webView.URL\n navigationAction: navigationAction]) {\n NSLog(@\"URL opened in SFSafariViewController.\");\n }\n\n return nil;\n}\n\n// Implement the WKNavigationDelegate method.\n- (void)webView:(WKWebView *)webView\n decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction\n decisionHandler:\n (void (^)(WKNavigationActionPolicy))decisionHandler {\n // 3. Determine whether to optimize the behavior of the click URL.\n if ([self didHandleClickBehaviorForCurrentURL: webView.URL\n navigationAction: navigationAction]) {\n decisionHandler(WKNavigationActionPolicyCancel);\n return;\n }\n\n decisionHandler(WKNavigationActionPolicyAllow);\n}\n\n// Implement a helper method to handle click behavior.\n- (BOOL)didHandleClickBehaviorForCurrentURL:(NSURL *)currentURL\n navigationAction:(WKNavigationAction *)navigationAction {\n NSURL *targetURL = navigationAction.request.URL;\n\n // Handle custom URL schemes such as itms-apps:// by attempting to\n // launch the corresponding application.\n if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {\n NSString *scheme = targetURL.scheme;\n if (![scheme isEqualToString:@\"http\"] && ![scheme isEqualToString:@\"https\"]) {\n [UIApplication.sharedApplication openURL:targetURL options:@{} completionHandler:nil];\n return YES;\n }\n }\n\n NSString *currentDomain = currentURL.host;\n NSString *targetDomain = targetURL.host;\n\n if (!currentDomain || !targetDomain) {\n return NO;\n }\n\n // Check if the navigationType is a link with an href attribute or\n // if the target of the navigation is a new window.\n if ((navigationAction.navigationType == WKNavigationTypeLinkActivated\n || !navigationAction.targetFrame)\n // If the current domain does not equal the target domain,\n // the assumption is the user is navigating away from the site.\n && ![currentDomain isEqualToString: targetDomain]) {\n // 4. Open the URL in a SFSafariViewController.\n SFSafariViewController *safariViewController =\n [[SFSafariViewController alloc] initWithURL:targetURL];\n [self presentViewController:safariViewController animated:YES\n completion:nil];\n return YES;\n }\n\n return NO;\n}\n```\n\nExample:\n```text\nhttps://google.github.io/webview-ads/test/#click-behavior-tests\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.917Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":192,"estimatedTokens":1531}}505{"id":"doc-migrate_banner_ads_android_google_for_developers-7b857678","source":"documentation","title":"Migrate banner ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/migration/migrate-banner","text":"Example:\n```text\n<com.google.android.libraries.ads.mobile.sdk.banner.AdView\n android:id=\"@+id/adView\"\n android:layout_width=\"wrap_content\"\n android:layout_height=\"wrap_content\"\n app:layout_constraintBottom_toBottomOf=\"parent\"\n app:layout_constraintEnd_toEndOf=\"parent\"\n app:layout_constraintStart_toStartOf=\"parent\" />\n```\n\nExample:\n```text\nimport com.google.android.gms.ads.AdView\n\nclass MainActivity : AppCompatActivity() {\n\n private lateinit var binding: ActivityMainBinding\n private lateinit var adView: AdView\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n binding = ActivityMainBinding.inflate(layoutInflater)\n setContentView(binding.root)\n\n // Step 1 - Create an AdView object with ad unit ID and size.\n adView = AdView(this)\n adView.adUnitId = \"AD_UNIT_ID\"\n adView.setAdSize(AdSize.getLargeAnchoredAdaptiveBannerAdSize(this, 320))\n // Step 2 - Add the AdView to view hierarchy.\n binding.bannerViewContainer.addView(adView)\n\n // Step 3 - Load the ad.\n val adRequest = AdRequest.Builder().build()\n adView.loadAd(adRequest)\n }\n}\n```\n\nExample:\n```text\nimport com.google.android.gms.ads.AdRequest;\nimport com.google.android.gms.ads.AdSize;\nimport com.google.android.gms.ads.AdView;\n\npublic class MainActivity extends AppCompatActivity {\n\n private ActivityMainBinding binding;\n private AdView adView;\n\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n binding = ActivityMainBinding.inflate(getLayoutInflater());\n setContentView(binding.getRoot());\n\n // Step 1 - Create an AdView object with ad unit ID and size.\n adView = new AdView(this);\n adView.setAdUnitId(\"AD_UNIT_ID\");\n adView.setAdSize(AdSize.getLargeAnchoredAdaptiveBannerAdSize(this, 320));\n\n // Step 2 - Add the AdView to view hierarchy.\n binding.bannerViewContainer.addView(adView);\n\n // Step 3 - Load the ad.\n AdRequest adRequest = new AdRequest.Builder().build();\n adView.loadAd(adRequest);\n }\n}\n```\n\nExample:\n```text\nimport android.util.Log\nimport com.google.android.libraries.ads.mobile.sdk.banner.AdSize\nimport com.google.android.libraries.ads.mobile.sdk.banner.AdView\nimport com.google.android.libraries.ads.mobile.sdk.banner.BannerAd\nimport com.google.android.libraries.ads.mobile.sdk.banner.BannerAdEventCallback\nimport com.google.android.libraries.ads.mobile.sdk.banner.BannerAdRequest\nimport com.google.android.libraries.ads.mobile.sdk.common.AdLoadCallback\nimport com.google.android.libraries.ads.mobile.sdk.common.LoadAdError\n\nclass MainActivity : AppCompatActivity() {\n\n private val TAG = \"MainActivity\"\n private lateinit var adView: AdView\n private lateinit var binding: ActivityMainBinding\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n binding = ActivityMainBinding.inflate(layoutInflater)\n setContentView(binding.root)\n\n // Step 1 - Create an AdView object.\n adView = binding.adView\n\n // Step 2 - Load the ad.\n val adSize = AdSize.getLargeAnchoredAdaptiveBannerAdSize(this, 360)\n val adRequest = BannerAdRequest.Builder(\"AD_UNIT_ID\", adSize).build()\n adView.loadAd(\n adRequest,\n object : AdLoadCallback<BannerAd> {\n override fun onAdLoaded(ad: BannerAd) {\n ad.adEventCallback =\n object : BannerAdEventCallback {\n override fun onAdImpression() {\n Log.d(TAG, \"Banner ad recorded an impression.\")\n }\n\n override fun onAdClicked() {\n Log.d(TAG, \"Banner ad clicked.\")\n }\n }\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n Log.e(TAG, \"Banner ad failed to load: $adError\")\n }\n },\n )\n }\n}\n```\n\nExample:\n```text\nimport android.util.Log;\nimport com.google.android.libraries.ads.mobile.sdk.banner.AdSize;\nimport com.google.android.libraries.ads.mobile.sdk.banner.AdView;\nimport com.google.android.libraries.ads.mobile.sdk.banner.BannerAd;\nimport com.google.android.libraries.ads.mobile.sdk.banner.BannerAdEventCallback;\nimport com.google.android.libraries.ads.mobile.sdk.banner.BannerAdRequest;\nimport com.google.android.libraries.ads.mobile.sdk.common.AdLoadCallback;\nimport com.google.android.libraries.ads.mobile.sdk.common.LoadAdError;\n\npublic class MainActivity extends AppCompatActivity {\n\n private static final String TAG = \"MainActivity\";\n private AdView adView;\n private ActivityMainBinding binding;\n\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n binding = ActivityMainBinding.inflate(getLayoutInflater());\n setContentView(binding.getRoot());\n\n // Step 1 - Create an AdView object.\n adView = binding.adView;\n\n // Step 2 - Load the ad.\n AdSize adSize = AdSize.getLargeAnchoredAdaptiveBannerAdSize(this, 360);\n BannerAdRequest adRequest = new BannerAdRequest.Builder(\"AD_UNIT_ID\", adSize).build();\n adView.loadAd(\n adRequest,\n new AdLoadCallback<BannerAd>() {\n @Override\n public void onAdLoaded(@NonNull BannerAd ad) {\n ad.setAdEventCallback(\n new BannerAdEventCallback() {\n @Override\n public void onAdImpression() {\n Log.d(TAG, \"Banner ad recorded an impression.\");\n }\n\n @Override\n public void onAdClicked() {\n Log.d(TAG, \"Banner ad clicked.\");\n }\n });\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n Log.e(TAG, \"Banner ad failed to load: \" + adError);\n }\n });\n }\n}\n```\n\nExample:\n```text\nadView.adListener = object : AdListener() {\n override fun onAdLoaded() {\n // Called when an ad has loaded.\n }\n\n override fun onAdFailedToLoad(adError : LoadAdError) {\n // Called when ad fails to load.\n }\n}\n```\n\nExample:\n```text\nadView.setAdListener(\n new AdListener() {\n @Override\n public void onAdLoaded() {\n // Called when an ad has loaded.\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Called when ad fails to load.\n }\n });\n```\n\nExample:\n```devsite-click-to-copy\nadView.loadAd(\n BannerAdRequest.Builder(\"AD_UNIT_ID\", adSize).build(),\n object : AdLoadCallback<BannerAd> {\n override fun onAdLoaded(ad: BannerAd) {\n // Called when an ad has loaded.\n ad.adEventCallback =\n object : BannerAdEventCallback {}\n\n ad.bannerAdRefreshCallback =\n object : BannerAdRefreshCallback {\n // Set the ad refresh callbacks.\n override fun onAdRefreshed() {\n // Called when the ad refreshes.\n }\n\n override fun onAdFailedToRefresh(adError: LoadAdError) {\n // Called when the ad fails to refresh.\n }\n }\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // Called when ad fails to load.\n }\n }\n)\n```\n\nExample:\n```devsite-click-to-copy\nadView.loadAd(\n new BannerAdRequest.Builder(\"AD_UNIT_ID\", adSize).build(),\n new AdLoadCallback<BannerAd>() {\n @Override\n public void onAdLoaded(@NonNull BannerAd ad) {\n // Called when an ad has loaded.\n ad.setAdEventCallback(new BannerAdEventCallback() {});\n\n ad.setBannerAdRefreshCallback(\n // Set the ad refresh callbacks.\n new BannerAdRefreshCallback() {\n @Override\n public void onAdRefreshed() {\n // Called when the ad refreshes.\n }\n\n @Override\n public void onAdFailedToRefresh(@NonNull LoadAdError adError) {\n // Called when the ad fails to refresh.\n }\n });\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Called when ad fails to load.\n }\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.918Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":271,"estimatedTokens":2010}}506{"id":"doc-inline_adaptive_banner_ads_android_google_for_de-56608e9e","source":"documentation","title":"Inline adaptive banner ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/banner/inline-adaptive","text":"Example:\n```text\nprivate fun loadAd() {\n // Create an inline adaptive ad size. 320 is a placeholder value.\n // Replace 320 with your banner container width.\n val adSize = AdSize.getCurrentOrientationInlineAdaptiveBannerAdSize(this, 320)\n\n // Step 1 - Create a BannerAdRequest object with ad unit ID and size.\n val adRequest = BannerAdRequest.Builder(\"AD_UNIT_ID\", adSize).build()\n\n // Step 2 - Load the ad.\n BannerAd.load(\n adRequest,\n object : AdLoadCallback<BannerAd> {\n override fun onAdLoaded(ad: BannerAd) {\n // Assign the loaded ad to the BannerAd object.\n bannerAd = ad\n // Step 3 - Call BannerAd.getView() to get the View and add it\n // to view hierarchy on the UI thread.\n activity?.runOnUiThread {\n binding.bannerViewContainer.addView(ad.getView(requireActivity()))\n }\n }\n\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n bannerAd = null\n }\n }\n )\n}\n```\n\nExample:\n```text\nprivate void loadAd() {\n // Create an inline adaptive ad size. 320 is a placeholder value.\n // Replace 320 with your banner container width.\n AdSize adSize = AdSize.getCurrentOrientationInlineAdaptiveBannerAdSize(this, 320);\n\n // Step 1 - Create a BannerAdRequest object with ad unit ID and size.\n BannerAdRequest adRequest = new BannerAdRequest.Builder(\"AD_UNIT_ID\",\n adSize).build();\n\n // Step 2 - Load the ad.\n BannerAd.load(\n adRequest,\n new AdLoadCallback<BannerAd>() {\n @Override\n public void onAdLoaded(@NonNull BannerAd ad) {\n // Assign the loaded ad to the BannerAd object.\n bannerAd = ad;\n // Step 3 - Call BannerAd.getView() to get the View and add it\n // to view hierarchy on the UI thread.\n if (getActivity() != null) {\n getActivity()\n .runOnUiThread(() ->\n binding.bannerViewContainer.addView(ad.getView(getActivity())));\n }\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n bannerAd = null;\n }\n });\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.919Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":69,"estimatedTokens":532}}507{"id":"doc-migrate_rewarded_ads_android_google_for_develope-c63c2bb7","source":"documentation","title":"Migrate rewarded ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/migration/migrate-rewarded","text":"Example:\n```text\nRewardedAd.load(\n this@RewardedActivity,\n \"AD_UNIT_ID\",\n AdRequest.Builder().build(),\n object : RewardedAdLoadCallback() {\n override fun onAdLoaded(ad: RewardedAd) {\n // Called when an ad has loaded.\n ad.fullScreenContentCallback =\n object : FullScreenContentCallback() {\n }\n rewardedAd = ad\n }\n\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // Called when ad fails to load.\n }\n }\n)\n```\n\nExample:\n```text\nRewardedAd.load(\n this,\n \"AD_UNIT_ID\",\n new AdRequest.Builder().build(),\n new RewardedAdLoadCallback() {\n @Override\n public void onAdLoaded(@NonNull RewardedAd ad) {\n // Called when an ad has loaded.\n ad.setFullScreenContentCallback(new FullScreenContentCallback() {});\n rewardedAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {\n // Called when ad fails to load.\n }\n }\n);\n```\n\nExample:\n```text\nRewardedAd.load(\n AdRequest.Builder(\"AD_UNIT_ID\").build(),\n object : AdLoadCallback<RewardedAd> {\n override fun onAdLoaded(ad: RewardedAd) {\n // Called when an ad has loaded.\n ad.adEventCallback =\n object : RewardedAdEventCallback {\n }\n rewardedAd = ad\n }\n\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // Called when ad fails to load.\n }\n }\n)\n```\n\nExample:\n```text\nRewardedAd.load(\n new AdRequest.Builder(\"AD_UNIT_ID\").build(),\n new AdLoadCallback<RewardedAd>() {\n @Override\n public void onAdLoaded(@NonNull RewardedAd ad) {\n // Called when an ad has loaded.\n ad.setAdEventCallback(new RewardedAdEventCallback() {});\n rewardedAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Called when ad fails to load.\n }\n });\n```\n\nExample:\n```text\nrewardedAd?.show(\n this@RewardedActivity,\n object : OnUserEarnedRewardListener {\n override fun onUserEarnedReward(rewardItem: RewardItem) {\n // User earned the reward.\n val rewardAmount = rewardItem.amount\n val rewardType = rewardItem.type\n }\n }\n)\n```\n\nExample:\n```text\nrewardedAd.show(\n this,\n new OnUserEarnedRewardListener() {\n @Override\n public void onUserEarnedReward(@NonNull RewardItem rewardItem) {\n // User earned the reward.\n int rewardAmount = rewardItem.getAmount();\n String rewardType = rewardItem.getType();\n }\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.919Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":112,"estimatedTokens":618}}508{"id":"doc-migrate_interstitial_ads_android_google_for_deve-cab731a6","source":"documentation","title":"Migrate interstitial ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/migration/migrate-interstitial","text":"Example:\n```text\nInterstitialAd.load(\n this@InterstitialActivity,\n \"AD_UNIT_ID\",\n AdRequest.Builder().build(),\n object : InterstitialAdLoadCallback() {\n override fun onAdLoaded(ad: InterstitialAd) {\n // Called when an ad has loaded.\n ad.fullScreenContentCallback =\n object : FullScreenContentCallback() {\n }\n interstitialAd = ad\n }\n\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // Called when ad fails to load.\n }\n }\n)\n```\n\nExample:\n```text\nInterstitialAd.load(\n this,\n \"AD_UNIT_ID\",\n new AdRequest.Builder().build(),\n new InterstitialAdLoadCallback() {\n @Override\n public void onAdLoaded(@NonNull InterstitialAd ad) {\n // Called when an ad has loaded.\n ad.setFullScreenContentCallback(new FullScreenContentCallback() {});\n interstitialAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {\n // Called when ad fails to load.\n }\n }\n);\n```\n\nExample:\n```text\nInterstitialAd.load(\n AdRequest.Builder(\"AD_UNIT_ID\").build(),\n object : AdLoadCallback<InterstitialAd> {\n override fun onAdLoaded(ad: InterstitialAd) {\n // Called when an ad has loaded.\n ad.adEventCallback =\n object : InterstitialAdEventCallback {\n }\n interstitialAd = ad\n }\n\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // Called when ad fails to load.\n }\n }\n)\n```\n\nExample:\n```text\nInterstitialAd.load(\n new AdRequest.Builder(\"AD_UNIT_ID\").build(),\n new AdLoadCallback<InterstitialAd>() {\n @Override\n public void onAdLoaded(@NonNull InterstitialAd ad) {\n // Called when an ad has loaded.\n ad.setAdEventCallback(new InterstitialAdEventCallback() {});\n interstitialAd = ad;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Called when ad fails to load.\n }\n });\n```\n\nExample:\n```text\ninterstitialAd?.show(this@InterstitialActivity)\n```\n\nExample:\n```text\ninterstitialAd.show(this);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.920Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":94,"estimatedTokens":516}}509{"id":"doc-set_up_interstitial_ads_android_google_for_devel-b27518a7","source":"documentation","title":"Set up interstitial ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/interstitial","text":"Example:\n```text\nprivate fun startPreloading(adUnitId: String) {\n // Call start() once after SDK initialization.\n // Preload only one ad unit per format to optimize performance.\n val adRequest = AdRequest.Builder(adUnitId).build()\n val preloadConfig = PreloadConfiguration(adRequest)\n InterstitialAdPreloader.start(adUnitId, preloadConfig)\n}\n```\n\nExample:\n```text\nprivate void startPreloading(String adUnitId) {\n // Call start() once after SDK initialization.\n // Preload only one ad unit per format to optimize performance.\n AdRequest adRequest = new AdRequest.Builder(adUnitId).build();\n PreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest);\n InterstitialAdPreloader.start(adUnitId, preloadConfig);\n}\n```\n\nExample:\n```text\nprivate fun pollAndShowAd(activity: Activity, adUnitId: String) {\n // Polling returns the next available ad and loads another ad in the background.\n val ad = InterstitialAdPreloader.pollAd(adUnitId)\n if (ad == null) {\n Log.e(TAG, \"Interstitial ad is not available.\")\n return\n }\n\n // Interact with the ad object as needed.\n Log.d(TAG, \"Interstitial ad response info: ${ad.getResponseInfo()}\")\n ad.adEventCallback =\n object : InterstitialAdEventCallback {\n override fun onAdImpression() {\n Log.d(TAG, \"Interstitial ad recorded an impression.\")\n }\n }\n ad.show(activity)\n}\n```\n\nExample:\n```text\nprivate void pollAndShowAd(Activity activity, String adUnitId) {\n // Polling returns the next available ad and loads another ad in the background.\n final InterstitialAd ad = InterstitialAdPreloader.pollAd(adUnitId);\n\n // Interact with the ad object as needed.\n if (ad == null) {\n Log.e(TAG, \"Interstitial ad is not available.\");\n return;\n }\n\n Log.d(TAG, \"Interstitial ad response info: \" + ad.getResponseInfo());\n ad.setAdEventCallback(\n new InterstitialAdEventCallback() {\n @Override\n public void onAdImpression() {\n Log.d(TAG, \"Interstitial ad recorded an impression.\");\n }\n });\n\n // Show the ad.\n ad.show(activity);\n}\n```\n\nExample:\n```text\n// Listen for ad events.\nval ad = interstitialAd\nif (ad == null) {\n Log.e(TAG, \"Interstitial ad is not ready yet.\")\n return\n}\n\nad.adEventCallback =\n object : InterstitialAdEventCallback {\n override fun onAdShowedFullScreenContent() {\n // Interstitial ad did show.\n }\n\n override fun onAdDismissedFullScreenContent() {\n // Interstitial ad did dismiss.\n interstitialAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(\n fullScreenContentError: FullScreenContentError\n ) {\n // Interstitial ad failed to show.\n Log.e(TAG, \"Interstitial ad failed to show: ${fullScreenContentError.message}\")\n }\n\n override fun onAdImpression() {\n // Interstitial ad did record an impression.\n }\n\n override fun onAdClicked() {\n // Interstitial ad did record a click.\n }\n }\n```\n\nExample:\n```text\n// Listen for ad events.\nif (interstitialAd == null) {\n Log.e(TAG, \"Interstitial ad is not ready yet.\");\n return;\n}\n\ninterstitialAd.setAdEventCallback(\n new InterstitialAdEventCallback() {\n @Override\n public void onAdShowedFullScreenContent() {\n // Interstitial ad did show.\n }\n\n @Override\n public void onAdDismissedFullScreenContent() {\n // Interstitial ad did dismiss.\n interstitialAd = null;\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(\n @NonNull FullScreenContentError fullScreenContentError) {\n // Interstitial ad failed to show.\n Log.e(TAG, \"Interstitial ad failed to show: \" + fullScreenContentError.getMessage());\n }\n\n @Override\n public void onAdImpression() {\n // Interstitial ad did record an impression.\n }\n\n @Override\n public void onAdClicked() {\n // Interstitial ad did record a click.\n }\n });\n```\n\nExample:\n```text\nval preloadCallback =\n // [Important] Don't call ad preloader start() or pollAd() within the PreloadCallback.\n object : PreloadCallback {\n override fun onAdFailedToPreload(preloadId: String, adError: LoadAdError) {\n Log.d(\n TAG,\n (\"Interstitial preload ad $preloadId failed to load with error: ${adError.message}\"),\n )\n }\n\n override fun onAdsExhausted(preloadId: String) {\n Log.i(TAG, \"Interstitial preload ad $preloadId is not available\")\n // [Important] Don't call ad preloader start() or pollAd() from onAdsExhausted.\n }\n\n override fun onAdPreloaded(preloadId: String, responseInfo: ResponseInfo) {\n Log.i(TAG, \"Interstitial preload ad $preloadId is available\")\n }\n }\nval adRequest = AdRequest.Builder(adUnitId).build()\nval preloadConfig = PreloadConfiguration(adRequest)\nInterstitialAdPreloader.start(adUnitId, preloadConfig, preloadCallback)\n```\n\nExample:\n```text\nPreloadCallback preloadCallback =\n // [Important] Don't call ad preloader start() or pollAd() within the PreloadCallback.\n new PreloadCallback() {\n @Override\n public void onAdFailedToPreload(@NonNull String preloadId, @NonNull LoadAdError adError) {\n Log.d(\n TAG,\n String.format(\n \"Interstitial preload ad %s failed to load with error: %s\",\n preloadId, adError.getMessage()));\n // [Optional] Get the error response info for additional details.\n // ResponseInfo responseInfo = adError.getResponseInfo();\n }\n\n @Override\n public void onAdsExhausted(@NonNull String preloadId) {\n Log.i(TAG, \"Interstitial preload ad \" + preloadId + \" is not available\");\n // [Important] Don't call ad preloader start() or pollAd() from onAdsExhausted.\n }\n\n @Override\n public void onAdPreloaded(@NonNull String preloadId, @NonNull ResponseInfo responseInfo) {\n Log.i(TAG, \"Interstitial preload ad \" + preloadId + \" is available\");\n }\n };\n\nAdRequest adRequest = new AdRequest.Builder(adUnitId).build();\nPreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest);\nInterstitialAdPreloader.start(adUnitId, preloadConfig, preloadCallback);\n```\n\nExample:\n```text\nprivate fun isAdAvailable(adUnitId: String): Boolean {\n return InterstitialAdPreloader.isAdAvailable(adUnitId)\n}\n```\n\nExample:\n```text\nprivate boolean isAdAvailable(String adUnitId) {\n return InterstitialAdPreloader.isAdAvailable(adUnitId);\n}\n```\n\nExample:\n```text\nprivate fun setBufferSize(adUnitId: String) {\n val adRequest = AdRequest.Builder(adUnitId).build()\n // Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\n val preloadConfig = PreloadConfiguration(adRequest, bufferSize = 2)\n InterstitialAdPreloader.start(adUnitId, preloadConfig)\n}\n```\n\nExample:\n```text\nprivate void setBufferSize(String adUnitId) {\n AdRequest adRequest = new AdRequest.Builder(adUnitId).build();\n // Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\n PreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest, 2);\n InterstitialAdPreloader.start(adUnitId, preloadConfig);\n}\n```\n\nExample:\n```text\nprivate fun stopPreloading(adUnitId: String) {\n // Stops the preloading and destroy preloaded ads.\n InterstitialAdPreloader.destroy(adUnitId)\n}\n```\n\nExample:\n```text\nprivate void stopPreloading(String adUnitId) {\n // Stops the preloading and destroy preloaded ads.\n InterstitialAdPreloader.destroy(adUnitId);\n}\n```\n\nExample:\n```text\nval responseInfo = InterstitialAdPreloader.peekAdResponseInfo(preloadId)\nif (responseInfo == null) {\n Log.e(TAG, \"Failed to peek ad response info.\")\n return\n}\n\nLog.d(TAG, \"Peeked ad response ID: ${responseInfo.responseId}\")\n```\n\nExample:\n```text\nResponseInfo responseInfo = InterstitialAdPreloader.peekAdResponseInfo(preloadId);\nif (responseInfo == null) {\n Log.e(TAG, \"Failed to peek ad response info.\");\n return;\n}\n\nLog.d(TAG, \"Peeked ad response ID: \" + responseInfo.getResponseId());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.921Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":279,"estimatedTokens":1987}}510{"id":"doc-set_up_app_open_ads_android_google_for_developer-e1a255d4","source":"documentation","title":"Set up app open ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/app-open","text":"Example:\n```text\nval adRequest = AdRequest.Builder(adUnitId).build()\nval preloadConfig = PreloadConfiguration(adRequest)\nAppOpenAdPreloader.start(adUnitId, preloadConfig)\n```\n\nExample:\n```text\nAdRequest adRequest = new AdRequest.Builder(adUnitId).build();\nPreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest);\nAppOpenAdPreloader.start(adUnitId, preloadConfig);\n```\n\nExample:\n```text\nprivate fun pollAndShowAd(activity: Activity, adUnitId: String) {\n // Polling returns the next available ad and loads another ad in the background.\n val ad = AppOpenAdPreloader.pollAd(adUnitId)\n if (ad == null) {\n Log.e(TAG, \"App open ad is not available.\")\n return\n }\n\n // Interact with the ad object as needed.\n Log.d(TAG, \"App open ad response info: ${ad.getResponseInfo()}\")\n ad.adEventCallback =\n object : AppOpenAdEventCallback {\n override fun onAdImpression() {\n Log.d(TAG, \"App open ad recorded an impression.\")\n }\n }\n ad.show(activity)\n}\n```\n\nExample:\n```text\nprivate void pollAndShowAd(Activity activity, String adUnitId) {\n // Polling returns the next available ad and loads another ad in the background.\n AppOpenAd ad = AppOpenAdPreloader.pollAd(adUnitId);\n\n // Interact with the ad object as needed.\n if (ad == null) {\n Log.e(TAG, \"App open ad is not available.\");\n return;\n }\n\n Log.d(TAG, \"App open ad response info: \" + ad.getResponseInfo());\n ad.setAdEventCallback(\n new AppOpenAdEventCallback() {\n @Override\n public void onAdImpression() {\n Log.d(TAG, \"App open ad recorded an impression.\");\n }\n });\n\n // Show the ad.\n ad.show(activity);\n}\n```\n\nExample:\n```text\nprivate fun listenToAdEvents() {\n // Listen for ad events.\n val ad = appOpenAd\n if (ad == null) {\n Log.e(TAG, \"App open ad is not ready yet.\")\n return\n }\n\n ad.adEventCallback =\n object : AppOpenAdEventCallback {\n override fun onAdShowedFullScreenContent() {\n // App open ad did show.\n }\n\n override fun onAdDismissedFullScreenContent() {\n // App open ad did dismiss.\n appOpenAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(\n fullScreenContentError: FullScreenContentError\n ) {\n // App open ad failed to show.\n Log.e(TAG, \"App open ad failed to show: ${fullScreenContentError.message}\")\n }\n\n override fun onAdImpression() {\n // App open ad did record an impression.\n }\n\n override fun onAdClicked() {\n // App open ad did record a click.\n }\n }\n}\n```\n\nExample:\n```text\nprivate void listenToAdEvents() {\n // Listen for ad events.\n if (appOpenAd == null) {\n Log.e(TAG, \"App open ad is not ready yet.\");\n return;\n }\n\n appOpenAd.setAdEventCallback(\n new AppOpenAdEventCallback() {\n @Override\n public void onAdShowedFullScreenContent() {\n // App open ad did show.\n }\n\n @Override\n public void onAdDismissedFullScreenContent() {\n // App open ad did dismiss.\n appOpenAd = null;\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(\n @NonNull FullScreenContentError fullScreenContentError) {\n // App open ad failed to show.\n Log.e(TAG, \"App open ad failed to show: \" + fullScreenContentError.getMessage());\n }\n\n @Override\n public void onAdImpression() {\n // App open ad did record an impression.\n }\n\n @Override\n public void onAdClicked() {\n // App open ad did record a click.\n }\n });\n}\n```\n\nExample:\n```text\nval preloadCallback =\n object : PreloadCallback {\n override fun onAdFailedToPreload(preloadId: String, adError: LoadAdError) {\n Log.d(TAG, \"App open preload ad $preloadId failed to load with error: ${adError.message}\")\n }\n\n override fun onAdsExhausted(preloadId: String) {\n Log.i(TAG, \"App open preload ad $preloadId is not available\")\n }\n\n override fun onAdPreloaded(preloadId: String, responseInfo: ResponseInfo) {\n Log.i(TAG, \"App open preload ad $preloadId is available\")\n }\n }\nval adRequest = AdRequest.Builder(adUnitId).build()\nval preloadConfig = PreloadConfiguration(adRequest)\nAppOpenAdPreloader.start(adUnitId, preloadConfig, preloadCallback)\n```\n\nExample:\n```text\nPreloadCallback preloadCallback =\n new PreloadCallback() {\n @Override\n public void onAdFailedToPreload(@NonNull String preloadId, @NonNull LoadAdError adError) {\n Log.d(\n TAG,\n String.format(\n \"App open preload ad %s failed to load with error: %s\",\n preloadId, adError.getMessage()));\n }\n\n @Override\n public void onAdsExhausted(@NonNull String preloadId) {\n Log.i(TAG, String.format(\"App open preload ad %s is not available\", preloadId));\n }\n\n @Override\n public void onAdPreloaded(@NonNull String preloadId, @NonNull ResponseInfo responseInfo) {\n Log.i(TAG, String.format(\"App open preload ad %s is available\", preloadId));\n }\n };\nAdRequest adRequest = new AdRequest.Builder(adUnitId).build();\nPreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest);\nAppOpenAdPreloader.start(adUnitId, preloadConfig, preloadCallback);\n```\n\nExample:\n```text\nprivate fun isAdAvailable(adUnitId: String): Boolean {\n return AppOpenAdPreloader.isAdAvailable(adUnitId)\n}\n```\n\nExample:\n```text\nprivate boolean isAdAvailable(String adUnitId) {\n return AppOpenAdPreloader.isAdAvailable(adUnitId);\n}\n```\n\nExample:\n```text\nval adRequest = AdRequest.Builder(adUnitId).build()\n// Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\nval preloadConfig = PreloadConfiguration(adRequest, bufferSize = 2)\nAppOpenAdPreloader.start(adUnitId, preloadConfig)\n```\n\nExample:\n```text\nAdRequest adRequest = new AdRequest.Builder(adUnitId).build();\n// Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\nPreloadConfiguration preloadConfig = new PreloadConfiguration(adRequest, 2);\nAppOpenAdPreloader.start(adUnitId, preloadConfig);\n```\n\nExample:\n```text\nprivate fun stopPreloading(adUnitId: String) {\n // Stops the preloading and destroy preloaded ads.\n AppOpenAdPreloader.destroy(adUnitId)\n}\n```\n\nExample:\n```text\nprivate void stopPreloading(String adUnitId) {\n // Stops the preloading and destroy preloaded ads.\n AppOpenAdPreloader.destroy(adUnitId);\n}\n```\n\nExample:\n```text\nval responseInfo = AppOpenAdPreloader.peekAdResponseInfo(preloadId)\nif (responseInfo == null) {\n Log.e(TAG, \"Failed to peek ad response info.\")\n return\n}\n\nLog.d(TAG, \"Peeked ad response ID: ${responseInfo.responseId}\")\n```\n\nExample:\n```text\nResponseInfo responseInfo = AppOpenAdPreloader.peekAdResponseInfo(preloadId);\nif (responseInfo == null) {\n Log.e(TAG, \"Failed to peek ad response info.\");\n return;\n}\n\nLog.d(TAG, \"Peeked ad response ID: \" + responseInfo.getResponseId());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.922Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":261,"estimatedTokens":1738}}511{"id":"doc-set_up_banner_ads_android_google_for_developers-4639e3b6","source":"documentation","title":"Set up banner ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/banner","text":"Example:\n```text\nprivate fun createAdView(adViewContainer: FrameLayout, activity: Activity) {\n val adView = AdView(activity)\n adViewContainer.addView(adView)\n}\nBannerSnippets.kt\n```\n\nExample:\n```text\nprivate void createAdView(FrameLayout adViewContainer, Activity activity) {\n AdView adView = new AdView(activity);\n adViewContainer.addView(adView);\n}\nBannerSnippets.java\n```\n\nExample:\n```text\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n xmlns:tools=\"http://schemas.android.com/tools\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\">\n\n <com.google.android.libraries.ads.mobile.sdk.banner.AdView\n android:id=\"@+id/adView\"\n android:layout_width=\"wrap_content\"\n android:layout_height=\"wrap_content\"\n app:layout_constraintBottom_toBottomOf=\"parent\"\n app:layout_constraintEnd_toEndOf=\"parent\"\n app:layout_constraintStart_toStartOf=\"parent\" />\n</androidx.constraintlayout.widget.ConstraintLayout>\n```\n\nExample:\n```text\n// Initialize required variables.\nval context = LocalContext.current\nvar bannerAdState by remember { mutableStateOf<BannerAd?>(null) }\n\n// The AdView is placed at the bottom of the screen.\nColumn(modifier = modifier.fillMaxSize(), verticalArrangement = Arrangement.Bottom) {\n bannerAdState?.let { bannerAd ->\n Box(modifier = Modifier.fillMaxWidth()) {\n // Display the ad within an AndroidView.\n AndroidView(\n modifier = modifier.wrapContentSize(),\n factory = { bannerAd.getView(requireActivity()) },\n )\n }\n }\n}ComposeBannerFragment.kt\n```\n\nExample:\n```text\nprivate fun loadBannerAd(adView: AdView, activity: Activity) {\n // Get a BannerAdRequest for a 360 wide large anchored adaptive banner ad.\n val adSize = AdSize.getLargeAnchoredAdaptiveBannerAdSize(activity, 360)\n val adRequest = BannerAdRequest.Builder(AD_UNIT_ID, adSize).build()\n\n adView.loadAd(\n adRequest,\n object : AdLoadCallback<BannerAd> {\n override fun onAdLoaded(ad: BannerAd) {\n Log.d(TAG, \"Banner ad loaded.\")\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n Log.d(TAG, \"Banner ad failed to load: $adError\")\n }\n },\n )\n}\nBannerSnippets.kt\n```\n\nExample:\n```text\nprivate void loadBannerAd(AdView adView, Activity activity) {\n // Get a BannerAdRequest for a 360 wide large anchored adaptive banner ad.\n AdSize adSize = AdSize.getLargeAnchoredAdaptiveBannerAdSize(activity, 360);\n BannerAdRequest adRequest = new BannerAdRequest.Builder(AD_UNIT_ID, adSize).build();\n\n adView.loadAd(\n adRequest,\n new AdLoadCallback<BannerAd>() {\n @Override\n public void onAdLoaded(@NonNull BannerAd bannerAd) {\n Log.d(TAG, \"Banner ad loaded.\");\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n Log.d(TAG, \"Banner ad failed to load: \" + adError);\n }\n });\n}\nBannerSnippets.java\n```\n\nExample:\n```text\n// Request an large anchored adaptive banner with a width of 360.\nval adSize = AdSize.getLargeAnchoredAdaptiveBannerAdSize(requireContext(), 360)\n\n// Load the ad when the screen is active.\nval coroutineScope = rememberCoroutineScope()\nval isPreviewMode = LocalInspectionMode.current\nLaunchedEffect(context) {\n bannerAdState?.destroy()\n if (!isPreviewMode) {\n coroutineScope.launch {\n when (val result = BannerAd.load(BannerAdRequest.Builder(AD_UNIT_ID, adSize).build())) {\n is AdLoadResult.Success -> {\n bannerAdState = result.ad\n }\n is AdLoadResult.Failure -> {\n showToast(\"Banner failed to load.\")\n Log.w(Constant.TAG, \"Banner ad failed to load: $result.error\")\n }\n }\n }\n }\n}ComposeBannerFragment.kt\n```\n\nExample:\n```text\n// Remove banner from view hierarchy.\nval parentView = adView?.parent\nif (parentView is ViewGroup) {\n parentView.removeView(adView)\n}\n\n// Destroy the banner ad resources.\nadView?.destroy()\n\n// Drop reference to the banner ad.\nadView = null\n```\n\nExample:\n```text\n// Remove banner from view hierarchy.\nif (adView.getParent() instanceof ViewGroup) {\n ((ViewGroup) adView.getParent()).removeView(adView);\n}\n// Destroy the banner ad resources.\nadView.destroy();\n// Drop reference to the banner ad.\nadView = null;\n```\n\nExample:\n```text\n// Destroy the ad when the screen is disposed.\nDisposableEffect(Unit) { onDispose { bannerAdState?.destroy() } }ComposeBannerFragment.kt\n```\n\nExample:\n```text\noverride fun onAdLoaded(ad: BannerAd) {\n ad.adEventCallback =\n object : BannerAdEventCallback {\n override fun onAdImpression() {\n // Banner ad recorded an impression.\n Log.d(TAG, \"Banner ad recorded an impression.\")\n }\n\n override fun onAdClicked() {\n // Banner ad recorded a click.\n Log.d(TAG, \"Banner ad clicked.\")\n }\n\n override fun onAdShowedFullScreenContent() {\n // Banner ad showed.\n Log.d(TAG, \"Banner ad showed full screen content.\")\n }\n\n override fun onAdDismissedFullScreenContent() {\n // Banner ad dismissed.\n Log.d(TAG, \"Banner ad dismissed full screen content.\")\n }\n\n override fun onAdFailedToShowFullScreenContent(\n fullScreenContentError: FullScreenContentError\n ) {\n // Banner ad failed to show.\n Log.w(TAG, \"Banner ad failed to show full screen content: $fullScreenContentError\")\n }\n }\n}\nBannerSnippets.kt\n```\n\nExample:\n```text\n@Override\npublic void onAdLoaded(@NonNull BannerAd bannerAd) {\n bannerAd.setAdEventCallback(\n new BannerAdEventCallback() {\n @Override\n public void onAdImpression() {\n // Banner ad recorded an impression.\n Log.d(TAG, \"Banner ad recorded an impression.\");\n }\n\n @Override\n public void onAdClicked() {\n // Banner ad recorded a click.\n Log.d(TAG, \"Banner ad clicked.\");\n }\n\n @Override\n public void onAdShowedFullScreenContent() {\n // Banner ad showed.\n Log.d(TAG, \"Banner ad showed full screen content.\");\n }\n\n @Override\n public void onAdDismissedFullScreenContent() {\n // Banner ad dismissed.\n Log.d(TAG, \"Banner ad dismissed full screen content.\");\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(\n @NonNull FullScreenContentError fullScreenContentError) {\n // Banner ad failed to show.\n Log.w(\n TAG,\n \"Banner ad failed to show full screen content: \" + fullScreenContentError);\n }\n });\n}\nBannerSnippets.java\n```\n\nExample:\n```text\nBannerAd.load(\n BannerAdRequest.Builder(\"ca-app-pub-3940256099942544/9214589741\", adSize).build(),\n object : AdLoadCallback<BannerAd> {\n override fun onAdLoaded(ad: BannerAd) {\n ad.bannerAdRefreshCallback =\n object : BannerAdRefreshCallback {\n // Set the ad refresh callbacks.\n override fun onAdRefreshed() {\n // Called when the ad refreshes.\n }\n\n override fun onAdFailedToRefresh(loadAdError: LoadAdError) {\n // Called when the ad fails to refresh.\n }\n }\n\n // ...\n }\n }\n)\n```\n\nExample:\n```text\nBannerAd.load(\n new BannerAdRequest.Builder(\"ca-app-pub-3940256099942544/9214589741\", adSize).build(),\n new AdLoadCallback<BannerAd>() {\n @Override\n public void onAdLoaded(@NonNull BannerAd ad) {\n ad.setBannerAdRefreshCallback(\n // Set the ad refresh callbacks.\n new BannerAdRefreshCallback() {\n @Override\n public void onAdRefreshed() {\n // Called when the ad refreshes.\n }\n\n @Override\n public void onAdFailedToRefresh(@NonNull LoadAdError adError) {\n // Called when the ad fails to refresh.\n }\n });\n // ...\n }\n });\n```\n\nExample:\n```text\n<application android:hardwareAccelerated=\"true\">\n <!-- For activities that use ads, hardwareAcceleration should be true. -->\n <activity android:hardwareAccelerated=\"true\" />\n <!-- For activities that don't use ads, hardwareAcceleration can be false. -->\n <activity android:hardwareAccelerated=\"false\" />\n</application>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.924Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":303,"estimatedTokens":2104}}512{"id":"doc-enable_test_ads_android_google_for_developers-1a449ebf","source":"documentation","title":"Enable test ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/test-ads","text":"Example:\n```devsite-click-to-copy\nI/Ads: Use RequestConfiguration.Builder.setTestDeviceIds(Arrays.asList(\"33BE2250B43518CCDA7DE426D04EE231\"))\nto get test ads on this device.\"\n```\n\nExample:\n```text\nList<String> testDeviceIds = Arrays.asList(\"33BE2250B43518CCDA7DE426D04EE231\");\nRequestConfiguration configuration =\nnew RequestConfiguration.Builder().setTestDeviceIds(testDeviceIds).build();\nMobileAds.setRequestConfiguration(configuration);\n```\n\nExample:\n```text\nval testDeviceIds = Arrays.asList(\"33BE2250B43518CCDA7DE426D04EE231\")\nval configuration = RequestConfiguration.Builder().setTestDeviceIds(testDeviceIds).build()\nMobileAds.setRequestConfiguration(configuration)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.924Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":22,"estimatedTokens":173}}513{"id":"doc-integrate_mintegral_with_mediation_unity_google_-d0a86d54","source":"documentation","title":"Integrate Mintegral with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/mintegral","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.mintegral\n```\n\nExample:\n```text\ncom.mbridge.msdk\ncom.google.ads.mediation.mintegral.MintegralMediationAdapter\n```\n\nExample:\n```text\nGADMediationAdapterMintegral\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.925Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":17,"estimatedTokens":60}}514{"id":"doc-integrate_ly_ads_network_with_mediation_unity_go-a24c5330","source":"documentation","title":"Integrate LY Ads Network with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/line","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.line\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api;\nusing GoogleMobileAds.Mediation.Line.Api;\n// ...\n\nvar adRequest = new AdRequest();\nvar lineExtras = new LineMediationExtras();\nlineExtras.SetEnableAdSound(true);\nadRequest.MediationExtras.Add(lineExtras);\n```\n\nExample:\n```text\ncom.line.ads\ncom.google.ads.mediation.line.LineMediationAdapter\n```\n\nExample:\n```text\nGADMediationAdapterLine\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.926Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":29,"estimatedTokens":118}}515{"id":"doc-migrate_native_ads_android_google_for_developers-bcdebc2b","source":"documentation","title":"Migrate native ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/next-gen/migration/migrate-native","text":"Example:\n```text\nval adLoader =\n AdLoader.Builder(this, AD_UNIT_ID)\n .forNativeAd(object : NativeAd.OnNativeAdLoadedListener {\n override fun onNativeAdLoaded(nativeAd: NativeAd) {\n // Native ad loaded.\n }\n })\n .withAdListener(\n object : AdListener() {\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // Native ad failed to load.\n }\n }\n )\n .build()\n\nadLoader.loadAd(AdRequest.Builder().build())\n```\n\nExample:\n```text\nAdLoader adLoader = new AdLoader.Builder(this, AD_UNIT_ID)\n .forNativeAd(new NativeAd.OnNativeAdLoadedListener() {\n @Override\n public void onNativeAdLoaded(NativeAd nativeAd) {\n // Native ad loaded.\n }\n })\n .withAdListener(new AdListener() {\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {\n // Native ad failed to load.\n }\n })\n .build();\n\nadLoader.loadAd(new AdRequest.Builder().build());\n```\n\nExample:\n```text\nNativeAdLoader.load(\n NativeAdRequest.Builder(AD_UNIT_ID, listOf(NativeAd.NativeAdType.NATIVE)).build(),\n object : NativeAdLoaderCallback {\n override fun onNativeAdLoaded(nativeAd: NativeAd) {\n // Native ad loaded.\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // Native ad failed to load.\n }\n }\n)\n```\n\nExample:\n```text\nNativeAdLoader.load(\n new NativeAdRequest.Builder(AD_UNIT_ID, List.of(NativeAd.NativeAdType.NATIVE)).build(),\n new NativeAdLoaderCallback() {\n @Override\n public void onNativeAdLoaded(NativeAd nativeAd) {\n // Native ad loaded.\n }\n\n @Override\n public void onAdFailedToLoad(LoadAdError adError) {\n // Native ad failed to load.\n }\n }\n);\n```\n\nExample:\n```text\nval adLoader =\n AdLoader.Builder(this, AD_UNIT_ID)\n .forCustomFormatAd(CUSTOM_FORMAT_ID,\n object: NativeCustomFormatAd.OnCustomFormatAdLoadedListener {\n override fun onCustomFormatAdLoaded(nativeCustomFormatAd: NativeCustomFormatAd) {\n // Custom native ad loaded.\n }\n },\n object: NativeCustomFormatAd.OnCustomClickListener {\n override fun onCustomClick(\n nativeCustomFormatAd: NativeCustomFormatAd,\n assetName: String\n ) {\n // Custom native ad recorded a click.\n }\n })\n .withAdListener(\n object : AdListener() {\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // Custom native ad failed to load.\n }\n }\n )\n .build()\n\nadLoader.loadAd(AdRequest.Builder().build())\n```\n\nExample:\n```text\nAdLoader adLoader = new AdLoader.Builder(this, AD_UNIT_ID)\n .forCustomFormatAd(CUSTOM_FORMAT_ID,\n new NativeCustomFormatAd.OnCustomFormatAdLoadedListener() {\n @Override\n public void onCustomFormatAdLoaded(NativeCustomFormatAd nativeCustomFormatAd) {\n // Custom native ad loaded.\n }\n },\n new NativeCustomFormatAd.OnCustomClickListener() {\n @Override\n public void onCustomClick(NativeCustomFormatAd nativeCustomFormatAd, String assetName) {\n // Custom native ad recorded a click.\n }\n })\n .withAdListener(new AdListener() {\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {\n // Custom native ad failed to load.\n }\n })\n .build();\n\nadLoader.loadAd(new AdRequest.Builder().build());\n```\n\nExample:\n```text\nNativeAdLoader.load(\n NativeAdRequest\n .Builder(AD_UNIT_ID, listOf(NativeAd.NativeAdType.CUSTOM_NATIVE))\n .setCustomFormatIds(listOf(CUSTOM_FORMAT_ID))\n .build(),\n object : NativeAdLoaderCallback {\n override fun onCustomNativeAdLoaded(customNativeAd: CustomNativeAd) {\n // Custom native ad loaded.\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // Custom native ad failed to load.\n }\n }\n)\n```\n\nExample:\n```text\nNativeAdLoader.load(\n new NativeAdRequest.Builder(AD_UNIT_ID, List.of(NativeAdType.CUSTOM_NATIVE))\n .setCustomFormatIds(List.of(CUSTOM_FORMAT_ID))\n .build(),\n new NativeAdLoaderCallback() {\n @Override\n public void onCustomNativeAdLoaded(CustomNativeAd customNativeAd) {\n // Custom native ad loaded.\n }\n\n @Override\n public void onAdFailedToLoad(LoadAdError adError) {\n // Custom native ad failed to load.\n }\n }\n);\n```\n\nExample:\n```devsite-click-to-copy\nval videoOptions = VideoOptions.Builder().setStartMuted(true).build()\n\nval adLoader =\n AdLoader.Builder(this, AD_UNIT_ID)\n .withNativeAdOptions(NativeAdOptions.Builder().setVideoOptions(videoOptions).build())\n .build()\n```\n\nExample:\n```devsite-click-to-copy\nVideoOptions videoOptions = new VideoOptions.Builder()\n .setStartMuted(true)\n .build();\n\nAdLoader adLoader = new AdLoader.Builder(this, AD_UNIT_ID)\n .withNativeAdOptions(new NativeAdOptions.Builder()\n .setVideoOptions(videoOptions)\n .build())\n .build();\n```\n\nExample:\n```devsite-click-to-copy\nval videoOptions = VideoOptions.Builder().setStartMuted(true).build()\n\nval nativeAdRequest = NativeAdRequest\n .Builder(AD_UNIT_ID, listOf(NativeAd.NativeAdType.NATIVE))\n .setVideoOptions(videoOptions)\n .build()\n```\n\nExample:\n```devsite-click-to-copy\nVideoOptions videoOptions = new VideoOptions.Builder().setStartMuted(true).build();\n\nNativeAdRequest nativeAdRequest = new NativeAdRequest\n .Builder(AD_UNIT_ID, List.of(NativeAd.NativeAdType.NATIVE))\n .setVideoOptions(videoOptions)\n .build();\n```\n\nExample:\n```devsite-click-to-copy\nval adLoader =\n AdLoader.Builder(this, AD_UNIT_ID)\n .forNativeAd(object : NativeAd.OnNativeAdLoadedListener {\n override fun onNativeAdLoaded(nativeAd: NativeAd) {\n // Native ad loaded.\n }\n })\n .withAdListener(\n object : AdListener() {\n override fun onAdOpened() {\n // Native ad opened an overlay that covered the screen.\n }\n\n override fun onAdClosed() {\n // Native ad closed.\n }\n\n override fun onAdImpression() {\n // Native ad recorded an impression.\n }\n\n override fun onAdClicked() {\n // Native ad recorded a click.\n }\n }\n )\n .build()\n\nadLoader.loadAd(AdRequest.Builder().build())\n```\n\nExample:\n```devsite-click-to-copy\nAdLoader adLoader = new AdLoader.Builder(this, AD_UNIT_ID)\n .forNativeAd(new NativeAd.OnNativeAdLoadedListener() {\n @Override\n public void onNativeAdLoaded(NativeAd nativeAd) {\n // Native ad loaded.\n }\n })\n .withAdListener(new AdListener() {\n @Override\n public void onAdOpened() {\n // Native ad opened an overlay that covered the screen.\n }\n\n @Override\n public void onAdClosed() {\n // Native ad closed.\n }\n\n @Override\n public void onAdImpression() {\n // Native ad recorded an impression.\n }\n\n @Override\n public void onAdClicked() {\n // Native ad recorded a click.\n }\n })\n .build();\n\nadLoader.loadAd(new AdRequest.Builder().build());\n```\n\nExample:\n```devsite-click-to-copy\nNativeAdLoader.load(\n NativeAdRequest\n .Builder(AD_UNIT_ID, listOf(NativeAd.NativeAdType.NATIVE))\n .build(),\n object : NativeAdLoaderCallback {\n override fun onNativeAdLoaded(nativeAd: NativeAd) {\n // Native ad loaded.\n nativeAd.adEventCallback =\n object : NativeAdEventCallback {\n override fun onAdShowedFullScreenContent() {\n // Native ad showed full screen content.\n // Google Mobile Ads SDK (Legacy) equivalent: onAdOpened()\n }\n\n override fun onAdDismissedFullScreenContent() {\n // Native ad dismissed full screen content.\n // Google Mobile Ads SDK (Legacy) equivalent: onAdClosed()\n }\n\n override fun onAdFailedToShowFullScreenContent(\n fullScreenContentError: FullScreenContentError\n ) {\n // Native ad failed to show full screen content.\n // Google Mobile Ads SDK (Legacy) equivalent: N/A\n }\n\n override fun onAdImpression() {\n // Native ad recorded an impression.\n }\n\n override fun onAdClicked() {\n // Native ad recorded a click.\n }\n }\n }\n }\n)\n```\n\nExample:\n```devsite-click-to-copy\nNativeAdLoader.load(\n new NativeAdRequest.Builder(AD_UNIT_ID, List.of(NativeAd.NativeAdType.NATIVE))\n .build(),\n new NativeAdLoaderCallback() {\n @Override\n public void onNativeAdLoaded(NativeAd nativeAd) {\n // Native ad loaded.\n nativeAd.setAdEventCallback(new NativeAdEventCallback() {\n @Override\n public void onAdShowedFullScreenContent() {\n // Native ad showed full screen content.\n // Google Mobile Ads SDK (Legacy) equivalent: onAdOpened()\n }\n\n @Override\n public void onAdDismissedFullScreenContent() {\n // Native ad dismissed full screen content.\n // Google Mobile Ads SDK (Legacy) equivalent: onAdClosed()\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(FullScreenContentError fullScreenContentError) {\n // Native ad failed to show full screen content.\n // Google Mobile Ads SDK (Legacy) equivalent: N/A\n }\n\n @Override\n public void onAdImpression() {\n // Native ad recorded an impression.\n }\n\n @Override\n public void onAdClicked() {\n // Native ad recorded a click.\n }\n });\n }\n }\n);\n```\n\nExample:\n```devsite-click-to-copy\n<com.google.android.gms.ads.nativead.NativeAdView\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"wrap_content\">\n <!-- Layout assets such as the media view and call to action. -->\n</com.google.android.gms.ads.nativead.NativeAdView>\n```\n\nExample:\n```devsite-click-to-copy\n<com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdView\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"wrap_content\">\n <!-- Layout assets such as the media view and call to action. -->\n</com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdView>\n```\n\nExample:\n```text\nprivate fun displayNativeAd(nativeAd: NativeAd) {\n // Inflate the NativeAdView layout.\n val nativeAdBinding = NativeAdBinding.inflate(layoutInflater)\n\n // Add the NativeAdView to the view hierarchy.\n binding.nativeViewContainer.addView(nativeAdBinding.root)\n val nativeAdView = nativeAdBinding.root\n\n // Populate and register the asset views.\n nativeAdView.mediaView = nativeAdBinding.adMedia\n // ...\n\n // Register the native ad with the NativeAdView.\n nativeAdView.setNativeAd(nativeAd)\n}\n```\n\nExample:\n```text\nprivate void displayNativeAd(NativeAd nativeAd) {\n // Inflate the NativeAdView layout\n NativeAdBinding nativeAdBinding = NativeAdBinding.inflate(getLayoutInflater());\n\n // Add the NativeAdView to the view hierarchy\n binding.nativeViewContainer.addView(nativeAdBinding.getRoot());\n NativeAdView nativeAdView = nativeAdBinding.getRoot();\n\n // Populate and register the asset views\n nativeAdView.setMediaView(nativeAdBinding.adMedia);\n // ...\n\n // Register the native ad with the NativeAdView\n nativeAdView.setNativeAd(nativeAd);\n}\n```\n\nExample:\n```text\nprivate fun displayNativeAd(nativeAd: NativeAd) {\n // Inflate the NativeAdView layout.\n val nativeAdBinding = NativeAdBinding.inflate(layoutInflater)\n\n // Add the NativeAdView to the view hierarchy.\n binding.nativeViewContainer.addView(nativeAdBinding.root)\n val nativeAdView = nativeAdBinding.root\n\n // Populate and register the asset views.\n // ...\n\n // Register the native ad and media content asset with the NativeAdView.\n val mediaView = nativeAdBinding.adMedia\n nativeAdView.registerNativeAd(nativeAd, mediaView)\n}\n```\n\nExample:\n```text\nprivate void displayNativeAd(NativeAd nativeAd) {\n // Inflate the NativeAdView layout.\n NativeAdBinding nativeAdBinding = NativeAdBinding.inflate(getLayoutInflater());\n\n // Add the NativeAdView to the view hierarchy.\n binding.nativeViewContainer.addView(nativeAdBinding.getRoot());\n NativeAdView nativeAdView = nativeAdBinding.getRoot();\n\n // Populate and register the asset views.\n // ...\n\n // Register the native ad and media content asset with the NativeAdView.\n MediaView mediaView = nativeAdBinding.adMedia;\n nativeAdView.registerNativeAd(nativeAd, mediaView);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.927Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":22,"totalLines":463,"estimatedTokens":3149}}516{"id":"doc-integrate_dt_exchange_with_mediation_unity_googl-2f069887","source":"documentation","title":"Integrate DT Exchange with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/dt-exchange","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.dtexchange\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.Fyber;\n// ...\n\nFyber.SetGDPRConsent(true);\nFyber.SetGDPRConsentString(\"myGDPRConsentString\");\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.Fyber;\n// ...\n\nFyber.SetCCPAString(\"myCCPAConsentString\");\n\n// You can also clear CCPA consent information using the following method:\nFyber.ClearCCPAString();\n```\n\nExample:\n```text\ncom.google.ads.mediation.fyber.FyberMediationAdapter\n```\n\nExample:\n```text\nGADMediationAdapterFyber\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.929Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":36,"estimatedTokens":146}}517{"id":"doc-integrate_inmobi_with_mediation_unity_google_for-d3364565","source":"documentation","title":"Integrate InMobi with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/inmobi","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.inmobi\n```\n\nExample:\n```text\n<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />\n```\n\nExample:\n```text\ncom.google.ads.mediation.inmobi.InMobiAdapter\ncom.google.ads.mediation.inmobi.InMobiMediationAdapter\n```\n\nExample:\n```text\nGADMAdapterInMobi\nGADMediationAdapterInMobi\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.930Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":25,"estimatedTokens":129}}518{"id":"doc-integrate_meta_audience_network_with_bidding_uni-5cac2014","source":"documentation","title":"Integrate Meta Audience Network with bidding | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/meta","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.metaaudiencenetwork\n```\n\nExample:\n```text\ncom.google.ads.mediation.facebook.FacebookAdapter\ncom.google.ads.mediation.facebook.FacebookMediationAdapter\n```\n\nExample:\n```text\nGADMAdapterFacebook\nGADMediationAdapterFacebook\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.932Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":18,"estimatedTokens":75}}519{"id":"doc-integrate_mytarget_with_mediation_unity_google_f-c156e266","source":"documentation","title":"Integrate myTarget with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/mytarget","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.mytarget\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.MyTarget;\n// ...\n\nMyTarget.SetUserConsent(true);\n```\n\nExample:\n```text\nMyTarget.SetUserAgeRestricted(true);\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.MyTarget;\n// ...\n\nMyTarget.SetCCPAUserConsent(true);\n```\n\nExample:\n```text\ncom.google.ads.mediation.mytarget.MyTargetAdapter\ncom.google.ads.mediation.mytarget.MyTargetNativeAdapter\ncom.google.ads.mediation.mytarget.MyTargetRewardedAdapter\n```\n\nExample:\n```text\nGADMAdapterMyTarget\nGADMediationAdapterMyTargetNative\nGADMediationAdapterMyTargetRewarded\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.933Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":41,"estimatedTokens":166}}520{"id":"doc-integrate_pangle_with_mediation_unity_google_for-3fa91e26","source":"documentation","title":"Integrate Pangle with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/pangle","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.pangle\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.Pangle;\n// ...\n\nPangle.SetPAConsent(0);\n```\n\nExample:\n```text\ncom.pangle.ads\ncom.google.ads.mediation.pangle.PangleMediationAdapter\n```\n\nExample:\n```text\nGADMediationAdapterPangle\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.934Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":25,"estimatedTokens":81}}521{"id":"doc-ad_load_errors_unity_google_for_developers-33fef9e8","source":"documentation","title":"Ad load errors | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/ad-load-errors","text":"Example:\n```text\npublic void OnAdFailedToLoad(LoadAdError error)\n{\n // Gets the domain from which the error came.\n string domain = error.GetDomain();\n\n // Gets the error code. See\n // https://developers.google.com/admob/android/reference/com/google/android/gms/ads/AdRequest\n // and https://developers.google.com/admob/ios/api/reference/Enums/GADErrorCode\n // for a list of possible codes.\n int code = error.GetCode();\n\n // Gets an error message.\n // For example \"Account not approved yet\". See\n // https://support.google.com/admob/answer/9905175 for explanations of\n // common errors.\n string message = error.GetMessage();\n\n // Gets the cause of the error, if available.\n AdError underlyingError = error.GetCause();\n\n // All of this information is available through the error's toString() method.\n Debug.Log(\"Load error string: \" + error.ToString());\n\n // Get response information, which may include results of mediation requests.\n ResponseInfo responseInfo = error.GetResponseInfo();\n Debug.Log(\"Response info: \" + responseInfo.ToString());\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.935Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":32,"estimatedTokens":277}}522{"id":"doc-test_ad_units_unity_google_for_developers-cc8618eb","source":"documentation","title":"Test ad units | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/ad-inspector/test-ad-units","text":"Example:\n```text\nAd Unit has no applicable adapter for single ad source testing on network: AD_SOURCE_ADAPTER_CLASS_NAME\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.935Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":35}}523{"id":"doc-retrieve_information_about_the_ad_response_unity-634cee84","source":"documentation","title":"Retrieve information about the ad response | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/response-info","text":"Example:\n```text\nprivate void LoadInterstitialAd()\n{\n AdRequest adRequest = new AdRequest();\n InterstitialAd.Load(\"AD_UNIT_ID\", adRequest, (InterstitialAd insterstitialAd, LoadAdError error) =>\n {\n // If the operation failed with a reason.\n if (error != null)\n {\n ResponseInfo errorInfo = error.GetResponseInfo();\n Debug.LogError(\"Interstitial ad failed to load an ad with error : \" + error);\n return;\n }\n\n ResponseInfo loadInfo = insterstitialAd.GetResponseInfo();\n });\n}\n```\n\nExample:\n```text\n{\n \"Response ID\": \"COOllLGxlPoCFdAx4Aod-Q4A0g\",\n \"Mediation Adapter Class Name\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Adapter Responses\": [\n {\n \"Adapter\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Latency\": 328,\n \"Ad Source Name\": \"Reservation campaign\",\n \"Ad Source ID\": \"7068401028668408324\",\n \"Ad Source Instance Name\": \"[DO NOT EDIT] Publisher Test Interstitial\",\n \"Ad Source Instance ID\": \"4665218928925097\",\n \"Credentials\": {},\n \"Ad Error\": \"null\"\n }\n ],\n \"Loaded Adapter Response\": {\n \"Adapter\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Latency\": 328,\n \"Ad Source Name\": \"Reservation campaign\",\n \"Ad Source ID\": \"7068401028668408324\",\n \"Ad Source Instance Name\": \"[DO NOT EDIT] Publisher Test Interstitial\",\n \"Ad Source Instance ID\": \"4665218928925097\",\n \"Credentials\": {},\n \"Ad Error\": \"null\"\n },\n \"Response Extras\": {\n \"mediation_group_name\": \"Campaign\"\n }\n}\n```\n\nExample:\n```text\n** Response Info **\n Response ID: CIzs0ZO5kPoCFRqWAAAdJMINpQ\n Network: GADMAdapterGoogleAdMobAds\n\n ** Loaded Adapter Response **\n Network: GADMAdapterGoogleAdMobAds\n Ad Source Name: Reservation campaign\n Ad Source ID: 7068401028668408324\n Ad Source Instance Name: [DO NOT EDIT] Publisher Test Interstitial\n Ad Source Instance ID: [DO NOT EDIT] Publisher Test Interstitial\n AdUnitMapping:\n {\n }\n Error: (null)\n Latency: 0.391\n\n ** Extras Dictionary **\n {\n \"mediation_group_name\" = Campaign;\n }\n\n ** Mediation line items **\n Entry (1)\n Network: GADMAdapterGoogleAdMobAds\n Ad Source Name: Reservation campaign\n Ad Source ID:7068401028668408324\n Ad Source Instance Name: [DO NOT EDIT] Publisher Test Interstitial\n Ad Source Instance ID: [DO NOT EDIT] Publisher Test Interstitial\n AdUnitMapping:\n {\n }\n Error: (null)\n Latency: 0.391\n```\n\nExample:\n```text\nprivate void LoadInterstitialAd()\n{\n AdRequest adRequest = new AdRequest();\n InterstitialAd.Load(\"AD_UNIT_ID\", adRequest, (InterstitialAd insterstitialAd, LoadAdError error) =>\n {\n // If the operation failed with a reason.\n if (error != null)\n {\n Debug.LogError(\"Interstitial ad failed to load an ad with error : \" + error);\n return;\n }\n\n ResponseInfo responseInfo = insterstitialAd.GetResponseInfo();\n string responseId = responseInfo.GetResponseId();\n string mediationAdapterClassName = responseInfo.GetMediationAdapterClassName();\n List<AdapterResponseInfo> adapterResponses = responseInfo.GetAdapterResponses();\n AdapterResponseInfo loadedAdapterResponseInfo = responseInfo.GetLoadedAdapterResponseInfo();\n Dictionary<string, string> extras = responseInfo.GetResponseExtras();\n string mediationGroupName = extras[\"mediation_group_name\"];\n string mediationABTestName = extras[\"mediation_ab_test_name\"];\n string mediationABTestVariant = extras[\"mediation_ab_test_variant\"]; \n });\n}\n```\n\nExample:\n```text\n{\n \"Adapter\": \"com.google.ads.mediation.admob.AdMobAdapter\",\n \"Latency\": 328,\n \"Ad Source Name\": \"Reservation campaign\",\n \"Ad Source ID\": \"7068401028668408324\",\n \"Ad Source Instance Name\": \"[DO NOT EDIT] Publisher Test Interstitial\",\n \"Ad Source Instance ID\": \"4665218928925097\",\n \"Credentials\": {},\n \"Ad Error\": \"null\"\n}\n```\n\nExample:\n```text\nNetwork: GADMAdapterGoogleAdMobAds\n Ad Source Name: Reservation campaign\n Ad Source ID: 7068401028668408324\n Ad Source Instance Name: [DO NOT EDIT] Publisher Test Interstitial\n Ad Source Instance ID: [DO NOT EDIT] Publisher Test Interstitial\n AdUnitMapping:\n {\n }\n Error: (null)\n Latency: 0.391\n```\n\nExample:\n```text\nprivate void LoadInterstitialAd()\n{\n AdRequest adRequest = new AdRequest();\n InterstitialAd.Load(\"AD_UNIT_ID\", adRequest, (InterstitialAd insterstitialAd, LoadAdError error) =>\n {\n // If the operation failed with a reason.\n if (error != null)\n {\n Debug.LogError(\"Interstitial ad failed to load an ad with error : \" + error);\n return;\n }\n\n ResponseInfo responseInfo = insterstitialAd.GetResponseInfo();\n AdapterResponseInfo loadedAdapterResponseInfo = responseInfo.getLoadedAdapterResponseInfo();\n AdError adError = loadedAdapterResponseInfo.AdError;\n string adSourceId = loadedAdapterResponseInfo.AdSourceId;\n string adSourceInstanceId = loadedAdapterResponseInfo.AdSourceInstanceId;\n string adSourceInstanceName = loadedAdapterResponseInfo.AdSourceInstanceName;\n string adSourceName = loadedAdapterResponseInfo.AdSourceName;\n string adapterClassName = loadedAdapterResponseInfo.AdapterClassName;\n Dictionary<string, string> credentials = loadedAdapterResponseInfo.AdUnitMapping;\n long latencyMillis = loadedAdapterResponseInfo.LatencyMillis;\n });\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.936Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":174,"estimatedTokens":1334}}524{"id":"doc-integrate_maio_with_mediation_unity_google_for_d-ba6f75ae","source":"documentation","title":"Integrate maio with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/maio","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.maio\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.937Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":21}}525{"id":"doc-integrate_liftoff_monetize_with_mediation_unity_-71a09f73","source":"documentation","title":"Integrate Liftoff Monetize with mediation | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/liftoff-monetize","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.liftoffmonetize\n```\n\nExample:\n```text\nusing GoogleMobileAds.Mediation.LiftoffMonetize.Api;\n// ...\n\nLiftoffMonetize.SetCCPAStatus(true);\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api;\nusing GoogleMobileAds.Mediation.LiftoffMonetize.Api;\n// ...\n\nvar adRequest = new AdRequest();\nvar liftoffExtras = new LiftoffMonetizeMediationExtras();\nliftoffExtras.SetBackButtonImmediatelyEnabled(true);\nadRequest.MediationExtras.Add(liftoffExtras);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.939Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":26,"estimatedTokens":129}}526{"id":"doc-integrate_unity_ads_with_mediation_google_for_de-9abf021a","source":"documentation","title":"Integrate Unity Ads with mediation | Google for Developers","url":"https://developers.google.com/admob/unity/mediation/unity","text":"Example:\n```text\nopenupm add com.google.ads.mobile.mediation.unityads\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.UnityAds;\n// ...\n\nUnityAds.SetConsentMetaData(\"gdpr.consent\", true);\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.UnityAds;\n// ...\n\nUnityAds.SetConsentMetaData(\"privacy.consent\", true);\n```\n\nExample:\n```text\ncom.google.ads.mediation.unity.UnityAdapter\ncom.google.ads.mediation.unity.UnityMediationAdapter\n```\n\nExample:\n```text\nGADMAdapterUnity\nGADMediationAdapterUnity\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.940Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":34,"estimatedTokens":132}}527{"id":"doc-log_ad_response_id_to_crashlytics_unity_google_f-21e3d1c1","source":"documentation","title":"Log ad response ID to Crashlytics | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/crashlytics","text":"Example:\n```text\nusing GoogleMobileAds.Api;\nusing Fabric.Crashlytics;\n...\npublic class GameObjectScript : MonoBehaviour\n{\n bool isCrashlyticsInitialized = false;\n public void Start()\n {\n ....\n // Initialize Google Mobile Ads Unity Plugin.\n MobileAds.Initialize((InitializationStatus initStatus) => {});\n ....\n // Initialize Firebase\n Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {\n Firebase.DependencyStatus dependencyStatus = task.Result;\n if (dependencyStatus == Firebase.DependencyStatus.Available)\n {\n Firebase.FirebaseApp app = Firebase.FirebaseApp.DefaultInstance;\n isCrashlyticsInitialized = true;\n }\n else\n {\n UnityEngine.Debug.LogError(System.String.Format(\n \"Could not resolve all Firebase dependencies: {0}\", dependencyStatus));\n // Firebase Unity SDK is not safe to use here.\n }\n });\n }\n}\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api;\nusing Fabric.Crashlytics;\n...\npublic class GameObjectScript : MonoBehaviour\n{\n public void Start()\n {\n ...\n // Initialize Google Mobile Ads Unity Plugin.\n MobileAds.Initialize((InitializationStatus initStatus) => {});\n\n // Initialize Firebase.\n Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {\n Firebase.DependencyStatus dependencyStatus = task.Result;\n if (dependencyStatus == Firebase.DependencyStatus.Available)\n {\n // Create and hold a reference to your FirebaseApp,\n // where app is a Firebase.FirebaseApp property of your\n // application class.\n // Crashlytics will use the DefaultInstance, as well;\n // this ensures that Crashlytics is initialized.\n Firebase.FirebaseApp app = Firebase.FirebaseApp.DefaultInstance;\n isCrashlyticsInitialized = true;\n }\n else\n {\n UnityEngine.Debug.LogError(System.String.Format(\n \"Could not resolve all Firebase dependencies: {0}\",dependencyStatus));\n // Firebase Unity SDK is not safe to use here.\n }\n });\n\n // Request Banner View.\n this.RequestBanner();\n ...\n }\n\n public void RequestBanner()\n {\n #if UNITY_ANDROID\n string adUnitId = \"ca-app-pub-3940256099942544/6300978111\";\n #elif UNITY_IPHONE\n string adUnitId = \"ca-app-pub-1220882738324941/1255739139\";\n #else\n string adUnitId = \"unexpected_platform\";\n #endif\n\n // Create a 320x50 banner at the top of the screen.\n this.bannerView = new BannerView(adUnitId, AdSize.Banner, AdPosition.Bottom);\n\n // Called when an ad request has successfully loaded.\n this.bannerView.OnAdLoaded += this.HandleOnAdLoaded;\n\n AdRequest request = new AdRequest();\n this.bannerView.LoadAd(request);\n }\n}\n```\n\nExample:\n```text\npublic void HandleOnAdLoaded()\n{\n ResponseInfo responseInfo = this.bannerView.GetResponseInfo();\n if (responseInfo != null)\n {\n String adResponseId = responseInfo.GetResponseId();\n // Log to Crashlytics.\n if (isCrashlyticsInitialized)\n {\n Crashlytics.SetCustomKey(\"banner_ad_response_id\", adResponseId);\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.943Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":112,"estimatedTokens":880}}528{"id":"doc-create_custom_events_unity_google_for_developers-64f2ce0a","source":"documentation","title":"Create custom events | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/custom-events","text":"Example:\n```text\nAssets/AdPub/Editor/AdPubDependencies.xml\n```\n\nExample:\n```text\n<dependencies>\n <androidPackages>\n <androidPackage spec=\"com.adpub.android:adpub-sdk:1.0.0\" />\n <androidPackage spec=\"com.adpub.android:adpub-custom-event:1.0.0\">\n <repositories>\n <repository>https://repo.maven.apache.org/maven2/</repository>\n <repository>https://dl.google.com/dl/android/maven2/</repository>\n </repositories>\n </androidPackage>\n </androidPackages>\n <iosPods>\n <iosPod name=\"AdPubSDK\" version=\"1.0\" />\n <iosPod name=\"AdPubCustomEvent\" version=\"1.0\">\n <sources>\n <source>https://github.com/CocoaPods/Specs</source>\n </sources>\n </iosPod>\n </iosPods>\n</dependencies>\n```\n\nExample:\n```text\nAssets > External Dependency Manager > Android Resolver > Force Resolve\n```\n\nExample:\n```text\npackage com.adpub.android;\n\npublic class AdPubSdk\n{\n public static void setHasUserConsent(boolean hasUserConsent);\n}\n```\n\nExample:\n```text\n@interface AdPubSdk : NSObject\n+ (void)setHasUserConsent:(BOOL)hasUserConsent;\n@end\n```\n\nExample:\n```text\nnamespace AdPub.Common\n{\n public interface IAdPubClient\n {\n ///<summary>\n /// Sets a flag indicating if the app has user consent for advertisement.\n ///</summary>\n void SetHasUserConsent(bool hasUserConsent);\n }\n}\n```\n\nExample:\n```text\nnamespace AdPub.Common\n{\n public class DefaultClient : IAdPubClient\n {\n public void SetHasUserConsent(bool hasUserConsent)\n {\n Debug.Log(\"SetHasUserConsent was called.\");\n }\n }\n}\n```\n\nExample:\n```text\n// Wrap this class in a conditional operator to make sure it only runs on iOS.\n#if UNITY_IOS\n\n// Reference InteropServices to include the DLLImportAttribute type.\nusing System.Runtime.InteropServices;\n\nusing AdPub.Common;\n\nnamespace AdPub.Platforms.Android\n{\n public class iOSAdPubClient : IAdPubClient\n {\n public void SetHasUserConsent(bool hasUserConsent)\n {\n GADUAdPubSetHasUserConsent(hasUserConsent);\n }\n\n [DllImport(\"__Internal\")]\n internal static extern void GADUAdPubSetHasUserConsent(bool hasUserConsent);\n }\n}\n#endif\n```\n\nExample:\n```text\n#import <AdPubSDK/AdPubSDK.h>\n\nvoid GADUAdPubSetHasUserConsent(BOOL hasUserConsent) {\n [AdPubSDK setHasUserConsent:hasUserConsent];\n}\n```\n\nExample:\n```text\n// Wrap this class in a conditional operator to make sure it only runs on Android.\n#if UNITY_ANDROID\n\n// Reference the UnityEngine namespace which contains the JNI Helper classes.\nusing UnityEngine;\n\nusing AdPub.Common;\n\nnamespace AdPub.Platforms.Android\n{\n public class AndroidAdPubClient : IAdPubClient\n {\n public void SetHasUserConsent(bool hasUserConsent)\n {\n // Make a reference to the com.adpub.AdPubSDK.\n AndroidJavaClass adPubSdk = new AndroidJavaClass(\"com.adpub.AdPubSdk\");\n\n // Call the native setHasUserConsent method of com.adpub.AdPubSDK.\n adPubSdk.CallStatic(\"setHasUserConsent\", hasUserConsent);\n }\n }\n}\n#endif\n```\n\nExample:\n```text\nnamespace AdPub.Common\n{\n public class AdPubClientFactory\n {\n // Return the correct platform client.\n public static IAdPubClient GetClient()\n {\n#if !UNITY_EDITOR && UNITY_ANDROID\n return new AdPub.Platforms.Android.AndroidAdPubClient();\n#elif !UNITY_EDITOR && UNITY_IOS\n return new AdPub.Platforms.iOS.iOSAdPubClient();\n#else\n // Returned for the Unity Editor and unsupported platforms.\n return new DefaultClient();\n#endif\n }\n }\n}\n```\n\nExample:\n```text\nusing AdPub.Common;\n\nnamespace AdPub\n{\n public class AdPubApi\n {\n private static readonly IAdPubClient client = GetAdPubClient();\n\n // Returns the correct client for the current runtime platform.\n private static IAdPubClient GetAdPubClient()\n {\n return AdPubClientFactory.GetClient();\n }\n\n // Sets the user consent using the underlying SDK functionality.\n public static void SetHasUserConsent(bool hasUserConsent)\n {\n client.SetHasUserConsent(hasUserConsent);\n }\n }\n}\n```\n\nExample:\n```text\nusing UnityEngine;\nusing AdPub;\n\npublic class AdPubController : MonoBehaviour\n{\n // TODO: Get consent from the user and update this userConsent field.\n public bool userConsent;\n\n // Called on startup of the GameObject it's assigned to.\n public void Start()\n {\n // Pass the user consent to AdPub.\n AdPubApi.SetHasUserConsent(userConsent);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.944Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":207,"estimatedTokens":1151}}529{"id":"doc-requesting_additional_permissions_web_guides_goo-5b8a1784","source":"documentation","title":"Requesting additional permissions | Web guides | Google for Developers","url":"https://developers.google.com/identity/sign-in/web/incremental-auth","text":"Example:\n```text\nauth2 = gapi.auth2.init({\n client_id: 'CLIENT_ID.apps.googleusercontent.com',\n cookiepolicy: 'single_host_origin', /** Default value **/\n scope: 'profile' }); /** Base scope **/\n```\n\nExample:\n```text\nconst options = new gapi.auth2.SigninOptionsBuilder();\noptions.setScope('email https://www.googleapis.com/auth/drive');\n\ngoogleUser = auth2.currentUser.get();\ngoogleUser.grant(options).then(\n function(success){\n console.log(JSON.stringify({message: \"success\", value: success}));\n },\n function(fail){\n alert(JSON.stringify({message: \"fail\", value: fail}));\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.944Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":24,"estimatedTokens":161}}530{"id":"doc-impression_level_ad_revenue_unity_google_for_dev-f3037b02","source":"documentation","title":"Impression-level ad revenue | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/impression-level-ad-revenue","text":"Example:\n```text\nprivate void LoadRewardedAd()\n{\n // Send the request to load the ad.\n AdRequest adRequest = new AdRequest();\n RewardedAd.Load(\"AD_UNIT_ID\", adRequest, (RewardedAd rewardedAd, LoadAdError error) =>\n {\n // If the operation failed with a reason.\n if (error != null)\n {\n Debug.LogError(\"Rewarded ad failed to load an ad with error : \" + error);\n return;\n }\n\n rewardedAd.OnAdPaid += this.HandleAdPaidEvent;\n });\n}\n\npublic void HandleAdPaidEvent(AdValue adValue)\n{\n // TODO: Send the impression-level ad revenue information to your\n // preferred analytics server directly within this callback.\n\n long valueMicros = adValue.Value;\n string currencyCode = adValue.CurrencyCode;\n PrecisionType precision = adValue.Precision;\n\n ResponseInfo responseInfo = rewardedAd.GetResponseInfo();\n string responseId = responseInfo.GetResponseId();\n\n AdapterResponseInfo loadedAdapterResponseInfo = responseInfo.GetLoadedAdapterResponseInfo();\n string adSourceId = loadedAdapterResponseInfo.AdSourceId;\n string adSourceInstanceId = loadedAdapterResponseInfo.AdSourceInstanceId;\n string adSourceInstanceName = loadedAdapterResponseInfo.AdSourceInstanceName;\n string adSourceName = loadedAdapterResponseInfo.AdSourceName;\n string adapterClassName = loadedAdapterResponseInfo.AdapterClassName;\n long latencyMillis = loadedAdapterResponseInfo.LatencyMillis;\n Dictionary<string, string> credentials = loadedAdapterResponseInfo.AdUnitMapping;\n\n Dictionary<string, string> extras = responseInfo.GetResponseExtras();\n string mediationGroupName = extras[\"mediation_group_name\"];\n string mediationABTestName = extras[\"mediation_ab_test_name\"];\n string mediationABTestVariant = extras[\"mediation_ab_test_variant\"];\n}\n```\n\nExample:\n```text\nprivate string GetAdSourceName(AdapterResponseInfo loadedAdapterResponseInfo)\n{\n if (loadedAdapterResponseInfo == null)\n {\n return string.Empty;\n }\n\n string adSourceName = loadedAdapterResponseInfo.AdSourceName;\n\n if (adSourceName == \"Custom Event\")\n {\n\n #if UNITY_ANDROID\n if (loadedAdapterResponseInfo.AdapterClassName ==\n \"com.google.ads.mediation.sample.customevent.SampleCustomEvent\")\n {\n adSourceName = \"Sample Ad Network (Custom Event)\";\n }\n #elif UNITY_IPHONE\n if (loadedAdapterResponseInfo.AdapterClassName == \"SampleCustomEvent\")\n {\n adSourceName = \"Sample Ad Network (Custom Event)\";\n }\n #endif\n\n }\n return adSourceName;\n}ResponseInfoSnippets.cs\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.947Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":80,"estimatedTokens":667}}531{"id":"doc-authorization_scopes_apps_script_google_for_deve-28905fb9","source":"documentation","title":"Authorization Scopes | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/concepts/scopes","text":"Example:\n```text\n{\n ...\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/spreadsheets.readonly\",\n \"https://www.googleapis.com/auth/userinfo.email\"\n ],\n ...\n }\n```\n\nExample:\n```text\n// This function requires the Gmail and Sheets scopes.\nfunction sendEmail() {\n // Validates that the user has granted permission for the Gmail and Sheets scopes.\n // If not, the execution ends and prompts the user for authorization.\n ScriptApp.requireScopes(ScriptApp.AuthMode.FULL, [\n 'https://mail.google.com/',\n 'https://www.googleapis.com/auth/spreadsheets'\n ]);\n\n // Sends an email.\n GmailApp.sendEmail(\"dana@example.com\", \"Subject\", \"Body\");\n Logger.log(\"Email sent successfully!\");\n\n // Opens a spreadsheet and sheet to track the sent email.\n const ss = SpreadsheetApp.openById(\"abc1234567\");\n const sheet = ss.getSheetByName(\"Email Tracker\")\n\n // Gets the last row of the sheet.\n const lastRow = sheet.getLastRow();\n\n // Adds \"Sent\" to column E of the last row of the spreadsheet.\n sheet.getRange(lastRow, 5).setValue(\"Sent\");\n Logger.log(\"Sheet updated successfully!\");\n}\n\n// This function requires all scopes used by the script (Gmail,\n// Calendar, and Sheets).\nfunction createEventSendEmail() {\n // Validates that the user has granted permission for all scopes used by the\n // script. If not, the execution ends and prompts the user for authorization.\n ScriptApp.requireAllScopes(ScriptApp.AuthMode.FULL);\n\n // Creates an event.\n CalendarApp.getDefaultCalendar().createEvent(\n \"Meeting\",\n new Date(\"November 28, 2024 10:00:00\"),\n new Date(\"November 28, 2024 11:00:00\")\n );\n Logger.log(\"Calendar event created successfully!\");\n\n // Sends an email.\n GmailApp.sendEmail(\"dana@example.com\", \"Subject 2\", \"Body 2\");\n Logger.log(\"Email sent successfully!\");\n\n // Opens a spreadsheet and sheet to track the created meeting and sent email.\n const ss = SpreadsheetApp.openById(\"abc1234567\");\n const sheet = ss.getSheetByName(\"Email and Meeting Tracker\")\n // Gets the last row\n const lastRow = sheet.getLastRow();\n\n // Adds \"Sent\" to column E of the last row\n sheet.getRange(lastRow, 5).setValue(\"Sent\");\n // Adds \"Meeting created\" to column F of the last row\n sheet.getRange(lastRow, 6).setValue(\"Meeting created\");\n Logger.log(\"Sheet updated successfully!\");\n}\n```\n\nExample:\n```text\n// This function uses the Gmail scope and skips the email\n// capabilities if the scope for Gmail hasn't been granted.\nfunction myFunction() {\n const authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL, ['https://mail.google.com/']);\n if (authInfo.getAuthorizationStatus() === ScriptApp.AuthorizationStatus.NOT_REQUIRED) {\n GmailApp.sendEmail(\"dana@example.com\", \"Subject\", \"Body\");\n Logger.log(\"Email sent successfully!\");\n } else {\n const scopesGranted = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL).getAuthorizedScopes();\n console.warn(`Authorized scopes: ${scopesGranted} not enough to send mail, skipping.`);\n }\n // Continue the rest of the execution flow...\n}\n```\n\nExample:\n```text\n// This function requires scope Sheets.\nfunction trackFormSubmissions(e){\n // Opens a spreadsheet to track the sent email.\n const ss = SpreadsheetApp.openById(\"abc1234567\");\n const sheet = ss.getSheetByName(\"Submission Tracker\")\n\n // Gets the last row of the sheet.\n const lastRow = sheet.getLastRow();\n\n // Adds email address of user that submitted the form\n // to column E of the last row of the spreadsheet.\n sheet.getRange(lastRow, 5).setValue(e.name);\n Logger.log(\"Sheet updated successfully!\");\n}\n\nfunction installTrigger(){\n // Validates that the user has granted permissions for trigger\n // installation and execution. If not, trigger doesn't get\n // installed and prompts the user for authorization.\n ScriptApp.requireScopes(ScriptApp.AuthMode.FULL, [\n 'https://www.googleapis.com/auth/script.scriptapp',\n 'https://www.googleapis.com/auth/spreadsheets',\n 'https://www.googleapis.com/auth/forms.currentonly'\n ]);\n ScriptApp.newTrigger('trackFormSubmission')\n .forForm(FormApp.getActiveForm())\n .onFormSubmit()\n .create();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.948Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":123,"estimatedTokens":1046}}532{"id":"doc-ad_preloading_beta_unity_google_for_developers-db826f45","source":"documentation","title":"Ad preloading (beta) | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/ad-preloading","text":"Example:\n```text\nvar preloadConfiguration = new PreloadConfiguration\n{\n AdUnitId = \"AD_UNIT_ID\",\n Request = new AdRequest(),\n};\n\n// Start the preloading initialization process after MobileAds.Initialize().\nInterstitialAdPreloader.Preload(\n // The Preload ID can be any unique string to identify this configuration.\n \"AD_UNIT_ID\",\n preloadConfiguration);AdPreloaderSnippets.cs\n```\n\nExample:\n```text\n// DequeueAd returns the next available ad and loads another ad in the background.\nvar ad = InterstitialAdPreloader.DequeueAd(\"AD_UNIT_ID\");\n\nif (ad != null)\n{\n // [Optional] Interact with the ad object as needed.\n ad.OnAdPaid += (AdValue value) =>\n {\n Debug.Log($\"Ad paid: {value.CurrencyCode} {value.Value}\");\n // [Optional] Send the impression-level ad revenue information to your preferred\n // analytics server directly within this callback.\n };\n\n // Do not hold onto preloaded ads, always show a preloaded ad immediately.\n ad.Show();\n}AdPreloaderSnippets.cs\n```\n\nExample:\n```text\nvar isAdAvailable = InterstitialAdPreloader.IsAdAvailable(\"AD_UNIT_ID\");AdPreloaderSnippets.cs\n```\n\nExample:\n```text\nvoid StartPreloadWithCallbacks()\n{\n var preloadConfiguration = new PreloadConfiguration\n {\n AdUnitId = \"AD_UNIT_ID\",\n Request = new AdRequest(),\n };\n\n // Start the preloading initialization process after MobileAds.Initialize().\n InterstitialAdPreloader.Preload(\n // The Preload ID can be any unique string to identify this configuration.\n \"AD_UNIT_ID\",\n preloadConfiguration,\n onAdPreloaded,\n onAdFailedToPreload,\n onAdsExhausted);\n}\n\nvoid onAdPreloaded(string preloadId, ResponseInfo responseInfo)\n{\n Debug.Log($\"Preload ad configuration {preloadId} was preloaded.\");\n}\n\nvoid onAdFailedToPreload(string preloadId, AdError adError)\n{\n string errorMessage = $\"Preload ad configuration {preloadId} failed to \" +\n $\"preload with error : {adError.GetMessage()}.\";\n Debug.Log(errorMessage);\n}\n\nvoid onAdsExhausted(string preloadId)\n{\n Debug.Log($\"Preload ad configuration {preloadId} was exhausted\");\n // [Important] Don't call Preload() or DequeueAd() from onAdsExhausted.\n}AdPreloaderSnippets.cs\n```\n\nExample:\n```text\nInterstitialAdPreloader.Destroy(\"AD_UNIT_ID\");\nInterstitialAdPreloader.DestroyAll();AdPreloaderSnippets.cs\n```\n\nExample:\n```text\n// Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\nnew PreloadConfiguration\n{\n AdUnitId = \"AD_UNIT_ID\",\n Request = new AdRequest(),\n BufferSize = 2\n};AdPreloaderSnippets.cs\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.949Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":97,"estimatedTokens":659}}533{"id":"doc-google_cloud_projects_apps_script_google_for_dev-663e8e8a","source":"documentation","title":"Google Cloud projects | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/cloud-platform-projects","text":"Example:\n```text\ngcloud projects list --filter='parent.id=APPS_SCRIPT_FOLDER_ID'\ngcloud projects delete PROJECT_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.950Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":7,"estimatedTokens":33}}534{"id":"doc-access_google_apis_in_an_ios_app_sign_in_with_go-7be6ce0d","source":"documentation","title":"Access Google APIs in an iOS app | Sign in with Google for iOS | Google for Developers","url":"https://developers.google.com/identity/sign-in/ios/additional-scopes","text":"Example:\n```text\nlet driveScope = \"https://www.googleapis.com/auth/drive.readonly\"\nlet grantedScopes = user.grantedScopes\nif grantedScopes == nil || !grantedScopes!.contains(driveScope) {\n // Request additional Drive scope.\n}\n```\n\nExample:\n```text\nNSString *driveScope = @\"https://www.googleapis.com/auth/drive.readonly\";\n\n// Check if the user has granted the Drive scope\nif (![user.grantedScopes containsObject:driveScope]) {\n // request additional drive scope\n}\n```\n\nExample:\n```text\nlet additionalScopes = [\"https://www.googleapis.com/auth/drive.readonly\"]\nguard let currentUser = GIDSignIn.sharedInstance.currentUser else {\n return ; /* Not signed in. */\n}\n\ncurrentUser.addScopes(additionalScopes, presenting: self) { signInResult, error in\n guard error == nil else { return }\n guard let signInResult = signInResult else { return }\n\n // Check if the user granted access to the scopes you requested.\n}\n```\n\nExample:\n```text\nNSArray *additionalScopes = @[ @\"https://www.googleapis.com/auth/drive.readonly\" ];\nGIDGoogleUser *currentUser = GIDSignIn.sharedInstance.currentUser;\n\n[currentUser addScopes:additionalScopes\n presentingViewController:self\n completion:^(GIDSignInResult * _Nullable signInResult,\n NSError * _Nullable error) {\n if (error) { return; }\n if (signInResult == nil) { return; }\n\n // Check if the user granted access to the scopes you requested.\n}];\n```\n\nExample:\n```text\ncurrentUser.refreshTokensIfNeeded { user, error in\n guard error == nil else { return }\n guard let user = user else { return }\n\n // Get the access token to attach it to a REST or gRPC request.\n let accessToken = user.accessToken.tokenString\n\n // Or, get an object that conforms to GTMFetcherAuthorizationProtocol for\n // use with GTMAppAuth and the Google APIs client library.\n let authorizer = user.fetcherAuthorizer()\n}\n```\n\nExample:\n```text\n[currentUser refreshTokensIfNeededWithCompletion:^(\n GIDGoogleUser * _Nullable user,\n NSError * _Nullable error) {\n if (error) { return; }\n if (user == nil) { return; }\n\n // Get the access token to attach it to a REST or gRPC request.\n NSString *accessToken = user.accessToken.tokenString;\n\n // Or, get an object that conforms to GTMFetcherAuthorizationProtocol for\n // use with GTMAppAuth and the Google APIs client library.\n id<GTMFetcherAuthorizationProtocol> authorizer = [user fetcherAuthorizer];\n}];\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.950Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":83,"estimatedTokens":638}}535{"id":"doc-build_unity_for_android_google_for_developers-c3836ee3","source":"documentation","title":"Build Unity for Android | Google for Developers","url":"https://developers.google.com/admob/unity/android","text":"Example:\n```text\nandroid.jetifier.ignorelist=annotation-experimental-1.4.0.aar\n```\n\nExample:\n```text\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.google.unity.ads\"\n android:versionName=\"1.0\"\n android:versionCode=\"1\">\n <uses-sdk />\n <application>\n <uses-library android:required=\"false\" android:name=\"org.apache.http.legacy\"/>\n </application>\n</manifest>\n```\n\nExample:\n```devsite-click-to-copy\nplugins {\n id 'com.android.application' version '8.1.1' apply false\n id 'com.android.library' version '8.1.1' apply false\n}\n\ntask clean(type: Delete) {\n delete rootProject.buildDir\n}\n```\n\nExample:\n```devsite-click-to-copy\napply plugin: 'com.android.application'\n\ndependencies {\n implementation project(':unityLibrary')\n}\n\nandroid {\n namespace \"com.google.android.gms.example\"\n compileSdkVersion 35\n buildToolsVersion '35.0.0'\n\n compileOptions {\n sourceCompatibility JavaVersion.VERSION_17\n targetCompatibility JavaVersion.VERSION_17\n }\n\n defaultConfig {\n minSdkVersion 28\n targetSdkVersion 35\n applicationId 'com.google.android.gms.example'\n ndk {\n abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'\n }\n versionCode 1\n versionName '1.0'\n }\n\n aaptOptions {\n noCompress = ['.unity3d', '.ress', '.resource', '.obb', '.bundle', '.unityexp']\n ignoreAssetsPattern = \"!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~\"\n }\n\n lintOptions {\n abortOnError false\n }\n\n buildTypes {\n debug {\n minifyEnabled false\n proguardFiles getDefaultProguardFile('proguard-android.txt')\n signingConfig signingConfigs.debug\n jniDebuggable true\n }\n release {\n minifyEnabled false\n proguardFiles getDefaultProguardFile('proguard-android.txt')\n signingConfig signingConfigs.debug\n }\n }\n\n packagingOptions {\n doNotStrip '*/armeabi-v7a/*.so'\n doNotStrip '*/arm64-v8a/*.so'\n doNotStrip '*/x86/*.so'\n doNotStrip '*/x86_64/*.so'\n jniLibs {\n useLegacyPackaging true\n }\n }\n\n bundle {\n language {\n enableSplit = false\n }\n density {\n enableSplit = false\n }\n abi {\n enableSplit = true\n }\n }\n}\n\napply from: '../unityLibrary/GoogleMobileAdsPlugin.androidlib/packaging_options.gradle'\n```\n\nExample:\n```devsite-click-to-copy\npluginManagement {\n repositories {\n gradlePluginPortal()\n google()\n mavenCentral()\n }\n}\n\ninclude ':launcher', ':unityLibrary'\ninclude 'unityLibrary:GoogleMobileAdsPlugin.androidlib'\n\ndependencyResolutionManagement {\n repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)\n repositories {\n\n google()\n mavenCentral()\n flatDir {\n dirs \"${project(':unityLibrary').projectDir}/libs\"\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\napply plugin: 'com.android.library'\n\n dependencies {\n implementation fileTree(dir: 'libs', include: ['*.jar'])\n // Android Resolver Dependencies Start\n implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n implementation 'com.google.android.gms:play-services-ads:23.6.0'\n implementation 'com.google.android.ump:user-messaging-platform:3.1.0'\n // Android Resolver Dependencies End\n implementation(name: 'googlemobileads-unity', ext:'aar')\n implementation project('GoogleMobileAdsPlugin.androidlib')\n }\n\n // Android Resolver Exclusions Start\n android {\n packagingOptions {\n exclude ('/lib/armeabi/*' + '*')\n exclude ('/lib/mips/*' + '*')\n exclude ('/lib/mips64/*' + '*')\n exclude ('/lib/x86/*' + '*')\n }\n }\n // Android Resolver Exclusions End\n\n android {\n namespace \"com.unity3d.player\"\n compileSdkVersion 35\n buildToolsVersion '30.0.2'\n\n compileOptions {\n sourceCompatibility JavaVersion.VERSION_17\n targetCompatibility JavaVersion.VERSION_17\n }\n\n defaultConfig {\n minSdkVersion 28\n targetSdkVersion 34\n ndk {\n abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'\n }\n versionCode 1\n versionName '1.0'\n consumerProguardFiles 'proguard-unity.txt'\n }\n\n lintOptions {\n abortOnError false\n }\n\n aaptOptions {\n ignoreAssetsPattern = \"!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~\"\n }\n\n packagingOptions {\n doNotStrip '*/armeabi-v7a/*.so'\n doNotStrip '*/arm64-v8a/*.so'\n doNotStrip '*/x86_64/*.so'\n }\n }\n\n\n apply from: 'GoogleMobileAdsPlugin.androidlib/packaging_options.gradle'\n gradle.projectsEvaluated { apply from: 'GoogleMobileAdsPlugin.androidlib/validate_dependencies.gradle' }\n```\n\nExample:\n```devsite-click-to-copy\napply plugin: 'android-library'\n\ndependencies {\n implementation fileTree(dir: 'bin', include: ['<em>.jar'])\n implementation fileTree(dir: 'libs', include: ['</em>.jar'])\n}\n\nandroid {\n namespace \"com.google.unity.ads\"\n sourceSets {\n main {\n manifest.srcFile 'AndroidManifest.xml'\n //java.srcDirs = ['src']\n res.srcDirs = ['res']\n assets.srcDirs = ['assets']\n jniLibs.srcDirs = ['libs']\n }\n }\n\n compileSdkVersion 34\n buildToolsVersion '30.0.2'\n defaultConfig {\n targetSdkVersion 31\n }\n\n lintOptions {\n abortOnError false\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.951Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":236,"estimatedTokens":1442}}536{"id":"doc-targeting_unity_google_for_developers-2045c90a","source":"documentation","title":"Targeting | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/targeting","text":"Example:\n```text\nMobileAds.SetRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.cs\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration = new RequestConfiguration\n{\n // Indicate that ad requests should have child age treatment.\n AgeRestrictedTreatment = AgeRestrictedTreatment.Child\n};\nMobileAds.SetRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.cs\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration = new RequestConfiguration\n{\n TagForUnderAgeOfConsent = TagForUnderAgeOfConsent.True\n};\nMobileAds.SetRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.cs\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration = new RequestConfiguration\n{\n MaxAdContentRating = MaxAdContentRating.G\n};\nMobileAds.SetRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.cs\n```\n\nExample:\n```text\nvar adRequest = new AdRequest();\nadRequest.Extras.Add(\"collapsible\", \"bottom\");AdRequestSnippets.cs\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.954Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":40,"estimatedTokens":257}}537{"id":"doc-integrate_google_mobile_ads_sdk_legacy_with_ai_t-7d2b23f0","source":"documentation","title":"Integrate Google Mobile Ads SDK (Legacy) with AI tools (beta) | Android | Google for Developers","url":"https://developers.google.com/admob/android/ai-tools","text":"Example:\n```text\n---\nname: gma-android-integrate\ndescription: Provides technical specifications and implementation details for\n the play-services-ads Google Mobile Ads SDK\n (com.google.android.gms:play-services-ads), including Gradle dependencies,\n manifest metadata, initialization patterns, and banner ad configurations. Use\n ONLY for the play-services-ads Google Mobile Ads SDK. Do NOT use for GMA\n Next-Gen SDK (com.google.android.libraries.ads.mobile.sdk:ads-mobile-sdk)\n integrations.\nmetadata:\n version: 1.0\n---\n\n# AI Integration Agent Instructions for the Play Services Google Mobile Ads SDK\n\n## SDK Integration Workflow\n\n* **Configure Gradle**:\n - [ ] Add the latest stable version of\n `com.google.android.gms:play-services-ads` to dependencies.\n - [ ] Configure `minSdk` (23+) and `compileSdk` (35+).\n - [ ] Sync Gradle before moving on to the next step.\n* **Manifest Configuration**:\n\n - [ ] Add the following metadata to the `<application>` tag in the\n `AndroidManifest.xml` file:\n\n ```xml\n <!-- Sample AdMob app ID: ca-app-pub-3940256099942544~3347511713 -->\n <meta-data\n android:name=\"com.google.android.gms.ads.APPLICATION_ID\"\n android:value=\"ca-app-pub-3940256099942544~3347511713\"/>\n ```\n\n **Note**: The sample AdMob App ID\n `ca-app-pub-3940256099942544~3347511713` is for testing purposes only.\n **ALWAYS** remind the user to replace it with their actual AdMob App ID\n before publishing.\n\n* **Initialize SDK**:\n\n - [ ] Initialize SDK on a background thread.\n\n### Implementation Details\n\n* **Version Management**: **ALWAYS** look up and use the latest stable\n version. Do not assume a version number.\n* **Initialization**: **ALWAYS** call `MobileAds.initialize()` on a background\n thread.\n\n## Banner Ads\n\nBanner ads are rectangular image or text ads that occupy a spot within an app's\nlayout. They remain on screen during user interaction and can refresh\nautomatically.\n\n### Strategic Recommendations\n\n* **Confirm Ad Type**: If the user asks for a \"banner ad\" without specifying a\n type, confirm the desired type.\n* **Suggest Large Anchored Adaptive**: Suggest large anchored adaptive banners\n over \"fixed size\". Explain they are designed to increase engagement and\n revenue potential. If told that large adaptive is too large, suggest\n standard anchored adaptive over fixed size ads.\n* **Type Clarifications**:\n * **Anchored Adaptive**: Ask if it should be anchored to the **top** or\n **bottom**.\n * **Inline Adaptive**: Use this type for ads placed inside scrollable\n content (e.g., `RecyclerView` or `ScrollView`). **Validate** the ad\n container is scrollable before implementing; if not scrollable, default\n to **Large Anchored Adaptive**.\n\n### Implementation Checklist\n\n- [ ] Create UI container for `AdView`.\n- [ ] Initialize `AdView` with ad unit ID and ad size.\n- [ ] Call `adView.loadAd()`.\n- [ ] **Mandatory**: Add `adView.destroy()` to the appropriate lifecycle\n cleanup (e.g., `onDestroy`).\n```\n\nExample:\n```text\n@gma-android-integrate Integrate the latest version of Google Mobile Ads SDK (Legacy) to my project.\n```\n\nExample:\n```text\n@gma-android-integrate Add an anchored adaptive banner ad to the bottom of the screen.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.954Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":94,"estimatedTokens":844}}538{"id":"doc-global_settings_unity_google_for_developers-b6609e6f","source":"documentation","title":"Global settings | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/global-settings","text":"Example:\n```text\n// Google Mobile Ads events are raised off the Unity main thread.\n\n// This log is executed off the Unity main thread.\n// Write all time-sensitive code before ExecuteInUpdate().\nDebug.Log(\"Executing off the Unity main thread.\");\n\n// Use ExecuteInUpdate to run code on the main thread, allowing you to\n// interact with Unity UI and GameObjects.\n// Changed to fully-qualified name to resolve CS0103\nGoogleMobileAds.Common.MobileAdsEventExecutor.ExecuteInUpdate(() =>\n{\n // This callback may be delayed on Android until the user returns to the app.\n Debug.Log(\"Executing on the Unity main thread.\");\n\n // Place all code that interacts with Unity UI and GameObjects inside this callback.\n if (_myGameObject != null)\n {\n _myGameObject.SetActive(true);\n }\n});GlobalSettingsSnippets.cs\n```\n\nExample:\n```text\n...\nusing GoogleMobileAds.Api;\n...\npublic class GoogleMobileAdsDemoScript : MonoBehaviour\n{\n public void Start()\n {\n // When true all events raised by GoogleMobileAds will be raised\n // on the Unity main thread. The default value is false.\n MobileAds.RaiseAdEventsOnUnityMainThread = true;\n }\n}\n```\n\nExample:\n```text\n// Set app volume to be half of current device volume.\nMobileAds.SetApplicationVolume(0.5f);\n```\n\nExample:\n```text\n// Set app to be muted.\nMobileAds.SetApplicationMuted(true);\n```\n\nExample:\n```text\n// Enable limited ads\nApplicationPreferences.SetInt(\"gad_has_consent_for_cookies\", 0);\n```\n\nExample:\n```text\n-keep class com.google.** { public *; }\n```\n\nExample:\n```text\n<manifest>\n <application>\n <meta-data\n android:name=\"com.google.android.gms.ads.flag.DISABLE_CRASH_REPORTING\"\n android:value=\"true\" />\n </application>\n</manifest>\n```\n\nExample:\n```text\nvoid Awake() {\n MobileAds.DisableSDKCrashReporting();\n}\n```\n\nExample:\n```text\n// Get the Unity SDK version.\nDebug.Log(\"Unity SDK Version: \" + MobileAds.GetVersion());\n```\n\nExample:\n```text\n// Get the underlying platform SDK version.\nDebug.Log(\"Platform SDK Version: \" + MobileAds.GetPlatformVersion());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.955Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":94,"estimatedTokens":524}}539{"id":"doc-validate_server_side_verification_ssv_callbacks_-eb439b64","source":"documentation","title":"Validate server-side verification (SSV) callbacks | Unity | Google for Developers","url":"https://developers.google.com/admob/unity/ssv","text":"Example:\n```text\nRewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()\n .fetchVerifyingPublicKeysWith(\n RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)\n .build();\nString rewardUrl = ...;\nverifier.verify(rewardUrl);\n```\n\nExample:\n```text\nprivate void LoadRewardedAd(string adUnitId)\n{\n // Send the request to load the ad.\n AdRequest adRequest = new AdRequest();\n RewardedAd.Load(adUnitId, adRequest, (RewardedAd rewardedAd, LoadAdError error) =>\n {\n // If the operation failed with a reason.\n if (error != null)\n {\n Debug.LogError(\"Rewarded ad failed to load an ad with error : \" + error);\n return;\n }\n\n var options = new ServerSideVerificationOptions\n .Builder()\n .SetCustomData(\"SAMPLE_CUSTOM_DATA_STRING\")\n .Build()\n rewardedAd.SetServerSideVerificationOptions(options);\n });\n}\n```\n\nExample:\n```text\n{\n \"keys\": [\n {\n keyId: 1916455855,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...YTPcw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFkwEwYHKoZIzj0CAQYI...ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw==\"\n },\n {\n keyId: 3901585526,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...aDUsw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFYwEAYHKoZIzj0CAQYF...4akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw==\"\n },\n ],\n}\n```\n\nExample:\n```text\nString url = ...;\nNetHttpTransport httpTransport = new NetHttpTransport.Builder().build();\nHttpRequest httpRequest =\n httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(url));\nHttpResponse httpResponse = httpRequest.execute();\nif (httpResponse.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) {\n throw new IOException(\"Unexpected status code = \" + httpResponse.getStatusCode());\n}\nString data;\nInputStream contentStream = httpResponse.getContent();\ntry {\n InputStreamReader reader = new InputStreamReader(contentStream, UTF_8);\n data = readerToString(reader);\n} finally {\n contentStream.close();\n}\n```\n\nExample:\n```text\nprivate static Map<Integer, ECPublicKey> parsePublicKeysJson(String publicKeysJson)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = new HashMap<>();\n try {\n JSONArray keys = new JSONObject(publicKeysJson).getJSONArray(\"keys\");\n for (int i = 0; i < keys.length(); i++) {\n JSONObject key = keys.getJSONObject(i);\n publicKeys.put(\n key.getInt(\"keyId\"),\n EllipticCurves.getEcPublicKey(Base64.decode(key.getString(\"base64\"))));\n }\n } catch (JSONException e) {\n throw new GeneralSecurityException(\"failed to extract trusted signing public keys\", e);\n }\n if (publicKeys.isEmpty()) {\n throw new GeneralSecurityException(\"No trusted keys are available.\");\n }\n return publicKeys;\n}\n```\n\nExample:\n```devsite-click-to-copy\nhttps://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins\n×tamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887\n```\n\nExample:\n```text\npublic static final String SIGNATURE_PARAM_NAME = \"signature=\";\n...\nURI uri;\ntry {\n uri = new URI(rewardUrl);\n} catch (URISyntaxException ex) {\n throw new GeneralSecurityException(ex);\n}\nString queryString = uri.getQuery();\nint i = queryString.indexOf(SIGNATURE_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a signature query parameter\");\n}\nbyte[] queryParamContentData =\n queryString\n .substring(0, i - 1)\n // i - 1 instead of i because of & in the query string\n .getBytes(Charset.forName(\"UTF-8\"));\n```\n\nExample:\n```text\npublic static final String KEY_ID_PARAM_NAME = \"key_id=\";\n...\nString sigAndKeyId = queryString.substring(i);\ni = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a key_id query parameter\");\n}\nString sig =\n sigAndKeyId.substring(\n SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */);\nint keyId = Integer.valueOf(sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length()));\n```\n\nExample:\n```text\nprivate void verify(final byte[] dataToVerify, int keyId, final byte[] signature)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = parsePublicKeysJson();\n if (publicKeys.containsKey(keyId)) {\n foundKeyId = true;\n ECPublicKey publicKey = publicKeys.get(keyId);\n EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, HashType.SHA256, EcdsaEncoding.DER);\n verifier.verify(signature, dataToVerify);\n } else {\n throw new GeneralSecurityException(\"cannot find verifying key with key ID: \" + keyId);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.956Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":155,"estimatedTokens":1162}}540{"id":"doc-openid_connect_sign_in_with_google_google_for_de-61a7678c","source":"documentation","title":"OpenID Connect | Sign in with Google | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/openid-connect","text":"Example:\n```text\n// Create a state token to prevent request forgery.\n// Store it in the session for later validation.\n$state = bin2hex(random_bytes(128/8));\n$app['session']->set('state', $state);\n// Set the client ID, token state, and application name in the HTML while\n// serving it.\nreturn $app['twig']->render('index.html', array(\n 'CLIENT_ID' => CLIENT_ID,\n 'STATE' => $state,\n 'APPLICATION_NAME' => APPLICATION_NAME\n));\n```\n\nExample:\n```text\n// Create a state token to prevent request forgery.\n// Store it in the session for later validation.\nString state = new BigInteger(130, new SecureRandom()).toString(32);\nrequest.session().attribute(\"state\", state);\n// Read index.html into memory, and set the client ID,\n// token state, and application name in the HTML before serving it.\nreturn new Scanner(new File(\"index.html\"), \"UTF-8\")\n .useDelimiter(\"\\\\A\").next()\n .replaceAll(\"[{]{2}\\\\s*CLIENT_ID\\\\s*[}]{2}\", CLIENT_ID)\n .replaceAll(\"[{]{2}\\\\s*STATE\\\\s*[}]{2}\", state)\n .replaceAll(\"[{]{2}\\\\s*APPLICATION_NAME\\\\s*[}]{2}\",\n APPLICATION_NAME);\n```\n\nExample:\n```text\n# Create a state token to prevent request forgery.\n# Store it in the session for later validation.\nstate = hashlib.sha256(os.urandom(1024)).hexdigest()\nsession['state'] = state\n# Set the client ID, token state, and application name in the HTML while\n# serving it.\nresponse = make_response(\n render_template('index.html',\n CLIENT_ID=CLIENT_ID,\n STATE=state,\n APPLICATION_NAME=APPLICATION_NAME))\n```\n\nExample:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth?\n response_type=code&\n client_id=424911365001.apps.googleusercontent.com&\n scope=openid%20email&\n redirect_uri=https%3A//developers.google.com/oauthplayground&\n state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2-login-demo.example.com%2FmyHome&\n login_hint=jsmith@example.com&\n nonce=0394852-3190485-2490358&\n hd=example.com\n```\n\nExample:\n```text\nhttps://developers.google.com/oauthplayground?state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foa2cb.example.com%2FmyHome&code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&scope=openid%20email%20https://www.googleapis.com/auth/userinfo.email\n```\n\nExample:\n```text\n// Ensure that there is no request forgery going on, and that the user\n// sending us this connect request is the user that was supposed to.\nif ($request->get('state') != ($app['session']->get('state'))) {\n return new Response('Invalid state parameter', 401);\n}\n```\n\nExample:\n```text\n// Ensure that there is no request forgery going on, and that the user\n// sending us this connect request is the user that was supposed to.\nif (!request.queryParams(\"state\").equals(\n request.session().attribute(\"state\"))) {\n response.status(401);\n return GSON.toJson(\"Invalid state parameter.\");\n}\n```\n\nExample:\n```text\n# Ensure that the request is not a forgery and that the user sending\n# this connect request is the expected user.\nif request.args.get('state', '') != session['state']:\n response = make_response(json.dumps('Invalid state parameter.'), 401)\n response.headers['Content-Type'] = 'application/json'\n return response\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.googleapis.com\nContent-Type: application/x-www-form-urlencoded\n\ncode=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&\nclient_id=your-client-id&\nclient_secret=your-client-secret&\nredirect_uri=https%3A//developers.google.com/oauthplayground&\ngrant_type=authorization_code\n```\n\nExample:\n```text\n{\n \"iss\": \"https://accounts.google.com\",\n \"azp\": \"1234987819200.apps.googleusercontent.com\",\n \"aud\": \"1234987819200.apps.googleusercontent.com\",\n \"sub\": \"10769150350006150715113082367\",\n \"at_hash\": \"HK6E_P6Dh8Y93mRNtsDB1Q\",\n \"hd\": \"example.com\",\n \"email\": \"jsmith@example.com\",\n \"email_verified\": \"true\",\n \"iat\": 1353601026,\n \"exp\": 1353604926,\n \"nonce\": \"0394852-3190485-2490358\"\n}\n```\n\nExample:\n```text\nscope=openid%20profile%20email\n```\n\nExample:\n```text\nhttps://accounts.google.com/.well-known/openid-configuration\n```\n\nExample:\n```text\n{\n \"issuer\": \"https://accounts.google.com\",\n \"authorization_endpoint\": \"https://accounts.google.com/o/oauth2/v2/auth\",\n \"device_authorization_endpoint\": \"https://oauth2.googleapis.com/device/code\",\n \"token_endpoint\": \"https://oauth2.googleapis.com/token\",\n \"userinfo_endpoint\": \"https://openidconnect.googleapis.com/v1/userinfo\",\n \"revocation_endpoint\": \"https://oauth2.googleapis.com/revoke\",\n \"jwks_uri\": \"https://www.googleapis.com/oauth2/v3/certs\",\n \"response_types_supported\": [\n \"code\",\n \"token\",\n \"id_token\",\n \"code token\",\n \"code id_token\",\n \"token id_token\",\n \"code token id_token\",\n \"none\"\n ],\n \"subject_types_supported\": [\n \"public\"\n ],\n \"id_token_signing_alg_values_supported\": [\n \"RS256\"\n ],\n \"scopes_supported\": [\n \"openid\",\n \"email\",\n \"profile\"\n ],\n \"token_endpoint_auth_methods_supported\": [\n \"client_secret_post\",\n \"client_secret_basic\"\n ],\n \"claims_supported\": [\n \"aud\",\n \"email\",\n \"email_verified\",\n \"exp\",\n \"family_name\",\n \"given_name\",\n \"iat\",\n \"iss\",\n \"locale\",\n \"name\",\n \"picture\",\n \"sub\"\n ],\n \"code_challenge_methods_supported\": [\n \"plain\",\n \"S256\"\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.959Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":191,"estimatedTokens":1311}}541{"id":"doc-optimize_initialization_and_ad_loading_android_g-a38e9496","source":"documentation","title":"Optimize initialization and ad loading | Android | Google for Developers","url":"https://developers.google.com/admob/android/optimize-initialization","text":"Example:\n```text\n<manifest>\n ...\n <application>\n ...\n <meta-data\n android:name=\"com.google.android.gms.ads.flag.OPTIMIZE_INITIALIZATION\"\n android:value=\"true\"/>\n </application>\n</manifest>\n```\n\nExample:\n```text\n<manifest>\n ...\n <application>\n ...\n <meta-data\n android:name=\"com.google.android.gms.ads.flag.OPTIMIZE_AD_LOADING\"\n android:value=\"true\"/>\n </application>\n</manifest>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.960Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":27,"estimatedTokens":115}}542{"id":"doc-enable_test_ads_android_google_for_developers-ab5fbad1","source":"documentation","title":"Enable test ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/test-ads","text":"Example:\n```devsite-click-to-copy\nI/Ads: Use RequestConfiguration.Builder.setTestDeviceIds(Arrays.asList(\"33BE2250B43518CCDA7DE426D04EE231\"))\nto get test ads on this device.\"\n```\n\nExample:\n```text\nList<String> testDeviceIds = Arrays.asList(\"TEST_DEVICE_ID\");\nRequestConfiguration configuration =\n new RequestConfiguration.Builder().setTestDeviceIds(testDeviceIds).build();\nMobileAds.setRequestConfiguration(configuration);RequestConfigurationSnippets.java\n```\n\nExample:\n```text\nval testDeviceIds = listOf(\"TEST_DEVICE_ID\")\nval configuration = RequestConfiguration.Builder().setTestDeviceIds(testDeviceIds).build()\nMobileAds.setRequestConfiguration(configuration)RequestConfigurationSnippets.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.960Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":22,"estimatedTokens":179}}543{"id":"doc-smart_banners_android_google_for_developers-65ef3687","source":"documentation","title":"Smart banners | Android | Google for Developers","url":"https://developers.google.com/admob/android/banner/smart","text":"Example:\n```devsite-click-to-copy\n<com.google.android.gms.ads.AdView\n xmlns:ads=\"http://schemas.android.com/apk/res-auto\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"wrap_content\"\n ads:adSize=\"SMART_BANNER\"\n ads:adUnitId=\"ca-app-pub-3940256099942544/6300978111\">\n</com.google.android.gms.ads.AdView>\n```\n\nExample:\n```text\nAdView adView = new AdView(this);\nadView.setAdSize(AdSize.SMART_BANNER);\n```\n\nExample:\n```text\nval adView = AdView(this)\nadView.adSize = AdSize.SMART_BANNER\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.961Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":24,"estimatedTokens":130}}544{"id":"doc-use_inline_adaptive_for_scrolling_banners_androi-a243cd6f","source":"documentation","title":"Use inline adaptive for scrolling banners | Android | Google for Developers","url":"https://developers.google.com/admob/android/banner/inline-adaptive","text":"Example:\n```text\nprivate val adWidth: Int\n get() {\n val displayMetrics = resources.displayMetrics\n val adWidthPixels =\n if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n val windowMetrics: WindowMetrics = this.windowManager.currentWindowMetrics\n windowMetrics.bounds.width()\n } else {\n displayMetrics.widthPixels\n }\n val density = displayMetrics.density\n return (adWidthPixels / density).toInt()\n }\nMainActivity.kt\n```\n\nExample:\n```text\npublic int getAdWidth() {\n DisplayMetrics displayMetrics = getResources().getDisplayMetrics();\n int adWidthPixels = displayMetrics.widthPixels;\n\n if (VERSION.SDK_INT >= VERSION_CODES.R) {\n WindowMetrics windowMetrics = this.getWindowManager().getCurrentWindowMetrics();\n adWidthPixels = windowMetrics.getBounds().width();\n }\n\n float density = displayMetrics.density;\n return (int) (adWidthPixels / density);\n}\nMainActivity.java\n```\n\nExample:\n```text\nval adView = AdView(this@MainActivity)\nadView.setAdSize(AdSize.getCurrentOrientationInlineAdaptiveBannerAdSize(this, adWidth))MainActivity.kt\n```\n\nExample:\n```text\nfinal AdView adView = new AdView(MainActivity.this);\nadView.setAdSize(AdSize.getCurrentOrientationInlineAdaptiveBannerAdSize(this, getAdWidth()));MainActivity.java\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.961Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":48,"estimatedTokens":325}}545{"id":"doc-use_collapsible_banners_android_google_for_devel-3a8d9838","source":"documentation","title":"Use collapsible banners | Android | Google for Developers","url":"https://developers.google.com/admob/android/banner/collapsible","text":"Example:\n```text\nprivate void loadCollapsibleBanner() {\n // Create an extra parameter that aligns the bottom of the expanded ad to\n // the bottom of the bannerView.\n Bundle extras = new Bundle();\n extras.putString(\"collapsible\", \"bottom\");\n\n // Create an ad request.\n AdRequest adRequest =\n new AdRequest.Builder().addNetworkExtrasBundle(AdMobAdapter.class, extras).build();\n\n // ...\n\n // Start loading a collapsible banner ad.\n adView.loadAd(adRequest);\n}\nCollapsibleBannerFragment.java\n```\n\nExample:\n```text\nprivate fun loadCollapsibleBanner() {\n // Create an extra parameter that aligns the bottom of the expanded ad to\n // the bottom of the bannerView.\n val extras = Bundle()\n extras.putString(\"collapsible\", \"bottom\")\n\n // Create an ad request.\n val adRequest =\n AdRequest.Builder().addNetworkExtrasBundle(AdMobAdapter::class.java, extras).build()\n\n // ...\n\n // Start loading a collapsible banner ad.\n adView.loadAd(adRequest)\n}\nCollapsibleBannerFragment.kt\n```\n\nExample:\n```text\npublic void onAdLoaded() {\n Log.i(\n MainActivity.LOG_TAG,\n String.format(\"Ad loaded. adView.isCollapsible() is %b.\", adView.isCollapsible()));\n}CollapsibleBannerFragment.java\n```\n\nExample:\n```text\noverride fun onAdLoaded() {\n Log.i(\n MainActivity.LOG_TAG,\n \"Ad loaded. adView.isCollapsible() is ${adView.isCollapsible}.\",\n )\n}CollapsibleBannerFragment.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.962Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":60,"estimatedTokens":351}}546{"id":"doc-load_a_native_ad_android_google_for_developers-e6b62e96","source":"documentation","title":"Load a native ad | Android | Google for Developers","url":"https://developers.google.com/admob/android/native","text":"Example:\n```text\n// It is recommended to call AdLoader.Builder on a background thread.\nnew Thread(\n () -> {\n AdLoader adLoader =\n new AdLoader.Builder(context, \"AD_UNIT_ID\")\n .forNativeAd(\n new NativeAd.OnNativeAdLoadedListener() {\n @Override\n // The native ad loaded successfully. You can show the ad.\n public void onNativeAdLoaded(@NonNull NativeAd nativeAd) {}\n })\n .withAdListener(\n new AdListener() {\n @Override\n // The native ad load failed. Check the adError message for failure\n // reasons.\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {}\n })\n // Use the NativeAdOptions.Builder class to specify individual options\n // settings.\n .withNativeAdOptions(new NativeAdOptions.Builder().build())\n .build();\n })\n .start();NativeAdSnippets.java\n```\n\nExample:\n```text\n// It is recommended to call AdLoader.Builder on a background thread.\nCoroutineScope(Dispatchers.IO).launch {\n val adLoader =\n AdLoader.Builder(context, \"AD_UNIT_ID\")\n .forNativeAd { nativeAd ->\n // The native ad loaded successfully. You can show the ad.\n }\n .withAdListener(\n object : AdListener() {\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // The native ad load failed. Check the adError message for failure reasons.\n }\n }\n )\n // Use the NativeAdOptions.Builder class to specify individual options settings.\n .withNativeAdOptions(NativeAdOptions.Builder().build())\n .build()\n}NativeAdSnippets.kt\n```\n\nExample:\n```text\nadLoaderBuilder.withAdListener(\n // Override AdListener callbacks here.\n new AdListener() {});NativeAdSnippets.java\n```\n\nExample:\n```text\nadLoaderBuilder.withAdListener(\n // Override AdListener callbacks here.\n object : AdListener() {}\n)NativeAdSnippets.kt\n```\n\nExample:\n```text\nadLoader.loadAd(new AdRequest.Builder().build());NativeAdSnippets.java\n```\n\nExample:\n```text\nadLoader.loadAd(AdRequest.Builder().build())NativeAdSnippets.kt\n```\n\nExample:\n```text\n// Load three native ads.\nadLoader.loadAds(new AdRequest.Builder().build(), 3);NativeAdSnippets.java\n```\n\nExample:\n```text\n// Load three native ads.\nadLoader.loadAds(AdRequest.Builder().build(), 3)NativeAdSnippets.kt\n```\n\nExample:\n```text\nadLoaderBuilder\n .forNativeAd(\n nativeAd -> {\n // This callback is invoked when a native ad is successfully loaded.\n })\n .build();NativeAdSnippets.java\n```\n\nExample:\n```text\nadLoaderBuilder\n .forNativeAd { nativeAd ->\n // This callback is invoked when a native ad is successfully loaded.\n }\n .build()NativeAdSnippets.kt\n```\n\nExample:\n```text\nnativeAd.destroy();NativeAdSnippets.java\n```\n\nExample:\n```text\nnativeAd.destroy()NativeAdSnippets.kt\n```\n\nExample:\n```text\n<application android:hardwareAccelerated=\"true\">\n <!-- For activities that use ads, hardwareAcceleration should be true. -->\n <activity android:hardwareAccelerated=\"true\" />\n <!-- For activities that don't use ads, hardwareAcceleration can be false. -->\n <activity android:hardwareAccelerated=\"false\" />\n</application>\nHardwareAccelerationSnippet.xml\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.963Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":128,"estimatedTokens":870}}547{"id":"doc-display_a_full_screen_native_ad_android_google_f-6fce042e","source":"documentation","title":"Display a full-screen native ad | Android | Google for Developers","url":"https://developers.google.com/admob/android/native/full-screen","text":"Example:\n```text\nNativeAdOptions nativeAdOptions =\n new NativeAdOptions.Builder().setMediaAspectRatio(MediaAspectRatio.PORTRAIT).build();\n\nAdLoader loader =\n new AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build();NativeAdOptionsSnippets.java\n```\n\nExample:\n```text\nval nativeAdOptions =\n NativeAdOptions.Builder().setMediaAspectRatio(MediaAspectRatio.PORTRAIT).build()\n\nval loader = AdLoader.Builder(context, \"AD_UNIT_ID\").withNativeAdOptions(nativeAdOptions).build()NativeAdOptionsSnippets.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.963Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":18,"estimatedTokens":138}}548{"id":"doc-app_open_ads_android_google_for_developers-d113903a","source":"documentation","title":"App open ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/app-open","text":"Example:\n```text\nca-app-pub-3940256099942544/9257395921\n```\n\nExample:\n```text\npublic class MyApplication extends Application\n implements ActivityLifecycleCallbacks, DefaultLifecycleObserver {\n\n private AppOpenAdManager appOpenAdManager;\n private Activity currentActivity;\n\n @Override\n public void onCreate() {\n super.onCreate();\n this.registerActivityLifecycleCallbacks(this);\n\n ProcessLifecycleOwner.get().getLifecycle().addObserver(this);\n appOpenAdManager = new AppOpenAdManager();\n }\nMyApplication.java\n```\n\nExample:\n```text\nclass MyApplication :\n Application(), Application.ActivityLifecycleCallbacks, DefaultLifecycleObserver {\n\n private lateinit var appOpenAdManager: AppOpenAdManager\n private var currentActivity: Activity? = null\n\n override fun onCreate() {\n super<Application>.onCreate()\n registerActivityLifecycleCallbacks(this)\n\n ProcessLifecycleOwner.get().lifecycle.addObserver(this)\n appOpenAdManager = AppOpenAdManager()\n }\nMyApplication.kt\n```\n\nExample:\n```text\n<!-- TODO: Update to reference your actual package name. -->\n<application\n android:name=\"com.google.android.gms.example.appopendemo.MyApplication\" ...>\n...\n</application>\n```\n\nExample:\n```text\nprivate class AppOpenAdManager {\n\n private static final String LOG_TAG = \"AppOpenAdManager\";\n private static final String AD_UNIT_ID = \"ca-app-pub-3940256099942544/9257395921\";\n\n private AppOpenAd appOpenAd = null;\n private boolean isLoadingAd = false;\n private boolean isShowingAd = false;\n\n /** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */\n private long loadTime = 0;\n\n /** Constructor. */\n public AppOpenAdManager() {}MyApplication.java\n```\n\nExample:\n```text\nprivate inner class AppOpenAdManager {\n\n private var appOpenAd: AppOpenAd? = null\n private var isLoadingAd = false\n var isShowingAd = false\n\n /** Keep track of the time an app open ad is loaded to ensure you don't show an expired ad. */\n private var loadTime: Long = 0MyApplication.kt\n```\n\nExample:\n```text\nAppOpenAd.load(\n context,\n \"AD_UNIT_ID\",\n new AdRequest.Builder().build(),\n new AppOpenAdLoadCallback() {\n @Override\n public void onAdLoaded(AppOpenAd ad) {\n // Called when an app open ad has loaded.\n Log.d(LOG_TAG, \"App open ad loaded.\");\n\n appOpenAd = ad;\n isLoadingAd = false;\n loadTime = (new Date()).getTime();\n }\n\n @Override\n public void onAdFailedToLoad(LoadAdError loadAdError) {\n // Called when an app open ad has failed to load.\n Log.d(LOG_TAG, \"App open ad failed to load with error: \" + loadAdError.getMessage());\n\n isLoadingAd = false;\n }\n });MyApplication.java\n```\n\nExample:\n```text\nAppOpenAd.load(\n context,\n \"AD_UNIT_ID\",\n AdRequest.Builder().build(),\n object : AppOpenAdLoadCallback() {\n override fun onAdLoaded(ad: AppOpenAd) {\n // Called when an app open ad has loaded.\n Log.d(LOG_TAG, \"App open ad loaded.\")\n\n appOpenAd = ad\n isLoadingAd = false\n loadTime = Date().time\n }\n\n override fun onAdFailedToLoad(loadAdError: LoadAdError) {\n // Called when an app open ad has failed to load.\n Log.d(LOG_TAG, \"App open ad failed to load with error: \" + loadAdError.message)\n\n isLoadingAd = false\n }\n },\n)MyApplication.kt\n```\n\nExample:\n```text\npublic void showAdIfAvailable(\n @NonNull final Activity activity,\n @NonNull OnShowAdCompleteListener onShowAdCompleteListener) {\n // If the app open ad is already showing, do not show the ad again.\n if (isShowingAd) {\n Log.d(TAG, \"The app open ad is already showing.\");\n return;\n }\n\n // If the app open ad is not available yet, invoke the callback then load the ad.\n if (appOpenAd == null) {\n Log.d(TAG, \"The app open ad is not ready yet.\");\n onShowAdCompleteListener.onShowAdComplete();\n // Load an ad.\n return;\n }\n\n isShowingAd = true;\n appOpenAd.show(activity);\n}\nAppOpenAdSnippets.java\n```\n\nExample:\n```text\nfun showAdIfAvailable(activity: Activity, onShowAdCompleteListener: OnShowAdCompleteListener) {\n // If the app open ad is already showing, do not show the ad again.\n if (isShowingAd) {\n Log.d(TAG, \"The app open ad is already showing.\")\n return\n }\n\n // If the app open ad is not available yet, invoke the callback then load the ad.\n if (appOpenAd == null) {\n Log.d(TAG, \"The app open ad is not ready yet.\")\n onShowAdCompleteListener.onShowAdComplete()\n // Load an ad.\n return\n }\n\n isShowingAd = true\n appOpenAd?.show(activity)\n}\nAppOpenAdSnippets.kt\n```\n\nExample:\n```text\nappOpenAd.setFullScreenContentCallback(\n new FullScreenContentCallback() {\n @Override\n public void onAdDismissedFullScreenContent() {\n // Called when full screen content is dismissed.\n Log.d(TAG, \"Ad dismissed fullscreen content.\");\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n appOpenAd = null;\n isShowingAd = false;\n\n onShowAdCompleteListener.onShowAdComplete();\n // Load an ad.\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) {\n // Called when full screen content failed to show.\n Log.d(TAG, adError.getMessage());\n appOpenAd = null;\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n isShowingAd = false;\n\n onShowAdCompleteListener.onShowAdComplete();\n // Load an ad.\n }\n\n @Override\n public void onAdShowedFullScreenContent() {\n Log.d(TAG, \"Ad showed fullscreen content.\");\n }\n\n @Override\n public void onAdImpression() {\n // Called when an impression is recorded for an ad.\n Log.d(TAG, \"The ad recorded an impression.\");\n }\n\n @Override\n public void onAdClicked() {\n // Called when ad is clicked.\n Log.d(TAG, \"The ad was clicked.\");\n }\n });AppOpenAdSnippets.java\n```\n\nExample:\n```text\nappOpenAd?.fullScreenContentCallback =\n object : FullScreenContentCallback() {\n override fun onAdDismissedFullScreenContent() {\n // Called when full screen content is dismissed.\n Log.d(TAG, \"Ad dismissed fullscreen content.\")\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n appOpenAd = null\n isShowingAd = false\n\n onShowAdCompleteListener.onShowAdComplete()\n // Load an ad.\n }\n\n override fun onAdFailedToShowFullScreenContent(adError: AdError) {\n // Called when full screen content failed to show.\n Log.d(TAG, adError.message)\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n appOpenAd = null\n isShowingAd = false\n\n onShowAdCompleteListener.onShowAdComplete()\n // Load an ad.\n }\n\n override fun onAdShowedFullScreenContent() {\n Log.d(TAG, \"Ad showed fullscreen content.\")\n }\n\n override fun onAdImpression() {\n // Called when an impression is recorded for an ad.\n Log.d(TAG, \"The ad recorded an impression.\")\n }\n\n override fun onAdClicked() {\n // Called when ad is clicked.\n Log.d(TAG, \"The ad was clicked.\")\n }\n }AppOpenAdSnippets.kt\n```\n\nExample:\n```text\n/** Check if ad was loaded more than n hours ago. */\nprivate boolean wasLoadTimeLessThanNHoursAgo(long numHours) {\n long dateDifference = (new Date()).getTime() - loadTime;\n long numMilliSecondsPerHour = 3600000;\n return (dateDifference < (numMilliSecondsPerHour * numHours));\n}\n\n/** Check if ad exists and can be shown. */\nprivate boolean isAdAvailable() {\n // For time interval details, see: https://support.google.com/admob/answer/9341964\n return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4);\n}\nMyApplication.java\n```\n\nExample:\n```text\n/** Check if ad was loaded more than n hours ago. */\nprivate fun wasLoadTimeLessThanNHoursAgo(numHours: Long): Boolean {\n val dateDifference: Long = Date().time - loadTime\n val numMilliSecondsPerHour: Long = 3600000\n return dateDifference < numMilliSecondsPerHour * numHours\n}\n\n/** Check if ad exists and can be shown. */\nprivate fun isAdAvailable(): Boolean {\n // For time interval details, see: https://support.google.com/admob/answer/9341964\n return appOpenAd != null && wasLoadTimeLessThanNHoursAgo(4)\n}\nMyApplication.kt\n```\n\nExample:\n```text\n@Override\npublic void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {}\n\n@Override\npublic void onActivityStarted(@NonNull Activity activity) {\n // An ad activity is started when an ad is showing, which could be AdActivity class from Google\n // SDK or another activity class implemented by a third party mediation partner. Updating the\n // currentActivity only when an ad is not showing will ensure it is not an ad activity, but the\n // one that shows the ad.\n if (!appOpenAdManager.isShowingAd) {\n currentActivity = activity;\n }\n}\n\n@Override\npublic void onActivityResumed(@NonNull Activity activity) {}\n\n@Override\npublic void onActivityPaused(@NonNull Activity activity) {}\n\n@Override\npublic void onActivityStopped(@NonNull Activity activity) {}\n\n@Override\npublic void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {}\n\n@Override\npublic void onActivityDestroyed(@NonNull Activity activity) {}\nMyApplication.java\n```\n\nExample:\n```text\noverride fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}\n\noverride fun onActivityStarted(activity: Activity) {\n // An ad activity is started when an ad is showing, which could be AdActivity class from Google\n // SDK or another activity class implemented by a third party mediation partner. Updating the\n // currentActivity only when an ad is not showing will ensure it is not an ad activity, but the\n // one that shows the ad.\n if (!appOpenAdManager.isShowingAd) {\n currentActivity = activity\n }\n}\n\noverride fun onActivityResumed(activity: Activity) {}\n\noverride fun onActivityPaused(activity: Activity) {}\n\noverride fun onActivityStopped(activity: Activity) {}\n\noverride fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}\n\noverride fun onActivityDestroyed(activity: Activity) {}\nMyApplication.kt\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"androidx.lifecycle:lifecycle-process:2.8.3\")\n }\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'androidx.lifecycle:lifecycle-process:2.8.3'\n }\n```\n\nExample:\n```text\n@Override\npublic void onStart(@NonNull LifecycleOwner owner) {\n DefaultLifecycleObserver.super.onStart(owner);\n // Show the ad (if available) when the app moves to foreground.\n appOpenAdManager.showAdIfAvailable(currentActivity);\n}\nMyApplication.java\n```\n\nExample:\n```text\noverride fun onStart(owner: LifecycleOwner) {\n super.onStart(owner)\n currentActivity?.let {\n // Show the ad (if available) when the app moves to foreground.\n appOpenAdManager.showAdIfAvailable(it)\n }\n}\nMyApplication.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.965Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":20,"totalLines":408,"estimatedTokens":2806}}549{"id":"doc-set_a_fixed_banner_size_android_google_for_devel-40455e54","source":"documentation","title":"Set a fixed banner size | Android | Google for Developers","url":"https://developers.google.com/admob/android/banner/fixed-size","text":"Example:\n```text\nW/Ads: Not enough space to show ad. Needs 320x50 dp, but only has 288x495 dp.\n```\n\nExample:\n```text\n<com.google.android.gms.ads.AdView\n xmlns:ads=\"http://schemas.android.com/apk/res-auto\"\n android:id=\"@+id/banner_ad_view\"\n android:layout_width=\"wrap_content\"\n android:layout_height=\"wrap_content\"\n android:layout_centerHorizontal=\"true\"\n android:layout_alignParentBottom=\"true\"\n ads:adSize=\"BANNER\"\n ads:adUnitId=\"ca-app-pub-3940256099942544/6300978111\" />\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.965Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":19,"estimatedTokens":129}}550{"id":"doc-validate_your_native_ads_android_google_for_deve-daf90fd7","source":"documentation","title":"Validate your native ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/native/validator","text":"Example:\n```text\n<manifest>\n <application>\n <!-- Sample AdMob App ID: ca-app-pub-3940256099942544~3347511713 -->\n <meta-data\n android:name=\"com.google.android.gms.ads.APPLICATION_ID\"\n android:value=\"ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy\"/>\n <meta-data android:name=\"com.google.android.gms.ads.flag.NATIVE_AD_DEBUGGER_ENABLED\"\n android:value=\"false\" />\n </application>\n</manifest>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.966Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":15,"estimatedTokens":108}}551{"id":"doc-set_up_banner_ads_android_google_for_developers-c22d3d81","source":"documentation","title":"Set up banner ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/banner","text":"Example:\n```text\n<!-- Ad view container that fills the width of the screen and adjusts its\n height to the content of the ad. -->\n<FrameLayout\n android:id=\"@+id/ad_view_container\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"wrap_content\"\n android:layout_centerInParent=\"true\"\n android:layout_alignParentBottom=\"true\" />\n```\n\nExample:\n```text\n// Place the ad view at the bottom of the screen.\nColumn(modifier = modifier.fillMaxSize(), verticalArrangement = Arrangement.Bottom) {\n Box(modifier = modifier.fillMaxWidth()) { BannerAd(adView, modifier) }\n}BannerScreen.kt\n```\n\nExample:\n```text\n// Request a large anchored adaptive banner with a width of 360.\nadView.setAdSize(AdSize.getLargeAnchoredAdaptiveBannerAdSize(this, 360));MyActivity.java\n```\n\nExample:\n```text\n// Request a large anchored adaptive banner with a width of 360.\nadView.setAdSize(AdSize.getLargeAnchoredAdaptiveBannerAdSize(this, 360))MainActivity.kt\n```\n\nExample:\n```text\n// Set a large anchored adaptive banner ad size with a given width.\nval adSize = AdSize.getLargeAnchoredAdaptiveBannerAdSize(LocalContext.current, 360)\nadView.setAdSize(adSize)BannerScreen.kt\n```\n\nExample:\n```text\n// Create a new ad view.\nadView = new AdView(this);\nadView.setAdUnitId(AD_UNIT_ID);\n// Request a large anchored adaptive banner with a width of 360.\nadView.setAdSize(AdSize.getLargeAnchoredAdaptiveBannerAdSize(this, 360));\n\n// Replace ad container with new ad view.\nadContainerView.removeAllViews();\nadContainerView.addView(adView);MyActivity.java\n```\n\nExample:\n```text\n// Create a new ad view.\nval adView = AdView(this)\nadView.adUnitId = AD_UNIT_ID\n// Request a large anchored adaptive banner with a width of 360.\nadView.setAdSize(AdSize.getLargeAnchoredAdaptiveBannerAdSize(this, 360))\nthis.adView = adView\n\n// Replace ad container with new ad view.\nbinding.adViewContainer.removeAllViews()\nbinding.adViewContainer.addView(adView)MainActivity.kt\n```\n\nExample:\n```text\nval adView = remember { AdView(context) }\n\n// Setup and load the adview.\n// Set the unique ID for this specific ad unit.\nadView.adUnitId = BANNER_AD_UNIT_ID\n\n// Set a large anchored adaptive banner ad size with a given width.\nval adSize = AdSize.getLargeAnchoredAdaptiveBannerAdSize(LocalContext.current, 360)\nadView.setAdSize(adSize)\n\n// Place the ad view at the bottom of the screen.\nColumn(modifier = modifier.fillMaxSize(), verticalArrangement = Arrangement.Bottom) {\n Box(modifier = modifier.fillMaxWidth()) { BannerAd(adView, modifier) }\n}BannerScreen.kt\n```\n\nExample:\n```text\nAdRequest adRequest = new AdRequest.Builder().build();\nadView.loadAd(adRequest);MyActivity.java\n```\n\nExample:\n```text\nval adRequest = AdRequest.Builder().build()\nadView.loadAd(adRequest)MainActivity.kt\n```\n\nExample:\n```text\npublic void destroyBanner() {\n // Remove banner from view hierarchy.\n if (adView != null) {\n View parentView = (View) adView.getParent();\n if (parentView instanceof ViewGroup) {\n ((ViewGroup) parentView).removeView(adView);\n }\n\n // Destroy the banner ad resources.\n adView.destroy();\n }\n\n // Drop reference to the banner ad.\n adView = null;\n}\nBannerSnippets.java\n```\n\nExample:\n```text\nfun destroyBanner() {\n // Remove banner from view hierarchy.\n val parentView = adView?.parent\n if (parentView is ViewGroup) {\n parentView.removeView(adView)\n }\n\n // Destroy the banner ad resources.\n adView?.destroy()\n\n // Drop reference to the banner ad.\n adView = null\n}\nBannerSnippets.kt\n```\n\nExample:\n```text\nif (adView != null) {\n adView.setAdListener(\n new AdListener() {\n @Override\n public void onAdClicked() {\n // Code to be executed when the user clicks on an ad.\n }\n\n @Override\n public void onAdClosed() {\n // Code to be executed when the user is about to return\n // to the app after tapping on an ad.\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError adError) {\n // Code to be executed when an ad request fails.\n }\n\n @Override\n public void onAdImpression() {\n // Code to be executed when an impression is recorded\n // for an ad.\n }\n\n @Override\n public void onAdLoaded() {\n // Code to be executed when an ad finishes loading.\n }\n\n @Override\n public void onAdOpened() {\n // Code to be executed when an ad opens an overlay that\n // covers the screen.\n }\n });\n}BannerSnippets.java\n```\n\nExample:\n```text\nadView?.adListener =\n object : AdListener() {\n override fun onAdClicked() {\n // Code to be executed when the user clicks on an ad.\n }\n\n override fun onAdClosed() {\n // Code to be executed when the user is about to return\n // to the app after tapping on an ad.\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // Code to be executed when an ad request fails.\n }\n\n override fun onAdImpression() {\n // Code to be executed when an impression is recorded\n // for an ad.\n }\n\n override fun onAdLoaded() {\n // Code to be executed when an ad finishes loading.\n }\n\n override fun onAdOpened() {\n // Code to be executed when an ad opens an overlay that\n // covers the screen.\n }\n }BannerSnippets.kt\n```\n\nExample:\n```text\n<application android:hardwareAccelerated=\"true\">\n <!-- For activities that use ads, hardwareAcceleration should be true. -->\n <activity android:hardwareAccelerated=\"true\" />\n <!-- For activities that don't use ads, hardwareAcceleration can be false. -->\n <activity android:hardwareAccelerated=\"false\" />\n</application>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.966Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":219,"estimatedTokens":1426}}552{"id":"doc-migrate_sdk_versions_android_google_for_develope-0ec162c9","source":"documentation","title":"Migrate SDK versions | Android | Google for Developers","url":"https://developers.google.com/admob/android/migration","text":"Example:\n```text\npublic class MyActivity extends AppCompatActivity {\n ...\n private AdSize getFullWidthAdaptiveSize() {\n Display display = getWindowManager().getDefaultDisplay();\n DisplayMetrics outMetrics = new DisplayMetrics();\n display.getMetrics(outMetrics);\n\n float widthPixels = outMetrics.widthPixels;\n float density = outMetrics.density;\n\n int adWidth = (int) (widthPixels / density);\n return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(this, adWidth);\n }\n}\n```\n\nExample:\n```text\nclass MyActivity : AppCompatActivity() {\n ...\n private val adaptiveAdSize: AdSize\n get() {\n val display = windowManager.defaultDisplay\n val outMetrics = DisplayMetrics()\n display.getMetrics(outMetrics)\n\n val density = outMetrics.density\n\n var adWidthPixels = ad_view_container.width.toFloat()\n if (adWidthPixels == 0f) {\n adWidthPixels = outMetrics.widthPixels.toFloat()\n }\n\n val adWidth = (adWidthPixels / density).toInt()\n return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(this, adWidth)\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.968Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":42,"estimatedTokens":278}}553{"id":"doc-display_a_native_ad_android_google_for_developer-00ef46bc","source":"documentation","title":"Display a native ad | Android | Google for Developers","url":"https://developers.google.com/admob/android/native/advanced","text":"Example:\n```text\n<com.google.android.gms.ads.nativead.NativeAdView\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"wrap_content\">\n <LinearLayout\n android:orientation=\"vertical\">\n <LinearLayout\n android:orientation=\"horizontal\">\n <ImageView\n android:id=\"@+id/ad_app_icon\" />\n <TextView\n android:id=\"@+id/ad_headline\" />\n </LinearLayout>\n <!--Add remaining assets such as the image and media view.-->\n </LinearLayout>\n</com.google.android.gms.ads.nativead.NativeAdView>\n```\n\nExample:\n```text\nimport com.google.android.gms.compose_util.NativeAdAttribution\n import com.google.android.gms.compose_util.NativeAdView\n\n @Composable\n /** Display a native ad with a user defined template. */\n fun DisplayNativeAdView(nativeAd: NativeAd) {\n NativeAdView {\n // Display the ad attribution.\n NativeAdAttribution(text = context.getString(\"Ad\"))\n // Add remaining assets such as the image and media view.\n }\n }\n```\n\nExample:\n```text\nAdLoader.Builder builder = new AdLoader.Builder(this, \"ca-app-pub-3940256099942544/2247696110\")\n .forNativeAd(new NativeAd.OnNativeAdLoadedListener() {\n @Override\n public void onNativeAdLoaded(NativeAd nativeAd) {\n // Assumes you have a placeholder FrameLayout in your View layout\n // (with ID fl_adplaceholder) where the ad is to be placed.\n FrameLayout frameLayout =\n findViewById(R.id.fl_adplaceholder);\n // Assumes that your ad layout is in a file call native_ad_layout.xml\n // in the res/layout folder\n NativeAdView adView = (NativeAdView) getLayoutInflater()\n .inflate(R.layout.native_ad_layout, null);\n // This method sets the assets into the ad view.\n displayNativeAd(nativeAd, adView);\n frameLayout.removeAllViews();\n frameLayout.addView(adView);\n }\n});\n```\n\nExample:\n```text\nval builder = AdLoader.Builder(this, \"ca-app-pub-3940256099942544/2247696110\")\n .forNativeAd { nativeAd ->\n // Assumes you have a placeholder FrameLayout in your View layout\n // (with ID fl_adplaceholder) where the ad is to be placed.\n val frameLayout: FrameLayout = findViewById(R.id.fl_adplaceholder)\n // Assumes that your ad layout is in a file call native_ad_layout.xml\n // in the res/layout folder\n val adView = layoutInflater\n .inflate(R.layout.native_ad_layout, null) as NativeAdView\n // This method sets the assets into the ad view.\n displayNativeAd(nativeAd, adView)\n frameLayout.removeAllViews()\n frameLayout.addView(adView)\n }\n```\n\nExample:\n```text\n@Composable\n/** Load and display a native ad. */\nfun NativeScreen() {\n var nativeAd by remember { mutableStateOf<NativeAd?>(null) }\n val context = LocalContext.current\n var isDisposed by remember { mutableStateOf(false) }\n\n DisposableEffect(Unit) {\n // Load the native ad when we launch this screen\n loadNativeAd(\n context = context,\n onAdLoaded = { ad ->\n // Handle the native ad being loaded.\n if (!isDisposed) {\n nativeAd = ad\n } else {\n // Destroy the native ad if loaded after the screen is disposed.\n ad.destroy()\n }\n },\n )\n // Destroy the native ad to prevent memory leaks when we dispose of this screen.\n onDispose {\n isDisposed = true\n nativeAd?.destroy()\n nativeAd = null\n }\n }\n\n // Display the native ad view with a user defined template.\n nativeAd?.let { adValue -> DisplayNativeAdView(adValue) }\n}\n\nfun loadNativeAd(context: Context, onAdLoaded: (NativeAd) -> Unit) {\n val adLoader =\n AdLoader.Builder(context, NATIVE_AD_UNIT_ID)\n .forNativeAd { nativeAd -> onAdLoaded(nativeAd) }\n .withAdListener(\n object : AdListener() {\n override fun onAdFailedToLoad(error: LoadAdError) {\n Log.e(TAG, \"Native ad failed to load: ${error.message}\")\n }\n\n override fun onAdLoaded() {\n Log.d(TAG, \"Native ad was loaded.\")\n }\n\n override fun onAdImpression() {\n Log.d(TAG, \"Native ad recorded an impression.\")\n }\n\n override fun onAdClicked() {\n Log.d(TAG, \"Native ad was clicked.\")\n }\n }\n )\n .build()\n adLoader.loadAd(AdRequest.Builder().build())\n}\nNativeScreen.kt\n```\n\nExample:\n```text\nprivate void displayNativeAd(ViewGroup parent, NativeAd ad) {\n\n // Inflate a layout and add it to the parent ViewGroup.\n LayoutInflater inflater = (LayoutInflater) parent.getContext()\n .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n NativeAdView adView = (NativeAdView) inflater\n .inflate(R.layout.ad_layout_file, parent);\n\n // Locate the view that will hold the headline, set its text, and call the\n // NativeAdView's setHeadlineView method to register it.\n TextView headlineView = adView.findViewById<TextView>(R.id.ad_headline);\n headlineView.setText(ad.getHeadline());\n adView.setHeadlineView(headlineView);\n\n // Repeat the process for the other assets in the NativeAd\n // using additional view objects (Buttons, ImageViews, etc).\n\n // If you use a MediaView, call theNativeAdView.setMediaView() method\n // before calling the NativeAdView.setNativeAd() method.\n MediaView mediaView = (MediaView) adView.findViewById(R.id.ad_media);\n adView.setMediaView(mediaView);\n\n // Register the native ad with its ad view.\n adView.setNativeAd(ad);\n\n // Ensure that the parent view doesn't already contain an ad view.\n parent.removeAllViews();\n\n // Place the AdView into the parent.\n parent.addView(adView);\n}\n```\n\nExample:\n```text\nfun displayNativeAd(parent: ViewGroup, ad: NativeAd) {\n\n // Inflate a layout and add it to the parent ViewGroup.\n val inflater = parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)\n as LayoutInflater\n val adView = inflater.inflate(R.layout.ad_layout_file, parent) as NativeAdView\n\n // Locate the view that will hold the headline, set its text, and use the\n // NativeAdView's headlineView property to register it.\n val headlineView = adView.findViewById<TextView>(R.id.ad_headline)\n headlineView.text = ad.headline\n adView.headlineView = headlineView\n\n // Repeat the process for the other assets in the NativeAd using\n // additional view objects (Buttons, ImageViews, etc).\n\n val mediaView = adView.findViewById<MediaView>(R.id.ad_media)\n adView.mediaView = mediaView\n\n // Call the NativeAdView's setNativeAd method to register the\n // NativeAdObject.\n adView.setNativeAd(ad)\n\n // Ensure that the parent view doesn't already contain an ad view.\n parent.removeAllViews()\n\n // Place the AdView into the parent.\n parent.addView(adView)\n}\n```\n\nExample:\n```text\n@Composable\n/** Display a native ad with a user defined template. */\nfun DisplayNativeAdView(nativeAd: NativeAd) {\n Box(modifier = Modifier.padding(8.dp)) {\n // Call the NativeAdView composable to display the native ad.\n NativeAdView(nativeAd) {\n Column(modifier = Modifier.fillMaxWidth()) {\n Box {\n Row(modifier = Modifier.fillMaxWidth()) {\n // If available, display the icon asset.\n nativeAd.icon?.let { icon ->\n NativeAdIconView(Modifier.padding(5.dp)) {\n icon.drawable?.toBitmap()?.let { bitmap ->\n Image(bitmap = bitmap.asImageBitmap(), \"Icon\")\n }\n }\n }\n Column {\n // If available, display the headline asset.\n nativeAd.headline?.let {\n NativeAdHeadlineView {\n Text(text = it, style = MaterialTheme.typography.headlineLarge)\n }\n }\n // If available, display the star rating asset.\n nativeAd.starRating?.let {\n NativeAdStarRatingView {\n Text(text = \"Rated $it\", style = MaterialTheme.typography.labelMedium)\n }\n }\n }\n }\n // Display the ad attribution.\n NativeAdAttribution(\n modifier = Modifier.align(Alignment.TopStart),\n text = stringResource(R.string.attribution),\n )\n }\n\n // Display the media asset.\n NativeAdMediaView(modifier = Modifier.fillMaxWidth())\n\n // If available, display the body asset.\n nativeAd.body?.let {\n NativeAdBodyView(modifier = Modifier.padding(5.dp)) { Text(text = it) }\n }\n\n Row(Modifier.align(Alignment.End).padding(5.dp)) {\n // If available, display the price asset.\n nativeAd.price?.let {\n NativeAdPriceView(Modifier.padding(5.dp).align(Alignment.CenterVertically)) {\n Text(text = it)\n }\n }\n // If available, display the store asset.\n nativeAd.store?.let {\n NativeAdStoreView(Modifier.padding(5.dp).align(Alignment.CenterVertically)) {\n Text(text = it)\n }\n }\n // If available, display the call to action asset.\n nativeAd.callToAction?.let { callToAction ->\n NativeAdCallToActionView(Modifier.padding(5.dp)) { NativeAdButton(text = callToAction) }\n }\n }\n }\n }\n }\n}\nNativeScreen.kt\n```\n\nExample:\n```text\nAdLoader adLoader = new AdLoader.Builder(context, \"ca-app-pub-3940256099942544/2247696110\")\n // ...\n .withAdListener(new AdListener() {\n @Override\n public void onAdFailedToLoad(LoadAdError adError) {\n // Handle the failure by logging.\n }\n @Override\n public void onAdClicked() {\n // Log the click event or other custom behavior.\n }\n })\n .build();\n```\n\nExample:\n```text\nval adLoader = AdLoader.Builder(this, \"ca-app-pub-3940256099942544/2247696110\")\n // ...\n .withAdListener(object : AdListener() {\n override fun onAdFailedToLoad(adError: LoadAdError) {\n // Handle the failure.\n }\n override fun onAdClicked() {\n // Log the click event or other custom behavior.\n }\n })\n .build()\n```\n\nExample:\n```text\nmediaView.setImageScaleType(ImageView.ScaleType.CENTER_CROP);\n```\n\nExample:\n```text\nmediaView.imageScaleType = ImageView.ScaleType.CENTER_CROP\n```\n\nExample:\n```text\nnativeAd.destroy();\n```\n\nExample:\n```text\nnativeAd.destroy()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.970Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":335,"estimatedTokens":2643}}554{"id":"doc-release_notes_android_google_for_developers-4f486d0f","source":"documentation","title":"Release notes | Android | Google for Developers","url":"https://developers.google.com/admob/android/rel-notes","text":"Example:\n```text\nFatal Exception: java.lang.IllegalArgumentException:\ncom.mycompany.myapp: Targeting S+ (version 10000 and above)\nrequires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be\nspecified when creating a PendingIntent.\nStrongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE\nif some functionality depends on the PendingIntent being mutable,\ne.g. if it needs to be used with inline replies or bubbles.\n at android.app.PendingIntent.checkFlags(PendingIntent.java:386)\n at android.app.PendingIntent.getBroadcastAsUser(PendingIntent.java:657)\n at android.app.PendingIntent.getBroadcast(PendingIntent.java:644)\n at androidx.work.impl.utils.ForceStopRunnable.getPendingIntent(ForceStopRunnable.java:174)\n at androidx.work.impl.utils.ForceStopRunnable.isForceStopped(ForceStopRunnable.java:108)\n at androidx.work.impl.utils.ForceStopRunnable.run(ForceStopRunnable.java:86)\n at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:75)\n at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)\n at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)\n at java.lang.Thread.run(Thread.java:920)\n```\n\nExample:\n```text\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:20.4.0'\n\n // For apps targeting Android 12, add WorkManager dependency.\n constraints {\n implementation('androidx.work:work-runtime:2.7.0') {\n because '''androidx.work:work-runtime:2.1.0 pulled from\n play-services-ads has a bug using PendingIntent without\n FLAG_IMMUTABLE or FLAG_MUTABLE and will fail in Apps\n targeting S+.'''\n }\n }\n}\n```\n\nExample:\n```text\ncom.google.android.gms:play-services-measurement:17.0.0\ncom.google.android.gms:play-services-measurement-sdk:17.0.0\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.973Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":45,"estimatedTokens":451}}555{"id":"doc-integrate_chartboost_with_mediation_android_goog-3160c982","source":"documentation","title":"Integrate Chartboost with mediation | Android | Google for Developers","url":"https://developers.google.com/admob/android/mediation/chartboost","text":"Example:\n```text\ndependencyResolutionManagement {\n repositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://cboost.jfrog.io/artifactory/chartboost-ads/\")\n }\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation(\"com.google.android.gms:play-services-ads:25.4.0\")\n implementation(\"com.google.ads.mediation:chartboost:9.13.0.0\")\n}\n```\n\nExample:\n```devsite-click-to-copy\ndependencies {\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n implementation 'com.google.ads.mediation:chartboost:9.13.0.0'\n}\n```\n\nExample:\n```text\nDataUseConsent dataUseConsent = new CCPA(CCPA.CCPA_CONSENT.OPT_IN_SALE);\nChartboost.addDataUseConsent(context, dataUseConsent);\n```\n\nExample:\n```text\nval dataUseConsent = CCPA(CCPA.CCPA_CONSENT.OPT_IN_SALE)\nChartboost.addDataUseConsent(context, dataUseConsent)\n```\n\nExample:\n```text\nandroid:configChanges=\"keyboardHidden|orientation|screenSize\"\n```\n\nExample:\n```text\n<uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n```\n\nExample:\n```text\ncom.google.ads.mediation.chartboost.ChartboostAdapter\ncom.google.ads.mediation.chartboost.ChartboostMediationAdapter\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.975Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":58,"estimatedTokens":299}}556{"id":"doc-style_ad_layouts_with_native_templates_android_g-2c0137c6","source":"documentation","title":"Style ad layouts with native templates | Android | Google for Developers","url":"https://developers.google.com/admob/android/native/templates","text":"Example:\n```text\n@layout/gnt_small_template_view\n```\n\nExample:\n```text\n@layout/gnt_medium_template_view\n```\n\nExample:\n```text\ndependencies {\n ...\n implementation project(':nativetemplates')\n ...\n}\n```\n\nExample:\n```text\n<LinearLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n xmlns:tools=\"http://schemas.android.com/tools\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\"\n tools:context=\".MainActivity\"\n tools:showIn=\"@layout/activity_main\" >\n\n<!-- This is your template view -->\n<com.google.android.ads.nativetemplates.TemplateView\n android:id=\"@+id/my_template\"\n <!-- this attribute determines which template is used. The other option is\n @layout/gnt_medium_template_view -->\n app:gnt_template_type=\"@layout/gnt_small_template_view\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\" />\n\n...\n</LinearLayout>\n```\n\nExample:\n```text\nMobileAds.initialize(this);\nAdLoader adLoader = new AdLoader.Builder(this, \"ca-app-pub-3940256099942544/2247696110\")\n .forNativeAd(new NativeAd.OnNativeAdLoadedListener() {\n @Override\n public void onNativeAdLoaded(NativeAd nativeAd) {\n NativeTemplateStyle styles = new\n NativeTemplateStyle.Builder().withMainBackgroundColor(background).build();\n TemplateView template = findViewById(R.id.my_template);\n template.setStyles(styles);\n template.setNativeAd(nativeAd);\n }\n })\n .build();\n\nadLoader.loadAd(new AdRequest.Builder().build());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.975Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":63,"estimatedTokens":411}}557{"id":"doc-interstitial_ads_android_google_for_developers-71f6a4d8","source":"documentation","title":"Interstitial ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/interstitial","text":"Example:\n```text\nInterstitialAd.load(\n this,\n AD_UNIT_ID,\n new AdRequest.Builder().build(),\n new InterstitialAdLoadCallback() {\n @Override\n public void onAdLoaded(@NonNull InterstitialAd interstitialAd) {\n Log.d(TAG, \"Ad was loaded.\");\n MyActivity.this.interstitialAd = interstitialAd;\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {\n Log.d(TAG, loadAdError.getMessage());\n interstitialAd = null;\n }\n });MyActivity.java\n```\n\nExample:\n```text\nInterstitialAd.load(\n this,\n AD_UNIT_ID,\n AdRequest.Builder().build(),\n object : InterstitialAdLoadCallback() {\n override fun onAdLoaded(ad: InterstitialAd) {\n Log.d(TAG, \"Ad was loaded.\")\n interstitialAd = ad\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n Log.d(TAG, adError.message)\n interstitialAd = null\n }\n },\n)MainActivity.kt\n```\n\nExample:\n```text\ninterstitialAd.setFullScreenContentCallback(\n new FullScreenContentCallback() {\n @Override\n public void onAdDismissedFullScreenContent() {\n // Called when fullscreen content is dismissed.\n Log.d(TAG, \"The ad was dismissed.\");\n // Make sure to set your reference to null so you don't\n // show it a second time.\n MyActivity.this.interstitialAd = null;\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(AdError adError) {\n // Called when fullscreen content failed to show.\n Log.d(TAG, \"The ad failed to show.\");\n // Make sure to set your reference to null so you don't\n // show it a second time.\n MyActivity.this.interstitialAd = null;\n }\n\n @Override\n public void onAdShowedFullScreenContent() {\n // Called when fullscreen content is shown.\n Log.d(TAG, \"The ad was shown.\");\n }\n\n @Override\n public void onAdImpression() {\n // Called when an impression is recorded for an ad.\n Log.d(TAG, \"The ad recorded an impression.\");\n }\n\n @Override\n public void onAdClicked() {\n // Called when ad is clicked.\n Log.d(TAG, \"The ad was clicked.\");\n }\n });MyActivity.java\n```\n\nExample:\n```text\ninterstitialAd?.fullScreenContentCallback =\n object : FullScreenContentCallback() {\n override fun onAdDismissedFullScreenContent() {\n // Called when fullscreen content is dismissed.\n Log.d(TAG, \"Ad was dismissed.\")\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n interstitialAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(adError: AdError) {\n // Called when fullscreen content failed to show.\n Log.d(TAG, \"Ad failed to show.\")\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n interstitialAd = null\n }\n\n override fun onAdShowedFullScreenContent() {\n // Called when fullscreen content is shown.\n Log.d(TAG, \"Ad showed fullscreen content.\")\n }\n\n override fun onAdImpression() {\n // Called when an impression is recorded for an ad.\n Log.d(TAG, \"Ad recorded an impression.\")\n }\n\n override fun onAdClicked() {\n // Called when ad is clicked.\n Log.d(TAG, \"Ad was clicked.\")\n }\n }MainActivity.kt\n```\n\nExample:\n```text\nif (interstitialAd != null) {\n interstitialAd.show(this);\n} else {\n Log.d(TAG, \"The interstitial ad is still loading.\");\n}MyActivity.java\n```\n\nExample:\n```text\ninterstitialAd?.show(this)MainActivity.kt\n```\n\nExample:\n```text\n/*\n * Copyright (C) 2013 Google, Inc.\n *\n * 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 * http://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.\n */\npackage com.google.android.gms.example.interstitialexample;\n\nimport android.annotation.SuppressLint;\nimport android.os.Bundle;\nimport android.os.CountDownTimer;\nimport android.util.Log;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.PopupMenu;\nimport android.widget.TextView;\nimport android.widget.Toast;\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.core.graphics.Insets;\nimport androidx.core.view.ViewCompat;\nimport androidx.core.view.WindowInsetsCompat;\nimport com.google.android.gms.ads.AdError;\nimport com.google.android.gms.ads.AdRequest;\nimport com.google.android.gms.ads.FullScreenContentCallback;\nimport com.google.android.gms.ads.LoadAdError;\nimport com.google.android.gms.ads.MobileAds;\nimport com.google.android.gms.ads.RequestConfiguration;\nimport com.google.android.gms.ads.interstitial.InterstitialAd;\nimport com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback;\nimport java.util.Arrays;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/** Main Activity. Inflates main activity xml. */\n@SuppressLint(\"SetTextI18n\")\npublic class MyActivity extends AppCompatActivity {\n\n // Check your logcat output for the test device hashed ID e.g.\n // \"Use RequestConfiguration.Builder().setTestDeviceIds(Arrays.asList(\"ABCDEF012345\"))\n // to get test ads on this device\" or\n // \"Use new ConsentDebugSettings.Builder().addTestDeviceHashedId(\"ABCDEF012345\") to set this as\n // a debug device\".\n public static final String TEST_DEVICE_HASHED_ID = \"ABCDEF012345\";\n\n private static final long GAME_LENGTH_MILLISECONDS = 3000;\n private static final String AD_UNIT_ID = \"ca-app-pub-3940256099942544/1033173712\";\n private static final String TAG = \"MyActivity\";\n\n private final AtomicBoolean isMobileAdsInitializeCalled = new AtomicBoolean(false);\n private GoogleMobileAdsConsentManager googleMobileAdsConsentManager;\n private InterstitialAd interstitialAd;\n private CountDownTimer countDownTimer;\n private Button retryButton;\n private boolean gamePaused;\n private boolean gameOver;\n private boolean adIsLoading;\n private long timerMilliseconds;\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_my);\n setSupportActionBar(findViewById(R.id.toolBar));\n\n ViewCompat.setOnApplyWindowInsetsListener(\n findViewById(R.id.container),\n (view, insets) -> {\n Insets systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars());\n view.setPadding(\n systemBarsInsets.left, 0, systemBarsInsets.right, systemBarsInsets.bottom);\n return insets;\n });\n\n // Log the Mobile Ads SDK version.\n Log.d(TAG, \"Google Mobile Ads SDK Version: \" + MobileAds.getVersion());\n\n googleMobileAdsConsentManager =\n GoogleMobileAdsConsentManager.getInstance(getApplicationContext());\n googleMobileAdsConsentManager.gatherConsent(\n this,\n consentError -> {\n if (consentError != null) {\n // Consent not obtained in current session.\n Log.w(\n TAG,\n String.format(\"%s: %s\", consentError.getErrorCode(), consentError.getMessage()));\n }\n\n startGame();\n\n if (googleMobileAdsConsentManager.canRequestAds()) {\n initializeMobileAdsSdk();\n }\n\n if (googleMobileAdsConsentManager.isPrivacyOptionsRequired()) {\n // Regenerate the options menu to include a privacy setting.\n invalidateOptionsMenu();\n }\n });\n\n // This sample attempts to load ads using consent obtained in the previous session.\n if (googleMobileAdsConsentManager.canRequestAds()) {\n initializeMobileAdsSdk();\n }\n\n // Create the \"retry\" button, which tries to show an interstitial between game plays.\n retryButton = findViewById(R.id.retry_button);\n retryButton.setVisibility(View.INVISIBLE);\n retryButton.setOnClickListener(\n new View.OnClickListener() {\n @Override\n public void onClick(View view) {\n showInterstitial();\n }\n });\n }\n\n public void loadAd() {\n // Request a new ad if one isn't already loaded.\n if (adIsLoading || interstitialAd != null) {\n return;\n }\n adIsLoading = true;\n InterstitialAd.load(\n this,\n AD_UNIT_ID,\n new AdRequest.Builder().build(),\n new InterstitialAdLoadCallback() {\n @Override\n public void onAdLoaded(@NonNull InterstitialAd interstitialAd) {\n Log.d(TAG, \"Ad was loaded.\");\n MyActivity.this.interstitialAd = interstitialAd;\n adIsLoading = false;\n Toast.makeText(MyActivity.this, \"onAdLoaded()\", Toast.LENGTH_SHORT).show();\n interstitialAd.setFullScreenContentCallback(\n new FullScreenContentCallback() {\n @Override\n public void onAdDismissedFullScreenContent() {\n // Called when fullscreen content is dismissed.\n Log.d(TAG, \"The ad was dismissed.\");\n // Make sure to set your reference to null so you don't\n // show it a second time.\n MyActivity.this.interstitialAd = null;\n }\n\n @Override\n public void onAdFailedToShowFullScreenContent(AdError adError) {\n // Called when fullscreen content failed to show.\n Log.d(TAG, \"The ad failed to show.\");\n // Make sure to set your reference to null so you don't\n // show it a second time.\n MyActivity.this.interstitialAd = null;\n }\n\n @Override\n public void onAdShowedFullScreenContent() {\n // Called when fullscreen content is shown.\n Log.d(TAG, \"The ad was shown.\");\n }\n\n @Override\n public void onAdImpression() {\n // Called when an impression is recorded for an ad.\n Log.d(TAG, \"The ad recorded an impression.\");\n }\n\n @Override\n public void onAdClicked() {\n // Called when ad is clicked.\n Log.d(TAG, \"The ad was clicked.\");\n }\n });\n }\n\n @Override\n public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {\n Log.d(TAG, loadAdError.getMessage());\n interstitialAd = null;\n adIsLoading = false;\n String error =\n String.format(\n java.util.Locale.US,\n \"domain: %s, code: %d, message: %s\",\n loadAdError.getDomain(),\n loadAdError.getCode(),\n loadAdError.getMessage());\n Toast.makeText(\n MyActivity.this, \"onAdFailedToLoad() with error: \" + error, Toast.LENGTH_SHORT)\n .show();\n }\n });\n }\n\n private void createTimer(final long milliseconds) {\n // Create the game timer, which counts down to the end of the level\n // and shows the \"retry\" button.\n if (countDownTimer != null) {\n countDownTimer.cancel();\n }\n\n final TextView textView = findViewById(R.id.timer);\n\n countDownTimer =\n new CountDownTimer(milliseconds, 50) {\n @Override\n public void onTick(long millisUnitFinished) {\n timerMilliseconds = millisUnitFinished;\n textView.setText(\"seconds remaining: \" + ((millisUnitFinished / 1000) + 1));\n }\n\n @Override\n public void onFinish() {\n gameOver = true;\n textView.setText(\"done!\");\n retryButton.setVisibility(View.VISIBLE);\n }\n };\n\n countDownTimer.start();\n }\n\n @Override\n public void onResume() {\n // Start or resume the game.\n super.onResume();\n resumeGame();\n }\n\n @Override\n public void onPause() {\n super.onPause();\n pauseGame();\n }\n\n @Override\n public boolean onCreateOptionsMenu(Menu menu) {\n getMenuInflater().inflate(R.menu.action_menu, menu);\n return true;\n }\n\n @Override\n public boolean onOptionsItemSelected(MenuItem item) {\n View menuItemView = findViewById(item.getItemId());\n PopupMenu popup = new PopupMenu(this, menuItemView);\n popup.getMenuInflater().inflate(R.menu.popup_menu, popup.getMenu());\n popup.show();\n popup\n .getMenu()\n .findItem(R.id.privacy_settings)\n .setVisible(googleMobileAdsConsentManager.isPrivacyOptionsRequired());\n popup.setOnMenuItemClickListener(\n popupMenuItem -> {\n if (popupMenuItem.getItemId() == R.id.privacy_settings) {\n pauseGame();\n // Handle changes to user consent.\n googleMobileAdsConsentManager.showPrivacyOptionsForm(\n this,\n formError -> {\n if (formError != null) {\n Toast.makeText(this, formError.getMessage(), Toast.LENGTH_SHORT).show();\n }\n resumeGame();\n });\n return true;\n } else if (popupMenuItem.getItemId() == R.id.ad_inspector) {\n MobileAds.openAdInspector(\n this,\n error -> {\n // Error will be non-null if ad inspector closed due to an error.\n if (error != null) {\n Toast.makeText(this, error.getMessage(), Toast.LENGTH_SHORT).show();\n }\n });\n return true;\n }\n return false;\n });\n return super.onOptionsItemSelected(item);\n }\n\n private void showInterstitial() {\n // Show the ad if it's ready. Otherwise restart the game.\n if (interstitialAd != null) {\n interstitialAd.show(this);\n } else {\n Log.d(TAG, \"The interstitial ad is still loading.\");\n startGame();\n if (googleMobileAdsConsentManager.canRequestAds()) {\n loadAd();\n }\n }\n }\n\n private void startGame() {\n // Hide the button, and kick off the timer.\n retryButton.setVisibility(View.INVISIBLE);\n createTimer(GAME_LENGTH_MILLISECONDS);\n gamePaused = false;\n gameOver = false;\n }\n\n private void resumeGame() {\n if (gameOver || !gamePaused) {\n return;\n }\n // Create a new timer for the correct length.\n gamePaused = false;\n createTimer(timerMilliseconds);\n }\n\n private void pauseGame() {\n if (gameOver || gamePaused) {\n return;\n }\n countDownTimer.cancel();\n gamePaused = true;\n }\n\n private void initializeMobileAdsSdk() {\n if (isMobileAdsInitializeCalled.getAndSet(true)) {\n return;\n }\n\n // Set your test devices.\n MobileAds.setRequestConfiguration(\n new RequestConfiguration.Builder()\n .setTestDeviceIds(Arrays.asList(TEST_DEVICE_HASHED_ID))\n .build());\n\n new Thread(\n () -> {\n // Initialize the Google Mobile Ads SDK on a background thread.\n MobileAds.initialize(this, initializationStatus -> {});\n\n // Load an ad on the main thread.\n runOnUiThread(() -> loadAd());\n })\n .start();\n }\n}\n```\n\nExample:\n```text\npackage com.google.android.gms.example.interstitialexample\n\nimport android.os.Bundle\nimport android.os.CountDownTimer\nimport android.util.Log\nimport android.view.Menu\nimport android.view.MenuItem\nimport android.view.View\nimport android.widget.PopupMenu\nimport android.widget.Toast\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.view.ViewCompat\nimport androidx.core.view.WindowInsetsCompat\nimport com.google.android.gms.ads.*\nimport com.google.android.gms.ads.interstitial.InterstitialAd\nimport com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback\nimport com.google.android.gms.example.interstitialexample.databinding.ActivityMainBinding\nimport java.util.concurrent.atomic.AtomicBoolean\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\n\nclass MainActivity : AppCompatActivity() {\n\n private val isMobileAdsInitializeCalled = AtomicBoolean(false)\n private lateinit var binding: ActivityMainBinding\n private lateinit var googleMobileAdsConsentManager: GoogleMobileAdsConsentManager\n private var interstitialAd: InterstitialAd? = null\n private var countdownTimer: CountDownTimer? = null\n private var gamePaused = false\n private var gameOver = false\n private var adIsLoading: Boolean = false\n private var timerMilliseconds = 0L\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n binding = ActivityMainBinding.inflate(layoutInflater)\n val view = binding.root\n setContentView(view)\n setSupportActionBar(binding.toolBar)\n\n ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, insets ->\n val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())\n view.setPadding(systemBars.left, 0, systemBars.right, systemBars.bottom)\n insets\n }\n\n // Log the Mobile Ads SDK version.\n Log.d(TAG, \"Google Mobile Ads SDK Version: \" + MobileAds.getVersion())\n\n googleMobileAdsConsentManager = GoogleMobileAdsConsentManager.getInstance(this)\n googleMobileAdsConsentManager.gatherConsent(this) { consentError ->\n if (consentError != null) {\n // Consent not obtained in current session.\n Log.w(TAG, \"${consentError.errorCode}: ${consentError.message}\")\n }\n\n // Kick off the first play of the \"game\".\n startGame()\n\n if (googleMobileAdsConsentManager.canRequestAds) {\n initializeMobileAdsSdk()\n }\n if (googleMobileAdsConsentManager.isPrivacyOptionsRequired) {\n // Regenerate the options menu to include a privacy setting.\n invalidateOptionsMenu()\n }\n }\n\n // This sample attempts to load ads using consent obtained in the previous session.\n if (googleMobileAdsConsentManager.canRequestAds) {\n initializeMobileAdsSdk()\n }\n\n // Create the \"retry\" button, which triggers an interstitial between game plays.\n binding.retryButton.visibility = View.INVISIBLE\n binding.retryButton.setOnClickListener { showInterstitial() }\n }\n\n override fun onCreateOptionsMenu(menu: Menu?): Boolean {\n menuInflater.inflate(R.menu.action_menu, menu)\n return super.onCreateOptionsMenu(menu)\n }\n\n override fun onOptionsItemSelected(item: MenuItem): Boolean {\n val menuItemView = findViewById<View>(item.itemId)\n val activity = this\n PopupMenu(this, menuItemView).apply {\n menuInflater.inflate(R.menu.popup_menu, menu)\n menu\n .findItem(R.id.privacy_settings)\n .setVisible(googleMobileAdsConsentManager.isPrivacyOptionsRequired)\n show()\n setOnMenuItemClickListener { popupMenuItem ->\n when (popupMenuItem.itemId) {\n R.id.privacy_settings -> {\n pauseGame()\n // Handle changes to user consent.\n googleMobileAdsConsentManager.showPrivacyOptionsForm(activity) { formError ->\n if (formError != null) {\n Toast.makeText(activity, formError.message, Toast.LENGTH_SHORT).show()\n }\n resumeGame()\n }\n true\n }\n R.id.ad_inspector -> {\n MobileAds.openAdInspector(activity) { error ->\n // Error will be non-null if ad inspector closed due to an error.\n error?.let { Toast.makeText(activity, it.message, Toast.LENGTH_SHORT).show() }\n }\n true\n }\n // Handle other branches here.\n else -> false\n }\n }\n return super.onOptionsItemSelected(item)\n }\n }\n\n private fun loadAd() {\n // Request a new ad if one isn't already loaded.\n if (adIsLoading || interstitialAd != null) {\n return\n }\n adIsLoading = true\n\n InterstitialAd.load(\n this,\n AD_UNIT_ID,\n AdRequest.Builder().build(),\n object : InterstitialAdLoadCallback() {\n override fun onAdLoaded(ad: InterstitialAd) {\n Log.d(TAG, \"Ad was loaded.\")\n interstitialAd = ad\n adIsLoading = false\n Toast.makeText(this@MainActivity, \"onAdLoaded()\", Toast.LENGTH_SHORT).show()\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n Log.d(TAG, adError.message)\n interstitialAd = null\n adIsLoading = false\n val error =\n \"domain: ${adError.domain}, code: ${adError.code}, \" + \"message: ${adError.message}\"\n Toast.makeText(\n this@MainActivity,\n \"onAdFailedToLoad() with error $error\",\n Toast.LENGTH_SHORT,\n )\n .show()\n }\n },\n )\n }\n\n // Create the game timer, which counts down to the end of the level\n // and shows the \"retry\" button.\n private fun createTimer(milliseconds: Long) {\n countdownTimer?.cancel()\n\n countdownTimer =\n object : CountDownTimer(milliseconds, 50) {\n override fun onTick(millisUntilFinished: Long) {\n timerMilliseconds = millisUntilFinished\n binding.timer.text = \"seconds remaining: ${ millisUntilFinished / 1000 + 1 }\"\n }\n\n override fun onFinish() {\n gameOver = true\n binding.timer.text = \"done!\"\n binding.retryButton.visibility = View.VISIBLE\n }\n }\n\n countdownTimer?.start()\n }\n\n // Show the ad if it's ready. Otherwise restart the game.\n private fun showInterstitial() {\n if (interstitialAd != null) {\n interstitialAd?.fullScreenContentCallback =\n object : FullScreenContentCallback() {\n override fun onAdDismissedFullScreenContent() {\n // Called when fullscreen content is dismissed.\n Log.d(TAG, \"Ad was dismissed.\")\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n interstitialAd = null\n }\n\n override fun onAdFailedToShowFullScreenContent(adError: AdError) {\n // Called when fullscreen content failed to show.\n Log.d(TAG, \"Ad failed to show.\")\n // Don't forget to set the ad reference to null so you\n // don't show the ad a second time.\n interstitialAd = null\n }\n\n override fun onAdShowedFullScreenContent() {\n // Called when fullscreen content is shown.\n Log.d(TAG, \"Ad showed fullscreen content.\")\n }\n\n override fun onAdImpression() {\n // Called when an impression is recorded for an ad.\n Log.d(TAG, \"Ad recorded an impression.\")\n }\n\n override fun onAdClicked() {\n // Called when ad is clicked.\n Log.d(TAG, \"Ad was clicked.\")\n }\n }\n\n interstitialAd?.show(this)\n } else {\n startGame()\n if (googleMobileAdsConsentManager.canRequestAds) {\n loadAd()\n }\n }\n }\n\n // Hide the button, and kick off the timer.\n private fun startGame() {\n binding.retryButton.visibility = View.INVISIBLE\n createTimer(GAME_LENGTH_MILLISECONDS)\n gamePaused = false\n gameOver = false\n }\n\n private fun pauseGame() {\n if (gameOver || gamePaused) {\n return\n }\n countdownTimer?.cancel()\n gamePaused = true\n }\n\n private fun resumeGame() {\n if (gameOver || !gamePaused) {\n return\n }\n createTimer(timerMilliseconds)\n gamePaused = true\n }\n\n private fun initializeMobileAdsSdk() {\n if (isMobileAdsInitializeCalled.getAndSet(true)) {\n return\n }\n\n // Set your test devices.\n MobileAds.setRequestConfiguration(\n RequestConfiguration.Builder().setTestDeviceIds(listOf(TEST_DEVICE_HASHED_ID)).build()\n )\n\n CoroutineScope(Dispatchers.IO).launch {\n // Initialize the Google Mobile Ads SDK on a background thread.\n MobileAds.initialize(this@MainActivity) {}\n runOnUiThread {\n // Load an ad on the main thread.\n loadAd()\n }\n }\n }\n\n // Resume the game if it's in progress.\n public override fun onResume() {\n super.onResume()\n resumeGame()\n }\n\n public override fun onPause() {\n super.onPause()\n pauseGame()\n }\n\n companion object {\n // This is an ad unit ID for a test ad. Replace with your own interstitial ad unit ID.\n private const val AD_UNIT_ID = \"ca-app-pub-3940256099942544/1033173712\"\n private const val GAME_LENGTH_MILLISECONDS = 3000L\n private const val TAG = \"MainActivity\"\n\n // Check your logcat output for the test device hashed ID e.g.\n // \"Use RequestConfiguration.Builder().setTestDeviceIds(Arrays.asList(\"ABCDEF012345\"))\n // to get test ads on this device\" or\n // \"Use new ConsentDebugSettings.Builder().addTestDeviceHashedId(\"ABCDEF012345\") to set this as\n // a debug device\".\n const val TEST_DEVICE_HASHED_ID = \"ABCDEF012345\"\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.977Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":788,"estimatedTokens":6341}}558{"id":"doc-configure_the_docs_mcp_server_google_docs_google-9348e4f3","source":"documentation","title":"Configure the Docs MCP server | Google Docs | Google for Developers","url":"https://developers.google.com/workspace/docs/api/guides/configure-mcp-server","text":"Example:\n```text\ngcloud services enable docs.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable docsmcp.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\n{\n \"mcpServers\": {\n \"docs\": {\n \"serverUrl\": \"https://docsmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n }\n }\n}\n```\n\nExample:\n```text\nagy\n```\n\nExample:\n```text\n/mcp\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.978Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":36,"estimatedTokens":120}}559{"id":"doc-set_a_fixed_banner_size_flutter_google_for_devel-c27f458b","source":"documentation","title":"Set a fixed banner size | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/banner/fixed-size","text":"Example:\n```text\nW/Ads: Not enough space to show ad. Needs 320x50 dp, but only has 288x495 dp.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.979Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":28}}560{"id":"doc-use_collapsible_banners_flutter_google_for_devel-25a8f0de","source":"documentation","title":"Use collapsible banners | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/banner/collapsible","text":"Example:\n```text\nvoid _loadAd() async {\n // Replace these test ad units with your own ad units.\n final String adUnitId = Platform.isAndroid\n ? 'ca-app-pub-3940256099942544/2014213617'\n : 'ca-app-pub-3940256099942544/8388050270';\n\n // Get the size before loading the ad.\n final size = await AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(\n MediaQuery.sizeOf(context).width.truncate());\n\n if (size == null) {\n // Unable to get the size.\n return;\n }\n\n // Create an extra parameter that aligns the bottom of the expanded ad to the\n // bottom of the banner ad.\n const adRequest = AdRequest(extras: {\n \"collapsible\": \"bottom\",\n });\n\n BannerAd(\n adUnitId: adUnitId,\n request: adRequest,\n size: size,\n listener: const BannerAdListener()\n ).load();\n}\n```\n\nExample:\n```text\nBannerAd(\n // ...\n listener: BannerAdListener(\n onAdLoaded: (ad) {\n final bannerAd = ad as BannerAd;\n final isCollapsible = bannerAd.isCollapsible;\n print('The last loaded banner is ${isCollapsible ? \"\" : \"not\"} collapsible.');\n },\n ),\n).load();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.979Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":47,"estimatedTokens":279}}561{"id":"doc-integrate_bidmachine_with_mediation_flutter_goog-464f211f","source":"documentation","title":"Integrate BidMachine with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/bidmachine","text":"Example:\n```text\ndependencies:\n gma_mediation_bidmachine: ^1.4.1\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_bidmachine:\n path: path/to/local/package\n```\n\nExample:\n```text\nio.bidmachine\ncom.google.ads.mediation.bidmachine\n```\n\nExample:\n```text\nGADMediationAdapterBidMachine\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.980Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":25,"estimatedTokens":76}}562{"id":"doc-interstitial_ads_flutter_google_for_developers-4e99bc21","source":"documentation","title":"Interstitial ads | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/interstitial","text":"Example:\n```text\nca-app-pub-3940256099942544/1033173712\n```\n\nExample:\n```text\nca-app-pub-3940256099942544/4411468910\n```\n\nExample:\n```text\nInterstitialAd.load(\n adUnitId: \"_adUnitId\",\n request: const AdRequest(),\n adLoadCallback: InterstitialAdLoadCallback(\n onAdLoaded: (InterstitialAd ad) {\n // Called when an ad is successfully received.\n debugPrint('Ad was loaded.');\n // Keep a reference to the ad so you can show it later.\n _interstitialAd = ad;\n },\n onAdFailedToLoad: (LoadAdError error) {\n // Called when an ad request failed.\n debugPrint('Ad failed to load with error: $error');\n },\n ),\n);interstitial_ad_snippets.dart\n```\n\nExample:\n```text\nad.fullScreenContentCallback = FullScreenContentCallback(\n onAdShowedFullScreenContent: (ad) {\n // Called when the ad showed the full screen content.\n debugPrint('Ad showed full screen content.');\n },\n onAdFailedToShowFullScreenContent: (ad, err) {\n // Called when the ad failed to show full screen content.\n debugPrint('Ad failed to show full screen content with error: $err');\n // Dispose the ad here to free resources.\n ad.dispose();\n },\n onAdDismissedFullScreenContent: (ad) {\n // Called when the ad dismissed full screen content.\n debugPrint('Ad was dismissed.');\n // Dispose the ad here to free resources.\n ad.dispose();\n },\n onAdImpression: (ad) {\n // Called when an impression occurs on the ad.\n debugPrint('Ad recorded an impression.');\n },\n onAdClicked: (ad) {\n // Called when a click is recorded for an ad.\n debugPrint('Ad was clicked.');\n },\n);interstitial_ad_snippets.dart\n```\n\nExample:\n```text\n_interstitialAd?.show();main.dart\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.980Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":66,"estimatedTokens":427}}563{"id":"doc-rewarded_interstitial_ads_flutter_google_for_dev-0f27b554","source":"documentation","title":"Rewarded interstitial ads | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/rewarded-interstitial","text":"Example:\n```text\nca-app-pub-3940256099942544/5354046379\n```\n\nExample:\n```text\nca-app-pub-3940256099942544/6978759866\n```\n\nExample:\n```text\nRewardedInterstitialAd.load(\n adUnitId: \"_adUnitId\",\n request: const AdRequest(),\n rewardedInterstitialAdLoadCallback: RewardedInterstitialAdLoadCallback(\n onAdLoaded: (RewardedInterstitialAd ad) {\n // Called when an ad is successfully received.\n debugPrint('Ad was loaded.');\n // Keep a reference to the ad so you can show it later.\n _rewardedInterstitialAd = ad;\n },\n onAdFailedToLoad: (LoadAdError error) {\n // Called when an ad request failed.\n debugPrint('Ad failed to load with error: $error');\n },\n ),\n);rewarded_interstitial_ad_snippets.dart\n```\n\nExample:\n```text\nad.fullScreenContentCallback = FullScreenContentCallback(\n onAdShowedFullScreenContent: (ad) {\n // Called when the ad showed the full screen content.\n debugPrint('Ad showed full screen content.');\n },\n onAdFailedToShowFullScreenContent: (ad, err) {\n // Called when the ad failed to show full screen content.\n debugPrint('Ad failed to show full screen content with error: $err');\n // Dispose the ad here to free resources.\n ad.dispose();\n },\n onAdDismissedFullScreenContent: (ad) {\n // Called when the ad dismissed full screen content.\n debugPrint('Ad was dismissed.');\n // Dispose the ad here to free resources.\n ad.dispose();\n },\n onAdImpression: (ad) {\n // Called when an impression occurs on the ad.\n debugPrint('Ad recorded an impression.');\n },\n onAdClicked: (ad) {\n // Called when a click is recorded for an ad.\n debugPrint('Ad was clicked.');\n },\n);rewarded_interstitial_ad_snippets.dart\n```\n\nExample:\n```text\n_rewardedInterstitialAd?.show(\n onUserEarnedReward: (AdWithoutView view, RewardItem rewardItem) {\n debugPrint('Reward amount: ${rewardItem.amount}');\n },\n);main.dart\n```\n\nExample:\n```readonly\nRewardedInterstitialAd.load(\n adUnitId: \"_adUnitId\",\n request: AdRequest(),\n rewardedInterstitialAdLoadCallback: RewardedInterstitialAdLoadCallback(\n onAdLoaded: (ad) {\n ServerSideVerificationOptions _options =\n ServerSideVerificationOptions(\n customData: 'SAMPLE_CUSTOM_DATA_STRING',\n );\n ad.setServerSideOptions(_options);\n _rewardedInterstitialAd = ad;\n },\n onAdFailedToLoad: (error) {},\n ),\n);rewarded_interstitial_ad_snippets.dart\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.981Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":89,"estimatedTokens":609}}564{"id":"doc-manage_native_ads_in_android_and_ios_flutter_goo-b43e0f6f","source":"documentation","title":"Manage native ads in Android and iOS | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/native/platforms","text":"Example:\n```text\nca-app-pub-3940256099942544/2247696110\n```\n\nExample:\n```text\nca-app-pub-3940256099942544/3986624511\n```\n\nExample:\n```text\ndef flutterProjectRoot = rootProject.projectDir.parentFile.toPath()\ndef plugins = new Properties()\ndef pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')\nif (pluginsFile.exists()) {\n pluginsFile.withInputStream { stream -> plugins.load(stream) }\n}\n\nplugins.each { name, path ->\n def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()\n include \":$name\"\n project(\":$name\").projectDir = pluginDirectory\n}\n```\n\nExample:\n```text\npackage io.flutter.plugins.googlemobileadsexample;\n\nimport android.graphics.Color;\nimport android.view.LayoutInflater;\nimport android.widget.TextView;\nimport com.google.android.gms.ads.nativead.NativeAd;\nimport com.google.android.gms.ads.nativead.NativeAdView;\nimport io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin.NativeAdFactory;\nimport java.util.Map;\n\n/**\n * my_native_ad.xml can be found at\n * github.com/googleads/googleads-mobile-flutter/blob/main/packages/google_mobile_ads/\n * example/android/app/src/main/res/layout/my_native_ad.xml\n */\nclass NativeAdFactoryExample implements NativeAdFactory {\n private final LayoutInflater layoutInflater;\n\n NativeAdFactoryExample(LayoutInflater layoutInflater) {\n this.layoutInflater = layoutInflater;\n }\n\n @Override\n public NativeAdView createNativeAd(\n NativeAd nativeAd, Map<String, Object> customOptions) {\n final NativeAdView adView =\n (NativeAdView) layoutInflater.inflate(R.layout.my_native_ad, null);\n\n // Set the media view.\n adView.setMediaView((MediaView) adView.findViewById(R.id.ad_media));\n\n // Set other ad assets.\n adView.setHeadlineView(adView.findViewById(R.id.ad_headline));\n adView.setBodyView(adView.findViewById(R.id.ad_body));\n adView.setCallToActionView(adView.findViewById(R.id.ad_call_to_action));\n adView.setIconView(adView.findViewById(R.id.ad_app_icon));\n adView.setPriceView(adView.findViewById(R.id.ad_price));\n adView.setStarRatingView(adView.findViewById(R.id.ad_stars));\n adView.setStoreView(adView.findViewById(R.id.ad_store));\n adView.setAdvertiserView(adView.findViewById(R.id.ad_advertiser));\n\n // The headline and mediaContent are guaranteed to be in every NativeAd.\n ((TextView) adView.getHeadlineView()).setText(nativeAd.getHeadline());\n adView.getMediaView().setMediaContent(nativeAd.getMediaContent());\n\n // These assets aren't guaranteed to be in every NativeAd, so it's important to\n // check before trying to display them.\n if (nativeAd.getBody() == null) {\n adView.getBodyView().setVisibility(View.INVISIBLE);\n } else {\n adView.getBodyView().setVisibility(View.VISIBLE);\n ((TextView) adView.getBodyView()).setText(nativeAd.getBody());\n }\n\n if (nativeAd.getCallToAction() == null) {\n adView.getCallToActionView().setVisibility(View.INVISIBLE);\n } else {\n adView.getCallToActionView().setVisibility(View.VISIBLE);\n ((Button) adView.getCallToActionView()).setText(nativeAd.getCallToAction());\n }\n\n if (nativeAd.getIcon() == null) {\n adView.getIconView().setVisibility(View.GONE);\n } else {\n ((ImageView) adView.getIconView()).setImageDrawable(nativeAd.getIcon().getDrawable());\n adView.getIconView().setVisibility(View.VISIBLE);\n }\n\n if (nativeAd.getPrice() == null) {\n adView.getPriceView().setVisibility(View.INVISIBLE);\n } else {\n adView.getPriceView().setVisibility(View.VISIBLE);\n ((TextView) adView.getPriceView()).setText(nativeAd.getPrice());\n }\n\n if (nativeAd.getStore() == null) {\n adView.getStoreView().setVisibility(View.INVISIBLE);\n } else {\n adView.getStoreView().setVisibility(View.VISIBLE);\n ((TextView) adView.getStoreView()).setText(nativeAd.getStore());\n }\n\n if (nativeAd.getStarRating() == null) {\n adView.getStarRatingView().setVisibility(View.INVISIBLE);\n } else {\n ((RatingBar) adView.getStarRatingView()).setRating(nativeAd.getStarRating()\n .floatValue());\n adView.getStarRatingView().setVisibility(View.VISIBLE);\n }\n\n if (nativeAd.getAdvertiser() == null) {\n adView.getAdvertiserView().setVisibility(View.INVISIBLE);\n } else {\n adView.getAdvertiserView().setVisibility(View.VISIBLE);\n ((TextView) adView.getAdvertiserView()).setText(nativeAd.getAdvertiser());\n }\n\n // This method tells Google Mobile Ads Flutter Plugin that you have finished populating your\n // native ad view with this native ad.\n adView.setNativeAd(nativeAd);\n\n return adView;\n }\n}\n```\n\nExample:\n```text\npackage my.app.path;\n\nimport io.flutter.embedding.android.FlutterActivity;\nimport io.flutter.embedding.engine.FlutterEngine;\nimport io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin;\n\npublic class MainActivity extends FlutterActivity {\n @Override\n public void configureFlutterEngine(FlutterEngine flutterEngine) {\n flutterEngine.getPlugins().add(new GoogleMobileAdsPlugin());\n super.configureFlutterEngine(flutterEngine);\n\n GoogleMobileAdsPlugin.registerNativeAdFactory(flutterEngine,\n \"adFactoryExample\", NativeAdFactoryExample());\n }\n\n @Override\n public void cleanUpFlutterEngine(FlutterEngine flutterEngine) {\n GoogleMobileAdsPlugin.unregisterNativeAdFactory(flutterEngine, \"adFactoryExample\");\n }\n}\n```\n\nExample:\n```text\n#import \"FLTGoogleMobileAdsPlugin.h\"\n\n/**\n * The example NativeAdView.xib can be found at\n * github.com/googleads/googleads-mobile-flutter/blob/main/packages/google_mobile_ads/\n * example/ios/Runner/NativeAdView.xib\n */\n@interface NativeAdFactoryExample : NSObject <FLTNativeAdFactory>\n@end\n\n@implementation NativeAdFactoryExample\n- (GADNativeAdView *)createNativeAd:(GADNativeAd *)nativeAd\n customOptions:(NSDictionary *)customOptions {\n // Create and place the ad in the view hierarchy.\n GADNativeAdView *adView =\n [[NSBundle mainBundle] loadNibNamed:@\"NativeAdView\" owner:nil options:nil].firstObject;\n\n // Populate the native ad view with the native ad assets.\n // The headline is guaranteed to be present in every native ad.\n ((UILabel *)adView.headlineView).text = nativeAd.headline;\n\n // These assets are not guaranteed to be present. Check that they are before\n // showing or hiding them.\n ((UILabel *)adView.bodyView).text = nativeAd.body;\n adView.bodyView.hidden = nativeAd.body ? NO : YES;\n\n [((UIButton *)adView.callToActionView) setTitle:nativeAd.callToAction\n forState:UIControlStateNormal];\n adView.callToActionView.hidden = nativeAd.callToAction ? NO : YES;\n\n ((UIImageView *)adView.iconView).image = nativeAd.icon.image;\n adView.iconView.hidden = nativeAd.icon ? NO : YES;\n\n ((UILabel *)adView.storeView).text = nativeAd.store;\n adView.storeView.hidden = nativeAd.store ? NO : YES;\n\n ((UILabel *)adView.priceView).text = nativeAd.price;\n adView.priceView.hidden = nativeAd.price ? NO : YES;\n\n ((UILabel *)adView.advertiserView).text = nativeAd.advertiser;\n adView.advertiserView.hidden = nativeAd.advertiser ? NO : YES;\n\n // In order for the SDK to process touch events properly, user interaction\n // should be disabled.\n adView.callToActionView.userInteractionEnabled = NO;\n\n // Associate the native ad view with the native ad object. This is\n // required to make the ad clickable.\n // Note: this should always be done after populating the ad views.\n adView.nativeAd = nativeAd;\n\n return adView;\n}\n@end\n```\n\nExample:\n```text\n#import \"FLTGoogleMobileAdsPlugin.h\"\n#import \"NativeAdFactoryExample.h\"\n\n@implementation AppDelegate\n- (BOOL)application:(UIApplication *)application\n didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n [GeneratedPluginRegistrant registerWithRegistry:self];\n\n // Must be added after GeneratedPluginRegistrant registerWithRegistry:self];\n // is called.\n NativeAdFactoryExample *nativeAdFactory = [[NativeAdFactoryExample alloc] init];\n [FLTGoogleMobileAdsPlugin registerNativeAdFactory:self\n factoryId:@\"adFactoryExample\"\n nativeAdFactory:nativeAdFactory];\n\n return [super application:application didFinishLaunchingWithOptions:launchOptions];\n}\n@end\n```\n\nExample:\n```text\nclass NativeExampleState extends State<NativeExample> {\n NativeAd? _nativeAd;\n bool _nativeAdIsLoaded = false;\n\n // TODO: replace this test ad unit with your own ad unit.\n final String _adUnitId = Platform.isAndroid\n ? 'ca-app-pub-3940256099942544/2247696110'\n : 'ca-app-pub-3940256099942544/3986624511';\n\n /// Loads a native ad.\n void loadAd() {\n _nativeAd = NativeAd(\n adUnitId: _adUnitId,\n // Factory ID registered by your native ad factory implementation.\n factoryId: 'adFactoryExample',\n listener: NativeAdListener(\n onAdLoaded: (ad) {\n print('$NativeAd loaded.');\n setState(() {\n _nativeAdIsLoaded = true;\n });\n },\n onAdFailedToLoad: (ad, error) {\n // Dispose the ad here to free resources.\n print('$NativeAd failedToLoad: $error');\n ad.dispose();\n },\n ),\n request: const AdRequest(),\n // Optional: Pass custom options to your native ad factory implementation.\n customOptions: {'custom-option-1', 'custom-value-1'}\n );\n _nativeAd.load();\n }\n}\n```\n\nExample:\n```text\nclass NativeExampleState extends State<NativeExample> {\n NativeAd? _nativeAd;\n bool _nativeAdIsLoaded = false;\n\n // TODO: replace this test ad unit with your own ad unit.\n final String _adUnitId = Platform.isAndroid\n ? 'ca-app-pub-3940256099942544/2247696110'\n : 'ca-app-pub-3940256099942544/3986624511';\n\n /// Loads a native ad.\n void loadAd() {\n _nativeAd = NativeAd(\n adUnitId: _adUnitId,\n // Factory ID registered by your native ad factory implementation.\n factoryId: 'adFactoryExample',\n listener: NativeAdListener(\n onAdLoaded: (ad) {\n print('$NativeAd loaded.');\n setState(() {\n _nativeAdIsLoaded = true;\n });\n },\n onAdFailedToLoad: (ad, error) {\n // Dispose the ad here to free resources.\n print('$NativeAd failedToLoad: $error');\n ad.dispose();\n },\n // Called when a click is recorded for a NativeAd.\n onAdClicked: (ad) {},\n // Called when an impression occurs on the ad.\n onAdImpression: (ad) {},\n // Called when an ad removes an overlay that covers the screen.\n onAdClosed: (ad) {},\n // Called when an ad opens an overlay that covers the screen.\n onAdOpened: (ad) {},\n // For iOS only. Called before dismissing a full screen view\n onAdWillDismissScreen: (ad) {},\n // Called when an ad receives revenue value.\n onPaidEvent: (ad, valueMicros, precision, currencyCode) {},\n ),\n request: const AdRequest(),\n // Optional: Pass custom options to your native ad factory implementation.\n customOptions: {'custom-option-1', 'custom-value-1'}\n );\n _nativeAd.load();\n \n }\n}\n```\n\nExample:\n```text\nfinal Container adContainer = Container(\n alignment: Alignment.center,\n child: AdWidget adWidget = AdWidget(ad: _nativeAd!),\n width: WIDTH,\n height: HEIGHT,\n);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.983Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":341,"estimatedTokens":2878}}565{"id":"doc-mediation_c_google_for_developers-771c949e","source":"documentation","title":"Mediation | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/mediation","text":"Example:\n```text\n// Initialize the Google Mobile Ads library\nfirebase::gma::Initialize(*app);\n\n// In a game loop, monitor the initialization status\nauto initialize_future = firebase::gma::InitializeLastResult();\n\nif (initialize_future.status() == firebase::kFutureStatusComplete &&\n initialize_future.error() == firebase::gma::kAdErrorCodeNone) {\n // Initialization completed successfully, log the adapter status:\n std::map<std::string, firebase::gma::AdapterStatus> adapter_status_map =\n firebase::gma::GetInitializationStatus().GetAdapterStatusMap();\n\n for (auto it = adapter_status_map.begin(); it != adapter_status_map.end(); ++it) {\n std::string adapter_class_name = it->first;\n firebase::gma::AdapterStatus adapter_status = it->second;\n printf(“adapter: %s \\t description: %s \\t is_initialized: %d latency: %d\\n”,\n adapter_class_name.c_str(),\n adapter_status.description().c_str(),\n adapter_status.is_initialized(),\n adpater_status.latency());\n }\n} else {\n // Handle initialization error.\n}\n```\n\nExample:\n```text\nfirebase::Future<AdResult> load_ad_future = banner_view.loadAd(ad_request);\n\n// In a game loop, monitor the ad load status\nif (load_ad_future.status() == firebase::kFutureStatusComplete &&\n load_ad_future.error() == firebase::gma::kAdErrorCodeNone) {\n const AdResult* ad_result = load_ad_future.result();\n printf(“Loaded ad with adapter class name: %s\\n”,\n ad_result->adapter_class_name().c_str());\n} else {\n // Handle the load ad error.\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.984Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":44,"estimatedTokens":383}}566{"id":"doc-banner_ads_c_google_for_developers-bb09e276","source":"documentation","title":"Banner Ads | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/banner","text":"Example:\n```text\n#include \"firebase/gma/ad_view.h\"\n```\n\nExample:\n```text\nfirebase::gma::AdView* ad_view;\n ad_view = new firebase::gma::AdView();\n```\n\nExample:\n```text\n// my_ad_parent is a jobject reference\n // to an Android Activity or a pointer to an iOS UIView.\n firebase::gma::AdParent ad_parent = static_cast<firebase::gma::AdParent>(my_ad_parent);\n firebase::Future result =\n ad_view->Initialize(ad_parent, kBannerAdUnit, firebase::gma::AdSize::kBanner);\n```\n\nExample:\n```text\n// Monitor the status of the future in your game loop:\n firebase::Future<void> result = ad_view->InitializeLastResult();\n if (result.status() == firebase::kFutureStatusComplete) {\n // Initialization completed.\n if(future.error() == firebase::gma::kAdErrorCodeNone) {\n // Initialization successful.\n } else {\n // An error has occurred.\n }\n } else {\n // Initialization on-going.\n }\n```\n\nExample:\n```text\nfirebase::Future<void> result = ad_view->SetPosition(firebase::gma::AdView::kPositionTop);\n```\n\nExample:\n```text\nfirebase::gma::AdRequest ad_request;\nfirebase::Future<firebase::gma::AdResult> load_ad_result = ad_view->LoadAd(my_ad_request);\n```\n\nExample:\n```text\nfirebase::Future<void> result = ad_view->Show();\n```\n\nExample:\n```text\nclass ExampleAdListener\n : public firebase::gma::AdListener {\n public:\n ExampleAdListener() {}\n void OnAdClicked() override {\n // This method is invoked when the user clicks the ad.\n }\n\n void OnAdClosed() override {\n // This method is invoked when the user closes the ad.\n }\n\n void OnAdImpression() override {\n // This method is invoked when an impression is recorded for an ad.\n }\n\n void OnAdOpened() override {\n // This method is invoked when an ad opens an overlay that covers the screen.\n }\n};\n\nExampleAdListener* ad_listener = new ExampleAdListener();\nad_view->SetAdListener(ad_listener);\n```\n\nExample:\n```text\nfirebase::gma::AdSize ad_size(/*width=*/320, /*height=*/50);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.984Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":85,"estimatedTokens":492}}567{"id":"doc-targeting_c_google_for_developers-d5f0ea36","source":"documentation","title":"Targeting | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/targeting","text":"Example:\n```text\nfirebase::gma::RequestConfiguration retrieved_configuration =\n firebase::gma::GetRequestConfiguration();\n\n // .. apply your changes, then:\n\n firebase::gma::SetRequestConfiguration(request_configuration);\n```\n\nExample:\n```text\nfirebase::gma::RequestConfiguration request_configuration =\n firebase::gma::GetRequestConfiguration();\n\n request_configuration.tag_for_child_directed_treatment =\n firebase::RequestConfiguration::kChildDirectedTreatmentTrue;\n\n firebase::gma::SetRequestConfiguration(request_configuration);\n```\n\nExample:\n```text\nfirebase::gma::RequestConfiguration request_configuration =\n firebase::gma::GetRequestConfiguration();\n\n request_configuration.tag_for_under_age_of_consent =\n firebase::RequestConfiguration::kUnderAgeOfConsentTrue;\n\n firebase::gma::SetRequestConfiguration(request_configuration);\n```\n\nExample:\n```text\nfirebase::gma::RequestConfiguration request_configuration =\n firebase::gma::GetRequestConfiguration();\n\n request_configuration.max_ad_content_rating =\n firebase::RequestConfiguration::kMaxAdContentRatingG;\n\n firebase::gma::SetRequestConfiguration(request_configuration);\n```\n\nExample:\n```text\n// AdRequest with content URL:\n firebase::admob::AdRequest ad_request(/*content_url=*/\"https://www.example.com\");\n\n // AdRequest without content URL:\n firebase::admob::AdRequest ad_request();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.985Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":53,"estimatedTokens":348}}568{"id":"doc-ad_load_errors_c_google_for_developers-8883ca49","source":"documentation","title":"Ad load errors | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/ad-load-errors","text":"Example:\n```text\nfirebase::Future<firebase::gma::AdResult> load_ad_future =\n ad_view->LoadAd(request);\n\n// In a game loop, monitor the load ad status\nif (load_ad_future.status() == firebase::kFutureStatusComplete) {\n const firebase::gma::AdResult* ad_result = load_ad_future.result();\n if (!ad_result.is_successful()) {\n // There was an error loading the ad.\n const AdError& ad_error = ad_result.ad_error();\n firebase::gma::AdErrorCode code = ad_error.code();\n std::string domain = ad_error.domain();\n std::string message = ad_error.message();\n const firebase::gma::ResponseInfo response_info = ad_error.response_info();\n printf(\"Received error with domain: %s, code: %d, message: %s and response info: %s\\n”,\n domain.c_str(), message.c_str(), response_info.ToString().c_str());\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.985Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":22,"estimatedTokens":208}}569{"id":"doc-configure_the_gmail_mcp_server_google_for_develo-a8274a4a","source":"documentation","title":"Configure the Gmail MCP server | Google for Developers","url":"https://developers.google.com/workspace/gmail/api/guides/configure-mcp-server","text":"Example:\n```text\ngcloud services enable gmail.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable gmailmcp.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\n{\n \"mcpServers\": {\n \"gmail\": {\n \"serverUrl\": \"https://gmailmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n }\n }\n}\n```\n\nExample:\n```text\nagy\n```\n\nExample:\n```text\n/mcp\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.986Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":36,"estimatedTokens":121}}570{"id":"doc-authorized_sellers_for_apps_app_ads_txt_c_google-a18bcef8","source":"documentation","title":"Authorized Sellers for Apps (app-ads.txt) | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/app-ads","text":"Example:\n```text\ngoogle.com, pub-00000000000000, DIRECT, f08c47fec0942fa0\n```\n\nExample:\n```text\nfirebase init\n```\n\nExample:\n```text\nfirebase deploy --only hosting\n```\n\nExample:\n```text\n\"hosting\": {\n ...\n \"redirects\": [\n {\n \"source\": \"/\",\n \"destination\": \"URL_TO_REDIRECT\",\n \"type\": 301\n }\n ]\n}\n```\n\nExample:\n```text\n\"hosting\": {\n ...\n \"redirects\": [\n {\n \"source\": \"/\",\n \"destination\": \"https://www.example.com\",\n \"type\": 301\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.988Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":44,"estimatedTokens":125}}571{"id":"doc-validate_server_side_verification_ssv_callbacks_-08ace702","source":"documentation","title":"Validate server-side verification (SSV) callbacks | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/ssv","text":"Example:\n```text\nRewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()\n .fetchVerifyingPublicKeysWith(\n RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)\n .build();\nString rewardUrl = ...;\nverifier.verify(rewardUrl);\n```\n\nExample:\n```devsite-click-to-copy\nfirebase::gma::RewardedAd* rewarded_ad;\n rewarded_ad = new firebase::gma::RewardedAd();\n\n firebase::gma::RewardedAd::ServerSideVerificationOptions options;\n options.custom_data = \"SAMPLE_CUSTOM_DATA_STRING\";\n rewarded_ad->SetServerSideVerificationOptions(options);\n```\n\nExample:\n```text\n{\n \"keys\": [\n {\n keyId: 1916455855,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...YTPcw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFkwEwYHKoZIzj0CAQYI...ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw==\"\n },\n {\n keyId: 3901585526,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...aDUsw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFYwEAYHKoZIzj0CAQYF...4akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw==\"\n },\n ],\n}\n```\n\nExample:\n```text\nString url = ...;\nNetHttpTransport httpTransport = new NetHttpTransport.Builder().build();\nHttpRequest httpRequest =\n httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(url));\nHttpResponse httpResponse = httpRequest.execute();\nif (httpResponse.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) {\n throw new IOException(\"Unexpected status code = \" + httpResponse.getStatusCode());\n}\nString data;\nInputStream contentStream = httpResponse.getContent();\ntry {\n InputStreamReader reader = new InputStreamReader(contentStream, UTF_8);\n data = readerToString(reader);\n} finally {\n contentStream.close();\n}\n```\n\nExample:\n```text\nprivate static Map<Integer, ECPublicKey> parsePublicKeysJson(String publicKeysJson)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = new HashMap<>();\n try {\n JSONArray keys = new JSONObject(publicKeysJson).getJSONArray(\"keys\");\n for (int i = 0; i < keys.length(); i++) {\n JSONObject key = keys.getJSONObject(i);\n publicKeys.put(\n key.getInt(\"keyId\"),\n EllipticCurves.getEcPublicKey(Base64.decode(key.getString(\"base64\"))));\n }\n } catch (JSONException e) {\n throw new GeneralSecurityException(\"failed to extract trusted signing public keys\", e);\n }\n if (publicKeys.isEmpty()) {\n throw new GeneralSecurityException(\"No trusted keys are available.\");\n }\n return publicKeys;\n}\n```\n\nExample:\n```devsite-click-to-copy\nhttps://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins\n×tamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887\n```\n\nExample:\n```text\npublic static final String SIGNATURE_PARAM_NAME = \"signature=\";\n...\nURI uri;\ntry {\n uri = new URI(rewardUrl);\n} catch (URISyntaxException ex) {\n throw new GeneralSecurityException(ex);\n}\nString queryString = uri.getQuery();\nint i = queryString.indexOf(SIGNATURE_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a signature query parameter\");\n}\nbyte[] queryParamContentData =\n queryString\n .substring(0, i - 1)\n // i - 1 instead of i because of & in the query string\n .getBytes(Charset.forName(\"UTF-8\"));\n```\n\nExample:\n```text\npublic static final String KEY_ID_PARAM_NAME = \"key_id=\";\n...\nString sigAndKeyId = queryString.substring(i);\ni = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a key_id query parameter\");\n}\nString sig =\n sigAndKeyId.substring(\n SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */);\nint keyId = Integer.valueOf(sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length()));\n```\n\nExample:\n```text\nprivate void verify(final byte[] dataToVerify, int keyId, final byte[] signature)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = parsePublicKeysJson();\n if (publicKeys.containsKey(keyId)) {\n foundKeyId = true;\n ECPublicKey publicKey = publicKeys.get(keyId);\n EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, HashType.SHA256, EcdsaEncoding.DER);\n verifier.verify(signature, dataToVerify);\n } else {\n throw new GeneralSecurityException(\"cannot find verifying key with key ID: \" + keyId);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.989Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":141,"estimatedTokens":1070}}572{"id":"doc-analyze_and_label_gmail_messages_with_gemini_and-2c49b135","source":"documentation","title":"Analyze and label Gmail messages with Gemini and Vertex AI | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/samples/gmail-sentiment-analysis-ai","text":"Example:\n```text\n/*\nCopyright 2024 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Triggered when the add-on is opened from the Gmail homepage.\n *\n * @param {Object} e - The event object.\n * @returns {Card} - The homepage card.\n */\nfunction onHomepageTrigger(e) {\n return buildHomepageCard();\n}\n```\n\nExample:\n```text\n/*\nCopyright 2024-2025 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Builds the main card displayed on the Gmail homepage.\n *\n * @returns {Card} - The homepage card.\n */\nfunction buildHomepageCard() {\n // Create a new card builder\n const cardBuilder = CardService.newCardBuilder();\n\n // Create a card header\n const cardHeader = CardService.newCardHeader();\n cardHeader.setImageUrl(\n \"https://fonts.gstatic.com/s/i/googlematerialicons/mail/v6/black-24dp/1x/gm_mail_black_24dp.png\",\n );\n cardHeader.setImageStyle(CardService.ImageStyle.CIRCLE);\n cardHeader.setTitle(\"Analyze your Gmail\");\n\n // Add the header to the card\n cardBuilder.setHeader(cardHeader);\n\n // Create a card section\n const cardSection = CardService.newCardSection();\n\n // Create buttons for generating sample emails and analyzing sentiment\n const buttonSet = CardService.newButtonSet();\n\n // Create \"Generate sample emails\" button\n const generateButton = createFilledButton(\n \"Generate sample emails\",\n \"generateSampleEmails\",\n \"#34A853\",\n );\n buttonSet.addButton(generateButton);\n\n // Create \"Analyze emails\" button\n const analyzeButton = createFilledButton(\n \"Analyze emails\",\n \"analyzeSentiment\",\n \"#FF0000\",\n );\n buttonSet.addButton(analyzeButton);\n\n // Add the button set to the section\n cardSection.addWidget(buttonSet);\n\n // Add the section to the card\n cardBuilder.addSection(cardSection);\n\n // Build and return the card\n return cardBuilder.build();\n}\n\n/**\n * Creates a filled text button with the specified text, function, and color.\n *\n * @param {string} text - The text to display on the button.\n * @param {string} functionName - The name of the function to call when the button is clicked.\n * @param {string} color - The background color of the button.\n * @returns {TextButton} - The created text button.\n */\nfunction createFilledButton(text, functionName, color) {\n // Create a new text button\n const textButton = CardService.newTextButton();\n\n // Set the button text\n textButton.setText(text);\n\n // Set the action to perform when the button is clicked\n const action = CardService.newAction();\n action.setFunctionName(functionName);\n textButton.setOnClickAction(action);\n\n // Set the button style to filled\n textButton.setTextButtonStyle(CardService.TextButtonStyle.FILLED);\n\n // Set the background color\n textButton.setBackgroundColor(color);\n\n return textButton;\n}\n\n/**\n * Creates a notification response with the specified text.\n *\n * @param {string} notificationText - The text to display in the notification.\n * @returns {ActionResponse} - The created action response.\n */\nfunction buildNotificationResponse(notificationText) {\n // Create a new notification\n const notification = CardService.newNotification();\n notification.setText(notificationText);\n\n // Create a new action response builder\n const actionResponseBuilder = CardService.newActionResponseBuilder();\n\n // Set the notification for the action response\n actionResponseBuilder.setNotification(notification);\n\n // Build and return the action response\n return actionResponseBuilder.build();\n}\n```\n\nExample:\n```text\n/*\nCopyright 2024-2025 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Analyzes the sentiment of the first 10 threads in the inbox\n * and labels them accordingly.\n *\n * @returns {ActionResponse} - A notification confirming completion.\n */\nfunction analyzeSentiment() {\n // Analyze and label emails\n analyzeAndLabelEmailSentiment();\n\n // Return a notification\n return buildNotificationResponse(\"Successfully completed sentiment analysis\");\n}\n\n/**\n * Analyzes the sentiment of emails and applies appropriate labels.\n */\nfunction analyzeAndLabelEmailSentiment() {\n // Define label names\n const labelNames = [\"HAPPY TONE 😊\", \"NEUTRAL TONE 😐\", \"UPSET TONE 😡\"];\n\n // Get or create labels for each sentiment\n const positiveLabel =\n GmailApp.getUserLabelByName(labelNames[0]) ||\n GmailApp.createLabel(labelNames[0]);\n const neutralLabel =\n GmailApp.getUserLabelByName(labelNames[1]) ||\n GmailApp.createLabel(labelNames[1]);\n const negativeLabel =\n GmailApp.getUserLabelByName(labelNames[2]) ||\n GmailApp.createLabel(labelNames[2]);\n\n // Get the first 10 threads in the inbox\n const threads = GmailApp.getInboxThreads(0, 10);\n\n // Iterate through each thread\n for (const thread of threads) {\n // Iterate through each message in the thread\n const messages = thread.getMessages();\n for (const message of messages) {\n // Get the plain text body of the message\n const emailBody = message.getPlainBody();\n\n // Analyze the sentiment of the email body\n const sentiment = processSentiment(emailBody);\n\n // Apply the appropriate label based on the sentiment\n if (sentiment === \"positive\") {\n thread.addLabel(positiveLabel);\n } else if (sentiment === \"neutral\") {\n thread.addLabel(neutralLabel);\n } else if (sentiment === \"negative\") {\n thread.addLabel(negativeLabel);\n }\n }\n }\n}\n\n/**\n * Generates sample emails for testing the sentiment analysis.\n *\n * @returns {ActionResponse} - A notification confirming email generation.\n */\nfunction generateSampleEmails() {\n // Get the current user's email address\n const userEmail = Session.getActiveUser().getEmail();\n\n // Define sample emails\n const sampleEmails = [\n {\n subject: \"Thank you for amazing service!\",\n body: \"Hi, I really enjoyed working with you. Thank you again!\",\n name: \"Customer A\",\n },\n {\n subject: \"Request for information\",\n body: \"Hello, I need more information on your recent product launch. Thank you.\",\n name: \"Customer B\",\n },\n {\n subject: \"Complaint!\",\n body: \"\",\n htmlBody: `<p>Hello, You are late in delivery, again.</p>\n<p>Please contact me ASAP before I cancel our subscription.</p>`,\n name: \"Customer C\",\n },\n ];\n\n // Send each sample email\n for (const email of sampleEmails) {\n GmailApp.sendEmail(userEmail, email.subject, email.body, {\n name: email.name,\n htmlBody: email.htmlBody,\n });\n }\n\n // Return a notification\n return buildNotificationResponse(\"Successfully generated sample emails\");\n}\n```\n\nExample:\n```text\n/*\nCopyright 2024-2025 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Replace with your project ID\nconst PROJECT_ID = \"[ADD YOUR GCP PROJECT ID HERE]\";\n\n// Location for your Vertex AI model\nconst VERTEX_AI_LOCATION = \"us-central1\";\n\n// Model ID to use for sentiment analysis\nconst MODEL_ID = \"gemini-2.5-flash\";\n\n/**\n * Sends the email text to Vertex AI for sentiment analysis.\n *\n * @param {string} emailText - The text of the email to analyze.\n * @returns {string} - The sentiment of the email ('positive', 'negative', or 'neutral').\n */\nfunction processSentiment(emailText) {\n // Construct the API endpoint URL\n const apiUrl = `https://${VERTEX_AI_LOCATION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${VERTEX_AI_LOCATION}/publishers/google/models/${MODEL_ID}:generateContent`;\n\n // Prepare the request payload\n const payload = {\n contents: [\n {\n role: \"user\",\n parts: [\n {\n text: `Analyze the sentiment of the following message: ${emailText}`,\n },\n ],\n },\n ],\n generationConfig: {\n temperature: 0.9,\n maxOutputTokens: 1024,\n responseMimeType: \"application/json\",\n // Expected response format for simpler parsing.\n responseSchema: {\n type: \"object\",\n properties: {\n response: {\n type: \"string\",\n enum: [\"positive\", \"negative\", \"neutral\"],\n },\n },\n },\n },\n };\n\n // Prepare the request options\n const options = {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${ScriptApp.getOAuthToken()}`,\n },\n contentType: \"application/json\",\n muteHttpExceptions: true, // Set to true to inspect the error response\n payload: JSON.stringify(payload),\n };\n\n // Make the API request\n const response = UrlFetchApp.fetch(apiUrl, options);\n\n // Parse the response. There are two levels of JSON responses to parse.\n const parsedResponse = JSON.parse(response.getContentText());\n const sentimentResponse = JSON.parse(\n parsedResponse.candidates[0].content.parts[0].text,\n ).response;\n\n // Return the sentiment\n return sentimentResponse;\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/Toronto\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/cloud-platform\",\n \"https://www.googleapis.com/auth/gmail.addons.execute\",\n \"https://www.googleapis.com/auth/gmail.labels\",\n \"https://www.googleapis.com/auth/gmail.modify\",\n \"https://www.googleapis.com/auth/script.external_request\",\n \"https://www.googleapis.com/auth/userinfo.email\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Sentiment Analysis\",\n \"logoUrl\": \"https://fonts.gstatic.com/s/i/googlematerialicons/sentiment_extremely_dissatisfied/v6/black-24dp/1x/gm_sentiment_extremely_dissatisfied_black_24dp.png\"\n },\n \"gmail\": {\n \"homepageTrigger\": {\n \"runFunction\": \"onHomepageTrigger\",\n \"enabled\": true\n }\n }\n },\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.991Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":390,"estimatedTokens":2886}}573{"id":"doc-caldav_api_developer_s_guide_google_calendar_goo-fb8ae781","source":"documentation","title":"CalDAV API Developer's Guide | Google Calendar | Google for Developers","url":"https://developers.google.com/workspace/calendar/caldav","text":"Example:\n```text\nhttps://apidata.googleusercontent.com/caldav/v2/CALENDAR_ID/user\n```\n\nExample:\n```text\nhttps://apidata.googleusercontent.com/caldav/v2/CALENDAR_ID/events\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.992Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":47}}574{"id":"doc-manage_holds_google_vault_google_for_developers-7e275f90","source":"documentation","title":"Manage holds | Google Vault | Google for Developers","url":"https://developers.google.com/workspace/vault/guides/holds","text":"Example:\n```text\nHeldMailQuery mailQuery = new HeldMailQuery().setTerms(\"to:ceo@company.com\");\nList accounts = Lists.newArrayList();\naccounts.add(new HeldAccount().setAccountId(user1accountId));\naccounts.add(new HeldAccount().setEmail(user2Email));\nHold hold = new Hold()\n .setName(\"My First mail Accounts Hold\")\n .setCorpus(\"MAIL\");\n .setQuery(new CorpusQuery().setMailQuery(mailQuery))\n .setAccounts(accounts);\nHold createdHold = client.matters().holds().create(matterId, hold).execute();\n```\n\nExample:\n```text\ndef create_hold_mail_accounts(service, matter_id, account_id):\n mail_query = {'terms': 'to:ceo@company.com'}\n accounts = [\n {'accountId': user1_account_id},\n {'email': user2_email}\n ]\n wanted_hold = {\n 'name': 'My First mail Accounts Hold',\n 'corpus': 'MAIL',\n 'query': {\n 'mailQuery': mail_query\n },\n 'accounts': accounts\n }\n return service.matters().holds().create(\n matterId=matter_id, body=wanted_hold).execute()\n```\n\nExample:\n```text\nHeldOrgUnit orgUnit = new HeldOrgUnit().setOrgUnitId(orgUnitId);\n// Include shared drives content.\nHeldDriveQuery driveQuery = new HeldDriveQuery().setIncludeSharedDriveFiles(true);\n// Create the hold.\nHold hold = new Hold()\n .setName(\"My First Drive OU Hold\")\n .setCorpus(\"DRIVE\")\n .setQuery(new CorpusQuery().setDriveQuery(driveQuery))\n .setOrgUnit(orgUnit);\nHold createdHold = client.matters().holds().create(matterId, hold).execute();\nreturn createdHold;\n```\n\nExample:\n```text\ndef create_hold_drive_org(service, matter_id, org_unit_id):\n drive_query = {'includeSharedDriveFiles': True}\n org_unit = {'orgUnitId': org_unit_id}\n wanted_hold = {\n 'name': 'My First Drive OU Hold',\n 'corpus': 'DRIVE',\n 'orgUnit': org_unit,\n 'query': {\n 'driveQuery': drive_query\n }\n }\n return service.matters().holds().create(\n matterId=matter_id, body=wanted_hold).execute()\n```\n\nExample:\n```text\nString APRIL_2_2017_GMT = \"2017-04-02T00:00:00Z\"; // See below for format*.\n \nList accounts = Lists.newArrayList();\naccounts.add(new HeldAccount().setAccountId(accountId));\naccounts.add(new HeldAccount().setAccountId(accountId2));\nHeldGroupsQuery groupQuery = new HeldGroupsQuery();\n// Restrict by sent date.\ngroupQuery.setStartTime(APRIL_2_2017_GMT);\ngroupQuery.setEndTime(APRIL_2_2017_GMT);\n// create the hold\nHold hold = new Hold()\n .setName(\"My First Group Hold\")\n .setCorpus(\"GROUPS\")\n .setQuery(new CorpusQuery().setGroupsQuery(groupQuery));\n hold.setAccounts(accounts);\nHold createdHold = client.matters().holds().create(matterId, hold).execute();\n```\n\nExample:\n```text\ndef create_hold_groups_date_range(service, matter_id, group_account_id):\n groups_query = {\n 'startTime': '2017-04-02T00:00:00Z', # See below for format*\n 'endTime': '2017-04-02T00:00:00Z'\n }\n accounts = [{'accountId': group_account_id}]\n wanted_hold = {\n 'name': 'My First Group Hold',\n 'corpus': 'GROUPS',\n 'query': {\n 'groupsQuery': groups_query\n },\n 'accounts': accounts\n }\n return service.matters().holds().create(\n matterId=matter_id, body=wanted_hold).execute()\n```\n\nExample:\n```text\nclient.matters().holds().accounts().list(matterId, holdId).execute().getAccounts();\n```\n\nExample:\n```text\n# If no accounts are on hold, ['accounts'] will raise an error.\ndef list_held_accounts(service, matter_id, hold_id):\n return service.matters().holds().accounts().list(\n matterId=matter_id, holdId=hold_id).execute()['accounts']\n```\n\nExample:\n```text\n// Add an account by ID.\nclient\n .matters()\n .holds()\n .accounts()\n .create(matterId, holdId, new HeldAccount().setAccountId(accountId))\n .execute();\n// Remove an account by ID.\nclient.matters().holds().accounts().delete(matterId, holdId, accountId).execute();\n\nString email = \"email@email.com\";\n// Add an account by email.\nclient\n .matters()\n .holds()\n .accounts()\n .create(matterId, holdId, new HeldAccount().setEmail(email))\n .execute();\n```\n\nExample:\n```text\ndef add_held_account(service, matter_id, hold_id, account_id):\n held_account = {'accountId': account_id}\n return service.matters().holds().accounts().create(\n matterId=matter_id, holdId=hold_id, body=held_account).execute()\n\ndef remove_held_account(service, matter_id, hold_id, account_id):\n return service.matters().holds().accounts().delete(\n matterId=matter_id, holdId=hold_id, accountId=account_id).execute()\n\ndef add_held_account(service, matter_id, hold_id, email):\n held_account = {'email': email}\n return service.matters().holds().accounts().create(\n matterId=matter_id, holdId=hold_id, body=held_account).execute()\n```\n\nExample:\n```text\nHold hold = client.matters().holds().get(matterId, holdId).execute();\nhold.getOrgUnit().setOrgUnitId(newOrgUnitId);\nHold modifiedHold = client.matters().holds().update(matterId, holdId, hold).execute();\nreturn modifiedHold;\n```\n\nExample:\n```text\ndef update_hold_ou(service, matter_id, hold_id, org_unit_id):\n current_hold = get_hold(matter_id, hold_id)\n current_hold['orgUnit'] = {'orgUnitId': org_unit_id}\n return service.matters().holds().update(\n matterId=matter_id, holdId=hold_id, body=current_hold).execute()\n```\n\nExample:\n```text\nString matterId = \"Matter Id\";\n\n// List all holds.\nList holdsList =\n client.matters().holds().list(matterId).execute().getHolds();\n\n// Paginate on holds.\nListHoldsResponse response = client\n .matters()\n .holds()\n .list(matterId)\n .setPageSize(10)\n .execute();\n\nString nextPageToken = response.getNextPageToken();\nif (nextPageToken != null) {\n client\n .matters()\n .holds()\n .list(matterId)\n .setPageSize(10)\n .setPageToken(nextPageToken)\n .execute();\n}\n```\n\nExample:\n```text\n# This can paginate in the same manner as with matters.\ndef list_holds(service, matter_id):\n return service.matters().holds().list(matterId=matter_id).execute()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.996Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":211,"estimatedTokens":1537}}575{"id":"doc-manage_exports_google_vault_google_for_developer-6e492f44","source":"documentation","title":"Manage exports | Google Vault | Google for Developers","url":"https://developers.google.com/workspace/vault/guides/exports","text":"Example:\n```text\npublic Export createMailAccountHeldDataExports(Vault client, String matterId) {\n AccountInfo emailsToSearch = new AccountInfo().setEmails(ImmutableList.of(\"email1\", \"email2\"));\n MailOptions mailQueryOptions = new MailOptions().setExportFormat(\"PST\");\n String queryTerms = \"to:ceo@solarmora.com\";\n Query mailQuery =\n new Query()\n .setCorpus(\"MAIL\")\n .setDataScope(\"HELD_DATA\")\n .setSearchMethod(\"ACCOUNT\")\n .setAccountInfo(emailsToSearch)\n .setTerms(queryTerms)\n .setMailOptions(mailQueryOptions);\n MailExportOptions mailExportOptions =\n new MailExportOptions()\n .setExportFormat(\"MBOX\")\n .showConfidentialModeContent(true);\n Export wantedExport =\n new Export()\n .setMatterId(matterId)\n .setName(\"My first mail accounts export\")\n .setQuery(mailQuery)\n .setExportOptions(new ExportOptions().setMailOptions(mailExportOptions));\n return client.matters().exports().create(matter, wantedExport).execute();\n}\n```\n\nExample:\n```text\ndef create_mail_account_held_data_export(service, matter_id):\n emails_to_search = ['email1', 'email2']\n mail_query_options = {'excludeDrafts': True}\n query_terms = 'to:ceo@solarmora.com'\n mail_query = {\n 'corpus': 'MAIL',\n 'dataScope': 'HELD_DATA',\n 'searchMethod': 'ACCOUNT',\n 'accountInfo': {\n 'emails': emails_to_search\n },\n 'terms': query_terms,\n 'mailOptions': mail_query_options,\n }\n mail_export_options = {\n 'exportFormat': 'MBOX',\n 'showConfidentialModeContent': True\n }\n wanted_export = {\n 'name': 'My first mail accounts export',\n 'query': mail_query,\n 'exportOptions': {\n 'mailOptions': mail_export_options\n }\n}\nreturn service.matters().exports().create(\n matterId=matter_id, body=wanted_export).execute()\n```\n\nExample:\n```text\npublic Export createDriveOuAllDataExport(Vault client, String matterId) {\n OrgUnitInfo ouToSearch = new OrgUnitInfo().setOrgUnitId(\"ou id retrieved from admin sdk\");\n DriveOptions driveQueryOptions = new DriveOptions().setIncludeSharedDrives(true);\n Query driveQuery =\n new Query()\n .setCorpus(\"DRIVE\")\n .setDataScope(\"ALL_DATA\")\n .setSearchMethod(\"ORG_UNIT\")\n .setOrgUnitInfo(ouToSearch)\n .setDriveOptions(driveQueryOptions)\n .setStartTime(\"2017-03-16T00:00:00Z\")\n .setEndTime(\"2017-03-16T00:00:00Z\")\n .setTimeZone(\"Etc/GMT+2\");\n DriveExportOptions driveExportOptions = new DriveExportOptions().setIncludeAccessInfo(false);\n Export wantedExport =\n new Export()\n .setName(\"My first drive ou export\")\n .setQuery(driveQuery)\n .setExportOptions(new ExportOptions().setDriveOptions(driveExportOptions));\n return client.matters().exports().create(matter, wantedExport).execute();\n}\n```\n\nExample:\n```text\ndef create_drive_ou_all_data_export(service, matter_id):\n ou_to_search = 'ou id retrieved from admin sdk'\n drive_query_options = {'includeSharedDrives': True}\n drive_query = {\n 'corpus': 'DRIVE',\n 'dataScope': 'ALL_DATA',\n 'searchMethod': 'ORG_UNIT',\n 'orgUnitInfo': {\n 'org_unit_id': ou_to_search\n },\n 'driveOptions': drive_query_options,\n 'startTime': '2017-03-16T00:00:00Z',\n 'endTime': '2017-09-23T00:00:00Z',\n 'timeZone': 'Etc/GMT+2'\n }\n drive_export_options = {'includeAccessInfo': False}\n wanted_export = {\n 'name': 'My first drive ou export',\n 'query': drive_query,\n 'exportOptions': {\n 'driveOptions': drive_export_options\n }\n }\nreturn service.matters().exports().create(\n matterId=matter_id, body=wanted_export).execute()\n```\n\nExample:\n```text\ndef create_meet_export(service, matter_id, ou_to_search, export_name):\n export = {\n 'name': export_name,\n 'query': {\n 'corpus': 'DRIVE',\n 'dataScope': 'ALL_DATA',\n 'searchMethod': 'ORG_UNIT',\n 'terms': 'title:\"...-...-... \\\\(....-..-.. at ..:.. *\\\\)\"',\n 'orgUnitInfo': {\n 'orgUnitId': 'id:'+ou_to_search\n },\n 'driveOptions': {\n 'includeTeamDrives': True,\n 'includeSharedDrives': True\n },\n 'timeZone': 'Etc/GMT',\n 'method': 'ORG_UNIT'\n },\n 'exportOptions': {\n 'driveOptions': {},\n 'region': 'ANY'\n },\n }\n\n return service.matters().exports().create(\n matterId=matter_id, body=export).execute()\n```\n\nExample:\n```text\ndef create_mail_export_from_saved_query(service, matter_id, saved_query_id, export_name):\n export = {\n 'name': export_name,\n 'exportOptions': {\n 'mailOptions': {\n 'exportFormat': 'PST',\n 'showConfidentialModeContent': True\n },\n 'region': 'ANY'\n }\n }\n\n export['query'] = service.matters().savedQueries().get(\n savedQueryId=saved_query_id, matterId=matter_id).execute()['query']\n return service.matters().exports().create(\n matterId=matter_id, body=export).execute()\n```\n\nExample:\n```text\npublic class exports {\n public ListExportsResponse listExports(Vault client, String matterId) {\n return client.matters().exports().list(matterId).execute();\n}\n```\n\nExample:\n```text\ndef list_exports(service, matter_id):\n return service.matters().exports().list(matterId=matter_id).execute()\n```\n\nExample:\n```text\npublic Export getExportById(Vault client, String matterId, String exportId) {\n return client.matters().exports().get(matterId, exportId).execute();\n}\n```\n\nExample:\n```text\ndef get_export_by_id(service, matter_id, export_id):\n return service.matters().exports().get(\n matterId=matter_id, exportId=export_id).execute()\n```\n\nExample:\n```text\ndef download_exports(service, matter_id):\n\"\"\"Google Cloud storage service is authenticated by running\n`gcloud auth application-default login` and expects a billing enabled project\nin ENV variable `GOOGLE_CLOUD_PROJECT` \"\"\"\ngcpClient = storage.Client()\nmatter_id = os.environ['MATTERID']\n for export in vaultService.matters().exports().list(\n matterId=matter_id).execute()['exports']:\n if 'cloudStorageSink' in export:\n directory = export['name']\n if not os.path.exists(directory):\n os.makedirs(directory)\n print(export['id'])\n for sinkFile in export['cloudStorageSink']['files']:\n filename = '%s/%s' % (directory, sinkFile['objectName'].split('/')[-1])\n objectURI = 'gs://%s/%s' % (sinkFile['bucketName'],\n sinkFile['objectName'])\n print('get %s to %s' % (objectURI, filename))\n gcpClient.download_blob_to_file(objectURI, open(filename, 'wb+'))\n```\n\nExample:\n```text\npublic void deleteExportById(Vault client, String matterId, String exportId) {\n client.matters().exports().delete(matterId, exportId).execute();\n```\n\nExample:\n```text\ndef delete_export_by_id(service, matter_id, export_id):\n return service.matters().exports().delete(\n matterId=matter_id, exportId=export_id).execute()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.998Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":228,"estimatedTokens":1746}}576{"id":"doc-manage_saved_queries_google_vault_google_for_dev-6ae7cec8","source":"documentation","title":"Manage saved queries | Google Vault | Google for Developers","url":"https://developers.google.com/workspace/vault/guides/saved-queries","text":"Example:\n```text\npublic SavedQuery createMailAllDataAccountSavedQuery(String matterId) throws Exception {\n AccountInfo emailsToSearch =\n new AccountInfo().setEmails((ImmutableList.of(\"email1\", \"email2\")));\n MailOptions mailOptions = new MailOptions().setExcludeDrafts(true);\n String queryTerms = \"to:ceo@solarmora.com\";\n Query mailQuery =\n new Query()\n .setCorpus(\"MAIL\")\n .setDataScope(\"ALL_DATA\")\n .setSearchMethod(\"ACCOUNT\")\n .setAccountInfo(emailsToSearch)\n .setTerms(queryTerms)\n .setMailOptions(mailOptions);\n SavedQuery savedQuery =\n new SavedQuery()\n .setDisplayName(\"NEW SAVED QUERY NAME\")\n .setQuery(mailQuery);\n return client.matters().savedQueries().create(matterId, savedQuery).execute();\n}\n```\n\nExample:\n```text\ndef create_mail_all_data_account_saved_query(self, matter_id):\n emails_to_search = ['email1', 'email2']\n mail_query_options = {'excludeDrafts': True}\n query_terms = 'to:ceo@solarmora.com'\n mail_query = {\n 'corpus': 'MAIL',\n 'dataScope': 'ALL_DATA',\n 'searchMethod': 'ACCOUNT',\n 'accountInfo': {\n 'emails': emails_to_search\n },\n 'terms': query_terms,\n 'mailOptions': mail_query_options,\n }\n saved_query = {\n 'displayName': 'NEW SAVED QUERY NAME',\n 'query': mail_query,\n }\n return self.service.matters().savedQueries().create(\n matterId=matter_id, body=saved_query).execute()\n```\n\nExample:\n```text\npublic SavedQuery createDriveAllDataOUSavedQuery(String matterId) throws Exception {\n OrgUnitInfo ouToSearch = new OrgUnitInfo().setOrgUnitId(\"ou id retrieved from admin sdk\");\n DriveOptions driveQueryOptions = new DriveOptions().setIncludeTeamDrives(true);\n Query driveQuery =\n new Query()\n .setCorpus(\"DRIVE\")\n .setDataScope(\"ALL_DATA\")\n .setSearchMethod(\"ORG_UNIT\")\n .setOrgUnitInfo(ouToSearch)\n .setDriveOptions(driveQueryOptions);\n SavedQuery savedQuery =\n new SavedQuery()\n .setDisplayName(\"NEW SAVED QUERY NAME\")\n .setQuery(driveQuery);\n return client.matters().savedQueries().create(matterId, savedQuery).execute();\n }\n}\n```\n\nExample:\n```text\ndef create_drive_all_data_ou_saved_query(self, matter_id):\n ou_to_search = 'ou id retrieved from admin sdk'\n drive_query_options = {'includeTeamDrives': True}\n drive_query = {\n 'corpus': 'DRIVE',\n 'dataScope': 'ALL_DATA',\n 'searchMethod': 'ORG_UNIT',\n 'orgUnitInfo': {\n 'org_unit_id': ou_to_search,\n },\n 'driveOptions': drive_query_options\n }\n saved_query = {\n 'displayName': 'NEW SAVED QUERY NAME',\n 'query': drive_query,\n }\n return self.service.matters().savedQueries().create(\n matterId=matter_id, body=saved_query).execute()\n```\n\nExample:\n```text\npublic Empty deleteSavedQuery(String matterId, String savedQueryId) throws Exception {\n return client.matters().savedQueries().delete(matterId, savedQueryId).execute();\n}\n```\n\nExample:\n```text\ndef delete_saved_query(self, matter_id, saved_query_id):\n empty_response = self.service.matters().savedQueries().delete(\n matterId=matter_id, savedQueryId=saved_query_id).execute()\n return empty_response\n```\n\nExample:\n```text\npublic SavedQuery getSavedQuery(String matterId, String savedQueryId) throws Exception {\n return client.matters().savedQueries().get(matterId, savedQueryId).execute();\n}\n```\n\nExample:\n```text\ndef get_saved_query(self, matter_id, saved_query_id):\n saved_query = self.service.matters().savedQueries().get(\n matterId=matter_id, savedQueryId=saved_query_id).execute()\n return saved_query\n```\n\nExample:\n```text\npublic void listSavedQueries(String matterId) throws Exception {\n ListSavedQueriesResponse firstPageResponse =\n client.matters().savedQueries().list(matterId).setPageSize(10).execute();\n String nextPageToken = firstPageResponse.getNextPageToken();\n if (nextPageToken != null) {\n client\n .matters()\n .savedQueries()\n .list(matterId)\n .setPageSize(10)\n .setPageToken(nextPageToken)\n .execute();\n }\n}\n```\n\nExample:\n```text\ndef list_saved_queries(self, matter_id):\n first_page_response = self.service.matters().savedQueries().list(\n matterId=matter_id, pageSize=10).execute()\n if 'nextPageToken' in first_page_response:\n self.service.matters().savedQueries().list(\n pageSize=10,\n pageToken=first_page_response['nextPageToken']).execute()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:50.998Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":150,"estimatedTokens":1170}}577{"id":"doc-rewarded_ads_flutter_google_for_developers-78daf1b2","source":"documentation","title":"Rewarded ads | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/rewarded","text":"Example:\n```text\nca-app-pub-3940256099942544/5224354917\n```\n\nExample:\n```text\nca-app-pub-3940256099942544/1712485313\n```\n\nExample:\n```text\nRewardedAd.load(\n adUnitId: \"_adUnitId\",\n request: const AdRequest(),\n rewardedAdLoadCallback: RewardedAdLoadCallback(\n onAdLoaded: (RewardedAd ad) {\n // Called when an ad is successfully received.\n debugPrint('Ad was loaded.');\n // Keep a reference to the ad so you can show it later.\n _rewardedAd = ad;\n },\n onAdFailedToLoad: (LoadAdError error) {\n // Called when an ad request failed.\n debugPrint('Ad failed to load with error: $error');\n },\n ),\n);rewarded_ad_snippets.dart\n```\n\nExample:\n```text\nad.fullScreenContentCallback = FullScreenContentCallback(\n onAdShowedFullScreenContent: (ad) {\n // Called when the ad showed the full screen content.\n debugPrint('Ad showed full screen content.');\n },\n onAdFailedToShowFullScreenContent: (ad, err) {\n // Called when the ad failed to show full screen content.\n debugPrint('Ad failed to show full screen content with error: $err');\n // Dispose the ad here to free resources.\n ad.dispose();\n },\n onAdDismissedFullScreenContent: (ad) {\n // Called when the ad dismissed full screen content.\n debugPrint('Ad was dismissed.');\n // Dispose the ad here to free resources.\n ad.dispose();\n },\n onAdImpression: (ad) {\n // Called when an impression occurs on the ad.\n debugPrint('Ad recorded an impression.');\n },\n onAdClicked: (ad) {\n // Called when a click is recorded for an ad.\n debugPrint('Ad was clicked.');\n },\n);rewarded_ad_snippets.dart\n```\n\nExample:\n```text\n_rewardedAd?.show(\n onUserEarnedReward:\n (AdWithoutView ad, RewardItem rewardItem) {\n debugPrint(\n 'Reward amount: ${rewardItem.amount}',\n );\n },\n);main.dart\n```\n\nExample:\n```readonly\nRewardedAd.load(\n adUnitId: \"_adUnitId\",\n request: AdRequest(),\n rewardedAdLoadCallback: RewardedAdLoadCallback(\n onAdLoaded: (ad) {\n ServerSideVerificationOptions _options =\n ServerSideVerificationOptions(\n customData: 'SAMPLE_CUSTOM_DATA_STRING',\n );\n ad.setServerSideOptions(_options);\n _rewardedAd = ad;\n },\n onAdFailedToLoad: (error) {},\n ),\n);rewarded_ad_snippets.dart\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:51.001Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":92,"estimatedTokens":577}}578{"id":"doc-authorized_sellers_for_apps_app_ads_txt_android_-01af6d17","source":"documentation","title":"Authorized Sellers for Apps (app-ads.txt) | Android | Google for Developers","url":"https://developers.google.com/admob/android/app-ads","text":"Example:\n```text\ngoogle.com, pub-00000000000000, DIRECT, f08c47fec0942fa0\n```\n\nExample:\n```text\nfirebase init\n```\n\nExample:\n```text\nfirebase deploy --only hosting\n```\n\nExample:\n```text\n\"hosting\": {\n ...\n \"redirects\": [\n {\n \"source\": \"/\",\n \"destination\": \"URL_TO_REDIRECT\",\n \"type\": 301\n }\n ]\n}\n```\n\nExample:\n```text\n\"hosting\": {\n ...\n \"redirects\": [\n {\n \"source\": \"/\",\n \"destination\": \"https://www.example.com\",\n \"type\": 301\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:51.002Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":44,"estimatedTokens":125}}579{"id":"doc-ad_preloading_beta_android_google_for_developers-be921c06","source":"documentation","title":"Ad preloading (beta) | Android | Google for Developers","url":"https://developers.google.com/admob/android/ad-preloading","text":"Example:\n```text\n// Define a PreloadConfiguration.\nval configuration = PreloadConfiguration.Builder(\"AD_UNIT_ID\").build()\n// Start the preloading with a given preload ID, preload configuration.\nInterstitialAdPreloader.start(\"AD_UNIT_ID\", configuration)InterstitialAdPreloaderSnippets.kt\n```\n\nExample:\n```text\n// Define a PreloadConfiguration.\nPreloadConfiguration configuration = new PreloadConfiguration.Builder(\"AD_UNIT_ID\").build();\n// Start the preloading with a given preload ID, preload configuration.\nInterstitialAdPreloader.start(\"AD_UNIT_ID\", configuration);InterstitialAdPreloaderSnippets.java\n```\n\nExample:\n```text\n// pollAd() returns the next available ad and loads another ad in the background.\nval ad = InterstitialAdPreloader.pollAd(\"AD_UNIT_ID\")\n\n// [Optional] Interact with the ad as needed.\nad?.onPaidEventListener = OnPaidEventListener {\n // [Optional] Send the impression-level ad revenue information to your preferred\n // analytics server directly within this callback.\n}\n\n// Show the ad immediately.\nad?.show(activity)InterstitialAdPreloaderSnippets.kt\n```\n\nExample:\n```text\n// pollAd() returns the next available ad and loads another ad in the background.\nInterstitialAd ad = InterstitialAdPreloader.pollAd(\"AD_UNIT_ID\");\n\nif (ad != null) {\n // [Optional] Interact with the ad object as needed.\n ad.setOnPaidEventListener(\n adValue -> {\n // [Optional] Send the impression-level ad revenue information to your preferred\n // analytics server directly within this callback.\n });\n\n // Show the ad immediately.\n ad.show(activity);\n}InterstitialAdPreloaderSnippets.java\n```\n\nExample:\n```text\n// Verify that a preloaded ad is available.\nif (!InterstitialAdPreloader.isAdAvailable(\"AD_UNIT_ID\")) {\n // No ads are available to show.\n}InterstitialAdPreloaderSnippets.kt\n```\n\nExample:\n```text\n// Verify that a preloaded ad is available.\nif (!InterstitialAdPreloader.isAdAvailable(\"AD_UNIT_ID\")) {\n // No ads are available to show.\n}InterstitialAdPreloaderSnippets.java\n```\n\nExample:\n```text\n// Define a callback to receive preload events.\nval callback =\n object : PreloadCallbackV2() {\n override fun onAdPreloaded(preloadId: String, responseInfo: ResponseInfo?) {\n // Called when preloaded ads are available.\n }\n\n override fun onAdsExhausted(preloadId: String) {\n // Called when no preloaded ads are available.\n }\n\n override fun onAdFailedToPreload(preloadId: String, adError: AdError) {\n // Called when preloaded ads failed to load.\n }\n }InterstitialAdPreloaderSnippets.kt\n```\n\nExample:\n```text\n// Define a callback to receive preload events.\nPreloadCallbackV2 callback =\n new PreloadCallbackV2() {\n @Override\n public void onAdPreloaded(\n @NonNull String preloadId, @Nullable ResponseInfo responseInfo) {\n // Called when preloaded ads are available.\n }\n\n @Override\n public void onAdsExhausted(@NonNull String preloadId) {\n // Called when no preloaded ads are available.\n }\n\n @Override\n public void onAdFailedToPreload(@NonNull String preloadId, @NonNull AdError adError) {\n // Called when preloaded ads failed to load.\n }\n };InterstitialAdPreloaderSnippets.java\n```\n\nExample:\n```text\n// Stops the preloading and destroy preloaded ads.\nInterstitialAdPreloader.destroy(\"AD_UNIT_ID\")\n// Stops the preloading and destroy all ads.\nInterstitialAdPreloader.destroyAll()InterstitialAdPreloaderSnippets.kt\n```\n\nExample:\n```text\n// Stops the preloading and destroy preloaded ads.\nInterstitialAdPreloader.destroy(\"AD_UNIT_ID\");\n// Stops the preloading and destroy all ads.\nInterstitialAdPreloader.destroyAll();InterstitialAdPreloaderSnippets.java\n```\n\nExample:\n```text\n// Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\nval configuration = PreloadConfiguration.Builder(\"AD_UNIT_ID\").setBufferSize(2).build()InterstitialAdPreloaderSnippets.kt\n```\n\nExample:\n```text\n// Define a PreloadConfiguration and set the buffer size to 2 preloaded ads.\nPreloadConfiguration configuration =\n new PreloadConfiguration.Builder(\"AD_UNIT_ID\").setBufferSize(2).build();InterstitialAdPreloaderSnippets.java\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:51.003Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":137,"estimatedTokens":1046}}580{"id":"doc-set_up_web_views_android_google_for_developers-bec2f010","source":"documentation","title":"Set up web views | Android | Google for Developers","url":"https://developers.google.com/admob/android/browser/webview","text":"Example:\n```text\nCookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);\n```\n\nExample:\n```text\nCookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)\n```\n\nExample:\n```text\nimport android.webkit.CookieManager;\nimport android.webkit.WebView;\n\npublic class MainActivity extends AppCompatActivity {\n private WebView webView;\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n webView = findViewById(R.id.webview);\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);\n // Let the web view use JavaScript.\n webView.getSettings().setJavaScriptEnabled(true);\n // Let the web view access local storage.\n webView.getSettings().setDomStorageEnabled(true);\n // Let HTML videos play automatically.\n webView.getSettings().setMediaPlaybackRequiresUserGesture(false);\n }\n}\n```\n\nExample:\n```text\nimport android.webkit.CookieManager\nimport android.webkit.WebView\n\nclass MainActivity : AppCompatActivity() {\n lateinit var webView: WebView\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n webView = findViewById(R.id.webview)\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)\n // Let the web view use JavaScript.\n webView.settings.javaScriptEnabled = true\n // Let the web view access local storage.\n webView.settings.domStorageEnabled = true\n // Let HTML videos play automatically.\n webView.settings.mediaPlaybackRequiresUserGesture = false\n }\n}\n```\n\nExample:\n```text\nimport android.webkit.CookieManager;\nimport android.webkit.WebView;\n\npublic class MainActivity extends AppCompatActivity {\n private WebView webView;\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n webView = findViewById(R.id.webview);\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);\n // Let the web view use JavaScript.\n webView.getSettings().setJavaScriptEnabled(true);\n // Let the web view access local storage.\n webView.getSettings().setDomStorageEnabled(true);\n // Let HTML videos play automatically.\n webView.getSettings().setMediaPlaybackRequiresUserGesture(false);\n\n // Load the URL for optimized web view performance.\n webView.loadUrl(\"https://google.github.io/webview-ads/test/\");\n }\n}\n```\n\nExample:\n```text\nimport android.webkit.CookieManager\nimport android.webkit.WebView\n\nclass MainActivity : AppCompatActivity() {\n lateinit var webView: WebView\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n webView = findViewById(R.id.webview)\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)\n // Let the web view use JavaScript.\n webView.settings.javaScriptEnabled = true\n // Let the web view access local storage.\n webView.settings.domStorageEnabled = true\n // Let HTML videos play automatically.\n webView.settings.mediaPlaybackRequiresUserGesture = false\n\n // Load the URL for optimized web view performance.\n webView.loadUrl(\"https://google.github.io/webview-ads/test/\")\n }\n}\n```\n\nExample:\n```text\nhttps://google.github.io/webview-ads/test/\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:51.003Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":124,"estimatedTokens":911}}581{"id":"doc-validate_server_side_verification_ssv_callbacks_-c2686042","source":"documentation","title":"Validate server-side verification (SSV) callbacks | Android | Google for Developers","url":"https://developers.google.com/admob/android/ssv","text":"Example:\n```text\nRewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()\n .fetchVerifyingPublicKeysWith(\n RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)\n .build();\nString rewardUrl = ...;\nverifier.verify(rewardUrl);\n```\n\nExample:\n```devsite-click-to-copy\nRewardedAd.load(MainActivity.this, \"AD_UNIT_ID\",\n new AdRequest.Builder().build(), new RewardedAdLoadCallback() {\n @Override\n public void onAdLoaded(RewardedAd ad) {\n Log.d(TAG, \"Ad was loaded.\");\n rewardedAd = ad;\n ServerSideVerificationOptions options = new ServerSideVerificationOptions\n .Builder()\n .setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\")\n .build();\n rewardedAd.setServerSideVerificationOptions(options);\n }\n @Override\n public void onAdFailedToLoad(LoadAdError loadAdError) {\n Log.d(TAG, loadAdError.toString());\n rewardedAd = null;\n }\n});\n```\n\nExample:\n```devsite-click-to-copy\nRewardedAd.load(this, \"AD_UNIT_ID\",\n AdRequest.Builder().build(), object : RewardedAdLoadCallback() {\n override fun onAdLoaded(ad: RewardedAd) {\n Log.d(TAG, \"Ad was loaded.\")\n rewardedInterstitialAd = ad\n val options = ServerSideVerificationOptions.Builder()\n .setCustomData(\"SAMPLE_CUSTOM_DATA_STRING\")\n .build()\n rewardedAd.setServerSideVerificationOptions(options)\n }\n\n override fun onAdFailedToLoad(adError: LoadAdError) {\n Log.d(TAG, adError?.toString())\n rewardedAd = null\n }\n})\n```\n\nExample:\n```text\n{\n \"keys\": [\n {\n keyId: 1916455855,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...YTPcw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFkwEwYHKoZIzj0CAQYI...ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw==\"\n },\n {\n keyId: 3901585526,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...aDUsw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFYwEAYHKoZIzj0CAQYF...4akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw==\"\n },\n ],\n}\n```\n\nExample:\n```text\nString url = ...;\nNetHttpTransport httpTransport = new NetHttpTransport.Builder().build();\nHttpRequest httpRequest =\n httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(url));\nHttpResponse httpResponse = httpRequest.execute();\nif (httpResponse.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) {\n throw new IOException(\"Unexpected status code = \" + httpResponse.getStatusCode());\n}\nString data;\nInputStream contentStream = httpResponse.getContent();\ntry {\n InputStreamReader reader = new InputStreamReader(contentStream, UTF_8);\n data = readerToString(reader);\n} finally {\n contentStream.close();\n}\n```\n\nExample:\n```text\nprivate static Map<Integer, ECPublicKey> parsePublicKeysJson(String publicKeysJson)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = new HashMap<>();\n try {\n JSONArray keys = new JSONObject(publicKeysJson).getJSONArray(\"keys\");\n for (int i = 0; i < keys.length(); i++) {\n JSONObject key = keys.getJSONObject(i);\n publicKeys.put(\n key.getInt(\"keyId\"),\n EllipticCurves.getEcPublicKey(Base64.decode(key.getString(\"base64\"))));\n }\n } catch (JSONException e) {\n throw new GeneralSecurityException(\"failed to extract trusted signing public keys\", e);\n }\n if (publicKeys.isEmpty()) {\n throw new GeneralSecurityException(\"No trusted keys are available.\");\n }\n return publicKeys;\n}\n```\n\nExample:\n```devsite-click-to-copy\nhttps://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins\n×tamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887\n```\n\nExample:\n```text\npublic static final String SIGNATURE_PARAM_NAME = \"signature=\";\n...\nURI uri;\ntry {\n uri = new URI(rewardUrl);\n} catch (URISyntaxException ex) {\n throw new GeneralSecurityException(ex);\n}\nString queryString = uri.getQuery();\nint i = queryString.indexOf(SIGNATURE_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a signature query parameter\");\n}\nbyte[] queryParamContentData =\n queryString\n .substring(0, i - 1)\n // i - 1 instead of i because of & in the query string\n .getBytes(Charset.forName(\"UTF-8\"));\n```\n\nExample:\n```text\npublic static final String KEY_ID_PARAM_NAME = \"key_id=\";\n...\nString sigAndKeyId = queryString.substring(i);\ni = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a key_id query parameter\");\n}\nString sig =\n sigAndKeyId.substring(\n SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */);\nint keyId = Integer.valueOf(sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length()));\n```\n\nExample:\n```text\nprivate void verify(final byte[] dataToVerify, int keyId, final byte[] signature)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = parsePublicKeysJson();\n if (publicKeys.containsKey(keyId)) {\n foundKeyId = true;\n ECPublicKey publicKey = publicKeys.get(keyId);\n EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, HashType.SHA256, EcdsaEncoding.DER);\n verifier.verify(signature, dataToVerify);\n } else {\n throw new GeneralSecurityException(\"cannot find verifying key with key ID: \" + keyId);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:51.004Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":173,"estimatedTokens":1293}}582{"id":"doc-targeting_android_google_for_developers-9beda327","source":"documentation","title":"Targeting | Android | Google for Developers","url":"https://developers.google.com/admob/android/targeting","text":"Example:\n```text\nval requestConfiguration = MobileAds.getRequestConfiguration()\nMobileAds.setRequestConfiguration(requestConfiguration)RequestConfigurationSnippets.kt\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration = MobileAds.getRequestConfiguration();\nMobileAds.setRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.java\n```\n\nExample:\n```text\nval requestConfiguration =\n MobileAds.getRequestConfiguration()\n .toBuilder()\n // Indicate that ad requests should have child age treatment.\n .setAgeRestrictedTreatment(AgeRestrictedTreatment.CHILD)\n .build()\nMobileAds.setRequestConfiguration(requestConfiguration)RequestConfigurationSnippets.kt\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration =\n MobileAds.getRequestConfiguration().toBuilder()\n // Indicate that ad requests should have child age treatment.\n .setAgeRestrictedTreatment(AgeRestrictedTreatment.CHILD)\n .build();\nMobileAds.setRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.java\n```\n\nExample:\n```text\nval requestConfiguration =\n MobileAds.getRequestConfiguration()\n .toBuilder()\n .setTagForChildDirectedTreatment(RequestConfiguration.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE)\n .build()\nMobileAds.setRequestConfiguration(requestConfiguration)RequestConfigurationSnippets.kt\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration =\n MobileAds.getRequestConfiguration().toBuilder()\n .setTagForChildDirectedTreatment(\n RequestConfiguration.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE)\n .build();\nMobileAds.setRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.java\n```\n\nExample:\n```text\nval requestConfiguration =\n MobileAds.getRequestConfiguration()\n .toBuilder()\n .setTagForUnderAgeOfConsent(RequestConfiguration.TAG_FOR_UNDER_AGE_OF_CONSENT_TRUE)\n .build()\nMobileAds.setRequestConfiguration(requestConfiguration)RequestConfigurationSnippets.kt\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration =\n MobileAds.getRequestConfiguration().toBuilder()\n .setTagForUnderAgeOfConsent(RequestConfiguration.TAG_FOR_UNDER_AGE_OF_CONSENT_TRUE)\n .build();\nMobileAds.setRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.java\n```\n\nExample:\n```text\nval requestConfiguration =\n MobileAds.getRequestConfiguration()\n .toBuilder()\n .setMaxAdContentRating(RequestConfiguration.MAX_AD_CONTENT_RATING_G)\n .build()\nMobileAds.setRequestConfiguration(requestConfiguration)RequestConfigurationSnippets.kt\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration =\n MobileAds.getRequestConfiguration().toBuilder()\n .setMaxAdContentRating(RequestConfiguration.MAX_AD_CONTENT_RATING_G)\n .build();\nMobileAds.setRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.java\n```\n\nExample:\n```text\nval requestConfiguration =\n MobileAds.getRequestConfiguration()\n .toBuilder()\n .setPublisherPrivacyPersonalizationState(\n RequestConfiguration.PublisherPrivacyPersonalizationState.DISABLED\n )\n .build()\nMobileAds.setRequestConfiguration(requestConfiguration)RequestConfigurationSnippets.kt\n```\n\nExample:\n```text\nRequestConfiguration requestConfiguration =\n MobileAds.getRequestConfiguration().toBuilder()\n .setPublisherPrivacyPersonalizationState(\n RequestConfiguration.PublisherPrivacyPersonalizationState.DISABLED)\n .build();\nMobileAds.setRequestConfiguration(requestConfiguration);RequestConfigurationSnippets.java\n```\n\nExample:\n```text\nval extras = Bundle()\nextras.putString(\"collapsible\", \"bottom\")\nval adRequest =\n AdRequest.Builder().addNetworkExtrasBundle(AdMobAdapter::class.java, extras).build()\nadView.loadAd(adRequest)AdRequestSnippets.kt\n```\n\nExample:\n```text\nBundle extras = new Bundle();\nextras.putString(\"collapsible\", \"bottom\");\nAdRequest adRequest =\n new AdRequest.Builder().addNetworkExtrasBundle(AdMobAdapter.class, extras).build();\nadView.loadAd(adRequest);AdRequestSnippets.java\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:51.006Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":132,"estimatedTokens":1018}}583{"id":"doc-set_up_charles_proxy_android_google_for_develope-8ea5606a","source":"documentation","title":"Set up Charles proxy | Android | Google for Developers","url":"https://developers.google.com/admob/android/charles","text":"Example:\n```text\n<network-security-config>\n <debug-overrides>\n <trust-anchors>\n <!-- Trust user added CAs while debuggable only -->\n <certificates src=\"user\" />\n </trust-anchors>\n </debug-overrides>\n</network-security-config>\n```\n\nExample:\n```text\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest ... >\n <application ...\n android:networkSecurityConfig=\"@xml/network_security_config\"\n ... >\n ...\n </application>\n</manifest>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:51.007Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":25,"estimatedTokens":131}}584{"id":"doc-optimize_custom_tabs_beta_android_google_for_dev-efaf2f01","source":"documentation","title":"Optimize Custom Tabs (Beta) | Android | Google for Developers","url":"https://developers.google.com/admob/android/browser/custom-tabs","text":"Example:\n```text\n<!-- Bypass APPLICATION_ID check for web view APIs for ads -->\n <meta-data\n android:name=\"com.google.android.gms.ads.INTEGRATION_MANAGER\"\n android:value=\"webview\"/>\n```\n\nExample:\n```text\nimport com.google.android.gms.ads.MobileAds\n\nclass MainActivity : ComponentActivity() {\n private var customTabsClient: CustomTabsClient? = null\n private var customTabsSession: CustomTabsSession? = null\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n\n // Get the default browser package name, this will be null if\n // the default browser does not provide a CustomTabsService.\n val packageName = CustomTabsClient.getPackageName(applicationContext, null);\n if (packageName == null) {\n // Do nothing as service connection is not supported.\n return\n }\n\n CustomTabsClient.bindCustomTabsService(\n applicationContext,\n packageName,\n object : CustomTabsServiceConnection() {\n override fun onCustomTabsServiceConnected(\n name: ComponentName, client: CustomTabsClient,\n ) {\n customTabsClient = client\n\n // Warm up the browser process.\n customTabsClient?.warmup(0L)\n // Create a new browser session using the Google Mobile Ads SDK (Legacy).\n customTabsSession = MobileAds.registerCustomTabsSession(\n this@MainActivity.applicationContext,\n client,\n // Checks the \"Digital Asset Link\" to connect the postMessage channel.\n ORIGIN,\n // Optional parameter to receive the delegated callbacks.\n customTabsCallback\n )\n\n // Create a new browser session if the Google Mobile Ads SDK (Legacy) is\n // unable to create one.\n if (customTabsSession == null) {\n customTabsSession = client.newSession(customTabsCallback)\n }\n\n // Pass the custom tabs session into the intent.\n val customTabsIntent = CustomTabsIntent.Builder(customTabsSession).build()\n customTabsIntent.launchUrl(this@MainActivity,\n Uri.parse(\"YOUR_URL\"))\n }\n\n override fun onServiceDisconnected(componentName: ComponentName) {\n // Remove the custom tabs client and custom tabs session.\n customTabsClient = null\n customTabsSession = null\n }\n })\n }\n\n // Listen for events from the CustomTabsSession delegated by the Google Mobile Ads SDK (Legacy).\n private val customTabsCallback: CustomTabsCallback = object : CustomTabsCallback() {\n @Synchronized\n override fun onNavigationEvent(navigationEvent: Int, extras: Bundle?) {\n // Called when a navigation event happens.\n }\n\n @Synchronized\n override fun onMessageChannelReady(extras: Bundle?) {\n // Called when the channel is ready for sending and receiving messages on both\n // ends.\n // This frequently happens, such as each time the SDK requests a\n // new channel.\n }\n\n @Synchronized\n override fun onPostMessage(message: String, extras: Bundle?) {\n // Called when a tab controlled by this CustomTabsSession has sent a postMessage.\n }\n\n override fun onRelationshipValidationResult(\n relation: Int, requestedOrigin: Uri, result: Boolean, extras: Bundle?\n ) {\n // Called when a relationship validation result is available.\n }\n\n override fun onActivityResized(height: Int, width: Int, extras: Bundle) {\n // Called when the tab is resized.\n }\n\n override fun extraCallback(callbackName: String, args: Bundle?) {\n\n }\n\n override fun extraCallbackWithResult(callbackName: String, args: Bundle?): Bundle? {\n return null\n }\n }\n\n companion object {\n // Replace this URL with an associated website.\n const val ORIGIN = \"https://www.google.com\"\n }\n}\n```\n\nExample:\n```text\nimport com.google.android.gms.ads.MobileAds;\n\nclass MainActivity extends ComponentActivity {\n // Replace this URL with an associated website.\n private static final String ORIGIN = \"https://www.google.com\";\n private CustomTabsClient customTabsClient;\n private CustomTabsSession customTabsSession;\n\n @Override\n protected void onCreate(@Nullable Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n\n // Get the default browser package name, this will be null if\n // the default browser does not provide a CustomTabsService.\n String packageName = CustomTabsClient.getPackageName(getApplicationContext(), null);\n if (packageName == null) {\n // Do nothing as service connection is not supported.\n return;\n }\n\n CustomTabsClient.bindCustomTabsService(\n getApplicationContext(),\n packageName,\n new CustomTabsServiceConnection() {\n @Override\n public void onCustomTabsServiceConnected(@NonNull ComponentName name,\n @NonNull CustomTabsClient client) {\n customTabsClient = client;\n\n // Warm up the browser process.\n customTabsClient.warmup(0);\n // Create a new browser session using the Google Mobile Ads SDK (Legacy).\n customTabsSession = MobileAds.registerCustomTabsSession(\n MainActivity.this.getApplicationContext(),\n client,\n // Checks the \"Digital Asset Link\" to connect the postMessage channel.\n ORIGIN,\n // Optional parameter to receive the delegated callbacks.\n customTabsCallback);\n\n // Create a new browser session if the Google Mobile Ads SDK (Legacy) is\n // unable to create one.\n if (customTabsSession == null) {\n customTabsSession = client.newSession(customTabsCallback);\n }\n\n // Pass the custom tabs session into the intent.\n CustomTabsIntent intent = new CustomTabsIntent.Builder(customTabsSession).build();\n intent.launchUrl(MainActivity.this, Uri.parse(\"YOUR_URL\"));\n }\n\n @Override\n public void onServiceDisconnected(ComponentName componentName) {\n // Remove the custom tabs client and custom tabs session.\n customTabsClient = null;\n customTabsSession = null;\n }\n }\n\n );\n }\n\n // Listen for events from the CustomTabsSession delegated by the Google Mobile Ads SDK (Legacy).\n private final CustomTabsCallback customTabsCallback = new CustomTabsCallback() {\n @Override\n public void onNavigationEvent(int navigationEvent, @Nullable Bundle extras) {\n // Called when a navigation event happens.\n super.onNavigationEvent(navigationEvent, extras);\n }\n\n @Override\n public void onMessageChannelReady(@Nullable Bundle extras) {\n // Called when the channel is ready for sending and receiving messages on both\n // ends.\n // This frequently happens, such as each time the SDK requests a\n // new channel.\n super.onMessageChannelReady(extras);\n }\n\n @Override\n public void onPostMessage(@NonNull String message, @Nullable Bundle extras) {\n // Called when a tab controlled by this CustomTabsSession has sent a postMessage.\n super.onPostMessage(message, extras);\n }\n\n @Override\n public void onRelationshipValidationResult(int relation, @NonNull Uri requestedOrigin,\n boolean result, @Nullable Bundle extras) {\n // Called when a relationship validation result is available.\n super.onRelationshipValidationResult(relation, requestedOrigin, result, extras);\n }\n\n @Override\n public void onActivityResized(int height, int width, @NonNull Bundle extras) {\n // Called when the tab is resized.\n super.onActivityResized(height, width, extras);\n }\n\n @Override\n public void extraCallback(@NonNull String callbackName, @Nullable Bundle args) {\n super.extraCallback(callbackName, args);\n }\n\n @Nullable\n @Override\n public Bundle extraCallbackWithResult(@NonNull String callbackName, @Nullable Bundle args) {\n return super.extraCallbackWithResult(callbackName, args);\n }\n };\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:51.007Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":230,"estimatedTokens":2051}}585{"id":"doc-impression_level_ad_revenue_android_google_for_d-3d51fac6","source":"documentation","title":"Impression-level ad revenue | Android | Google for Developers","url":"https://developers.google.com/admob/android/impression-level-ad-revenue","text":"Example:\n```text\nprivate void setOnPaidEventListener(RewardedAd ad) {\n ad.setOnPaidEventListener(\n new OnPaidEventListener() {\n @Override\n public void onPaidEvent(@NonNull AdValue adValue) {\n // Extract the impression-level ad revenue data.\n long valueMicros = adValue.getValueMicros();\n String currencyCode = adValue.getCurrencyCode();\n int precision = adValue.getPrecisionType();\n\n // Get the ad unit ID.\n String adUnitId = ad.getAdUnitId();\n\n // Extract ad response information.\n AdapterResponseInfo loadedAdapterResponseInfo =\n ad.getResponseInfo().getLoadedAdapterResponseInfo();\n if (loadedAdapterResponseInfo != null) {\n String adSourceName = loadedAdapterResponseInfo.getAdSourceName();\n String adSourceId = loadedAdapterResponseInfo.getAdSourceId();\n String adSourceInstanceName = loadedAdapterResponseInfo.getAdSourceInstanceName();\n String adSourceInstanceId = loadedAdapterResponseInfo.getAdSourceInstanceId();\n\n Bundle extras = ad.getResponseInfo().getResponseExtras();\n String mediationGroupName = extras.getString(\"mediation_group_name\");\n String mediationABTestName = extras.getString(\"mediation_ab_test_name\");\n String mediationABTestVariant = extras.getString(\"mediation_ab_test_variant\");\n }\n }\n });\n}ImpressionLevelAdRevenueSnippets.java\n```\n\nExample:\n```text\nprivate fun setOnPaidEventListener(ad: RewardedAd) {\n ad.onPaidEventListener = OnPaidEventListener { adValue ->\n // Extract the impression-level ad revenue data.\n val valueMicros = adValue.valueMicros\n val currencyCode = adValue.currencyCode\n val precision = adValue.precisionType\n\n // Get the ad unit ID.\n val adUnitId = ad.adUnitId\n\n // Extract ad response information.\n val loadedAdapterResponseInfo = ad.responseInfo.loadedAdapterResponseInfo\n val adSourceName = loadedAdapterResponseInfo?.adSourceName\n val adSourceId = loadedAdapterResponseInfo?.adSourceId\n val adSourceInstanceName = loadedAdapterResponseInfo?.adSourceInstanceName\n val adSourceInstanceId = loadedAdapterResponseInfo?.adSourceInstanceId\n val extras = ad.responseInfo.responseExtras\n val mediationGroupName = extras.getString(\"mediation_group_name\")\n val mediationABTestName = extras.getString(\"mediation_ab_test_name\")\n val mediationABTestVariant = extras.getString(\"mediation_ab_test_variant\")\n }\n}ImpressionLevelAdRevenueSnippets.kt\n```\n\nExample:\n```text\nprivate String getUniqueAdSourceName(@NonNull AdapterResponseInfo loadedAdapterResponseInfo) {\n\n String adSourceName = loadedAdapterResponseInfo.getAdSourceName();\n if (adSourceName.equals(\"Custom Event\")) {\n if (loadedAdapterResponseInfo\n .getAdapterClassName()\n .equals(\"com.google.ads.mediation.sample.customevent.SampleCustomEvent\")) {\n adSourceName = \"Sample Ad Network (Custom Event)\";\n }\n }\n return adSourceName;\n}\nResponseInfoSnippets.java\n```\n\nExample:\n```text\nprivate fun getUniqueAdSourceName(loadedAdapterResponseInfo: AdapterResponseInfo): String {\n\n var adSourceName = loadedAdapterResponseInfo.adSourceName\n if (adSourceName == \"Custom Event\") {\n if (\n loadedAdapterResponseInfo.adapterClassName ==\n \"com.google.ads.mediation.sample.customevent.SampleCustomEvent\"\n ) {\n adSourceName = \"Sample Ad Network (Custom Event)\"\n }\n }\n return adSourceName\n}\nResponseInfoSnippets.kt\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.347Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":96,"estimatedTokens":887}}586{"id":"doc-oauth_2_0_playground-314e77fd","source":"documentation","title":"OAuth 2.0 Playground","url":"https://developers.google.com/oauthplayground/","text":"[WARNING]\nFor better experience using the Drive API, make sure you have installed the OAuth 2.0 Playground Drive app on the Chrome Webstore. Dismiss\n\n[WARNING]\n⚠ You are using a custom OAuth configuration. Please note that your credentials will be sent to these ! That's fine. I don't want this. Reset all settings. Don't ask again for these endpoints on this browser\n\nOAuth 2.0 Playground Reset all settings Help - Feedback - Bugs Report a bug - Provide feedback Contact the team - Ask for help Close Create Link Here is a URL to initialize the playground with the current OAuth credentials and OAuth tokens in the link the option above is enabled this link may contain your OAuth credentials and OAuth tokens. In that case avoid sharing this link. Close OAuth 2.0 Configuration OAuth 2.0 configuration OAuth Server-side Client-side OAuth Google Custom Authorization : The OAuth endpoints above need to implement the OAuth 2.0 draft 10 specification or above. Other specification are likely to be incompatible. Access token header w/ Bearer prefix Authorization header w/ OAuth prefix Authorization header w/ Bearer prefix access_token URL parameter Access Offline Online Force Screen No Consent Screen Select Account Screen Use your own OAuth credentials You will need to list the URL https://developers.google.com/oauthplayground as a valid redirect URI in your Google APIs Console's project. Then enter the client ID and secret assigned to a web application on your project will need to list the URL https://developers.google.com/oauthplayground as a valid redirect URI in the developer console of your API. Then enter your client ID and secret Client Client : Your credentials will be sent to our server as we need to proxy the request. Your credentials will not be logged. Close\n\nStep 1Select & authorize APIs Select the scope for the APIs you would like to access or input your own OAuth scopes below. Then click the \"Authorize APIs\" button. Authorize APIs Authorize the selected APIs and scopes Step 2Exchange authorization code for tokens The access token below is provided after going through Step 1. It is a short lived token which gives you access to the user's OAuth protected resources. Once you got the Authorization Code from Step 1 click the Exchange authorization code for tokens button, you will get a refresh and an access token which is required to access OAuth protected resources. Authorization authorization code for tokens Refresh access token Auto-refresh the token before it expires. The access token will expire in seconds. The access token has expired. OAuth Playground will automatically revoke refresh tokens after 24h. You can avoid this by specifying your own application OAuth credentials using the Configuration panel. Step 3Configure request to API Construct your HTTP request by specifying the URI, HTTP Method, headers, content type and request body.Then click the \"Send the request\" button to initiate the HTTP Request. HTTP GET POST PUT DELETE PATCH Add headers0 Headers Add a Header name Header value Close Request request body0 Request Body Manual entryEnter the data that will be added to the body of the may choose to send a file as part of the request. When both a file and manual content are provided both will be sent using a multipart request. You may send files of maximum 1 MB using the Playground. Please select your /json application/json application/atom+xml text/plain text/csv Custom... Send the request List possible operations Available operations Fetching available operations... Something bad happened Close OAuth access token in Step 2 will be added to the Authorization header of the request. Request / Response No request. Wrap Lines\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.348Z","totalSectionsIncluded":4,"totalCodeBlocksIncluded":0,"totalLines":11,"estimatedTokens":928}}587{"id":"doc-integrate_the_webview_api_for_ads_android_google-c0caa3e4","source":"documentation","title":"Integrate the WebView API for Ads | Android | Google for Developers","url":"https://developers.google.com/admob/android/browser/webview/api-for-ads","text":"Example:\n```text\n<!-- Bypass APPLICATION_ID check for web view APIs for ads -->\n <meta-data\n android:name=\"com.google.android.gms.ads.INTEGRATION_MANAGER\"\n android:value=\"webview\"/>\n```\n\nExample:\n```text\nimport android.webkit.CookieManager\nimport android.webkit.WebView\nimport com.google.android.gms.ads.MobileAds\n\nclass MainActivity : AppCompatActivity() {\n lateinit var webView: WebView\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_main)\n webView = findViewById(R.id.webview)\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)\n // Let the web view use JavaScript.\n webView.settings.javaScriptEnabled = true\n // Let the web view access local storage.\n webView.settings.domStorageEnabled = true\n // Let HTML videos play automatically.\n webView.settings.mediaPlaybackRequiresUserGesture = false\n\n // Register the web view.\n MobileAds.registerWebView(webView)\n }\n}\n```\n\nExample:\n```text\nimport android.webkit.CookieManager;\nimport android.webkit.WebView;\nimport com.google.android.gms.ads.MobileAds;\n\npublic class MainActivity extends AppCompatActivity {\n private WebView webView;\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_main);\n webView = findViewById(R.id.webview);\n\n // Let the web view accept third-party cookies.\n CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);\n // Let the web view use JavaScript.\n webView.getSettings().setJavaScriptEnabled(true);\n // Let the web view access local storage.\n webView.getSettings().setDomStorageEnabled(true);\n // Let HTML videos play automatically.\n webView.getSettings().setMediaPlaybackRequiresUserGesture(false);\n\n // Register the web view.\n MobileAds.registerWebView(webView);\n }\n}\n```\n\nExample:\n```text\nhttps://google.github.io/webview-ads/test/#api-for-ads-tests\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.349Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":73,"estimatedTokens":522}}588{"id":"doc-enable_test_ads_flutter_google_for_developers-0b6b7674","source":"documentation","title":"Enable test ads | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/test-ads","text":"Example:\n```text\nvoid loadBanner() {\n \n final adUnitId = Platform.isAndroid\n ? 'ca-app-pub-3940256099942544/6300978111'\n : 'ca-app-pub-3940256099942544/2934735716';\n final bannerAd = BannerAd(\n adUnitId: adUnitId,\n request: AdRequest(),\n size: AdSize.banner,\n );\n bannerAd.load();\n \n}\n```\n\nExample:\n```text\nI/Ads: Use\n RequestConfiguration.Builder\n .setTestDeviceIds(Arrays.asList(\"33BE2250B43518CCDA7DE426D04EE231\"))\n to get test ads on this device.\n```\n\nExample:\n```text\n<Google> To get test ads on this device, set:\n GADMobileAds.sharedInstance.requestConfiguration.testDeviceIdentifiers =\n @[ @\"2077ef9a63d2b398840261c8221a0c9b\" ];\n```\n\nExample:\n```text\nMobileAds.instance.updateRequestConfiguration(\n RequestConfiguration(testDeviceIds: ['33BE2250B43518CCDA7DE426D04EE231']));\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.349Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":39,"estimatedTokens":207}}589{"id":"doc-log_ad_response_id_to_crashlytics_android_google-e2a43bba","source":"documentation","title":"Log ad response ID to Crashlytics | Android | Google for Developers","url":"https://developers.google.com/admob/android/crashlytics","text":"Example:\n```devsite-click-to-copy\napply plugin: 'com.android.application'\napply plugin: 'com.google.gms.google-services'\n\n// Add the Fabric plugin\napply plugin: 'io.fabric'\n\ndependencies {\n // ...\n\n // Add Google Mobile Ads SDK (Legacy)\n implementation 'com.google.android.gms:play-services-ads:25.4.0'\n\n // Add the Firebase Crashlytics dependency.\n implementation 'com.google.firebase:firebase-crashlytics:20.1.0'\n}\n```\n\nExample:\n```devsite-click-to-copy\nbuildscript {\n repositories {\n // ...\n // Add Google's Maven repository.\n google()\n }\n\n dependencies {\n // ...\n\n classpath 'com.google.gms:google-services:4.5.0'\n\n // Add the Fabric Crashlytics plugin.\n classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.7'\n }\n}\n\nallprojects {\n // ...\n repositories {\n // Check that Google's Maven repository is included (if not, add it).\n google()\n\n // ...\n }\n}\n```\n\nExample:\n```devsite-click-to-copy\nprotected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_my);\n\n // Gets the ad view defined in layout/ad_fragment.xml with ad unit ID set in\n // values/strings.xml.\n adView = findViewById(R.id.ad_view);\n\n // Start loading the ad in the background.\n adView.loadAd(new AdRequest.Builder().build());\n\n // Add a crash button.\n Button crashButton = new Button(this);\n crashButton.setText(\"Crash!\");\n crashButton.setOnClickListener(new View.OnClickListener() {\n public void onClick(View view) {\n throw new RuntimeException(\"Test Crash\"); // Force a crash\n }\n });\n\n addContentView(crashButton, new ViewGroup.LayoutParams(\n ViewGroup.LayoutParams.MATCH_PARENT,\n ViewGroup.LayoutParams.WRAP_CONTENT));\n}\n```\n\nExample:\n```devsite-click-to-copy\noverride fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_my)\n\n // Gets the ad view defined in layout/ad_fragment.xml with ad unit ID set in\n // values/strings.xml.\n adView = findViewById(R.id.ad_view)\n\n // Start loading the ad in the background.\n adView.loadAd(AdRequest.Builder().build())\n\n // Add a crash button.\n val crashButton = Button(this)\n crashButton.text = \"Crash!\"\n crashButton.setOnClickListener {\n throw RuntimeException(\"Test Crash\") // Force a crash\n }\n\n addContentView(crashButton, ViewGroup.LayoutParams(\n ViewGroup.LayoutParams.MATCH_PARENT,\n ViewGroup.LayoutParams.WRAP_CONTENT))\n}\n```\n\nExample:\n```devsite-click-to-copy\nprotected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.activity_my);\n\n // Gets the ad view defined in layout/ad_fragment.xml with ad unit ID set in\n // values/strings.xml.\n adView = findViewById(R.id.ad_view);\n\n adView.setAdListener(new AdListener() {\n @Override\n public void onAdLoaded() {\n String adResponseId = adView.getResponseInfo().getResponseId();\n FirebaseCrashlytics.getInstance().setCustomKey(\n \"banner_ad_response_id\", adResponseId);\n }\n });\n\n // Start loading the ad in the background.\n adView.loadAd(new AdRequest.Builder().build());\n\n // Add a crash button.\n Button crashButton = new Button(this);\n crashButton.setText(\"Crash!\");\n crashButton.setOnClickListener(new View.OnClickListener() {\n public void onClick(View view) {\n throw new RuntimeException(\"Test Crash\"); // Force a crash\n }\n });\n\n addContentView(crashButton, new ViewGroup.LayoutParams(\n ViewGroup.LayoutParams.MATCH_PARENT,\n ViewGroup.LayoutParams.WRAP_CONTENT));\n}\n```\n\nExample:\n```devsite-click-to-copy\noverride fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContentView(R.layout.activity_my)\n\n // Gets the ad view defined in layout/ad_fragment.xml with ad unit ID set in\n // values/strings.xml.\n adView = findViewById(R.id.ad_view)\n\n adView.adListener = object : AdListener() {\n override fun onAdLoaded() {\n mAdView.responseInfo?.responseId?.let { adResponseId ->\n FirebaseCrashlytics.getInstance().setCustomKey(\n \"banner_ad_response_id\", adResponseId)\n }\n }\n }\n\n // Start loading the ad in the background.\n adView.loadAd(AdRequest.Builder().build())\n\n // Add a crash button.\n val crashButton = Button(this)\n crashButton.text = \"Crash!\"\n crashButton.setOnClickListener {\n throw RuntimeException(\"Test Crash\") // Force a crash\n }\n\n addContentView(crashButton, ViewGroup.LayoutParams(\n ViewGroup.LayoutParams.MATCH_PARENT,\n ViewGroup.LayoutParams.WRAP_CONTENT))\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.350Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":176,"estimatedTokens":1166}}590{"id":"doc-oauth_2_0_for_tv_and_limited_input_device_applic-233ed17a","source":"documentation","title":"OAuth 2.0 for TV and Limited-Input Device Applications | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/limited-input-device","text":"Example:\n```text\nPOST /device/code HTTP/1.1\nHost: oauth2.googleapis.com\nContent-Type: application/x-www-form-urlencoded\n\nclient_id=client_id&scope=email%20profile\n```\n\nExample:\n```text\ncurl -d \"client_id=client_id&scope=email%20profile\" \\\n https://oauth2.googleapis.com/device/code\n```\n\nExample:\n```text\n{\n \"device_code\": \"4/4-GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8\",\n \"user_code\": \"GQVQ-JKEC\",\n \"verification_url\": \"https://www.google.com/device\",\n \"expires_in\": 1800,\n \"interval\": 5\n}\n```\n\nExample:\n```text\n{\n \"error_code\": \"rate_limit_exceeded\"\n}\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.googleapis.com\nContent-Type: application/x-www-form-urlencoded\n\nclient_id=client_id&\nclient_secret=client_secret&\ndevice_code=device_code&\ngrant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code\n```\n\nExample:\n```text\ncurl -d \"client_id=client_id&client_secret=client_secret& \\\n device_code=device_code& \\\n grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code\" \\\n -H \"Content-Type: application/x-www-form-urlencoded\" \\\n https://oauth2.googleapis.com/token\n```\n\nExample:\n```text\n{\n \"access_token\": \"1/fFAGRNJru1FTz70BzhT3Zg\",\n \"expires_in\": 3920,\n \"scope\": \"openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email\",\n \"token_type\": \"Bearer\",\n \"refresh_token\": \"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI\"\n}\n```\n\nExample:\n```text\n{\n \"error\": \"access_denied\",\n \"error_description\": \"Forbidden\"\n}\n```\n\nExample:\n```text\n{\n \"error\": \"authorization_pending\",\n \"error_description\": \"Precondition Required\"\n}\n```\n\nExample:\n```text\n{\n \"error\": \"slow_down\",\n \"error_description\": \"Forbidden\"\n}\n```\n\nExample:\n```text\nGET /drive/v2/files HTTP/1.1\nHost: www.googleapis.com\nAuthorization: Bearer access_token\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v2/files?access_token=access_token\n```\n\nExample:\n```text\ncurl -H \"Authorization: Bearer access_token\" https://www.googleapis.com/drive/v2/files\n```\n\nExample:\n```text\ncurl https://www.googleapis.com/drive/v2/files?access_token=access_token\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.googleapis.com\nContent-Type: application/x-www-form-urlencoded\n\nclient_id=your_client_id&\nrefresh_token=refresh_token&\ngrant_type=refresh_token\n```\n\nExample:\n```text\n{\n \"access_token\": \"1/fFAGRNJru1FTz70BzhT3Zg\",\n \"expires_in\": 3920,\n \"scope\": \"https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly\",\n \"token_type\": \"Bearer\"\n}\n```\n\nExample:\n```text\ncurl -d -X -POST --header \"Content-type:application/x-www-form-urlencoded\" \\\n https://oauth2.googleapis.com/revoke?token={token}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.352Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":17,"totalLines":139,"estimatedTokens":685}}591{"id":"doc-set_up_banner_ads_flutter_google_for_developers-6a032e2b","source":"documentation","title":"Set up banner ads | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/banner","text":"Example:\n```text\nca-app-pub-3940256099942544/9214589741\n```\n\nExample:\n```text\nca-app-pub-3940256099942544/2435281174\n```\n\nExample:\n```text\n// Get an AnchoredAdaptiveBannerAdSize before loading the ad.\nfinal size = await AdSize.getLargeAnchoredAdaptiveBannerAdSize(\n MediaQuery.sizeOf(context).width.truncate(),\n);main.dart\n```\n\nExample:\n```text\nvoid _loadAd() async {\n // Get an AnchoredAdaptiveBannerAdSize before loading the ad.\n final size = await AdSize.getLargeAnchoredAdaptiveBannerAdSize(\n MediaQuery.sizeOf(context).width.truncate(),\n );\n\n if (size == null) {\n // Unable to get width of anchored banner.\n return;\n }\n\n BannerAd(\n adUnitId: \"_adUnitId\",\n request: const AdRequest(),\n size: size,\n listener: BannerAdListener(\n onAdLoaded: (ad) {\n // Called when an ad is successfully received.\n debugPrint(\"Ad was loaded.\");\n setState(() {\n _bannerAd = ad as BannerAd;\n });\n },\n onAdFailedToLoad: (ad, err) {\n // Called when an ad request failed.\n debugPrint(\"Ad failed to load with error: $err\");\n ad.dispose();\n },\n ),\n ).load();\n}main.dart\n```\n\nExample:\n```text\nonAdOpened: (Ad ad) {\n // Called when an ad opens an overlay that covers the screen.\n debugPrint(\"Ad was opened.\");\n},\nonAdClosed: (Ad ad) {\n // Called when an ad removes an overlay that covers the screen.\n debugPrint(\"Ad was closed.\");\n},\nonAdImpression: (Ad ad) {\n // Called when an impression occurs on the ad.\n debugPrint(\"Ad recorded an impression.\");\n},\nonAdClicked: (Ad ad) {\n // Called when an a click event occurs on the ad.\n debugPrint(\"Ad was clicked.\");\n},\nonAdWillDismissScreen: (Ad ad) {\n // iOS only. Called before dismissing a full screen view.\n debugPrint(\"Ad will be dismissed.\");\n},main.dart\n```\n\nExample:\n```text\nif (_bannerAd != null)\n Align(\n alignment: Alignment.bottomCenter,\n child: SafeArea(\n child: SizedBox(\n width: _bannerAd!.size.width.toDouble(),\n height: _bannerAd!.size.height.toDouble(),\n child: AdWidget(ad: _bannerAd!),\n ),\n ),\n ),main.dart\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.353Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":93,"estimatedTokens":532}}592{"id":"doc-oauth_2_0_api_client_library_for_net_google_for_-2e515826","source":"documentation","title":"OAuth 2.0 | API Client Library for .NET | Google for Developers","url":"https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth","text":"Example:\n```text\nusing System;\nusing System.IO;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Books.v1;\nusing Google.Apis.Books.v1.Data;\nusing Google.Apis.Services;\nusing Google.Apis.Util.Store;\n\nnamespace Books.ListMyLibrary\n{\n /// <summary>\n /// Sample which demonstrates how to use the Books API.\n /// https://developers.google.com/books/docs/v1/getting_started\n /// <summary>\n internal class Program\n {\n [STAThread]\n static void Main(string[] args)\n {\n Console.WriteLine(\"Books API Sample: List MyLibrary\");\n Console.WriteLine(\"================================\");\n try\n {\n new Program().Run().Wait();\n }\n catch (AggregateException ex)\n {\n foreach (var e in ex.InnerExceptions)\n {\n Console.WriteLine(\"ERROR: \" + e.Message);\n }\n }\n Console.WriteLine(\"Press any key to continue...\");\n Console.ReadKey();\n }\n\n private async Task Run()\n {\n UserCredential credential;\n using (var stream = new FileStream(\"client_secrets.json\", FileMode.Open, FileAccess.Read))\n {\n credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(\n GoogleClientSecrets.Load(stream).Secrets,\n new[] { BooksService.Scope.Books },\n \"user\", CancellationToken.None, new FileDataStore(\"Books.ListMyLibrary\"));\n }\n\n // Create the service.\n var service = new BooksService(new BaseClientService.Initializer()\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Books API Sample\",\n });\n\n var bookshelves = await service.Mylibrary.Bookshelves.List().ExecuteAsync();\n ...\n }\n }\n}\n```\n\nExample:\n```text\ncredential = await GoogleWebAuthorizationBroker.AuthorizeAsync(\n new ClientSecrets\n {\n ClientId = \"PUT_CLIENT_ID_HERE\",\n ClientSecret = \"PUT_CLIENT_SECRETS_HERE\"\n },\n new[] { BooksService.Scope.Books },\n \"user\",\n CancellationToken.None,\n new FileDataStore(\"Books.ListMyLibrary\"));\n```\n\nExample:\n```text\nusing Google.Apis.Auth.AspNetCore3;\n```\n\nExample:\n```text\npublic void ConfigureServices(IServiceCollection services)\n{\n ...\n\n // This configures Google.Apis.Auth.AspNetCore3 for use in this app.\n services\n .AddAuthentication(o =>\n {\n // This forces challenge results to be handled by Google OpenID Handler, so there's no\n // need to add an AccountController that emits challenges for Login.\n o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;\n // This forces forbid results to be handled by Google OpenID Handler, which checks if\n // extra scopes are required and does automatic incremental auth.\n o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;\n // Default scheme that will handle everything else.\n // Once a user is authenticated, the OAuth2 token info is stored in cookies.\n o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;\n })\n .AddCookie()\n .AddGoogleOpenIdConnect(options =>\n {\n options.ClientId = {YOUR_CLIENT_ID};\n options.ClientSecret = {YOUR_CLIENT_SECRET};\n });\n}\n```\n\nExample:\n```text\npublic void Configure(IApplicationBuilder app, IWebHostEnvironment env)\n{\n ...\n app.UseHttpsRedirection();\n ...\n\n app.UseAuthentication();\n app.UseAuthorization();\n\n ...\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.AspNetCore3;\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n```\n\nExample:\n```text\n/// <summary>\n/// Lists the authenticated user's Google Drive files.\n/// Specifying the <see cref=\"GoogleScopedAuthorizeAttribute\"> will guarantee that the code\n/// executes only if the user is authenticated and has granted the scope specified in the attribute\n/// to this application.\n/// </summary>\n/// <param name=\"auth\">The Google authorization provider.\n/// This can also be injected on the controller constructor.</param>\n[GoogleScopedAuthorize(DriveService.ScopeConstants.DriveReadonly)]\npublic async Task<IActionResult> DriveFileList([FromServices] IGoogleAuthProvider auth)\n{\n GoogleCredential cred = await auth.GetCredentialAsync();\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = cred\n });\n var files = await service.Files.List().ExecuteAsync();\n var fileNames = files.Files.Select(x => x.Name).ToList();\n return View(fileNames);\n}\n```\n\nExample:\n```text\nusing System;\nusing System.Security.Cryptography.X509Certificates;\n\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Plus.v1;\nusing Google.Apis.Plus.v1.Data;\nusing Google.Apis.Services;\n\nnamespace Google.Apis.Samples.PlusServiceAccount\n{\n /// <summary>\n /// This sample demonstrates the simplest use case for a Service Account service.\n /// The certificate needs to be downloaded from the Google API Console\n /// <see cref=\"https://console.cloud.google.com/\">\n /// \"Create another client ID...\" -> \"Service Account\" -> Download the certificate,\n /// rename it as \"key.p12\" and add it to the project. Don't forget to change the Build action\n /// to \"Content\" and the Copy to Output Directory to \"Copy if newer\".\n /// </summary>\n public class Program\n {\n // A known public activity.\n private static String ACTIVITY_ID = \"z12gtjhq3qn2xxl2o224exwiqruvtda0i\";\n\n public static void Main(string[] args)\n {\n Console.WriteLine(\"Plus API - Service Account\");\n Console.WriteLine(\"==========================\");\n\n String serviceAccountEmail = \"SERVICE_ACCOUNT_EMAIL_HERE\";\n\n var certificate = new X509Certificate2(@\"key.p12\", \"notasecret\", X509KeyStorageFlags.Exportable);\n\n ServiceAccountCredential credential = new ServiceAccountCredential(\n new ServiceAccountCredential.Initializer(serviceAccountEmail)\n {\n Scopes = new[] { PlusService.Scope.PlusMe }\n }.FromCertificate(certificate));\n\n // Create the service.\n var service = new PlusService(new BaseClientService.Initializer()\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Plus API Sample\",\n });\n\n Activity activity = service.Activities.Get(ACTIVITY_ID).Execute();\n Console.WriteLine(\" Activity: \" + activity.Object.Content);\n Console.WriteLine(\" Video: \" + activity.Object.Attachments[0].Url);\n\n Console.WriteLine(\"Press any key to continue...\");\n Console.ReadKey();\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.354Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":220,"estimatedTokens":1768}}593{"id":"doc-install_gma_next_gen_sdk_flutter_google_for_deve-e9198bee","source":"documentation","title":"Install GMA Next-Gen SDK | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/android-next","text":"Example:\n```text\nflutter run --dart-define USE_NEXT_GEN_SDK=true\n```\n\nExample:\n```text\n// Replace this import:\nimport io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin.NativeAdFactory;\n\n// With this import:\nimport io.flutter.plugins.googlemobileads.NativeAdFactory;\n```\n\nExample:\n```text\n// Replace this import:\nimport io.flutter.plugins.googlemobileads.GoogleMobileAdsPlugin.NativeAdFactory\n\n// With this import:\nimport io.flutter.plugins.googlemobileads.NativeAdFactory\n```\n\nExample:\n```text\n<!-- Replace this element: -->\n<com.google.android.gms.ads.nativead.NativeAdView ... />\n\n<!-- With this element: -->\n<com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdView ... />\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.354Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":33,"estimatedTokens":178}}594{"id":"doc-app_open_ads_flutter_google_for_developers-1f75fc46","source":"documentation","title":"App open ads | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/app-open","text":"Example:\n```text\nca-app-pub-3940256099942544/9257395921\n```\n\nExample:\n```text\nca-app-pub-3940256099942544/5575463023\n```\n\nExample:\n```text\nimport 'package:google_mobile_ads/google_mobile_ads.dart';\nimport 'dart:io' show Platform;\n\nclass AppOpenAdManager {\n \n String adUnitId = Platform.isAndroid\n ? 'ca-app-pub-3940256099942544/9257395921'\n : 'ca-app-pub-3940256099942544/5575463023';\n \n AppOpenAd? _appOpenAd;\n bool _isShowingAd = false;\n\n /// Load an AppOpenAd.\n void loadAd() {\n // We will implement this below.\n }\n\n /// Whether an ad is available to be shown.\n bool get isAdAvailable {\n return _appOpenAd != null;\n }\n}\n```\n\nExample:\n```text\npublic class AppOpenAdManager {\n ...\n\n /// Load an AppOpenAd.\n void loadAd() {\n AppOpenAd.load(\n adUnitId: adUnitId,\n adRequest: AdRequest(),\n adLoadCallback: AppOpenAdLoadCallback(\n onAdLoaded: (ad) {\n _appOpenAd = ad;\n },\n onAdFailedToLoad: (error) {\n print('AppOpenAd failed to load: $error');\n // Handle the error.\n },\n ),\n );\n }\n}\n```\n\nExample:\n```text\npublic class AppOpenAdManager {\n ...\n\n public void showAdIfAvailable() {\n if (!isAdAvailable) {\n print('Tried to show ad before available.');\n loadAd();\n return;\n }\n if (_isShowingAd) {\n print('Tried to show ad while already showing an ad.');\n return;\n }\n // Set the fullScreenContentCallback and show the ad.\n _appOpenAd!.fullScreenContentCallback = FullScreenContentCallback(\n onAdShowedFullScreenContent: (ad) {\n _isShowingAd = true;\n print('$ad onAdShowedFullScreenContent');\n },\n onAdFailedToShowFullScreenContent: (ad, error) {\n print('$ad onAdFailedToShowFullScreenContent: $error');\n _isShowingAd = false;\n ad.dispose();\n _appOpenAd = null;\n },\n onAdDismissedFullScreenContent: (ad) {\n print('$ad onAdDismissedFullScreenContent');\n _isShowingAd = false;\n ad.dispose();\n _appOpenAd = null;\n loadAd();\n },\n );\n }\n}\n```\n\nExample:\n```text\nimport 'package:app_open_example/app_open_ad_manager.dart';\nimport 'package:google_mobile_ads/google_mobile_ads.dart';\n\n/// Listens for app foreground events and shows app open ads.\nclass AppLifecycleReactor {\n final AppOpenAdManager appOpenAdManager;\n\n AppLifecycleReactor({required this.appOpenAdManager});\n\n void listenToAppStateChanges() {\n AppStateEventNotifier.startListening();\n AppStateEventNotifier.appStateStream\n .forEach((state) => _onAppStateChanged(state));\n }\n\n void _onAppStateChanged(AppState appState) {\n // Try to show an app open ad if the app is being resumed and\n // we're not already showing an app open ad.\n if (appState == AppState.foreground) {\n appOpenAdManager.showAdIfAvailable();\n }\n }\n}\n```\n\nExample:\n```text\nimport 'package:app_open_example/app_open_ad_manager.dart';\nimport 'package:flutter/material.dart';\nimport 'package:google_mobile_ads/google_mobile_ads.dart';\n\nimport 'app_lifecycle_reactor.dart';\n\nvoid main() {\n WidgetsFlutterBinding.ensureInitialized();\n MobileAds.instance.initialize();\n runApp(MyApp());\n}\n\nclass MyApp extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return MaterialApp(\n title: 'App Open Example',\n theme: ThemeData(\n primarySwatch: Colors.blue,\n ),\n home: MyHomePage(title: 'App Open Demo Home Page'),\n );\n }\n}\n\nclass MyHomePage extends StatefulWidget {\n MyHomePage({Key? key, required this.title}) : super(key: key);\n\n final String title;\n\n @override\n _MyHomePageState createState() => _MyHomePageState();\n}\n\n/// Example home page for an app open ad.\nclass _MyHomePageState extends State<MyHomePage> {\n int _counter = 0;\n late AppLifecycleReactor _appLifecycleReactor;\n\n @override\n void initState() {\n super.initState();\n \n AppOpenAdManager appOpenAdManager = AppOpenAdManager()..loadAd();\n _appLifecycleReactor = AppLifecycleReactor(\n appOpenAdManager: appOpenAdManager);\n }\n```\n\nExample:\n```text\n/// Utility class that manages loading and showing app open ads.\nclass AppOpenAdManager {\n ...\n \n /// Maximum duration allowed between loading and showing the ad.\n final Duration maxCacheDuration = Duration(hours: 4);\n\n /// Keep track of load time so we don't show an expired ad.\n DateTime? _appOpenLoadTime;\n \n ...\n\n /// Load an AppOpenAd.\n void loadAd() {\n AppOpenAd.load(\n adUnitId: adUnitId,\n orientation: AppOpenAd.orientationPortrait,\n adRequest: AdRequest(),\n adLoadCallback: AppOpenAdLoadCallback(\n onAdLoaded: (ad) {\n print('$ad loaded');\n _appOpenLoadTime = DateTime.now();\n _appOpenAd = ad;\n },\n onAdFailedToLoad: (error) {\n print('AppOpenAd failed to load: $error');\n },\n ),\n );\n }\n\n /// Shows the ad, if one exists and is not already being shown.\n ///\n /// If the previously cached ad has expired, this just loads and caches a\n /// new ad.\n void showAdIfAvailable() {\n if (!isAdAvailable) {\n print('Tried to show ad before available.');\n loadAd();\n return;\n }\n if (_isShowingAd) {\n print('Tried to show ad while already showing an ad.');\n return;\n }\n if (DateTime.now().subtract(maxCacheDuration).isAfter(_appOpenLoadTime!)) {\n print('Maximum cache duration exceeded. Loading another ad.');\n _appOpenAd!.dispose();\n _appOpenAd = null;\n loadAd();\n return;\n }\n // Set the fullScreenContentCallback and show the ad.\n _appOpenAd!.fullScreenContentCallback = FullScreenContentCallback(...);\n _appOpenAd!.show();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.355Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":239,"estimatedTokens":1433}}595{"id":"doc-mobileads_android_google_for_developers-40277c03","source":"documentation","title":"MobileAds | Android | Google for Developers","url":"https://developers.google.com/admob/android/reference/com/google/android/gms/ads/MobileAds","text":"Example:\n```text\npublic class MobileAds\n```\n\nExample:\n```text\npublic static final String ERROR_DOMAIN = \"com.google.android.gms.ads\"\n```\n\nExample:\n```text\npublic static void disableMediationAdapterInitialization(Context context)\n```\n\nExample:\n```text\npublic static @Nullable InitializationStatus getInitializationStatus()\n```\n\nExample:\n```text\npublic static @NonNull RequestConfiguration getRequestConfiguration()\n```\n\nExample:\n```text\npublic static VersionInfo getVersion()\n```\n\nExample:\n```text\n@RequiresPermission(value = Manifest.permission.INTERNET)public static void initialize(Context context)\n```\n\nExample:\n```text\npublic static void initialize(Context context, OnInitializationCompleteListener listener)\n```\n\nExample:\n```text\npublic static void openAdInspector(Context context, OnAdInspectorClosedListener listener)\n```\n\nExample:\n```text\npublic static void openDebugMenu(Context context, String adUnitId)\n```\n\nExample:\n```text\npublic static boolean putPublisherFirstPartyIdEnabled(boolean enabled)\n```\n\nExample:\n```text\npublic static @Nullable CustomTabsSession registerCustomTabsSession( @NonNull Context context, @NonNull CustomTabsClient client, @NonNull String origin, @Nullable CustomTabsCallback callback)\n```\n\nExample:\n```text\npublic static void registerWebView(@NonNull WebView webview)\n```\n\nExample:\n```text\npublic static void setAppMuted(boolean muted)\n```\n\nExample:\n```text\npublic static void setAppVolume(float volume)\n```\n\nExample:\n```text\npublic static void setRequestConfiguration( @NonNull RequestConfiguration requestConfiguration)\n```\n\nExample:\n```text\npublic static void startPreload( @NonNull Context context, @NonNull List<PreloadConfiguration> preloadConfigurations, @NonNull PreloadCallback preloadCallback)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.356Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":17,"totalLines":86,"estimatedTokens":446}}596{"id":"doc-use_inline_adaptive_for_scrolling_banners_flutte-f0fa92f7","source":"documentation","title":"Use inline adaptive for scrolling banners | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/banner/inline-adaptive","text":"Example:\n```text\nimport 'package:flutter/material.dart';\nimport 'package:google_mobile_ads/google_mobile_ads.dart';\n\n/// This example demonstrates inline adaptive banner ads.\n///\n/// Loads and shows an inline adaptive banner ad in a scrolling view,\n/// and reloads the ad when the orientation changes.\nclass InlineAdaptiveExample extends StatefulWidget {\n @override\n _InlineAdaptiveExampleState createState() => _InlineAdaptiveExampleState();\n}\n\nclass _InlineAdaptiveExampleState extends State<InlineAdaptiveExample> {\n static const _insets = 16.0;\n BannerAd? _inlineAdaptiveAd;\n bool _isLoaded = false;\n AdSize? _adSize;\n late Orientation _currentOrientation;\n\n double get _adWidth => MediaQuery.of(context).size.width - (2 * _insets);\n\n @override\n void didChangeDependencies() {\n super.didChangeDependencies();\n _currentOrientation = MediaQuery.of(context).orientation;\n _loadAd();\n }\n\n void _loadAd() async {\n await _inlineAdaptiveAd?.dispose();\n setState(() {\n _inlineAdaptiveAd = null;\n _isLoaded = false;\n });\n\n // Get an inline adaptive size for the current orientation.\n AdSize size = AdSize.getCurrentOrientationInlineAdaptiveBannerAdSize(\n _adWidth.truncate());\n\n _inlineAdaptiveAd = BannerAd(\n // TODO: replace this test ad unit with your own ad unit.\n adUnitId: 'ca-app-pub-3940256099942544/9214589741',\n size: size,\n request: AdRequest(),\n listener: BannerAdListener(\n onAdLoaded: (Ad ad) async {\n print('Inline adaptive banner loaded: ${ad.responseInfo}');\n\n // After the ad is loaded, get the platform ad size and use it to\n // update the height of the container. This is necessary because the\n // height can change after the ad is loaded.\n BannerAd bannerAd = (ad as BannerAd);\n final AdSize? size = await bannerAd.getPlatformAdSize();\n if (size == null) {\n print('Error: getPlatformAdSize() returned null for $bannerAd');\n return;\n }\n\n setState(() {\n _inlineAdaptiveAd = bannerAd;\n _isLoaded = true;\n _adSize = size;\n });\n },\n onAdFailedToLoad: (Ad ad, LoadAdError error) {\n print('Inline adaptive banner failedToLoad: $error');\n ad.dispose();\n },\n ),\n );\n await _inlineAdaptiveAd!.load();\n }\n\n /// Gets a widget containing the ad, if one is loaded.\n ///\n /// Returns an empty container if no ad is loaded, or the orientation\n /// has changed. Also loads a new ad if the orientation changes.\n Widget _getAdWidget() {\n return OrientationBuilder(\n builder: (context, orientation) {\n if (_currentOrientation == orientation &&\n _inlineAdaptiveAd != null &&\n _isLoaded &&\n _adSize != null) {\n return Align(\n child: Container(\n width: _adWidth,\n height: _adSize!.height.toDouble(),\n child: AdWidget(\n ad: _inlineAdaptiveAd!,\n ),\n ));\n }\n // Reload the ad if the orientation changes.\n if (_currentOrientation != orientation) {\n _currentOrientation = orientation;\n _loadAd();\n }\n return Container();\n },\n );\n }\n\n @override\n Widget build(BuildContext context) => Scaffold(\n appBar: AppBar(\n title: Text('Inline adaptive banner example'),\n ),\n body: Center(\n child: Padding(\n padding: const EdgeInsets.symmetric(horizontal: _insets),\n child: ListView.separated(\n itemCount: 20,\n separatorBuilder: (BuildContext context, int index) {\n return Container(\n height: 40,\n );\n },\n itemBuilder: (BuildContext context, int index) {\n if (index == 10) {\n return _getAdWidget();\n }\n return Text(\n 'Placeholder text',\n style: TextStyle(fontSize: 24),\n );\n },\n ),\n ),\n ));\n\n @override\n void dispose() {\n super.dispose();\n _inlineAdaptiveAd?.dispose();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.357Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":142,"estimatedTokens":1057}}597{"id":"doc-optimize_click_behavior_android_google_for_devel-e21c6e3c","source":"documentation","title":"Optimize click behavior | Android | Google for Developers","url":"https://developers.google.com/admob/android/browser/webview/click-behavior","text":"Example:\n```text\ndependencies {\n implementation 'androidx.browser:browser:1.5.0'\n}\n```\n\nExample:\n```text\npublic class MainActivity extends AppCompatActivity {\n\n private WebView webView;\n\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n\n // ... Register the WebView.\n\n webView = new WebView(this);\n WebSettings webSettings = webView.getSettings();\n webSettings.setJavaScriptEnabled(true);\n webView.setWebViewClient(\n new WebViewClient() {\n // 1. Implement the web view click handler.\n @Override\n public boolean shouldOverrideUrlLoading(\n WebView view,\n WebResourceRequest request) {\n // 2. Determine whether to override the behavior of the URL.\n // If the target URL has no host and no scheme, return early.\n if (request.getUrl().getHost() == null && request.getUrl().getScheme() == null) {\n return false;\n }\n\n // Handle custom URL schemes such as market:// by attempting to\n // launch the corresponding application in a new intent.\n if (!request.getUrl().getScheme().equals(\"http\")\n && !request.getUrl().getScheme().equals(\"https\")) {\n Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());\n // If the URL cannot be opened, return early.\n try {\n MainActivity.this.startActivity(intent);\n } catch (ActivityNotFoundException exception) {\n Log.d(\"TAG\", \"Failed to load URL with scheme:\" + request.getUrl().getScheme());\n }\n return true;\n }\n\n String currentDomain;\n // If the current URL's host cannot be found, return early.\n try {\n currentDomain = new URI(view.getUrl()).toURL().getHost();\n } catch (URISyntaxException | MalformedURLException exception) {\n // Malformed URL.\n return false;\n }\n String targetDomain = request.getUrl().getHost();\n\n // If the current domain equals the target domain, the\n // assumption is the user is not navigating away from\n // the site. Reload the URL within the existing web view.\n if (currentDomain.equals(targetDomain)) {\n return false;\n }\n\n // 3. User is navigating away from the site, open the URL in\n // Custom Tabs to preserve the state of the web view.\n CustomTabsIntent intent = new CustomTabsIntent.Builder().build();\n intent.launchUrl(MainActivity.this, request.getUrl());\n return true;\n }\n });\n }\n}\n```\n\nExample:\n```text\nclass MainActivity : AppCompatActivity() {\n\n private lateinit var webView: WebView\n\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n\n // ... Register the WebView.\n\n webView.webViewClient = object : WebViewClient() {\n // 1. Implement the web view click handler.\n override fun shouldOverrideUrlLoading(\n view: WebView?,\n request: WebResourceRequest?\n ): Boolean {\n // 2. Determine whether to override the behavior of the URL.\n // If the target URL has no host and no scheme, return early.\n if (request?.url?.host == null && request.url.scheme == null) {\n return false\n }\n val currentDomain = URI(view?.url).toURL().host\n\n // Handle custom URL schemes such as market:// by attempting to\n // launch the corresponding application in a new intent.\n if (!request.url.scheme.equals(\"http\") &&\n !request.url.scheme.equals(\"https\")) {\n val intent = Intent(Intent.ACTION_VIEW, request.url)\n // If the URL cannot be opened, return early.\n try {\n this@MainActivity.startActivity(intent)\n } catch (exception: ActivityNotFoundException) {\n Log.d(\"TAG\", \"Failed to load URL with scheme: ${request.url.scheme}\")\n }\n return true\n }\n\n val targetDomain = request.url.host\n\n // If the current domain equals the target domain, the\n // assumption is the user is not navigating away from\n // the site. Reload the URL within the existing web view.\n if (currentDomain.equals(targetDomain)) {\n return false\n }\n\n // 3. User is navigating away from the site, open the URL in\n // Custom Tabs to preserve the state of the web view.\n val customTabsIntent = CustomTabsIntent.Builder().build()\n customTabsIntent.launchUrl(this@MainActivity, request.url)\n return true\n }\n }\n }\n}\n```\n\nExample:\n```text\nhttps://google.github.io/webview-ads/test/#click-behavior-tests\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.358Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":140,"estimatedTokens":1214}}598{"id":"doc-oauth_2_0_for_client_side_web_applications_googl-96528c7c","source":"documentation","title":"OAuth 2.0 for Client-side Web Applications | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow","text":"Example:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth?\n scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&\n include_granted_scopes=true&\n response_type=token&\n state=state_parameter_passthrough_value&\n redirect_uri=https%3A//developers.google.com/oauthplayground&\n client_id=client_id\n```\n\nExample:\n```text\n/*\n * Create form to request access token from Google's OAuth 2.0 server.\n */\nfunction oauthSignIn() {\n // Google's OAuth 2.0 endpoint for requesting an access token\n var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';\n\n // Create <form> element to submit parameters to OAuth 2.0 endpoint.\n var form = document.createElement('form');\n form.setAttribute('method', 'GET'); // Send as a GET request.\n form.setAttribute('action', oauth2Endpoint);\n\n // Parameters to pass to OAuth 2.0 endpoint.\n var params = {'client_id': 'YOUR_CLIENT_ID',\n 'redirect_uri': 'YOUR_REDIRECT_URI',\n 'response_type': 'token',\n 'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly',\n 'include_granted_scopes': 'true',\n 'state': 'pass-through value'};\n\n // Add form parameters as hidden input values.\n for (var p in params) {\n var input = document.createElement('input');\n input.setAttribute('type', 'hidden');\n input.setAttribute('name', p);\n input.setAttribute('value', params[p]);\n form.appendChild(input);\n }\n\n // Add form to page and submit it to open the OAuth 2.0 endpoint.\n document.body.appendChild(form);\n form.submit();\n}\n```\n\nExample:\n```text\nhttps://oauth2.example.com/callback#access_token=4/P7q7W91&token_type=Bearer&expires_in=3600\n```\n\nExample:\n```text\nhttps://oauth2.example.com/callback#error=access_denied\n```\n\nExample:\n```text\n{\n \"access_token\": \"1/fFAGRNJru1FTz70BzhT3Zg\",\n \"expires_in\": 3920,\n \"token_type\": \"Bearer\",\n \"scope\": \"https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly\",\n \"refresh_token\": \"1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI\"\n }\n```\n\nExample:\n```text\nGET /drive/v2/files HTTP/1.1\nHost: www.googleapis.com\nAuthorization: Bearer access_token\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v2/files?access_token=access_token\n```\n\nExample:\n```text\ncurl -H \"Authorization: Bearer access_token\" https://www.googleapis.com/drive/v2/files\n```\n\nExample:\n```text\ncurl https://www.googleapis.com/drive/v2/files?access_token=access_token\n```\n\nExample:\n```text\nvar xhr = new XMLHttpRequest();\nxhr.open('GET',\n 'https://www.googleapis.com/drive/v3/about?fields=user&' +\n 'access_token=' + params['access_token']);\nxhr.onreadystatechange = function (e) {\n console.log(xhr.response);\n};\nxhr.send(null);\n```\n\nExample:\n```text\n<html><head></head><body>\n<script>\n var YOUR_CLIENT_ID = 'REPLACE_THIS_VALUE';\n var YOUR_REDIRECT_URI = 'REPLACE_THIS_VALUE';\n\n // Parse query string to see if page request is coming from OAuth 2.0 server.\n var fragmentString = location.hash.substring(1);\n var params = {};\n var regex = /([^&=]+)=([^&]*)/g, m;\n while (m = regex.exec(fragmentString)) {\n params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);\n }\n if (Object.keys(params).length > 0 && params['state']) {\n if (params['state'] == localStorage.getItem('state')) {\n localStorage.setItem('oauth2-test-params', JSON.stringify(params) );\n\n trySampleRequest();\n } else {\n console.log('State mismatch. Possible CSRF attack');\n }\n }\n\n // Function to generate a random state value\n function generateCryptoRandomState() {\n const randomValues = new Uint32Array(2);\n window.crypto.getRandomValues(randomValues);\n\n // Encode as UTF-8\n const utf8Encoder = new TextEncoder();\n const utf8Array = utf8Encoder.encode(\n String.fromCharCode.apply(null, randomValues)\n );\n\n // Base64 encode the UTF-8 data\n return btoa(String.fromCharCode.apply(null, utf8Array))\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n }\n\n // If there's an access token, try an API request.\n // Otherwise, start OAuth 2.0 flow.\n function trySampleRequest() {\n var params = JSON.parse(localStorage.getItem('oauth2-test-params'));\n if (params && params['access_token']) { \n // User authorized the request. Now, check which scopes were granted.\n if (params['scope'].includes('https://www.googleapis.com/auth/drive.metadata.readonly')) {\n // User authorized read-only Drive activity permission.\n // Calling the APIs, etc.\n var xhr = new XMLHttpRequest();\n xhr.open('GET',\n 'https://www.googleapis.com/drive/v3/about?fields=user&' +\n 'access_token=' + params['access_token']);\n xhr.onreadystatechange = function (e) {\n if (xhr.readyState === 4 && xhr.status === 200) {\n console.log(xhr.response);\n } else if (xhr.readyState === 4 && xhr.status === 401) {\n // Token invalid, so prompt for user permission.\n oauth2SignIn();\n }\n };\n xhr.send(null);\n }\n else {\n // User didn't authorize read-only Drive activity permission.\n // Update UX and application accordingly\n console.log('User did not authorize read-only Drive activity permission.');\n }\n\n // Check if user authorized Calendar read permission.\n if (params['scope'].includes('https://www.googleapis.com/auth/calendar.readonly')) {\n // User authorized Calendar read permission.\n // Calling the APIs, etc.\n console.log('User authorized Calendar read permission.');\n }\n else {\n // User didn't authorize Calendar read permission.\n // Update UX and application accordingly\n console.log('User did not authorize Calendar read permission.');\n } \n } else {\n oauth2SignIn();\n }\n }\n\n /*\n * Create form to request access token from Google's OAuth 2.0 server.\n */\n function oauth2SignIn() {\n // create random state value and store in local storage\n var state = generateCryptoRandomState();\n localStorage.setItem('state', state);\n\n // Google's OAuth 2.0 endpoint for requesting an access token\n var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';\n\n // Create element to open OAuth 2.0 endpoint in new window.\n var form = document.createElement('form');\n form.setAttribute('method', 'GET'); // Send as a GET request.\n form.setAttribute('action', oauth2Endpoint);\n\n // Parameters to pass to OAuth 2.0 endpoint.\n var params = {'client_id': YOUR_CLIENT_ID,\n 'redirect_uri': YOUR_REDIRECT_URI,\n 'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly',\n 'state': state,\n 'include_granted_scopes': 'true',\n 'response_type': 'token'};\n\n // Add form parameters as hidden input values.\n for (var p in params) {\n var input = document.createElement('input');\n input.setAttribute('type', 'hidden');\n input.setAttribute('name', p);\n input.setAttribute('value', params[p]);\n form.appendChild(input);\n }\n\n // Add form to page and submit it to open the OAuth 2.0 endpoint.\n document.body.appendChild(form);\n form.submit();\n }\n</script>\n\n<button onclick=\"trySampleRequest();\">Try sample request</button>\n</body></html>\n```\n\nExample:\n```text\nvar SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly';\nvar params = JSON.parse(localStorage.getItem('oauth2-test-params'));\n\nvar current_scope_granted = false;\nif (params.hasOwnProperty('scope')) {\n var scopes = params['scope'].split(' ');\n for (var s = 0; s < scopes.length; s++) {\n if (SCOPE == scopes[s]) {\n current_scope_granted = true;\n }\n }\n}\n\nif (!current_scope_granted) {\n oauth2SignIn(); // This function is defined elsewhere in this document.\n} else {\n // Since you already have access, you can proceed with the API request.\n}\n```\n\nExample:\n```text\ncurl -d -X -POST --header \"Content-type:application/x-www-form-urlencoded\" \\\n https://oauth2.googleapis.com/revoke?token={token}\n```\n\nExample:\n```text\nfunction revokeAccess(accessToken) {\n // Google's OAuth 2.0 endpoint for revoking access tokens.\n var revokeTokenEndpoint = 'https://oauth2.googleapis.com/revoke';\n\n // Create <form> element to use to POST data to the OAuth 2.0 endpoint.\n var form = document.createElement('form');\n form.setAttribute('method', 'post');\n form.setAttribute('action', revokeTokenEndpoint);\n\n // Add access token to the form so it is set as value of 'token' parameter.\n // This corresponds to the sample curl request, where the URL is:\n // https://oauth2.googleapis.com/revoke?token={token}\n var tokenField = document.createElement('input');\n tokenField.setAttribute('type', 'hidden');\n tokenField.setAttribute('name', 'token');\n tokenField.setAttribute('value', accessToken);\n form.appendChild(tokenField);\n\n // Add form to page and submit it to actually revoke the token.\n document.body.appendChild(form);\n form.submit();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.360Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":288,"estimatedTokens":2318}}599{"id":"doc-requestconfiguration_builder_android_google_for_-12b818d0","source":"documentation","title":"RequestConfiguration.Builder | Android | Google for Developers","url":"https://developers.google.com/admob/android/reference/com/google/android/gms/ads/RequestConfiguration.Builder","text":"Example:\n```text\npublic class RequestConfiguration.Builder\n```\n\nExample:\n```text\npublic Builder()\n```\n\nExample:\n```text\npublic RequestConfiguration build()\n```\n\nExample:\n```text\n@CanIgnoreReturnValuepublic RequestConfiguration.Builder setAgeRestrictedTreatment( @Nullable AgeRestrictedTreatment ageRestrictedTreatment)\n```\n\nExample:\n```text\n@CanIgnoreReturnValuepublic RequestConfiguration.Builder setMaxAdContentRating( @RequestConfiguration.MaxAdContentRating @Nullable String maxAdContentRating)\n```\n\nExample:\n```text\n@CanIgnoreReturnValuepublic RequestConfiguration.Builder setPublisherPrivacyPersonalizationState( RequestConfiguration.PublisherPrivacyPersonalizationState publisherPrivacyPersonalizationState)\n```\n\nExample:\n```text\n@CanIgnoreReturnValuepublic RequestConfiguration.Builder setTagForChildDirectedTreatment( @RequestConfiguration.TagForChildDirectedTreatment int tagForChildDirectedTreatment)\n```\n\nExample:\n```text\n@CanIgnoreReturnValuepublic RequestConfiguration.Builder setTagForUnderAgeOfConsent( @RequestConfiguration.TagForUnderAgeOfConsent int tagForUnderAgeOfConsent)\n```\n\nExample:\n```text\n@CanIgnoreReturnValuepublic RequestConfiguration.Builder setTestDeviceIds(@Nullable List<String> testDeviceIds)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.361Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":46,"estimatedTokens":315}}600{"id":"doc-anchored_adaptive_banners_c_google_for_developer-0ef528f6","source":"documentation","title":"Anchored adaptive banners | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/banner/anchored-adaptive","text":"Example:\n```text\n// Determine view width in pixels based on your app's current width on the\n// device's screen. This process will vary depending on which windowing toolkit\n// you're using.\n\nfirebase::gma::AdSize adaptive_ad_size =\n AdSize::GetCurrentOrientationAnchoredAdaptiveBannerAdSize(view_width);\n\n// my_ad_parent is a reference to an iOS UIView or an Android Activity.\n// This is the parent UIView or Activity of the banner view.\nfirebase::gma::AdParent ad_parent =\n static_cast<firebase::gma::AdParent>(my_ad_parent);\nfirebase::Future<void> result =\n ad_view->Initialize(ad_parent, kBannerAdUnit, adaptive_ad_size);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.361Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":18,"estimatedTokens":162}}601{"id":"doc-enabling_test_ads_c_google_for_developers-774dd134","source":"documentation","title":"Enabling test ads | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/test-ads","text":"Example:\n```text\nI/Ads: Use RequestConfiguration.Builder.setTestDeviceIds(Arrays.asList(\"33BE2250B43518CCDA7DE426D04EE231\"))\n to get test ads on this device.\"\n```\n\nExample:\n```text\n<Google> To get test ads on this device, set:\n GADMobileAds.sharedInstance.requestConfiguration.testDeviceIdentifiers = @[ @\"2077ef9a63d2b398840261c8221a0c9b\" ];\n```\n\nExample:\n```text\n// Set a sample device ID of 2077ef9a63d2b398840261c8221a0c9b\n firebase::gma::RequestConfiguration request_configuration =\n firebase::gma::GetRequestConfiguration();\n request_configuration.test_device_ids.push_back(\"2077ef9a63d2b398840261c8221a0c9b\");\n firebase::gma::SetRequestConfiguration(request_configuration);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.362Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":22,"estimatedTokens":176}}602{"id":"doc-sensitive_scope_verification_app_verification_to-ab4f9bdc","source":"documentation","title":"Sensitive scope verification | App verification to use Google Authorization APIs | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/production-readiness/sensitive-scope-verification","text":"Example:\n```text\nhttps://console.developers.google.com/auth/branding?project=[PROJECT_ID]\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.365Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":27}}603{"id":"doc-restricted_scope_verification_app_verification_t-981e44c0","source":"documentation","title":"Restricted scope verification | App verification to use Google Authorization APIs | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/production-readiness/restricted-scope-verification","text":"Example:\n```text\nhttps://console.developers.google.com/auth/branding?project=[PROJECT_ID]\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.368Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":27}}604{"id":"doc-authenticate_with_a_backend_server_web_guides_go-42936077","source":"documentation","title":"Authenticate with a backend server | Web guides | Google for Developers","url":"https://developers.google.com/identity/sign-in/web/backend-auth","text":"Example:\n```text\nfunction onSignIn(googleUser) {\n var id_token = googleUser.getAuthResponse().id_token;\n ...\n}\n```\n\nExample:\n```text\nvar xhr = new XMLHttpRequest();\nxhr.open('POST', 'https://yourbackend.example.com/tokensignin');\nxhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');\nxhr.onload = function() {\n console.log('Signed in as: ' + xhr.responseText);\n};\nxhr.send('idtoken=' + id_token);\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;\n\n...\n\nGoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)\n // Specify the WEB_CLIENT_ID of the app that accesses the backend:\n .setAudience(Collections.singletonList(WEB_CLIENT_ID))\n // Or, if multiple clients access the backend:\n //.setAudience(Arrays.asList(WEB_CLIENT_ID_1, WEB_CLIENT_ID_2, WEB_CLIENT_ID_3))\n .build();\n\n// (Receive idTokenString by HTTPS POST)\n\nGoogleIdToken idToken = verifier.verify(idTokenString);\nif (idToken != null) {\n Payload payload = idToken.getPayload();\n\n // Print user identifier. This ID is unique to each Google Account, making it suitable for\n // use as a primary key during account lookup. Email is not a good choice because it can be\n // changed by the user.\n String userId = payload.getSubject();\n System.out.println(\"User ID: \" + userId);\n\n // Get profile information from payload\n String email = payload.getEmail();\n boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());\n String name = (String) payload.get(\"name\");\n String pictureUrl = (String) payload.get(\"picture\");\n String locale = (String) payload.get(\"locale\");\n String familyName = (String) payload.get(\"family_name\");\n String givenName = (String) payload.get(\"given_name\");\n\n // Use or store profile information\n // ...\n\n} else {\n System.out.println(\"Invalid ID token.\");\n}\n```\n\nExample:\n```text\nnpm install google-auth-library --save\n```\n\nExample:\n```text\nconst {OAuth2Client} = require('google-auth-library');\nconst client = new OAuth2Client();\nasync function verify() {\n const ticket = await client.verifyIdToken({\n idToken: token,\n audience: WEB_CLIENT_ID, // Specify the WEB_CLIENT_ID of the app that accesses the backend\n // Or, if multiple clients access the backend:\n //[WEB_CLIENT_ID_1, WEB_CLIENT_ID_2, WEB_CLIENT_ID_3]\n });\n const payload = ticket.getPayload();\n // This ID is unique to each Google Account, making it suitable for use as a primary key\n // during account lookup. Email is not a good choice because it can be changed by the user.\n const userid = payload['sub'];\n // If the request specified a Google Workspace domain:\n // const domain = payload['hd'];\n}\nverify().catch(console.error);\n```\n\nExample:\n```text\ncomposer require google/apiclient\n```\n\nExample:\n```text\nrequire_once 'vendor/autoload.php';\n\n// Get $id_token via HTTPS POST.\n\n$client = new Google_Client(['client_id' => $WEB_CLIENT_ID]); // Specify the WEB_CLIENT_ID of the app that accesses the backend\n$payload = $client->verifyIdToken($id_token);\nif ($payload) {\n // This ID is unique to each Google Account, making it suitable for use as a primary key\n // during account lookup. Email is not a good choice because it can be changed by the user.\n $userid = $payload['sub'];\n // If the request specified a Google Workspace domain\n //$domain = $payload['hd'];\n} else {\n // Invalid ID token\n}\n```\n\nExample:\n```text\nfrom google.oauth2 import id_token\nfrom google.auth.transport import requests\n\n# (Receive token by HTTPS POST)\n# ...\n\ntry:\n # Specify the WEB_CLIENT_ID of the app that accesses the backend:\n idinfo = id_token.verify_oauth2_token(token, requests.Request(), WEB_CLIENT_ID)\n\n # Or, if multiple clients access the backend server:\n # idinfo = id_token.verify_oauth2_token(token, requests.Request())\n # if idinfo['aud'] not in [WEB_CLIENT_ID_1, WEB_CLIENT_ID_2, WEB_CLIENT_ID_3]:\n # raise ValueError('Could not verify audience.')\n\n # If the request specified a Google Workspace domain\n # if idinfo['hd'] != DOMAIN_NAME:\n # raise ValueError('Wrong domain name.')\n\n # ID token is valid. Get the user's Google Account ID from the decoded token.\n # This ID is unique to each Google Account, making it suitable for use as a primary key\n # during account lookup. Email is not a good choice because it can be changed by the user.\n userid = idinfo['sub']\nexcept ValueError:\n # Invalid token\n pass\n```\n\nExample:\n```text\nhttps://oauth2.googleapis.com/tokeninfo?id_token=XYZ123\n```\n\nExample:\n```text\n{\n // These six fields are included in all Google ID Tokens.\n \"iss\": \"https://accounts.google.com\",\n \"sub\": \"110169484474386276334\",\n \"azp\": \"1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com\",\n \"aud\": \"1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com\",\n \"iat\": \"1433978353\",\n \"exp\": \"1433981953\",\n\n // These seven fields are only included when the user has granted the \"profile\" and\n // \"email\" OAuth scopes to the application.\n \"email\": \"testuser@gmail.com\",\n \"email_verified\": \"true\",\n \"name\" : \"Test User\",\n \"picture\": \"https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg\",\n \"given_name\": \"Test\",\n \"family_name\": \"User\",\n \"locale\": \"en\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.368Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":172,"estimatedTokens":1368}}605{"id":"doc-test_creative_types_c_google_for_developers-d53bd353","source":"documentation","title":"Test creative types | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/test-creative-types","text":"Example:\n```text\nAdRequest ad_request;\n\nad_request.add_extra(\n /*adapter_class_name=*/\"com.google.ads.mediation.admob.AdMobAdapter\",\n /*extra_key=*/\"ft_ctype\",\n /*extra_value=*/\"video_app_install\");\n```\n\nExample:\n```text\nAdRequest ad_request;\n\nad_request.add_extra(\n /*adapter_class_name=*/\"GADExtras\",\n /*extra_key=*/\"ft_ctype\",\n /*extra_value=*/\"video_app_install\");\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.369Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":21,"estimatedTokens":98}}606{"id":"doc-global_settings_c_google_for_developers-2c59f376","source":"documentation","title":"Global Settings | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/global-settings","text":"Example:\n```text\n#include \"firebase/gma.h\"\n\nfirebase::gma::SetIsSameAppKeyEnabled(/*is_enabled=*/false);\n```\n\nExample:\n```text\n#include “firebase/gma.h”\n\nfirebase::gma::DisableSDKCrashReporting();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.369Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":15,"estimatedTokens":54}}607{"id":"doc-brand_verification_app_verification_to_use_googl-aad62d6e","source":"documentation","title":"Brand verification | App verification to use Google Authorization APIs | Google for Developers","url":"https://developers.google.com/identity/protocols/oauth2/production-readiness/brand-verification","text":"Example:\n```text\nhttps://console.developers.google.com/auth/branding?project=[PROJECT_ID]\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.370Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":27}}608{"id":"doc-css_support_gmail_google_for_developers-3bc604b3","source":"documentation","title":"CSS Support | Gmail | Google for Developers","url":"https://developers.google.com/workspace/gmail/design/css","text":"Example:\n```text\n<html>\n <head>\n <style>\n .colored {\n color: blue;\n }\n #body {\n font-size: 14px;\n }\n </style>\n </head>\n <body>\n <div id='body'>\n <p>Hi Pierce,</p>\n <p class='colored'>This text is blue.</p>\n <p>Jerry</p>\n </div>\n </body>\n</html>\n```\n\nExample:\n```text\n<html>\n <head>\n <style>\n .colored {\n color: blue;\n }\n #body {\n font-size: 14px;\n }\n @media screen and (min-width: 500px) {\n .colored {\n color:red;\n }\n }\n </style>\n </head>\n <body>\n <div id='body'>\n <p>Hi Pierce,</p>\n <p class='colored'>\n This text is blue if the window width is\n below 500px and red otherwise.\n </p>\n <p>Jerry</p>\n </div>\n </body>\n</html>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.376Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":55,"estimatedTokens":206}}609{"id":"doc-android_content_provider_for_gmail_google_for_de-39826acc","source":"documentation","title":"Android content provider for Gmail | Google for Developers","url":"https://developers.google.com/workspace/gmail/android","text":"Example:\n```text\n// Get the account list, and pick the first one.\nfinal String ACCOUNT_TYPE_GOOGLE = \"com.google\";\nfinal String[] FEATURES_MAIL = {\n \"service_mail\"\n};\nAccountManager.get(this).getAccountsByTypeAndFeatures(ACCOUNT_TYPE_GOOGLE, FEATURES_MAIL,\n new AccountManagerCallback() {\n @Override\n public void run(AccountManagerFuture future) {\n Account[] accounts = null;\n try {\n accounts = future.getResult();\n if (accounts != null && accounts.length > 0) {\n String selectedAccount = accounts[0].name;\n queryLabels(selectedAccount);\n }\n\n } catch (OperationCanceledException oce) {\n // TODO: handle exception\n } catch (IOException ioe) {\n // TODO: handle exception\n } catch (AuthenticatorException ae) {\n // TODO: handle exception\n }\n }\n }, null /* handler */);\n```\n\nExample:\n```text\n// Query for all labels and find the Inbox.\ntry (Cursor labelsCursor = getContentResolver().query(\n GmailContract.Labels.getLabelsUri(selectedAccount), null, null, null, null)) {\n if (labelsCursor != null) {\n final String inboxCanonicalName = GmailContract.Labels.LabelCanonicalName.CANONICAL_NAME_INBOX;\n final int canonicalNameIndex = labelsCursor.getColumnIndexOrThrow(GmailContract.Labels.CANONICAL_NAME);\n while (labelsCursor.moveToNext()) {\n if (inboxCanonicalName.equals(labelsCursor.getString(canonicalNameIndex))) {\n // This row corresponds to the Inbox.\n }\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.379Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":48,"estimatedTokens":441}}610{"id":"doc-plan_travels_with_an_ai_agent_accessible_across_-677d46c7","source":"documentation","title":"Plan travels with an AI agent accessible across Google Workspace | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/samples/travel-concierge","text":"Example:\n```text\ngcloud projects create PROJECT_ID\n```\n\nExample:\n```text\ngcloud billing accounts list\n```\n\nExample:\n```text\ngcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID\n```\n\nExample:\n```text\ngcloud iam service-accounts create SERVICE_ACCOUNT_NAME \\\n --display-name=\"SERVICE_ACCOUNT_NAME\"SERVICE_ACCOUNT_NAMESERVICE_ACCOUNT_NAME\n```\n\nExample:\n```text\ngcloud auth application-default logingcloud config set project PROJECT_IDgcloud auth application-default set-quota-project PROJECT_ID\n```\n\nExample:\n```text\nunzip adk-samples-main.zipcd adk-samples-main/python/agents/travel-concierge\n```\n\nExample:\n```text\ngcloud storage buckets create gs://CLOUD_STORAGE_BUCKET_NAME --project=PROJECT_ID --location=PROJECT_LOCATION\n```\n\nExample:\n```text\nexport GOOGLE_GENAI_USE_VERTEXAI=1export GOOGLE_CLOUD_PROJECT=PROJECT_IDexport GOOGLE_CLOUD_LOCATION=PROJECT_LOCATIONexport GOOGLE_PLACES_API_KEY=PLACES_API_KEYexport GOOGLE_CLOUD_STORAGE_BUCKET=CLOUD_STORAGE_BUCKET_NAMEexport TRAVEL_CONCIERGE_SCENARIO=travel_concierge/profiles/itinerary_empty_default.json\n```\n\nExample:\n```text\nuv sync --group deploymentuv run python deployment/deploy.py --create\n```\n\nExample:\n```text\nCreated remote agent: projects/PROJECT_NUMBER/locations/us-central1/reasoningEngines/ENGINE_ID\n```\n\nExample:\n```text\nunzip add-ons-samples-main.zipcd add-ons-samples-main/python/travel-adk-ai-agent\n```\n\nExample:\n```text\ngcloud run deploy travel-concierge-app --quiet --source . \\\n --region PROJECT_LOCATION \\\n --function adk_ai_agent \\\n --set-env-vars LOCATION=LOCATION,PROJECT_NUMBER=PROJECT_NUMBER,ENGINE_ID=ENGINE_ID,BASE_URL=BASE_URL\n```\n\nExample:\n```text\ngcloud workspace-add-ons deployments create travel-concierge-addon \\\n --deployment-file=deployment.json\n```\n\nExample:\n```text\ngcloud workspace-add-ons deployments install travel-concierge-addon\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.381Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":76,"estimatedTokens":470}}611{"id":"doc-overview_admin_console_google_for_developers-c6d49b3c","source":"documentation","title":"Overview | Admin console | Google for Developers","url":"https://developers.google.com/workspace/admin/alertcenter","text":"Example:\n```text\n// First, authorize the API and create a client to make requests with.\nURL serviceAccountUrl = AuthUtils.class.getResource(\"/client_secret.json\");\nGoogleCredentials credentials = ServiceAccountCredentials\n .fromStream(serviceAccountUrl.openStream())\n .createDelegated(\"admin@xxxx.com\")\n .createScoped(Collections.singleton(\"https://www.googleapis.com/auth/apps.alerts\"));\nApacheHttpTransport transport = new ApacheHttpTransport();\nHttpCredentialsAdapter adapter = new HttpCredentialsAdapter(credentials);\nAlertCenter alertCenter = new AlertCenter.Builder(transport, new JacksonFactory(), adapter)\n .setApplicationName(\"Alert Center client\")\n .build();\n\n// List alerts in pages, printing each alert discovered.\nString pageToken = null;\ndo {\n ListAlertsResponse listResponse = service.alerts().list().setPageToken(pageToken)\n .setPageSize(20).execute();\n if (listResponse.getAlerts() != null) {\n for (Alert alert : listResponse.getAlerts()) {\n System.out.println(alert);\n }\n }\n pageToken = listResponse.getNextPageToken();\n} while (pageToken != null);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.385Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":29,"estimatedTokens":280}}612{"id":"doc-admin_settings_api_overview_admin_console_google-40f1c6cd","source":"documentation","title":"Admin Settings API overview | Admin console | Google for Developers","url":"https://developers.google.com/workspace/admin/admin-settings","text":"Example:\n```text\nhttps://apps-apis.google.com/a/feeds/domain/2.0/{domainName}/email/gateway\n```\n\nExample:\n```text\n<atom:entry xmlns:atom='http://www.w3.org/2005/Atom'\n xmlns:apps='http://schemas.google.com/apps/2006'>\n <apps:property name='smartHost' value='smtp.out.domain.com' />\n <apps:property name='smtpMode' value='SMTP' />\n</atom:entry>\n```\n\nExample:\n```text\n<?xml version='1.0' encoding='UTF-8'?>\n<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n<id>https://apps-apis.google.com/a/feeds/domain/2.0/domainName/email/gateway</id>\n<updated>2008-12-17T23:59:23.887Z</updated>\n<link rel='self' type='application/atom+xml' href='https://apps-apis.google.com/a/feeds/domain/\n 2.0/domainName/email/gateway'/>\n<link rel='edit' type='application/atom+xml' href='https://apps-apis.google.com/a/feeds/domain/\n 2.0/domainName/email/gateway'/>\n<apps:property name='smartHost' value='smtp.out.domain.com' />\n<apps:property name='smtpMode' value='SMTP' />\n</entry>\n```\n\nExample:\n```text\nhttps://apps-apis.google.com/a/feeds/domain/2.0/{domainName}/sso/general\n```\n\nExample:\n```text\n<?xml version='1.0' encoding='UTF-8'?>\n<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n<apps:property name='samlSignonUri' value='http://www.example.com/sso/signon'/>\n...\n<apps:property name='samlLogoutUri' value='http://www.example.com/sso/logout'/>\n<apps:property name='changePasswordUri' value='http://www.example.com/sso/changepassword'/>\n<apps:property name='enableSSO' value='true'/>\n<apps:property name='ssoWhitelist' value='CIDR formatted IP address'/>\n<apps:property name='useDomainSpecificIssuer' value='false'/>\n</entry>\n```\n\nExample:\n```text\n<atom:entry xmlns:atom='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n<apps:property name='enableSSO' value='false' />\n<apps:property name='samlSignonUri' value='http://www.example.com/sso/signon' />\n<apps:property name='samlLogoutUri' value='http://www.example.com/sso/logout' />\n<apps:property name='changePasswordUri' value='http://www.example.com/sso/changepassword' />\n<apps:property name='ssoWhitelist' value='127.0.0.1/32' />\n<apps:property name='useDomainSpecificIssuer' value='false'/>\n</atom:entry>\n```\n\nExample:\n```text\n<?xml version='1.0' encoding='UTF-8'?>\n<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n...\n<apps:property name='samlSignonUri' value='http://www.example.com/sso/signon'/>\n<apps:property name='samlLogoutUri' value='http://www.example.com/sso/logout'/>\n<apps:property name='changePasswordUri' value='http://www.example.com/sso/changepassword'/>\n<apps:property name='enableSSO' value='false'/>\n<apps:property name='ssoWhitelist' value='127.0.0.1/32'/>\n<apps:property name='useDomainSpecificIssuer' value='false'/>\n</entry>\n```\n\nExample:\n```text\nhttps://apps-apis.google.com/a/feeds/domain/2.0/{domainName}/sso/signingkey\n```\n\nExample:\n```text\n<?xml version='1.0' encoding='UTF-8'?>\n<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n...\n<apps:property name='signingKey' value='yourBase64EncodedPublicKey'/>\n</entry>\n```\n\nExample:\n```text\n<atom:entry xmlns:atom='http://www.w3.org/2005/Atom' xmlns:apps=\"http://schemas.google.com/apps/2006\">\n<apps:property name='signingKey' value='yourBase64EncodedPublicKey'/>\n</atom:entry>\n```\n\nExample:\n```text\n<?xml version='1.0' encoding='UTF-8'?>\n<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n...\n<apps:property name='smartHost' value='smtpout.domain.com'/>\n<apps:property name='smtpMode' value='SMTP'/>\n</entry>\n```\n\nExample:\n```text\n<atom:entry xmlns:atom='http://www.w3.org/2005/Atom' xmlns:apps=\"http://schemas.google.com/apps/2006\">\n<apps:property name='smartHost' value='smtp.out.domain.com' />\n<apps:property name='smtpMode' value='SMTP' />\n</atom:entry>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.387Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":114,"estimatedTokens":984}}613{"id":"doc-domain_shared_contacts_api_overview_admin_consol-72e7415b","source":"documentation","title":"Domain Shared Contacts API overview | Admin console | Google for Developers","url":"https://developers.google.com/workspace/admin/domain-shared-contacts","text":"Example:\n```text\nGData-Version: 3.0\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.388Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":14}}614{"id":"doc-admin_sdk_directory_service_apps_script_google_f-2b919cfb","source":"documentation","title":"Admin SDK Directory Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/advanced/admin-sdk-directory","text":"Example:\n```text\n/**\n * Lists all the users in a domain sorted by first name.\n * @see https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/list\n */\nfunction listAllUsers() {\n let pageToken;\n let page;\n do {\n page = AdminDirectory.Users.list({\n domain: \"example.com\",\n orderBy: \"givenName\",\n maxResults: 100,\n pageToken: pageToken,\n });\n const users = page.users;\n if (!users) {\n console.log(\"No users found.\");\n return;\n }\n // Print the user's full name and email.\n for (const user of users) {\n console.log(\"%s (%s)\", user.name.fullName, user.primaryEmail);\n }\n pageToken = page.nextPageToken;\n } while (pageToken);\n}\n```\n\nExample:\n```text\n/**\n * Get a user by their email address and logs all of their data as a JSON string.\n * @see https://developers.google.com/admin-sdk/directory/reference/rest/v1/users/get\n */\nfunction getUser() {\n // TODO (developer) - Replace userEmail value with yours\n const userEmail = \"liz@example.com\";\n try {\n const user = AdminDirectory.Users.get(userEmail);\n console.log(\"User data:\\n %s\", JSON.stringify(user, null, 2));\n } catch (err) {\n // TODO (developer)- Handle exception from the API\n console.log(\"Failed with error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Adds a new user to the domain, including only the required information. For\n * the full list of user fields, see the API's reference documentation:\n * @see https://developers.google.com/admin-sdk/directory/v1/reference/users/insert\n */\nfunction addUser() {\n let user = {\n // TODO (developer) - Replace primaryEmail value with yours\n primaryEmail: \"liz@example.com\",\n name: {\n givenName: \"Elizabeth\",\n familyName: \"Smith\",\n },\n // Generate a random password string.\n password: Math.random().toString(36),\n };\n try {\n user = AdminDirectory.Users.insert(user);\n console.log(\"User %s created with ID %s.\", user.primaryEmail, user.id);\n } catch (err) {\n // TODO (developer)- Handle exception from the API\n console.log(\"Failed with error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Creates an alias (nickname) for a user.\n * @see https://developers.google.com/admin-sdk/directory/reference/rest/v1/users.aliases/insert\n */\nfunction createAlias() {\n // TODO (developer) - Replace userEmail value with yours\n const userEmail = \"liz@example.com\";\n let alias = {\n alias: \"chica@example.com\",\n };\n try {\n alias = AdminDirectory.Users.Aliases.insert(alias, userEmail);\n console.log(\"Created alias %s for user %s.\", alias.alias, userEmail);\n } catch (err) {\n // TODO (developer)- Handle exception from the API\n console.log(\"Failed with error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Lists all the groups in the domain.\n * @see https://developers.google.com/admin-sdk/directory/reference/rest/v1/groups/list\n */\nfunction listAllGroups() {\n let pageToken;\n let page;\n do {\n page = AdminDirectory.Groups.list({\n domain: \"example.com\",\n maxResults: 100,\n pageToken: pageToken,\n });\n const groups = page.groups;\n if (!groups) {\n console.log(\"No groups found.\");\n return;\n }\n // Print group name and email.\n for (const group of groups) {\n console.log(\"%s (%s)\", group.name, group.email);\n }\n pageToken = page.nextPageToken;\n } while (pageToken);\n}\n```\n\nExample:\n```text\n/**\n * Adds a user to an existing group in the domain.\n * @see https://developers.google.com/admin-sdk/directory/reference/rest/v1/members/insert\n */\nfunction addGroupMember() {\n // TODO (developer) - Replace userEmail value with yours\n const userEmail = \"liz@example.com\";\n // TODO (developer) - Replace groupEmail value with yours\n const groupEmail = \"bookclub@example.com\";\n const member = {\n email: userEmail,\n role: \"MEMBER\",\n };\n try {\n AdminDirectory.Members.insert(member, groupEmail);\n console.log(\n \"User %s added as a member of group %s.\",\n userEmail,\n groupEmail,\n );\n } catch (err) {\n // TODO (developer)- Handle exception from the API\n console.log(\"Failed with error %s\", err.message);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.394Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":158,"estimatedTokens":1045}}615{"id":"doc-style_ad_layouts_with_native_templates_flutter_g-f9645a14","source":"documentation","title":"Style ad layouts with native templates | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/native/templates","text":"Example:\n```text\nca-app-pub-3940256099942544/2247696110\n```\n\nExample:\n```text\nca-app-pub-3940256099942544/3986624511\n```\n\nExample:\n```text\nclass NativeExampleState extends State<NativeExample> {\n NativeAd? nativeAd;\n bool _nativeAdIsLoaded = false;\n\n // TODO: replace this test ad unit with your own ad unit.\n final String _adUnitId = Platform.isAndroid\n ? 'ca-app-pub-3940256099942544/2247696110'\n : 'ca-app-pub-3940256099942544/3986624511';\n\n /// Loads a native ad.\n void loadAd() {\n _nativeAd = NativeAd(\n adUnitId: _adUnitId,\n listener: NativeAdListener(\n onAdLoaded: (ad) {\n debugPrint('$NativeAd loaded.');\n setState(() {\n _nativeAdIsLoaded = true;\n });\n },\n onAdFailedToLoad: (ad, error) {\n // Dispose the ad here to free resources.\n debugPrint('$NativeAd failed to load: $error');\n ad.dispose();\n },\n ),\n request: const AdRequest(),\n // Styling\n nativeTemplateStyle: NativeTemplateStyle(\n // Required: Choose a template.\n templateType: TemplateType.medium,\n // Optional: Customize the ad's style.\n mainBackgroundColor: Colors.purple,\n cornerRadius: 10.0,\n callToActionTextStyle: NativeTemplateTextStyle(\n textColor: Colors.cyan,\n backgroundColor: Colors.red,\n style: NativeTemplateFontStyle.monospace,\n size: 16.0),\n primaryTextStyle: NativeTemplateTextStyle(\n textColor: Colors.red,\n backgroundColor: Colors.cyan,\n style: NativeTemplateFontStyle.italic,\n size: 16.0),\n secondaryTextStyle: NativeTemplateTextStyle(\n textColor: Colors.green,\n backgroundColor: Colors.black,\n style: NativeTemplateFontStyle.bold,\n size: 16.0),\n tertiaryTextStyle: NativeTemplateTextStyle(\n textColor: Colors.brown,\n backgroundColor: Colors.amber,\n style: NativeTemplateFontStyle.normal,\n size: 16.0)))\n ..load();\n }\n}\n```\n\nExample:\n```text\nclass NativeExampleState extends State<NativeExample> {\n NativeAd? _nativeAd;\n bool _nativeAdIsLoaded = false;\n\n // TODO: replace this test ad unit with your own ad unit.\n final String _adUnitId = Platform.isAndroid\n ? 'ca-app-pub-3940256099942544/2247696110'\n : 'ca-app-pub-3940256099942544/3986624511';\n\n /// Loads a native ad.\n void loadAd() {\n _nativeAd = NativeAd(\n adUnitId: _adUnitId,\n listener: NativeAdListener(\n onAdLoaded: (ad) {\n print('$NativeAd loaded.');\n setState(() {\n _nativeAdIsLoaded = true;\n });\n },\n onAdFailedToLoad: (ad, error) {\n // Dispose the ad here to free resources.\n print('$NativeAd failedToLoad: $error');\n ad.dispose();\n },\n // Called when a click is recorded for a NativeAd.\n onAdClicked: (ad) {},\n // Called when an impression occurs on the ad.\n onAdImpression: (ad) {},\n // Called when an ad removes an overlay that covers the screen.\n onAdClosed: (ad) {},\n // Called when an ad opens an overlay that covers the screen.\n onAdOpened: (ad) {},\n // For iOS only. Called before dismissing a full screen view\n onAdWillDismissScreen: (ad) {},\n // Called when an ad receives revenue value.\n onPaidEvent: (ad, valueMicros, precision, currencyCode) {},\n ),\n request: const AdRequest(),\n // Styling\n nativeTemplateStyle: NativeTemplateStyle(\n // Required: Choose a template.\n templateType: TemplateType.medium,\n // Optional: Customize the ad's style.\n mainBackgroundColor: Colors.purple,\n cornerRadius: 10.0,\n callToActionTextStyle: NativeTemplateTextStyle(\n textColor: Colors.cyan,\n backgroundColor: Colors.red,\n style: NativeTemplateFontStyle.monospace,\n size: 16.0),\n primaryTextStyle: NativeTemplateTextStyle(\n textColor: Colors.red,\n backgroundColor: Colors.cyan,\n style: NativeTemplateFontStyle.italic,\n size: 16.0),\n secondaryTextStyle: NativeTemplateTextStyle(\n textColor: Colors.green,\n backgroundColor: Colors.black,\n style: NativeTemplateFontStyle.bold,\n size: 16.0),\n tertiaryTextStyle: NativeTemplateTextStyle(\n textColor: Colors.brown,\n backgroundColor: Colors.amber,\n style: NativeTemplateFontStyle.normal,\n size: 16.0)))\n ..load();\n }\n}\n```\n\nExample:\n```text\n// Small template\nfinal adContainer = ConstrainedBox(\n constraints: const BoxConstraints(\n minWidth: 320, // minimum recommended width\n minHeight: 90, // minimum recommended height\n maxWidth: 400,\n maxHeight: 200,\n ),\n child: AdWidget(ad: _nativeAd!),\n);\n\n// Medium template\nfinal adContainer = ConstrainedBox(\n constraints: const BoxConstraints(\n minWidth: 320, // minimum recommended width\n minHeight: 320, // minimum recommended height\n maxWidth: 400,\n maxHeight: 400,\n ),\n child: AdWidget(ad: _nativeAd!),\n);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.395Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":170,"estimatedTokens":1385}}616{"id":"doc-configure_access_to_the_google_cloud_search_api_-f3202551","source":"documentation","title":"Configure access to the Google Cloud Search API | Google for Developers","url":"https://developers.google.com/workspace/cloud-search/docs/guides/project-setup","text":"Example:\n```text\ncurl --request POST \\\n'https://cloudsearch.googleapis.com/v1:initializeCustomer' \\\n --header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \\\n --header 'Accept: application/json' \\\n --header 'Content-Type: application/json' \\\n --data '{}' \\\n --compressed\n```\n\nExample:\n```text\ncurl 'https://cloudsearch.googleapis.com/v1/operations/<var>operation_name</var>?key=[YOUR_API_KEY]' \\\n--header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \\\n--header 'Accept: application/json' \\\n--compressed\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.396Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":20,"estimatedTokens":131}}617{"id":"doc-create_notes_google_keep_google_for_developers-7f779e2f","source":"documentation","title":"Create notes | Google Keep | Google for Developers","url":"https://developers.google.com/workspace/keep/api/guides/create-notes","text":"Example:\n```text\n/**\n * Creates a new text note.\n *\n * @throws IOException\n * @return The newly created text note.\n */\nprivate Note createTextNote(String title, String textContent) throws IOException {\n Section noteBody = new Section().setText(new TextContent().setText(textContent));\n Note newNote = new Note().setTitle(title).setBody(noteBody);\n\n return keepService.notes().create(newNote).execute();\n}\n```\n\nExample:\n```text\n/**\n * Creates a new list note.\n *\n * @throws IOException\n * @return The newly created list note.\n */\nprivate Note createListNote() throws IOException {\n // Create a checked list item.\n ListItem checkedListItem =\n new ListItem().setText(new TextContent().setText(\"Send meeting invites\")).setChecked(true);\n\n // Create a list item with three children.\n ListItem uncheckedListItemWithChildren =\n new ListItem()\n .setText(new TextContent().setText(\"Prepare the presentation\"))\n .setChecked(false)\n .setChildListItems(\n Arrays.asList(\n new ListItem().setText(\n new TextContent().setText(\"Review metrics\")),\n new ListItem().setText(\n new TextContent().setText(\"Analyze sales projections\")),\n new ListItem().setText(\n new TextContent().setText(\"Share with leads\"))));\n\n // Create an unchecked list item.\n ListItem uncheckedListItem =\n new ListItem().setText(\n new TextContent().setText(\"Send summary email\")).setChecked(false);\n\n Note newNote =\n new Note()\n .setTitle(\"Marketing review meeting\")\n .setBody(\n new Section()\n .setList(\n new ListContent()\n .setListItems(\n Arrays.asList(\n checkedListItem,\n uncheckedListItemWithChildren,\n uncheckedListItem))));\n\n return keepService.notes().create(newNote).execute();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.397Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":66,"estimatedTokens":518}}618{"id":"doc-overview_google_forms_google_for_developers-1ff476d6","source":"documentation","title":"Overview | Google Forms | Google for Developers","url":"https://developers.google.com/workspace/forms/api/guides","text":"Example:\n```text\n{\n \"formId\": \"FORM_ID\",\n \"info\": {\n \"title\": \"Famous Black Women\",\n \"description\": \"Please complete this quiz based off of this week's readings for class.\",\n \"documentTitle\": \"Famous Black Women\"\n },\n \"settings\": {\n \"quizSettings\": {\n \"isQuiz\": true\n }\n },\n \"revisionId\": \"00000021\",\n \"responderUri\": \"https://docs.google.com/forms/d/e/1FAIpQLSd0iBLPh4suZoGW938EU1WIxzObQv_jXto0nT2U8HH2KsI5dg/viewform\",\n \"items\": [\n {\n \"itemId\": \"5d9f9786\",\n \"imageItem\": {\n \"image\": {\n \"contentUri\": \"DIRECT_URL\",\n \"properties\": {\n \"alignment\": \"LEFT\"\n }\n }\n }\n },\n {\n \"itemId\": \"72b30353\",\n \"title\": \"Which African American woman authored \\\"I Know Why the Caged Bird Sings\\\"?\",\n \"questionItem\": {\n \"question\": {\n \"questionId\": \"25405d4e\",\n \"required\": true,\n \"grading\": {\n \"pointValue\": 2,\n \"correctAnswers\": {\n \"answers\": [\n {\n \"value\": \"Maya Angelou\"\n }\n ]\n }\n },\n \"choiceQuestion\": {\n \"type\": \"RADIO\",\n \"options\": [\n {\n \"value\": \"Maya Angelou\"\n },\n {\n \"value\": \"bell hooks\"\n },\n {\n \"value\": \"Alice Walker\"\n },\n {\n \"value\": \"Roxane Gay\"\n }\n ]\n }\n }\n }\n },\n {\n \"itemId\": \"0a4859c8\",\n \"title\": \"Who was the first Dominican-American woman elected to state office?\",\n \"questionItem\": {\n \"question\": {\n \"questionId\": \"37fff47a\",\n \"grading\": {\n \"pointValue\": 2,\n \"correctAnswers\": {\n \"answers\": [\n {\n \"value\": \"Grace Diaz\"\n }\n ]\n }\n },\n \"choiceQuestion\": {\n \"type\": \"RADIO\",\n \"options\": [\n {\n \"value\": \"Rosa Clemente\"\n },\n {\n \"value\": \"Grace Diaz\"\n },\n {\n \"value\": \"Juana Matias\"\n },\n {\n \"value\": \"Sabrina Matos\"\n }\n ]\n }\n }\n }\n }\n ],\n \"publishSettings\" : {\n \"isPublished\": true,\n \"isAcceptingResponses\": true\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.398Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":110,"estimatedTokens":632}}619{"id":"doc-create_and_register_a_schema_cloud_search_google-dd85879b","source":"documentation","title":"Create and register a schema | Cloud Search | Google for Developers","url":"https://developers.google.com/workspace/cloud-search/docs/guides/schema-guide","text":"Example:\n```text\n{\n \"objectDefinitions\": [\n { \"name\": \"movie\" },\n { \"name\": \"person\" }\n ]\n}\n```\n\nExample:\n```text\n{\n \"objectDefinitions\": [{\n \"name\": \"movie\",\n \"propertyDefinitions\": [\n {\n \"name\": \"movieTitle\",\n \"isReturnable\": true,\n \"textPropertyOptions\": {\n \"retrievalImportance\": { \"importance\": \"HIGHEST\" },\n \"operatorOptions\": { \"operatorName\": \"title\" }\n },\n \"displayOptions\": { \"displayLabel\": \"Title\" }\n },\n {\n \"name\": \"releaseDate\",\n \"isReturnable\": true,\n \"isSortable\": true,\n \"datePropertyOptions\": {\n \"operatorOptions\": {\n \"operatorName\": \"released\",\n \"lessThanOperatorName\": \"releasedbefore\",\n \"greaterThanOperatorName\": \"releasedafter\"\n }\n }\n }\n ]\n }]\n}\n```\n\nExample:\n```text\n{\n \"name\": \"datasource/<data_source_id>/items/titanic\",\n \"metadata\": {\n \"title\": \"Titanic\",\n \"objectType\": \"movie\"\n },\n \"structuredData\": {\n \"object\": {\n \"properties\": [{\n \"name\": \"movieTitle\",\n \"textValues\": { \"values\": [\"Titanic\"] }\n }]\n }\n },\n \"itemType\": \"CONTENT_ITEM\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.399Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":63,"estimatedTokens":301}}620{"id":"doc-configure_the_drive_mcp_server_google_drive_goog-0befe085","source":"documentation","title":"Configure the Drive MCP server | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/configure-mcp-server","text":"Example:\n```text\ngcloud services enable drive.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable drivemcp.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\n{\n \"mcpServers\": {\n \"drive\": {\n \"serverUrl\": \"https://drivemcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n }\n }\n}\n```\n\nExample:\n```text\nagy\n```\n\nExample:\n```text\n/mcp\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.401Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":36,"estimatedTokens":121}}621{"id":"doc-create_a_content_connector_cloud_search_google_f-2fe35116","source":"documentation","title":"Create a content connector | Cloud Search | Google for Developers","url":"https://developers.google.com/workspace/cloud-search/docs/guides/content-connector","text":"Example:\n```text\njava -classpath myconnector.jar -Dconfig=MyConfig.properties MyConnector\n```\n\nExample:\n```text\n/**\n * This sample connector uses the Cloud Search SDK template class for a full\n * traversal connector.\n *\n * @param args program command line arguments\n * @throws InterruptedException thrown if an abort is issued during initialization\n */\npublic static void main(String[] args) throws InterruptedException {\n Repository repository = new SampleRepository();\n IndexingConnector connector = new FullTraversalConnector(repository);\n IndexingApplication application = new IndexingApplication.Builder(connector, args).build();\n application.start();\n}\n```\n\nExample:\n```text\n@Override\npublic void init(RepositoryContext context) {\n log.info(\"Initializing repository\");\n numberOfDocuments = Configuration.getInteger(\"sample.documentCount\", 10).get();\n}\n```\n\nExample:\n```text\nConfigValue<List<String>> repos = Configuration.getMultiValue(\n \"github.repos\",\n Collections.emptyList(),\n Configuration.STRING_PARSER);\n```\n\nExample:\n```text\n// Make the document publicly readable within the domain\nAcl acl = new Acl.Builder()\n .setReaders(Collections.singletonList(Acl.getCustomerPrincipal()))\n .build();\n```\n\nExample:\n```text\n// Url is required. Use google.com as a placeholder for this sample.\nString viewUrl = \"https://www.google.com\";\n\n// Version is required, set to current timestamp.\nbyte[] version = Longs.toByteArray(System.currentTimeMillis());\n\n// Using the SDK item builder class to create the document with appropriate attributes\n// (this can be expanded to include metadata fields etc.)\nItem item = IndexingItemBuilder.fromConfiguration(Integer.toString(id))\n .setItemType(IndexingItemBuilder.ItemType.CONTENT_ITEM)\n .setAcl(acl)\n .setSourceRepositoryUrl(IndexingItemBuilder.FieldOrValue.withValue(viewUrl))\n .setVersion(version)\n .build();\n```\n\nExample:\n```text\n// For this sample, content is just plain text\nString content = String.format(\"Hello world from sample doc %d\", id);\nByteArrayContent byteContent = ByteArrayContent.fromString(\"text/plain\", content);\n\n// Create the fully formed document\nRepositoryDoc doc = new RepositoryDoc.Builder()\n .setItem(item)\n .setContent(byteContent, IndexingService.ContentFormat.TEXT)\n .build();\n```\n\nExample:\n```text\nCheckpointCloseableIterable<ApiOperation> iterator =\n new CheckpointCloseableIterableImpl.Builder<>(allDocs).build();\n```\n\nExample:\n```text\n/**\n * This sample connector uses the Cloud Search SDK template class for a\n * list traversal connector.\n *\n * @param args program command line arguments\n * @throws InterruptedException thrown if an abort is issued during initialization\n */\npublic static void main(String[] args) throws InterruptedException {\n Repository repository = new SampleRepository();\n IndexingConnector connector = new ListingConnector(repository);\n IndexingApplication application = new IndexingApplication.Builder(connector, args).build();\n application.start();\n}\n```\n\nExample:\n```text\nPushItems.Builder allIds = new PushItems.Builder();\nfor (Map.Entry<Integer, Long> entry : this.documents.entrySet()) {\n String documentId = Integer.toString(entry.getKey());\n String hash = this.calculateMetadataHash(entry.getKey());\n PushItem item = new PushItem().setMetadataHash(hash);\n log.info(\"Pushing \" + documentId);\n allIds.addPushItem(documentId, item);\n}\n```\n\nExample:\n```text\nApiOperation pushOperation = allIds.build();\nCheckpointCloseableIterable<ApiOperation> iterator =\n new CheckpointCloseableIterableImpl.Builder<>(\n Collections.singletonList(pushOperation))\n .build();\nreturn iterator;\n```\n\nExample:\n```text\nString resourceName = item.getName();\nint documentId = Integer.parseInt(resourceName);\n\nif (!documents.containsKey(documentId)) {\n // Document no longer exists -- delete it\n log.info(() -> String.format(\"Deleting document %s\", item.getName()));\n return ApiOperations.deleteItem(resourceName);\n}\n```\n\nExample:\n```text\nString currentHash = this.calculateMetadataHash(documentId);\nif (this.canSkipIndexing(item, currentHash)) {\n // Document neither modified nor deleted, ack the push\n log.info(() -> String.format(\"Document %s not modified\", item.getName()));\n PushItem pushItem = new PushItem().setType(\"NOT_MODIFIED\");\n return new PushItems.Builder().addPushItem(resourceName, pushItem).build();\n}\n```\n\nExample:\n```text\n/**\n * Checks to see if an item is already up to date\n *\n * @param previousItem Polled item\n * @param currentHash Metadata hash of the current github object\n * @return PushItem operation\n */\nprivate boolean canSkipIndexing(Item previousItem, String currentHash) {\n if (previousItem.getStatus() == null || previousItem.getMetadata() == null) {\n return false;\n }\n String status = previousItem.getStatus().getCode();\n String previousHash = previousItem.getMetadata().getHash();\n return \"ACCEPTED\".equals(status)\n && previousHash != null\n && previousHash.equals(currentHash);\n}\n```\n\nExample:\n```text\n// Url is required. Use google.com as a placeholder for this sample.\nString viewUrl = \"https://www.google.com\";\n\n// Version is required, set to current timestamp.\nbyte[] version = Longs.toByteArray(System.currentTimeMillis());\n\n// Set metadata hash so queue can detect changes\nString metadataHash = this.calculateMetadataHash(documentId);\n\n// Using the SDK item builder class to create the document with\n// appropriate attributes. This can be expanded to include metadata\n// fields etc.\nItem item = IndexingItemBuilder.fromConfiguration(Integer.toString(documentId))\n .setItemType(IndexingItemBuilder.ItemType.CONTENT_ITEM)\n .setAcl(acl)\n .setSourceRepositoryUrl(IndexingItemBuilder.FieldOrValue.withValue(viewUrl))\n .setVersion(version)\n .setHash(metadataHash)\n .build();\n```\n\nExample:\n```text\n// For this sample, content is just plain text\nString content = String.format(\"Hello world from sample doc %d\", documentId);\nByteArrayContent byteContent = ByteArrayContent.fromString(\"text/plain\", content);\n\n// Create the fully formed document\nRepositoryDoc doc = new RepositoryDoc.Builder()\n .setItem(item)\n .setContent(byteContent, IndexingService.ContentFormat.TEXT)\n .build();\n```\n\nExample:\n```text\nPushItems.Builder allIds = new PushItems.Builder();\nPushItem item = new PushItem();\nallIds.addPushItem(\"root\", item);\n```\n\nExample:\n```text\nString resourceName = item.getName();\nif (documentExists(resourceName)) {\n return buildDocumentAndChildren(resourceName);\n}\n// Document doesn't exist, delete it\nlog.info(() -> String.format(\"Deleting document %s\", resourceName));\nreturn ApiOperations.deleteItem(resourceName);\n```\n\nExample:\n```text\n// Url is required. Use google.com as a placeholder for this sample.\nString viewUrl = \"https://www.google.com\";\n\n// Version is required, set to current timestamp.\nbyte[] version = Longs.toByteArray(System.currentTimeMillis());\n\n// Using the SDK item builder class to create the document with\n// appropriate attributes. This can be expanded to include metadata\n// fields etc.\nItem item = IndexingItemBuilder.fromConfiguration(documentId)\n .setItemType(IndexingItemBuilder.ItemType.CONTENT_ITEM)\n .setAcl(acl)\n .setSourceRepositoryUrl(IndexingItemBuilder.FieldOrValue.withValue(viewUrl))\n .setVersion(version)\n .build();\n```\n\nExample:\n```text\n// For this sample, content is just plain text\nString content = String.format(\"Hello world from sample doc %s\", documentId);\nByteArrayContent byteContent = ByteArrayContent.fromString(\"text/plain\", content);\n\nRepositoryDoc.Builder docBuilder = new RepositoryDoc.Builder()\n .setItem(item)\n .setContent(byteContent, IndexingService.ContentFormat.TEXT);\n```\n\nExample:\n```text\n// Queue the child nodes to visit after indexing this document\nSet<String> childIds = getChildItemNames(documentId);\nfor (String id : childIds) {\n log.info(() -> String.format(\"Pushing child node %s\", id));\n PushItem pushItem = new PushItem();\n docBuilder.addChildId(id, pushItem);\n}\n\nRepositoryDoc doc = docBuilder.build();\n```\n\nExample:\n```text\n{\n \"name\": \"datasource/<data_source_id>/items/titanic\",\n \"acl\": {\n \"readers\": [\n {\n \"gsuitePrincipal\": {\n \"gsuiteDomain\": true\n }\n }\n ]\n },\n \"metadata\": {\n \"title\": \"Titanic\",\n \"viewUrl\": \"http://www.imdb.com/title/tt2234155/\",\n \"objectType\": \"movie\"\n },\n \"structuredData\": {\n \"object\": {\n \"properties\": [\n {\n \"name\": \"movieTitle\",\n \"textValues\": { \"values\": [\"Titanic\"] }\n }\n ]\n }\n },\n \"content\": {\n \"inlineContent\": \"A seventeen-year-old aristocrat falls in love...\",\n \"contentFormat\": \"TEXT\"\n },\n \"version\": \"01\",\n \"itemType\": \"CONTENT_ITEM\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.404Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":22,"totalLines":302,"estimatedTokens":2183}}622{"id":"doc-google_docs_api_overview_google_for_developers-3ddfd3ac","source":"documentation","title":"Google Docs API overview | Google for Developers","url":"https://developers.google.com/workspace/docs/api/how-tos/overview","text":"Example:\n```text\nhttps://docs.google.com/document/d/DOCUMENT_ID/edit\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.406Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":22}}623{"id":"doc-extend_google_docs_apps_script_google_for_develo-5eff0c99","source":"documentation","title":"Extend Google Docs | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/docs","text":"Example:\n```text\nfunction createDoc() {\n var doc = DocumentApp.create('Sample Document');\n var documentTab = doc.getTab('t.0').asDocumentTab();\n var body = documentTab.getBody();\n var rowsData = [['Plants', 'Animals'], ['Ficus', 'Goat'], ['Basil', 'Cat'], ['Moss', 'Frog']];\n body.insertParagraph(0, doc.getName())\n .setHeading(DocumentApp.ParagraphHeading.HEADING1);\n table = body.appendTable(rowsData);\n table.getRow(0).editAsText().setBold(true);\n}\n```\n\nExample:\n```text\nfunction createPlaceholders() {\n var body = DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n body.appendParagraph('{name}');\n body.appendParagraph('{address}');\n body.appendParagraph('{city} {state} {zip}');\n}\n```\n\nExample:\n```text\nfunction searchAndReplace() {\n var body = DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n var client = {\n name: 'Joe Script-Guru',\n address: '100 Script Rd',\n city: 'Scriptville',\n state: 'GA',\n zip: 94043\n };\n\n body.replaceText('{name}', client.name);\n body.replaceText('{address}', client.address);\n body.replaceText('{city}', client.city);\n body.replaceText('{state}', client.state);\n body.replaceText('{zip}', client.zip);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.407Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":45,"estimatedTokens":311}}624{"id":"doc-drive_service_apps_script_google_for_developers-54486824","source":"documentation","title":"Drive Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/drive","text":"Example:\n```text\n// Logs the name of every file in the user's Drive.\nvar files = DriveApp.getFiles();\nwhile (files.hasNext()) {\n var file = files.next();\n console.log(file.getName());\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.409Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":11,"estimatedTokens":52}}625{"id":"doc-build_a_google_chat_app_with_an_agent2ui_agent_g-6d89d1cc","source":"documentation","title":"Build a Google Chat app with an Agent2UI agent | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/quickstart-a2ui-agent","text":"Example:\n```text\ngcloud iam service-accounts create SERVICE_ACCOUNT_NAME \\\n --display-name=\"SERVICE_ACCOUNT_NAME\"SERVICE_ACCOUNT_NAMESERVICE_ACCOUNT_NAME\n```\n\nExample:\n```text\ngcloud auth application-default logingcloud config set project PROJECT_IDgcloud auth application-default set-quota-project PROJECT_ID\n```\n\nExample:\n```text\nunzip add-ons-samples-main.zipcd add-ons-samples/apps-script/chat/a2ui-ai-agent/a2ui\n```\n\nExample:\n```text\ngcloud storage buckets create gs://CLOUD_STORAGE_BUCKET_NAME --project=PROJECT_ID --location=PROJECT_LOCATION\n```\n\nExample:\n```text\nexport GOOGLE_GENAI_USE_VERTEXAI=trueexport GOOGLE_CLOUD_PROJECT=PROJECT_IDexport GOOGLE_CLOUD_LOCATION=PROJECT_LOCATIONexport GOOGLE_CLOUD_STORAGE_BUCKET=CLOUD_STORAGE_BUCKET_NAME\n```\n\nExample:\n```text\npython3 -m venv myenvsource myenv/bin/activatepoetry install --with deploymentpython3 deployment/deploy.py --create\n```\n\nExample:\n```text\npython3 deployment/deploy.py --list\n```\n\nExample:\n```devsite-click-to-copy\n# Copyright 2026 Google LLC\n#\n# 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# http://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.\n\n\"\"\"A2UI agent.\"\"\"\n\nfrom google.adk.agents import LlmAgent\nfrom google.adk.tools.tool_context import ToolContext\nimport json\n\n# The schema for any A2UI message. This never changes.\nfrom .a2ui_schema import A2UI_SCHEMA\n\ndef get_user_profile(tool_context: ToolContext) -> str:\n \"\"\"Call this tool to get the current user profile.\"\"\"\n return json.dumps({\n \"name\": \"Pierrick Voulet\",\n # \"title\": \"DevRel Engineer @ Google Workspace | Gen AI & AI Agents & Agentic AI | Automation & Digital Transformation\",\n \"imageUrl\": \"https://io.google/2024/speakers/3ea87822-3160-4d54-89dd-57e185085f79_240.webp\",\n \"linkedin\": \"https://www.linkedin.com/in/pierrick-voulet/\"\n })\n\nAGENT_INSTRUCTION=\"\"\"\nYou are a user profile assistant. Your goal is to help users get their profile information using a rich UI.\n\nTo achieve this, you MUST follow these steps to answer user requests:\n\n1. You MUST call the `get_user_profile` tool and extract all the user profile information from the result.\n2. You MUST generate a final a2ui UI JSON based on the user profile information extracted in the previous step.\"\"\"\n\nA2UI_AND_AGENT_INSTRUCTION = AGENT_INSTRUCTION + f\"\"\"\n\nTo generate a valid a2ui UI JSON, you MUST follow these rules:\n1. Your response MUST be in two parts, separated by the delimiter: `---a2ui_JSON---`.\n2. The first part is your conversational text response.\n3. The second part is a single, raw JSON object which is a list of A2UI messages.\n4. The JSON part MUST validate against the A2UI JSON SCHEMA provided below.\n\nTo represent the user profile, you MUST use the following A2UI message types:\n1. Buttons MUST be used to represent links (e.g., LinkedIn profile link).\n2. Image MUST be used to represent the user's profile picture.\n\n---BEGIN A2UI JSON SCHEMA---\n{A2UI_SCHEMA}\n---END A2UI JSON SCHEMA---\n\"\"\"\n\nroot_agent = LlmAgent(\n name=\"user_profile\",\n model=\"gemini-2.5-flash\",\n instruction=A2UI_AND_AGENT_INSTRUCTION,\n description=\"An agent that returns the current user profile.\",\n tools=[get_user_profile]\n)\n```\n\nExample:\n```text\npython3 deployment/deploy.py --update --resource_id=RESOURCE_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.414Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":110,"estimatedTokens":932}}626{"id":"doc-configure_the_chat_mcp_server_google_chat_google-a5c52abb","source":"documentation","title":"Configure the Chat MCP server | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/api/guides/configure-mcp-server","text":"Example:\n```text\ngcloud services enable chat.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable chatmcp.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\n{\n \"mcpServers\": {\n \"chat\": {\n \"serverUrl\": \"https://chatmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n }\n }\n}\n```\n\nExample:\n```text\nagy\n```\n\nExample:\n```text\n/mcp\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.414Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":36,"estimatedTokens":120}}627{"id":"doc-build_a_google_chat_app_with_a_gemini_enterprise-d004058b","source":"documentation","title":"Build a Google Chat app with a Gemini Enterprise AI agent | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/quickstart-ge-agent","text":"Example:\n```text\ngcloud iam service-accounts create SERVICE_ACCOUNT_NAME \\\n --display-name=\"SERVICE_ACCOUNT_NAME\"SERVICE_ACCOUNT_NAMESERVICE_ACCOUNT_NAME\n```\n\nExample:\n```text\nprojects/PROJECT_ID/locations/APP_LOCATION/collections/default_collection/engines/APP_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.416Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":12,"estimatedTokens":71}}628{"id":"doc-forms_service_apps_script_google_for_developers-2481d63c","source":"documentation","title":"Forms Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/forms","text":"Example:\n```text\n// Create a new unpublished form, then add a checkbox question, a multiple choice question,\n// a page break, then a date question and a grid of questions, then publish the form and share\n// with responders.\nvar form = FormApp.create('New Form', /* isPublished= */ false);\nvar item = form.addCheckboxItem();\nitem.setTitle('What condiments would you like on your hot dog?');\nitem.setChoices([\n item.createChoice('Ketchup'),\n item.createChoice('Mustard'),\n item.createChoice('Relish')\n ]);\nform.addMultipleChoiceItem()\n .setTitle('Do you prefer cats or dogs?')\n .setChoiceValues(['Cats','Dogs'])\n .showOtherOption(true);\nform.addPageBreakItem()\n .setTitle('Getting to know you');\nform.addDateItem()\n .setTitle('When were you born?');\nform.addGridItem()\n .setTitle('Rate your interests')\n .setRows(['Cars', 'Computers', 'Celebrities'])\n .setColumns(['Boring', 'So-so', 'Interesting']);\n\nform.setPublished(true);\nform.addPublishedReaders(['user@example.com', 'group@example.com']);\n\nLogger.log('Published URL: ' + form.getPublishedUrl());\nLogger.log('Editor URL: ' + form.getEditUrl());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.421Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":34,"estimatedTokens":291}}629{"id":"doc-build_a_dialogflow_cx_google_chat_app_that_under-f8187305","source":"documentation","title":"Build a Dialogflow CX Google Chat app that understands and responds with natural language | Google for Developers","url":"https://developers.google.com/workspace/chat/build-dialogflow-chat-app-natural-language","text":"Example:\n```text\n{\n 'cardsV2': [{\n 'cardId': 'createCardMessage',\n 'card': {\n 'header': {\n 'title': 'A card message!',\n 'subtitle': 'Sent from Dialogflow',\n 'imageUrl': 'https://developers.google.com/chat/images/chat-product-icon.png',\n 'imageType': 'CIRCLE'\n },\n 'sections': [\n {\n 'widgets': [\n {\n 'buttonList': {\n 'buttons': [\n {\n 'text': 'Read the docs!',\n 'onClick': {\n 'openLink': {\n 'url': 'https://developers.google.com/workspace/chat'\n }\n }\n }\n ]\n }\n }\n ]\n }\n ]\n }\n }]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.425Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":38,"estimatedTokens":206}}630{"id":"doc-build_a_google_chat_app_with_an_agent2agent_agen-6c4bff7e","source":"documentation","title":"Build a Google Chat app with an Agent2Agent agent | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/quickstart-a2a-agent","text":"Example:\n```text\ngcloud iam service-accounts create SERVICE_ACCOUNT_NAME \\\n --display-name=\"SERVICE_ACCOUNT_NAME\"SERVICE_ACCOUNT_NAMESERVICE_ACCOUNT_NAME\n```\n\nExample:\n```text\ngcloud auth application-default logingcloud config set project PROJECT_IDgcloud auth application-default set-quota-project PROJECT_ID\n```\n\nExample:\n```text\nunzip adk-samples-main.zipcd adk-samples-main/python/agents/llm-auditor\n```\n\nExample:\n```text\n[project]\nname = \"llm-auditor\"\nversion = \"0.1.0\"\ndescription = \"The LLM Auditor evaluates LLM-generated answers, verifies actual accuracy using the web, and refines the response to ensure alignment with real-world knowledge.\"\nauthors = [\n { name = \"Chun-Sung Ferng\", email = \"csferng@google.com\" },\n { name = \"Cyrus Rashtchian\", email = \"cyroid@google.com\" },\n { name = \"Da-Cheng Juan\", email = \"dacheng@google.com\" },\n { name = \"Ivan Kuznetsov\", email = \"ivanku@google.com\" },\n]\nlicense = \"Apache License 2.0\"\nreadme = \"README.md\"\n\n[tool.poetry.dependencies]\npython = \"^3.10\"\ngoogle-adk = \"^1.0.0\"\ngoogle-cloud-aiplatform = { extras = [\n \"adk\",\n \"agent-engines\",\n], version = \"^1.93.0\" }\ngoogle-genai = \"^1.9.0\"\npydantic = \"^2.10.6\"\npython-dotenv = \"^1.0.1\"\n\n[tool.poetry.group.dev]\noptional = true\n\n[tool.poetry.group.dev.dependencies]\ngoogle-adk = { version = \"^1.0.0\", extras = [\"eval\"] }\npytest = \"^8.3.5\"\npytest-asyncio = \"^0.26.0\"\n\n[tool.poetry.group.deployment]\noptional = true\n\n[tool.poetry.group.deployment.dependencies]\nabsl-py = \"^2.2.1\"\ngoogle-adk = \"^1.0.0\"\na2a-sdk = \"^0.3.0\"\n\n[build-system]\nrequires = [\"poetry-core>=2.0.0,<3.0.0\"]\nbuild-backend = \"poetry.core.masonry.api\"\n```\n\nExample:\n```devsite-click-to-copy\n# Copyright 2025 Google LLC\n#\n# 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# http://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.\n\n\"\"\"Deployment script for LLM Auditor.\"\"\"\n\nimport os\n\nfrom absl import app\nfrom absl import flags\nfrom dotenv import load_dotenv\nfrom llm_auditor.agent import root_agent\nimport vertexai\nfrom vertexai import agent_engines\n\n# A2A wrapping\nfrom a2a.types import AgentSkill\nfrom google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor\nfrom google.adk.runners import InMemoryRunner\nfrom vertexai.preview.reasoning_engines.templates.a2a import create_agent_card\nfrom vertexai.preview.reasoning_engines import A2aAgent\n\nFLAGS = flags.FLAGS\nflags.DEFINE_string(\"project_id\", None, \"GCP project ID.\")\nflags.DEFINE_string(\"location\", None, \"GCP location.\")\nflags.DEFINE_string(\"bucket\", None, \"GCP bucket.\")\nflags.DEFINE_string(\"resource_id\", None, \"ReasoningEngine resource ID.\")\n\nflags.DEFINE_bool(\"list\", False, \"List all agents.\")\nflags.DEFINE_bool(\"create\", False, \"Creates a new agent.\")\nflags.DEFINE_bool(\"delete\", False, \"Deletes an existing agent.\")\nflags.mark_bool_flags_as_mutual_exclusive([\"create\", \"delete\"])\n\n\ndef create() -> None:\n \"\"\"Creates an agent engine for LLM Auditor.\"\"\"\n agent_card = create_agent_card(\n agent_name=root_agent.name,\n description=root_agent.description,\n skills=[AgentSkill(\n id='audit_llm_output',\n name='Audit LLM Output',\n description='Critiques and revises outputs from large language models.',\n tags=['LLM', 'Audit', 'Revision'],\n examples=[\n 'The earth is flat.',\n 'The capital of France is Berlin.',\n 'The last winner of the Super Bowl was the New England Patriots in 2020.',\n ],\n )]\n )\n a2a_agent = A2aAgent(\n agent_card=agent_card,\n agent_executor_builder=lambda: A2aAgentExecutor(\n runner=InMemoryRunner(\n app_name=root_agent.name,\n agent=root_agent,\n )\n )\n )\n a2a_agent.set_up()\n\n remote_agent = agent_engines.create(\n a2a_agent,\n display_name=root_agent.name,\n requirements=[\n \"google-adk (>=0.0.2)\",\n \"google-cloud-aiplatform[agent_engines] (>=1.88.0,<2.0.0)\",\n \"google-genai (>=1.5.0,<2.0.0)\",\n \"pydantic (>=2.10.6,<3.0.0)\",\n \"absl-py (>=2.2.1,<3.0.0)\",\n \"a2a-sdk>=0.3.22\",\n \"uvicorn\",\n ],\n # In-memory runner\n max_instances=1,\n env_vars ={\n \"NUM_WORKERS\": \"1\"\n },\n extra_packages=[\"./llm_auditor\"],\n )\n print(f\"Created remote agent: {remote_agent.resource_name}\")\n\n\ndef delete(resource_id: str) -> None:\n remote_agent = agent_engines.get(resource_id)\n remote_agent.delete(force=True)\n print(f\"Deleted remote agent: {resource_id}\")\n\n\ndef list_agents() -> None:\n remote_agents = agent_engines.list()\n TEMPLATE = '''\n{agent.name} (\"{agent.display_name}\")\n- Create time: {agent.create_time}\n- Update time: {agent.update_time}\n'''\n remote_agents_string = '\\n'.join(TEMPLATE.format(agent=agent) for agent in remote_agents)\n print(f\"All remote agents:\\n{remote_agents_string}\")\n\ndef main(argv: list[str]) -> None:\n del argv # unused\n load_dotenv()\n\n project_id = (\n FLAGS.project_id\n if FLAGS.project_id\n else os.getenv(\"GOOGLE_CLOUD_PROJECT\")\n )\n location = (\n FLAGS.location if FLAGS.location else os.getenv(\"GOOGLE_CLOUD_LOCATION\")\n )\n bucket = (\n FLAGS.bucket if FLAGS.bucket\n else os.getenv(\"GOOGLE_CLOUD_STORAGE_BUCKET\")\n )\n\n print(f\"PROJECT: {project_id}\")\n print(f\"LOCATION: {location}\")\n print(f\"BUCKET: {bucket}\")\n\n if not project_id:\n print(\"Missing required environment variable: GOOGLE_CLOUD_PROJECT\")\n return\n elif not location:\n print(\"Missing required environment variable: GOOGLE_CLOUD_LOCATION\")\n return\n elif not bucket:\n print(\n \"Missing required environment variable: GOOGLE_CLOUD_STORAGE_BUCKET\"\n )\n return\n\n vertexai.init(\n project=project_id,\n location=location,\n staging_bucket=f\"gs://{bucket}\",\n )\n\n if FLAGS.list:\n list_agents()\n elif FLAGS.create:\n create()\n elif FLAGS.delete:\n if not FLAGS.resource_id:\n print(\"resource_id is required for delete\")\n return\n delete(FLAGS.resource_id)\n else:\n print(\"Unknown command\")\n\n\nif __name__ == \"__main__\":\n app.run(main)\n```\n\nExample:\n```text\ngcloud storage buckets create gs://CLOUD_STORAGE_BUCKET_NAME --project=PROJECT_ID --location=PROJECT_LOCATION\n```\n\nExample:\n```text\nexport GOOGLE_GENAI_USE_VERTEXAI=trueexport GOOGLE_CLOUD_PROJECT=PROJECT_IDexport GOOGLE_CLOUD_LOCATION=PROJECT_LOCATIONexport GOOGLE_CLOUD_STORAGE_BUCKET=CLOUD_STORAGE_BUCKET_NAME\n```\n\nExample:\n```text\npython3 -m venv myenvsource myenv/bin/activatepoetry install --with deploymentpython3 deployment/deploy.py --create\n```\n\nExample:\n```text\npython3 deployment/deploy.py --list\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.427Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":252,"estimatedTokens":1830}}631{"id":"doc-build_a_google_chat_app_with_an_adk_ai_agent_goo-62eae232","source":"documentation","title":"Build a Google Chat app with an ADK AI agent | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/quickstart-adk-agent","text":"Example:\n```text\ngcloud iam service-accounts create SERVICE_ACCOUNT_NAME \\\n --display-name=\"SERVICE_ACCOUNT_NAME\"SERVICE_ACCOUNT_NAMESERVICE_ACCOUNT_NAME\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.428Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":7,"estimatedTokens":43}}632{"id":"doc-build_a_google_chat_app_as_a_webhook_google_for_-c9ac36cf","source":"documentation","title":"Build a Google Chat app as a webhook | Google for Developers","url":"https://developers.google.com/workspace/chat/quickstart/webhooks","text":"Example:\n```text\npip install httplib2\n```\n\nExample:\n```text\n/**\n * Sends asynchronous message to Google Chat\n * @return {Object} response\n */\nasync function webhook() {\n const url = \"https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN\"\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\"Content-Type\": \"application/json; charset=UTF-8\"},\n body: JSON.stringify({\n text: \"Hello from a Node script!\"\n })\n });\n return await res.json();\n}\n\nwebhook().then(res => console.log(res));\n```\n\nExample:\n```text\nfrom json import dumps\nfrom httplib2 import Http\n\n# Copy the webhook URL from the Chat space where the webhook is registered.\n# The values for SPACE_ID, KEY, and TOKEN are set by Chat, and are included\n# when you copy the webhook URL.\n\ndef main():\n \"\"\"Google Chat incoming webhook quickstart.\"\"\"\n url = \"https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN\"\n app_message = {\n \"text\": \"Hello from a Python script!\"\n }\n message_headers = {\"Content-Type\": \"application/json; charset=UTF-8\"}\n http_obj = Http()\n response = http_obj.request(\n uri=url,\n method=\"POST\",\n headers=message_headers,\n body=dumps(app_message),\n )\n print(response)\n\n\nif __name__ == \"__main__\":\n main()\n```\n\nExample:\n```text\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n\n <groupId>com.google.chat.webhook</groupId>\n <artifactId>webhook-app</artifactId>\n <version>0.1.0</version>\n <name>webhook-app</name>\n\n <properties>\n <maven.compiler.target>11</maven.compiler.target>\n <maven.compiler.source>11</maven.compiler.source>\n </properties>\n\n <dependencies>\n <dependency>\n <groupId>com.google.code.gson</groupId>\n <artifactId>gson</artifactId>\n <version>2.9.1</version>\n </dependency>\n </dependencies>\n\n <build>\n <pluginManagement>\n <plugins>\n <plugin>\n <artifactId>maven-compiler-plugin</artifactId>\n <version>3.8.0</version>\n </plugin>\n </plugins>\n </pluginManagement>\n </build>\n</project>\n```\n\nExample:\n```text\nimport com.google.gson.Gson;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\nimport java.util.Map;\nimport java.net.URI;\n\npublic class App {\n private static final String URL = \"https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN\";\n private static final Gson gson = new Gson();\n private static final HttpClient client = HttpClient.newHttpClient();\n\n public static void main(String[] args) throws Exception {\n String message = gson.toJson(Map.of(\n \"text\", \"Hello from Java!\"\n ));\n\n HttpRequest request = HttpRequest.newBuilder(URI.create(URL))\n .header(\"accept\", \"application/json; charset=UTF-8\")\n .POST(HttpRequest.BodyPublishers.ofString(message)).build();\n\n HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\n\n System.out.println(response.body());\n }\n}\n```\n\nExample:\n```text\nfunction webhook() {\n const url = \"https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN\"\n const options = {\n \"method\": \"post\",\n \"headers\": {\"Content-Type\": \"application/json; charset=UTF-8\"},\n \"payload\": JSON.stringify({\n \"text\": \"Hello from Apps Script!\"\n })\n };\n const response = UrlFetchApp.fetch(url, options);\n console.log(response);\n}\n```\n\nExample:\n```text\nnode index.js\n```\n\nExample:\n```text\npython3 quickstart.py\n```\n\nExample:\n```text\nmvn compile exec:java -Dexec.mainClass=App\n```\n\nExample:\n```text\n/**\n * Sends asynchronous message to Google Chat\n * @return {Object} response\n */\nasync function webhook() {\n const url = \"https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD\"\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\"Content-Type\": \"application/json; charset=UTF-8\"},\n body: JSON.stringify({\n text: \"Hello from a Node script!\",\n thread: {\n threadKey: \"THREAD_KEY_VALUE\"\n }\n })\n });\n return await res.json();\n}\n\nwebhook().then(res => console.log(res));\n```\n\nExample:\n```text\nfrom json import dumps\nfrom httplib2 import Http\n\n# Copy the webhook URL from the Chat space where the webhook is registered.\n# The values for SPACE_ID, KEY, and TOKEN are set by Chat, and are included\n# when you copy the webhook URL.\n#\n# Then, append messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD to the\n# webhook URL.\n\n\ndef main():\n \"\"\"Google Chat incoming webhook that starts or replies to a message thread.\"\"\"\n url = \"https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD\"\n app_message = {\n \"text\": \"Hello from a Python script!\",\n # To start a thread, set threadKey to an arbitratry string.\n # To reply to a thread, specify that thread's threadKey value.\n \"thread\": {\n \"threadKey\": \"THREAD_KEY_VALUE\"\n },\n }\n message_headers = {\"Content-Type\": \"application/json; charset=UTF-8\"}\n http_obj = Http()\n response = http_obj.request(\n uri=url,\n method=\"POST\",\n headers=message_headers,\n body=dumps(app_message),\n )\n print(response)\n\n\nif __name__ == \"__main__\":\n main()\n```\n\nExample:\n```text\nimport com.google.gson.Gson;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\nimport java.util.Map;\nimport java.net.URI;\n\npublic class App {\n private static final String URL = \"https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD\";\n private static final Gson gson = new Gson();\n private static final HttpClient client = HttpClient.newHttpClient();\n\n public static void main(String[] args) throws Exception {\n String message = gson.toJson(Map.of(\n \"text\", \"Hello from Java!\",\n \"thread\", Map.of(\n \"threadKey\", \"THREAD_KEY_VALUE\"\n )\n ));\n\n HttpRequest request = HttpRequest.newBuilder(URI.create(URL))\n .header(\"accept\", \"application/json; charset=UTF-8\")\n .POST(HttpRequest.BodyPublishers.ofString(message)).build();\n\n HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\n\n System.out.println(response.body());\n }\n}\n```\n\nExample:\n```text\nfunction webhook() {\n const url = \"https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD\"\n const options = {\n \"method\": \"post\",\n \"headers\": {\"Content-Type\": \"application/json; charset=UTF-8\"},\n \"payload\": JSON.stringify({\n \"text\": \"Hello from Apps Script!\",\n \"thread\": {\n \"threadKey\": \"THREAD_KEY_VALUE\"\n }\n })\n };\n const response = UrlFetchApp.fetch(url, options);\n console.log(response);\n}\n```\n\nExample:\n```text\n{\n \"code\": 503,\n \"message\": \"The service is currently unavailable.\",\n \"status\": \"UNAVAILABLE\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.433Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":279,"estimatedTokens":1825}}633{"id":"doc-import_data_to_google_chat_google_for_developers-fc7f1e10","source":"documentation","title":"Import data to Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/import-data","text":"Example:\n```text\nfunction createSpaceInImportMode() {\n const space = Chat.Spaces.create({\n spaceType: 'SPACE',\n displayName: 'DISPLAY_NAME',\n importMode: true,\n createTime: (new Date('January 1, 2000')).toJSON()\n });\n console.log(space.name);\n}\n```\n\nExample:\n```text\n\"\"\"Create a space in import mode.\"\"\"\n\nimport datetime\n\nfrom google.oauth2 import service_account\nfrom googleapiclient.discovery import build\n\n# Specify required scopes.\nSCOPES = [\n 'https://www.googleapis.com/auth/chat.import',\n]\n\nCREDENTIALS = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n .with_subject('EMAIL')\n)\n\n# Build a service endpoint for Chat API.\nservice = build('chat', 'v1', credentials=CREDENTIALS)\n\nresult = (\n service.spaces()\n .create(\n body={\n 'spaceType': 'SPACE',\n 'displayName': 'DISPLAY_NAME',\n 'importMode': True,\n 'createTime': f'{datetime.datetime(2000, 1, 1).isoformat()}Z',\n }\n )\n .execute()\n)\n\nprint(result)\n```\n\nExample:\n```text\n\"\"\"Create a message in import mode space.\"\"\"\n\nimport datetime\n\nfrom google.oauth2 import service_account\nfrom googleapiclient.discovery import build\n\n# Specify required scopes.\nSCOPES = [\n 'https://www.googleapis.com/auth/chat.import',\n]\n\nCREDENTIALS = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n .with_subject('EMAIL')\n)\n\n# Build a service endpoint for Chat API.\nservice = build('chat', 'v1', credentials=CREDENTIALS)\n\nNAME = 'spaces/SPACE_NAME'\nresult = (\n service.spaces()\n .messages()\n .create(\n parent=NAME,\n body={\n 'text': 'Hello, world!',\n 'createTime': f'{datetime.datetime(2000, 1, 2).isoformat()}Z',\n },\n )\n .execute()\n)\n\nprint(result)\n```\n\nExample:\n```text\n\"\"\"Create a historical membership in import mode space.\"\"\"\n\nimport datetime\n\nfrom google.oauth2 import service_account\nfrom googleapiclient.discovery import build\n\n# Specify required scopes.\nSCOPES = [\n 'https://www.googleapis.com/auth/chat.import',\n]\n\nCREDENTIALS = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n .with_subject('EMAIL')\n)\n\n# Build a service endpoint for Chat API.\nservice = build('chat', 'v1', credentials=CREDENTIALS)\n\nNAME = 'spaces/SPACE_NAME'\nUSER = 'users/USER_ID'\nresult = (\n service.spaces()\n .members()\n .create(\n parent=NAME,\n body={\n 'createTime': f'{datetime.datetime(2000, 1, 3).isoformat()}Z',\n 'deleteTime': f'{datetime.datetime(2000, 1, 4).isoformat()}Z',\n 'member': {'name': USER, 'type': 'HUMAN'},\n },\n )\n .execute()\n)\n\nprint(result)\n```\n\nExample:\n```text\n\"\"\"Complete import.\"\"\"\n\nfrom google.oauth2 import service_account\nfrom googleapiclient.discovery import build\n\n# Specify required scopes.\nSCOPES = [\n 'https://www.googleapis.com/auth/chat.import',\n]\n\nCREDENTIALS = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n .with_subject('EMAIL')\n)\n\n# Build a service endpoint for Chat API.\nservice = build('chat', 'v1', credentials=CREDENTIALS)\n\nNAME = 'spaces/SPACE_NAME'\nresult = service.spaces().completeImport(name=NAME).execute()\n\nprint(result)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.434Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":162,"estimatedTokens":844}}634{"id":"doc-preview_links_google_chat_google_for_developers-59aa8f0a","source":"documentation","title":"Preview links | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/preview-links","text":"Example:\n```text\nmessage: {\n matchedUrl: {\n url: \"https://support.example.com/cases/case123\"\n },\n ... // other message attributes redacted\n}\n```\n\nExample:\n```text\n// Reply with a text message for URLs of the subdomain \"text\"\nif (event.message.matchedUrl.url.includes(\"text.example.com\")) {\n return {\n text: 'event.message.matchedUrl.url: ' + event.message.matchedUrl.url\n };\n}\n```\n\nExample:\n```text\n# Reply with a text message for URLs of the subdomain \"text\"\nif 'text.example.com' in event.get('message').get('matchedUrl').get('url'):\n return {\n 'text': 'event.message.matchedUrl.url: ' +\n event.get('message').get('matchedUrl').get('url')\n }\n```\n\nExample:\n```text\n// Reply with a text message for URLs of the subdomain \"text\"\nif (event.at(\"/message/matchedUrl/url\").asText().contains(\"text.example.com\")) {\n return new Message().setText(\"event.message.matchedUrl.url: \" +\n event.at(\"/message/matchedUrl/url\").asText());\n}\n```\n\nExample:\n```text\n// Attach a card to the message for URLs of the subdomain \"support\"\nif (event.message.matchedUrl.url.includes(\"support.example.com\")) {\n // A hard-coded card is used in this example. In a real-life scenario,\n // the case information would be fetched and used to build the card.\n return {\n actionResponse: { type: 'UPDATE_USER_MESSAGE_CARDS' },\n cardsV2: [{\n cardId: 'attachCard',\n card: {\n header: {\n title: 'Example Customer Service Case',\n subtitle: 'Case basics',\n },\n sections: [{ widgets: [\n { decoratedText: { topLabel: 'Case ID', text: 'case123'}},\n { decoratedText: { topLabel: 'Assignee', text: 'Charlie'}},\n { decoratedText: { topLabel: 'Status', text: 'Open'}},\n { decoratedText: { topLabel: 'Subject', text: 'It won\\'t turn on...' }},\n { buttonList: { buttons: [{\n text: 'OPEN CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123'\n }},\n }, {\n text: 'RESOLVE CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123?resolved=y',\n }},\n }, {\n text: 'ASSIGN TO ME',\n onClick: { action: { function: 'assign'}}\n }]}}\n ]}]\n }\n }]\n };\n}\n```\n\nExample:\n```text\n# Attach a card to the message for URLs of the subdomain \"support\"\nif 'support.example.com' in event.get('message').get('matchedUrl').get('url'):\n # A hard-coded card is used in this example. In a real-life scenario,\n # the case information would be fetched and used to build the card.\n return {\n 'actionResponse': { 'type': 'UPDATE_USER_MESSAGE_CARDS' },\n 'cardsV2': [{\n 'cardId': 'attachCard',\n 'card': {\n 'header': {\n 'title': 'Example Customer Service Case',\n 'subtitle': 'Case basics',\n },\n 'sections': [{ 'widgets': [\n { 'decoratedText': { 'topLabel': 'Case ID', 'text': 'case123'}},\n { 'decoratedText': { 'topLabel': 'Assignee', 'text': 'Charlie'}},\n { 'decoratedText': { 'topLabel': 'Status', 'text': 'Open'}},\n { 'decoratedText': { 'topLabel': 'Subject', 'text': 'It won\\'t turn on...' }},\n { 'buttonList': { 'buttons': [{\n 'text': 'OPEN CASE',\n 'onClick': { 'openLink': {\n 'url': 'https://support.example.com/orders/case123'\n }},\n }, {\n 'text': 'RESOLVE CASE',\n 'onClick': { 'openLink': {\n 'url': 'https://support.example.com/orders/case123?resolved=y',\n }},\n }, {\n 'text': 'ASSIGN TO ME',\n 'onClick': { 'action': { 'function': 'assign'}}\n }]}}\n ]}]\n }\n }]\n }\n```\n\nExample:\n```text\n// Attach a card to the message for URLs of the subdomain \"support\"\nif (event.at(\"/message/matchedUrl/url\").asText().contains(\"support.example.com\")) {\n // A hard-coded card is used in this example. In a real-life scenario,\n // the case information would be fetched and used to build the card.\n return new Message()\n .setActionResponse(new ActionResponse()\n .setType(\"UPDATE_USER_MESSAGE_CARDS\"))\n .setCardsV2(List.of(new CardWithId()\n .setCardId(\"attachCard\")\n .setCard(new GoogleAppsCardV1Card()\n .setHeader(new GoogleAppsCardV1CardHeader()\n .setTitle(\"Example Customer Service Case\")\n .setSubtitle(\"Case basics\"))\n .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Case ID\")\n .setText(\"case123\")),\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Assignee\")\n .setText(\"Charlie\")),\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Status\")\n .setText(\"Open\")),\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Subject\")\n .setText(\"It won't turn on...\")),\n new GoogleAppsCardV1Widget()\n .setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(\n new GoogleAppsCardV1Button()\n .setText(\"OPEN CASE\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setOpenLink(new GoogleAppsCardV1OpenLink()\n .setUrl(\"https://support.example.com/orders/case123\"))),\n new GoogleAppsCardV1Button()\n .setText(\"RESOLVE CASE\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setOpenLink(new GoogleAppsCardV1OpenLink()\n .setUrl(\"https://support.example.com/orders/case123?resolved=y\"))),\n new GoogleAppsCardV1Button()\n .setText(\"ASSIGN TO ME\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setAction(new GoogleAppsCardV1Action().setFunction(\"assign\")))))))))))));\n}\n```\n\nExample:\n```text\n/**\n * Updates a card that was attached to a message with a previewed link.\n *\n * @param {Object} event The event object from Chat.\n *\n * @return {Object} Response from the Chat app. Either a new card attached to\n * the message with the previewed link, or an update to an existing card.\n */\nfunction onCardClick(event) {\n // To respond to the correct button, checks the button's actionMethodName.\n if (event.action.actionMethodName === 'assign') {\n // A hard-coded card is used in this example. In a real-life scenario,\n // an actual assign action would be performed before building the card.\n\n // Checks whether the message event originated from a human or a Chat app\n // and sets actionResponse.type to \"UPDATE_USER_MESSAGE_CARDS if human or\n // \"UPDATE_MESSAGE\" if Chat app.\n const actionResponseType = event.message.sender.type === 'HUMAN' ?\n 'UPDATE_USER_MESSAGE_CARDS' :\n 'UPDATE_MESSAGE';\n\n // Returns the updated card that displays \"You\" for the assignee\n // and that disables the button.\n return {\n actionResponse: { type: actionResponseType },\n cardsV2: [{\n cardId: 'attachCard',\n card: {\n header: {\n title: 'Example Customer Service Case',\n subtitle: 'Case basics',\n },\n sections: [{ widgets: [\n { decoratedText: { topLabel: 'Case ID', text: 'case123'}},\n // The assignee is now \"You\"\n { decoratedText: { topLabel: 'Assignee', text: 'You'}},\n { decoratedText: { topLabel: 'Status', text: 'Open'}},\n { decoratedText: { topLabel: 'Subject', text: 'It won\\'t turn on...' }},\n { buttonList: { buttons: [{\n text: 'OPEN CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123'\n }},\n }, {\n text: 'RESOLVE CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123?resolved=y',\n }},\n }, {\n text: 'ASSIGN TO ME',\n // The button is now disabled\n disabled: true,\n onClick: { action: { function: 'assign'}}\n }]}}\n ]}]\n }\n }]\n };\n }\n}\n```\n\nExample:\n```text\ndef on_card_click(event: dict) -> dict:\n \"\"\"Updates a card that was attached to a message with a previewed link.\"\"\"\n # To respond to the correct button, checks the button's actionMethodName.\n if 'assign' == event.get('action').get('actionMethodName'):\n # A hard-coded card is used in this example. In a real-life scenario,\n # an actual assign action would be performed before building the card.\n\n # Checks whether the message event originated from a human or a Chat app\n # and sets actionResponse.type to \"UPDATE_USER_MESSAGE_CARDS if human or\n # \"UPDATE_MESSAGE\" if Chat app.\n actionResponseType = 'UPDATE_USER_MESSAGE_CARDS' if \\\n event.get('message').get('sender').get('type') == 'HUMAN' else \\\n 'UPDATE_MESSAGE'\n\n # Returns the updated card that displays \"You\" for the assignee\n # and that disables the button.\n return {\n 'actionResponse': { 'type': actionResponseType },\n 'cardsV2': [{\n 'cardId': 'attachCard',\n 'card': {\n 'header': {\n 'title': 'Example Customer Service Case',\n 'subtitle': 'Case basics',\n },\n 'sections': [{ 'widgets': [\n { 'decoratedText': { 'topLabel': 'Case ID', 'text': 'case123'}},\n # The assignee is now \"You\"\n { 'decoratedText': { 'topLabel': 'Assignee', 'text': 'You'}},\n { 'decoratedText': { 'topLabel': 'Status', 'text': 'Open'}},\n { 'decoratedText': { 'topLabel': 'Subject', 'text': 'It won\\'t turn on...' }},\n { 'buttonList': { 'buttons': [{\n 'text': 'OPEN CASE',\n 'onClick': { 'openLink': {\n 'url': 'https://support.example.com/orders/case123'\n }},\n }, {\n 'text': 'RESOLVE CASE',\n 'onClick': { 'openLink': {\n 'url': 'https://support.example.com/orders/case123?resolved=y',\n }},\n }, {\n 'text': 'ASSIGN TO ME',\n # The button is now disabled\n 'disabled': True,\n 'onClick': { 'action': { 'function': 'assign'}}\n }]}}\n ]}]\n }\n }]\n }\n```\n\nExample:\n```text\n// Updates a card that was attached to a message with a previewed link.\nMessage onCardClick(JsonNode event) {\n // To respond to the correct button, checks the button's actionMethodName.\n if (event.at(\"/action/actionMethodName\").asText().equals(\"assign\")) {\n // A hard-coded card is used in this example. In a real-life scenario,\n // an actual assign action would be performed before building the card.\n\n // Checks whether the message event originated from a human or a Chat app\n // and sets actionResponse.type to \"UPDATE_USER_MESSAGE_CARDS if human or\n // \"UPDATE_MESSAGE\" if Chat app.\n String actionResponseType =\n event.at(\"/message/sender/type\").asText().equals(\"HUMAN\")\n ? \"UPDATE_USER_MESSAGE_CARDS\" : \"UPDATE_MESSAGE\";\n\n // Returns the updated card that displays \"You\" for the assignee\n // and that disables the button.\n return new Message()\n .setActionResponse(new ActionResponse()\n .setType(actionResponseType))\n .setCardsV2(List.of(new CardWithId()\n .setCardId(\"attachCard\")\n .setCard(new GoogleAppsCardV1Card()\n .setHeader(new GoogleAppsCardV1CardHeader()\n .setTitle(\"Example Customer Service Case\")\n .setSubtitle(\"Case basics\"))\n .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Case ID\")\n .setText(\"case123\")),\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Assignee\")\n // The assignee is now \"You\"\n .setText(\"You\")),\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Status\")\n .setText(\"Open\")),\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Subject\")\n .setText(\"It won't turn on...\")),\n new GoogleAppsCardV1Widget()\n .setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(\n new GoogleAppsCardV1Button()\n .setText(\"OPEN CASE\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setOpenLink(new GoogleAppsCardV1OpenLink()\n .setUrl(\"https://support.example.com/orders/case123\"))),\n new GoogleAppsCardV1Button()\n .setText(\"RESOLVE CASE\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setOpenLink(new GoogleAppsCardV1OpenLink()\n .setUrl(\"https://support.example.com/orders/case123?resolved=y\"))),\n new GoogleAppsCardV1Button()\n .setText(\"ASSIGN TO ME\")\n // The button is now disabled\n .setDisabled(true)\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setAction(new GoogleAppsCardV1Action().setFunction(\"assign\")))))))))))));\n }\n return null;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.436Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":351,"estimatedTokens":3419}}635{"id":"doc-create_and_manage_teachers_and_students_google_c-4f21eeb0","source":"documentation","title":"Create and manage teachers and students | Google Classroom | Google for Developers","url":"https://developers.google.com/workspace/classroom/guides/manage-users","text":"Example:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Classroom.v1;\nusing Google.Apis.Classroom.v1.Data;\nusing Google.Apis.Services;\nusing System;\nusing System.Net;\nusing Google;\n\nnamespace ClassroomSnippets\n{\n // Class to demonstrate the use of Classroom Create Teacher API\n public class AddTeacher\n {\n /// <summary>\n /// Add teacher to the Course\n /// </summary>\n /// <param name=\"courseId\"></param>\n /// <param name=\"teacherEmail\"></param>\n /// <returns></returns>\n public static Teacher ClassroomAddTeacher( string courseId,\n string teacherEmail)\n {\n try \n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(ClassroomService.Scope.ClassroomRosters);\n\n // Create Classroom API service.\n var service = new ClassroomService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Classroom API Snippet\"\n });\n\n var teacher = new Teacher\n {\n UserId = teacherEmail\n };\n // Add the teacher to the course.\n teacher = service.Courses.Teachers.Create(teacher, courseId).Execute();\n Console.WriteLine(\n \"User '{0}' was added as a teacher to the course with ID '{1}'.\\n\",\n teacher.Profile.Name.FullName, courseId);\n return teacher;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is GoogleApiException)\n {\n Console.WriteLine(\"Failed to Add the teacher. Error message: {0}\", e.Message);\n }\n else\n {\n throw;\n }\n }\n\n return null;\n }\n\n\n }\n\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.classroom.Classroom;\nimport com.google.api.services.classroom.ClassroomScopes;\nimport com.google.api.services.classroom.model.Teacher;\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\n/* Class to demonstrate the use of Classroom Add Teacher API */\npublic class AddTeacher {\n\n /* Scopes required by this API call. If modifying these scopes, delete your previously saved\n tokens/ folder. */\n static ArrayList<String> SCOPES =\n new ArrayList<>(Arrays.asList(ClassroomScopes.CLASSROOM_ROSTERS));\n\n /**\n * Add teacher to a specific course.\n *\n * @param courseId - Id of the course.\n * @param teacherEmail - Email address of the teacher.\n * @return newly created teacher\n * @throws IOException - if credentials file not found.\n * @throws GeneralSecurityException - if a new instance of NetHttpTransport was not created.\n */\n public static Teacher addTeacher(String courseId, String teacherEmail)\n throws GeneralSecurityException, IOException {\n\n // Create the classroom API client.\n final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();\n Classroom service =\n new Classroom.Builder(\n HTTP_TRANSPORT,\n GsonFactory.getDefaultInstance(),\n ClassroomCredentials.getCredentials(HTTP_TRANSPORT, SCOPES))\n .setApplicationName(\"Classroom samples\")\n .build();\n\n Teacher teacher = new Teacher().setUserId(teacherEmail);\n try {\n // Add a teacher to a specified course\n teacher = service.courses().teachers().create(courseId, teacher).execute();\n // Prints the course id with the teacher name\n System.out.printf(\n \"User '%s' was added as a teacher to the course with ID '%s'.\\n\",\n teacher.getProfile().getName().getFullName(), courseId);\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 409) {\n System.out.printf(\"User '%s' is already a member of this course.\\n\", teacherEmail);\n } else if (error.getCode() == 403) {\n System.out.println(\"The caller does not have permission.\\n\");\n } else {\n throw e;\n }\n }\n return teacher;\n }\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Classroom;\nuse Google\\Service\\Classroom\\Teacher;\nuse Google\\service\\Exception;\n\nfunction addTeacher($courseId, $teacherEmail)\n{\n /* Load pre-authorized user credentials from the environment.\n TODO (developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(\"https://www.googleapis.com/auth/classroom.profile.photos\");\n $service = new Classroom($client);\n $teacher = new Teacher([\n 'userId' => $teacherEmail\n ]);\n try {\n // calling create teacher\n $teacher = $service->courses_teachers->create($courseId, $teacher);\n printf(\"User '%s' was added as a teacher to the course with ID '%s'.\\n\",\n $teacher->profile->name->fullName, $courseId);\n } catch (Exception $e) {\n if ($e->getCode() == 409) {\n printf(\"User '%s' is already a member of this course.\\n\", $teacherEmail);\n } else {\n throw $e;\n }\n }\n return $teacher;\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef classroom_add_teacher(course_id):\n \"\"\"\n Adds a teacher to a course with specific course_id.\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n # pylint: disable=maybe-no-member\n service = build(\"classroom\", \"v1\", credentials=creds)\n\n teacher_email = \"gduser1@workspacesamples.dev\"\n teacher = {\"userId\": teacher_email}\n\n try:\n teachers = service.courses().teachers()\n teacher = teachers.create(courseId=course_id, body=teacher).execute()\n print(\n \"User %s was added as a teacher to the course with ID %s\"\n % (teacher.get(\"profile\").get(\"name\").get(\"fullName\"), course_id)\n )\n except HttpError as error:\n print('User \"{%s}\" is already a member of this course.' % teacher_email)\n return error\n return teachers\n\n\nif __name__ == \"__main__\":\n # Put the course_id of course for which Teacher needs to be added.\n classroom_add_teacher(453686957652)\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Classroom.v1;\nusing Google.Apis.Classroom.v1.Data;\nusing Google.Apis.Services;\nusing System;\nusing System.Net;\nusing Google;\n\nnamespace ClassroomSnippets\n{\n // Class to demonstrate the use of Classroom Create Student API\n public class AddStudent\n {\n public static Student ClassroomAddStudent(string courseId, string enrollmentCode)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(ClassroomService.Scope.ClassroomRosters);\n var service = new ClassroomService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Classroom API .NET Quickstart\"\n });\n\n var student = new Student\n {\n UserId = \"me\"\n };\n\n var request = service.Courses.Students.Create(student, courseId);\n request.EnrollmentCode = enrollmentCode;\n student = request.Execute();\n Console.WriteLine(\n \"User '{0}' was enrolled as a student in the course with ID '{1}'.\\n\",\n student.Profile.Name.FullName, courseId);\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is GoogleApiException)\n {\n Console.WriteLine(\"Failed to Add the Student. Error message: {0}\", e.Message);\n }\n else\n {\n throw;\n }\n }\n\n return null;\n }\n }\n\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.classroom.Classroom;\nimport com.google.api.services.classroom.ClassroomScopes;\nimport com.google.api.services.classroom.model.Student;\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\n/* Class to demonstrate the use of Classroom Add Student API */\npublic class AddStudent {\n\n /* Scopes required by this API call. If modifying these scopes, delete your previously saved\n tokens/ folder. */\n static ArrayList<String> SCOPES =\n new ArrayList<>(Arrays.asList(ClassroomScopes.CLASSROOM_ROSTERS));\n\n /**\n * Add a student in a specified course.\n *\n * @param courseId - Id of the course.\n * @param enrollmentCode - Code of the course to enroll.\n * @return newly added student\n * @throws IOException - if credentials file not found.\n * @throws GeneralSecurityException - if a new instance of NetHttpTransport was not created.\n */\n public static Student addStudent(String courseId, String enrollmentCode, String studentId)\n throws GeneralSecurityException, IOException {\n\n // Create the classroom API client.\n final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();\n Classroom service =\n new Classroom.Builder(\n HTTP_TRANSPORT,\n GsonFactory.getDefaultInstance(),\n ClassroomCredentials.getCredentials(HTTP_TRANSPORT, SCOPES))\n .setApplicationName(\"Classroom samples\")\n .build();\n\n Student student = new Student().setUserId(studentId);\n try {\n // Enrolling a student to a specified course\n student =\n service\n .courses()\n .students()\n .create(courseId, student)\n .setEnrollmentCode(enrollmentCode)\n .execute();\n // Prints the course id with the Student name\n System.out.printf(\n \"User '%s' was enrolled as a student in the course with ID '%s'.\\n\",\n student.getProfile().getName().getFullName(), courseId);\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 409) {\n System.out.println(\"You are already a member of this course.\");\n } else if (error.getCode() == 403) {\n System.out.println(\"The caller does not have permission.\\n\");\n } else {\n throw e;\n }\n }\n return student;\n }\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Classroom;\nuse Google\\Service\\Classroom\\Student;\nuse Google\\Service\\Exception;\n\nfunction enrollAsStudent($courseId,$enrollmentCode)\n{\n /* Load pre-authorized user credentials from the environment.\n TODO (developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(\"https://www.googleapis.com/auth/classroom.profile.emails\");\n $service = new Classroom($client);\n $student = new Student([\n 'userId' => 'me'\n ]);\n $params = [\n 'enrollmentCode' => $enrollmentCode\n ];\n try {\n $student = $service->courses_students->create($courseId, $student, $params);\n printf(\"User '%s' was enrolled as a student in the course with ID '%s'.\\n\",\n $student->profile->name->fullName, $courseId);\n } catch (Exception $e) {\n if ($e->getCode() == 409) {\n print \"You are already a member of this course.\\n\";\n } else {\n throw $e;\n }\n }\n return $student;\n}\n```\n\nExample:\n```text\nimport os\n\nfrom google.auth.transport.requests import Request\nfrom google.oauth2.credentials import Credentials\nfrom google_auth_oauthlib.flow import InstalledAppFlow\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\nSCOPES = [\"https://www.googleapis.com/auth/classroom.coursework.students\"]\n\n\ndef classroom_add_student_new(course_id):\n \"\"\"\n Adds a student to a course, the teacher has access to.\n The file token.json stores the user's access and refresh tokens, and is\n created automatically when the authorization flow completes for the first\n time.\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for the application.\n \"\"\"\n\n creds = None\n # The file token.json stores the user's access and refresh tokens, and is\n # created automatically when the authorization flow completes for the first\n # time.\n if os.path.exists(\"token.json\"):\n creds = Credentials.from_authorized_user_file(\"token.json\", SCOPES)\n # If there are no (valid) credentials available, let the user log in.\n if not creds or not creds.valid:\n if creds and creds.expired and creds.refresh_token:\n creds.refresh(Request())\n else:\n flow = InstalledAppFlow.from_client_secrets_file(\n \"credentials.json\", SCOPES\n )\n creds = flow.run_local_server(port=0)\n # Save the credentials for the next run\n with open(\"token.json\", \"w\", encoding=\"utf8\") as token:\n token.write(creds.to_json())\n\n enrollment_code = \"abc-def\"\n student = {\"userId\": \"gduser1@workspacesamples.dev\"}\n try:\n service = build(\"classroom\", \"v1\", credentials=creds)\n student = (\n service.courses()\n .students()\n .create(\n courseId=course_id, enrollmentCode=enrollment_code, body=student\n )\n .execute()\n )\n print(\n '''User {%s} was enrolled as a student in\n the course with ID \"{%s}\"'''\n % (student.get(\"profile\").get(\"name\").get(\"fullName\"), course_id)\n )\n return student\n except HttpError as error:\n print(error)\n return error\n\n\nif __name__ == \"__main__\":\n # Put the course_id of course for which student needs to be added.\n classroom_add_student_new(478800920837)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.438Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":471,"estimatedTokens":4029}}636{"id":"doc-push_notifications_in_the_classroom_api_google_c-97450e92","source":"documentation","title":"Push notifications in the Classroom API | Google Classroom | Google for Developers","url":"https://developers.google.com/workspace/classroom/guides/push-notifications","text":"Example:\n```text\n{\n \"collection\": \"courses.students\",\n \"eventType\": \"CREATED\",\n \"resourceId\": {\n \"courseId\": \"12345\",\n \"userId\": \"45678\"\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.439Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":43}}637{"id":"doc-create_and_manage_guardians_google_classroom_goo-59e4dd76","source":"documentation","title":"Create and manage guardians | Google Classroom | Google for Developers","url":"https://developers.google.com/workspace/classroom/guides/manage-guardians","text":"Example:\n```text\nGuardianInvitation guardianInvitation = null;\n\n/* Create a GuardianInvitation object with state set to PENDING. See\nhttps://developers.google.com/classroom/reference/rest/v1/userProfiles.guardianInvitations#guardianinvitationstate\nfor other possible states of guardian invitations. */\nGuardianInvitation content =\n new GuardianInvitation()\n .setStudentId(studentId)\n .setInvitedEmailAddress(guardianEmail)\n .setState(\"PENDING\");\ntry {\n guardianInvitation =\n service.userProfiles().guardianInvitations().create(studentId, content).execute();\n\n System.out.printf(\"Invitation created: %s\\n\", guardianInvitation.getInvitationId());\n} catch (GoogleJsonResponseException e) {\n // TODO (developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 404) {\n System.out.printf(\"There is no record of studentId: %s\", studentId);\n } else {\n throw e;\n }\n} catch (Exception e) {\n throw e;\n}\nreturn guardianInvitation;\n```\n\nExample:\n```text\nguardianInvitation = {\n 'invitedEmailAddress': 'guardian@gmail.com',\n}\nguardianInvitation = service.userProfiles().guardianInvitations().create(\n studentId='student@mydomain.edu',\n body=guardianInvitation).execute()\nprint(\"Invitation created with id: {0}\".format(guardianInvitation.get('invitationId')))\n```\n\nExample:\n```text\nGuardianInvitation guardianInvitation = null;\n\ntry {\n /* Change the state of the GuardianInvitation from PENDING to COMPLETE. See\n https://developers.google.com/classroom/reference/rest/v1/userProfiles.guardianInvitations#guardianinvitationstate\n for other possible states of guardian invitations. */\n GuardianInvitation content =\n service.userProfiles().guardianInvitations().get(studentId, invitationId).execute();\n content.setState(\"COMPLETE\");\n\n guardianInvitation =\n service\n .userProfiles()\n .guardianInvitations()\n .patch(studentId, invitationId, content)\n .set(\"updateMask\", \"state\")\n .execute();\n\n System.out.printf(\n \"Invitation (%s) state set to %s\\n.\",\n guardianInvitation.getInvitationId(), guardianInvitation.getState());\n} catch (GoogleJsonResponseException e) {\n // TODO (developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 404) {\n System.out.printf(\n \"There is no record of studentId (%s) or invitationId (%s).\", studentId, invitationId);\n } else {\n throw e;\n }\n} catch (Exception e) {\n throw e;\n}\nreturn guardianInvitation;\n```\n\nExample:\n```text\nguardian_invite = {\n 'state': 'COMPLETE'\n}\nguardianInvitation = service.userProfiles().guardianInvitations().patch(\n studentId='student@mydomain.edu',\n invitationId=1234, # Replace with the invitation ID of the invitation you want to cancel\n updateMask='state',\n body=guardianInvitation).execute()\n```\n\nExample:\n```text\nList<GuardianInvitation> guardianInvitations = new ArrayList<>();\nString pageToken = null;\n\ntry {\n do {\n ListGuardianInvitationsResponse response =\n service\n .userProfiles()\n .guardianInvitations()\n .list(studentId)\n .setPageToken(pageToken)\n .execute();\n\n /* Ensure that the response is not null before retrieving data from it to avoid errors. */\n if (response.getGuardianInvitations() != null) {\n guardianInvitations.addAll(response.getGuardianInvitations());\n pageToken = response.getNextPageToken();\n }\n } while (pageToken != null);\n\n if (guardianInvitations.isEmpty()) {\n System.out.println(\"No guardian invitations found.\");\n } else {\n for (GuardianInvitation invitation : guardianInvitations) {\n System.out.printf(\"Guardian invitation id: %s\\n\", invitation.getInvitationId());\n }\n }\n} catch (GoogleJsonResponseException e) {\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 404) {\n System.out.printf(\"There is no record of studentId (%s).\", studentId);\n } else {\n throw e;\n }\n} catch (Exception e) {\n throw e;\n}\nreturn guardianInvitations;\n```\n\nExample:\n```text\nguardian_invites = []\npage_token = None\n\nwhile True:\n response = service.userProfiles().guardianInvitations().list(\n studentId='student@mydomain.edu').execute()\n guardian_invites.extend(response.get('guardian_invites', []))\n page_token = response.get('nextPageToken', None)\n if not page_token:\n break\n\nif not courses:\n print('No guardians invited for this {0}.'.format(response.get('studentId')))\nelse:\n print('Guardian Invite:')\n for guardian in guardian_invites:\n print('An invite was sent to '.format(guardian.get('id'),\n guardian.get('guardianId')))\n```\n\nExample:\n```text\nList<Guardian> guardians = new ArrayList<>();\nString pageToken = null;\n\ntry {\n do {\n ListGuardiansResponse response =\n service.userProfiles().guardians().list(studentId).setPageToken(pageToken).execute();\n\n /* Ensure that the response is not null before retrieving data from it to avoid errors. */\n if (response.getGuardians() != null) {\n guardians.addAll(response.getGuardians());\n pageToken = response.getNextPageToken();\n }\n } while (pageToken != null);\n\n if (guardians.isEmpty()) {\n System.out.println(\"No guardians found.\");\n } else {\n for (Guardian guardian : guardians) {\n System.out.printf(\n \"Guardian name: %s, guardian id: %s, guardian email: %s\\n\",\n guardian.getGuardianProfile().getName().getFullName(),\n guardian.getGuardianId(),\n guardian.getInvitedEmailAddress());\n }\n }\n\n} catch (GoogleJsonResponseException e) {\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 404) {\n System.out.printf(\"There is no record of studentId (%s).\", studentId);\n } else {\n throw e;\n }\n} catch (Exception e) {\n throw e;\n}\nreturn guardians;\n```\n\nExample:\n```text\nguardian_invites = []\npage_token = None\n\nwhile True:\n response = service.userProfiles().guardians().list(studentId='student@mydomain.edu').execute()\n guardian_invites.extend(response.get('guardian_invites', []))\n page_token = response.get('nextPageToken', None)\n if not page_token:\n break\n\nif not courses:\n print('No guardians invited for this {0}.'.format(response.get('studentId')))\nelse:\n print('Guardian Invite:')\n for guardian in guardian_invites:\n print('An invite was sent to '.format(guardian.get('id'),\n guardian.get('guardianId')))\n```\n\nExample:\n```text\ntry {\n service.userProfiles().guardians().delete(studentId, guardianId).execute();\n System.out.printf(\"The guardian with id %s was deleted.\\n\", guardianId);\n} catch (GoogleJsonResponseException e) {\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 404) {\n System.out.printf(\"There is no record of guardianId (%s).\", guardianId);\n }\n}\n```\n\nExample:\n```text\nservice.userProfiles().guardians().delete(studentId='student@mydomain.edu',\n guardianId='guardian@gmail.com').execute()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.439Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":239,"estimatedTokens":1800}}638{"id":"doc-support_interactive_dialogs_google_chat_google_f-284394d2","source":"documentation","title":"Support interactive dialogs | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/how-tos/dialogs","text":"Example:\n```text\nbuttonList: { buttons: [{\n text: \"Add Contact\",\n onClick: { action: {\n function: \"openInitialDialog\",\n interaction: \"OPEN_DIALOG\"\n }}\n}]}\n```\n\nExample:\n```text\n'buttonList': { 'buttons': [{\n 'text': \"Add Contact\",\n 'onClick': { 'action': {\n 'function': \"openInitialDialog\",\n 'interaction': \"OPEN_DIALOG\"\n }}\n}]}\n```\n\nExample:\n```text\n.setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()\n .setText(\"Add Contact\")\n .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()\n .setFunction(\"openInitialDialog\")\n .setInteraction(\"OPEN_DIALOG\"))))))));\n```\n\nExample:\n```text\n/**\n * Opens the initial step of the dialog that lets users add contact details.\n *\n * @return {Object} a message with an action response to open a dialog.\n */\nfunction openInitialDialog() {\n return { actionResponse: {\n type: \"DIALOG\",\n dialogAction: { dialog: { body: { sections: [{\n header: \"Add new contact\",\n widgets: CONTACT_FORM_WIDGETS.concat([{\n buttonList: { buttons: [{\n text: \"Review and submit\",\n onClick: { action: { function: \"openConfirmation\" }}\n }]}\n }])\n }]}}}\n }};\n}\n```\n\nExample:\n```text\ndef open_initial_dialog() -> dict:\n \"\"\"Opens the initial step of the dialog that lets users add contact details.\"\"\"\n return { 'actionResponse': {\n 'type': \"DIALOG\",\n 'dialogAction': { 'dialog': { 'body': { 'sections': [{\n 'header': \"Add new contact\",\n 'widgets': CONTACT_FORM_WIDGETS + [{\n 'buttonList': { 'buttons': [{\n 'text': \"Review and submit\",\n 'onClick': { 'action': { 'function': \"openConfirmation\" }}\n }]}\n }]\n }]}}}\n }}\n```\n\nExample:\n```text\n// Opens the initial step of the dialog that lets users add contact details.\nMessage openInitialDialog() {\n return new Message().setActionResponse(new ActionResponse()\n .setType(\"DIALOG\")\n .setDialogAction(new DialogAction().setDialog(new Dialog().setBody(new GoogleAppsCardV1Card()\n .setSections(List.of(new GoogleAppsCardV1Section()\n .setHeader(\"Add new contact\")\n .setWidgets(Stream.concat(\n CONTACT_FORM_WIDGETS.stream(),\n List.of(new GoogleAppsCardV1Widget()\n .setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()\n .setText(\"Review and submit\")\n .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()\n .setFunction(\"openConfirmation\"))))))).stream()).collect(Collectors.toList()))))))));\n}\n```\n\nExample:\n```text\n/**\n * Responds to CARD_CLICKED interaction events in Google Chat.\n *\n * @param {Object} event the CARD_CLICKED interaction event from Google Chat.\n * @return {Object} message responses specific to the dialog handling.\n */\nfunction onCardClick(event) {\n // Initial dialog form page\n if (event.common.invokedFunction === \"openInitialDialog\") {\n return openInitialDialog();\n // Confirmation dialog form page\n } else if (event.common.invokedFunction === \"openConfirmation\") {\n return openConfirmation(event);\n // Submission dialog form page\n } else if (event.common.invokedFunction === \"submitForm\") {\n return submitForm(event);\n }\n}\n\n/**\n * Opens the initial step of the dialog that lets users add contact details.\n *\n * @return {Object} a message with an action response to open a dialog.\n */\nfunction openInitialDialog() {\n return { actionResponse: {\n type: \"DIALOG\",\n dialogAction: { dialog: { body: { sections: [{\n header: \"Add new contact\",\n widgets: CONTACT_FORM_WIDGETS.concat([{\n buttonList: { buttons: [{\n text: \"Review and submit\",\n onClick: { action: { function: \"openConfirmation\" }}\n }]}\n }])\n }]}}}\n }};\n}\n\n/**\n * Returns the second step as a dialog or card message that lets users confirm details.\n *\n * @param {Object} event the interactive event with form inputs.\n * @return {Object} returns a dialog or private card message.\n */\nfunction openConfirmation(event) {\n const name = fetchFormValue(event, \"contactName\") ?? \"\";\n const birthdate = fetchFormValue(event, \"contactBirthdate\") ?? \"\";\n const type = fetchFormValue(event, \"contactType\") ?? \"\";\n const cardConfirmation = {\n header: \"Your contact\",\n widgets: [{\n textParagraph: { text: \"Confirm contact information and submit:\" }}, {\n textParagraph: { text: \"<b>Name:</b> \" + name }}, {\n textParagraph: {\n text: \"<b>Birthday:</b> \" + convertMillisToDateString(birthdate)\n }}, {\n textParagraph: { text: \"<b>Type:</b> \" + type }}, {\n buttonList: { buttons: [{\n text: \"Submit\",\n onClick: { action: {\n function: \"submitForm\",\n parameters: [{\n key: \"contactName\", value: name }, {\n key: \"contactBirthdate\", value: birthdate }, {\n key: \"contactType\", value: type\n }]\n }}\n }]}\n }]\n };\n\n // Returns a dialog with contact information that the user input.\n if (event.isDialogEvent) {\n return { action_response: {\n type: \"DIALOG\",\n dialogAction: { dialog: { body: { sections: [ cardConfirmation ]}}}\n }};\n }\n\n // Updates existing card message with contact information that the user input.\n return {\n actionResponse: { type: \"UPDATE_MESSAGE\" },\n privateMessageViewer: event.user,\n cardsV2: [{\n card: { sections: [cardConfirmation]}\n }]\n }\n}\n```\n\nExample:\n```text\ndef on_card_click(event: dict) -> dict:\n \"\"\"Responds to CARD_CLICKED interaction events in Google Chat.\"\"\"\n # Initial dialog form page\n if \"openInitialDialog\" == event.get('common').get('invokedFunction'):\n return open_initial_dialog()\n # Confirmation dialog form page\n elif \"openConfirmation\" == event.get('common').get('invokedFunction'):\n return open_confirmation(event)\n # Submission dialog form page\n elif \"submitForm\" == event.get('common').get('invokedFunction'):\n return submit_form(event)\n\n\ndef open_initial_dialog() -> dict:\n \"\"\"Opens the initial step of the dialog that lets users add contact details.\"\"\"\n return { 'actionResponse': {\n 'type': \"DIALOG\",\n 'dialogAction': { 'dialog': { 'body': { 'sections': [{\n 'header': \"Add new contact\",\n 'widgets': CONTACT_FORM_WIDGETS + [{\n 'buttonList': { 'buttons': [{\n 'text': \"Review and submit\",\n 'onClick': { 'action': { 'function': \"openConfirmation\" }}\n }]}\n }]\n }]}}}\n }}\n\n\ndef open_confirmation(event: dict) -> dict:\n \"\"\"Returns the second step as a dialog or card message that lets users confirm details.\"\"\"\n name = fetch_form_value(event, \"contactName\") or \"\"\n birthdate = fetch_form_value(event, \"contactBirthdate\") or \"\"\n type = fetch_form_value(event, \"contactType\") or \"\"\n card_confirmation = {\n 'header': \"Your contact\",\n 'widgets': [{\n 'textParagraph': { 'text': \"Confirm contact information and submit:\" }}, {\n 'textParagraph': { 'text': \"<b>Name:</b> \" + name }}, {\n 'textParagraph': {\n 'text': \"<b>Birthday:</b> \" + convert_millis_to_date_string(birthdate)\n }}, {\n 'textParagraph': { 'text': \"<b>Type:</b> \" + type }}, {\n 'buttonList': { 'buttons': [{\n 'text': \"Submit\",\n 'onClick': { 'action': {\n 'function': \"submitForm\",\n 'parameters': [{\n 'key': \"contactName\", 'value': name }, {\n 'key': \"contactBirthdate\", 'value': birthdate }, {\n 'key': \"contactType\", 'value': type\n }]\n }}\n }]}\n }]\n }\n\n # Returns a dialog with contact information that the user input.\n if event.get('isDialogEvent'): \n return { 'action_response': {\n 'type': \"DIALOG\",\n 'dialogAction': { 'dialog': { 'body': { 'sections': [card_confirmation] }}}\n }}\n\n # Updates existing card message with contact information that the user input.\n return {\n 'actionResponse': { 'type': \"UPDATE_MESSAGE\" },\n 'privateMessageViewer': event.get('user'),\n 'cardsV2': [{\n 'card': { 'sections': [card_confirmation] }\n }]\n }\n```\n\nExample:\n```text\n// Responds to CARD_CLICKED interaction events in Google Chat.\nMessage onCardClick(JsonNode event) {\n String invokedFunction = event.at(\"/common/invokedFunction\").asText();\n // Initial dialog form page\n if (\"openInitialDialog\".equals(invokedFunction)) {\n return openInitialDialog();\n // Confirmation dialog form page\n } else if (\"openConfirmation\".equals(invokedFunction)) {\n return openConfirmation(event);\n // Submission dialog form page\n } else if (\"submitForm\".equals(invokedFunction)) {\n return submitForm(event);\n }\n return null; \n}\n\n// Opens the initial step of the dialog that lets users add contact details.\nMessage openInitialDialog() {\n return new Message().setActionResponse(new ActionResponse()\n .setType(\"DIALOG\")\n .setDialogAction(new DialogAction().setDialog(new Dialog().setBody(new GoogleAppsCardV1Card()\n .setSections(List.of(new GoogleAppsCardV1Section()\n .setHeader(\"Add new contact\")\n .setWidgets(Stream.concat(\n CONTACT_FORM_WIDGETS.stream(),\n List.of(new GoogleAppsCardV1Widget()\n .setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()\n .setText(\"Review and submit\")\n .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()\n .setFunction(\"openConfirmation\"))))))).stream()).collect(Collectors.toList()))))))));\n}\n\n// Returns the second step as a dialog or card message that lets users confirm details.\nMessage openConfirmation(JsonNode event) {\n String name = fetchFormValue(event, \"contactName\") != null ?\n fetchFormValue(event, \"contactName\") : \"\";\n String birthdate = fetchFormValue(event, \"contactBirthdate\") != null ?\n fetchFormValue(event, \"contactBirthdate\") : \"\";\n String type = fetchFormValue(event, \"contactType\") != null ?\n fetchFormValue(event, \"contactType\") : \"\";\n GoogleAppsCardV1Section cardConfirmationSection = new GoogleAppsCardV1Section()\n .setHeader(\"Your contact\")\n .setWidgets(List.of(\n new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()\n .setText(\"Confirm contact information and submit:\")),\n new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()\n .setText(\"<b>Name:</b> \" + name)),\n new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()\n .setText(\"<b>Birthday:</b> \" + convertMillisToDateString(birthdate))),\n new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()\n .setText(\"<b>Type:</b> \" + type)),\n new GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()\n .setText(\"Submit\")\n .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()\n .setFunction(\"submitForm\")\n .setParameters(List.of(\n new GoogleAppsCardV1ActionParameter().setKey(\"contactName\").setValue(name),\n new GoogleAppsCardV1ActionParameter().setKey(\"contactBirthdate\").setValue(birthdate),\n new GoogleAppsCardV1ActionParameter().setKey(\"contactType\").setValue(type))))))))));\n\n // Returns a dialog with contact information that the user input.\n if (event.at(\"/isDialogEvent\") != null && event.at(\"/isDialogEvent\").asBoolean()) {\n return new Message().setActionResponse(new ActionResponse()\n .setType(\"DIALOG\")\n .setDialogAction(new DialogAction().setDialog(new Dialog().setBody(new GoogleAppsCardV1Card()\n .setSections(List.of(cardConfirmationSection))))));\n }\n\n // Updates existing card message with contact information that the user input.\n return new Message()\n .setActionResponse(new ActionResponse()\n .setType(\"UPDATE_MESSAGE\"))\n .setPrivateMessageViewer(new User().setName(event.at(\"/user/name\").asText()))\n .setCardsV2(List.of(new CardWithId().setCard(new GoogleAppsCardV1Card()\n .setSections(List.of(cardConfirmationSection)))));\n}\n```\n\nExample:\n```text\n// The Chat app indicates that it received form data from the dialog or card.\n// Sends private text message that confirms submission.\nconst confirmationMessage = \"✅ \" + contactName + \" has been added to your contacts.\";\nif (event.dialogEventType === \"SUBMIT_DIALOG\") {\n return {\n actionResponse: {\n type: \"DIALOG\",\n dialogAction: { actionStatus: {\n statusCode: \"OK\",\n userFacingMessage: \"Success \" + contactName\n }}\n }\n };\n}\n```\n\nExample:\n```text\n# The Chat app indicates that it received form data from the dialog or card.\n# Sends private text message that confirms submission.\nconfirmation_message = \"✅ \" + contact_name + \" has been added to your contacts.\";\nif \"SUBMIT_DIALOG\" == event.get('dialogEventType'):\n return {\n 'actionResponse': {\n 'type': \"DIALOG\",\n 'dialogAction': { 'actionStatus': {\n 'statusCode': \"OK\",\n 'userFacingMessage': \"Success \" + contact_name\n }}\n }\n }\n```\n\nExample:\n```text\n// The Chat app indicates that it received form data from the dialog or card.\n// Sends private text message that confirms submission.\nString confirmationMessage = \"✅ \" + contactName + \" has been added to your contacts.\";\nif (event.at(\"/dialogEventType\") != null && \"SUBMIT_DIALOG\".equals(event.at(\"/dialogEventType\").asText())) {\n return new Message().setActionResponse(new ActionResponse()\n .setType(\"DIALOG\")\n .setDialogAction(new DialogAction().setActionStatus(new ActionStatus()\n .setStatusCode(\"OK\")\n .setUserFacingMessage(\"Success \" + contactName))));\n}\n```\n\nExample:\n```text\nconst contactName = event.common.parameters[\"contactName\"];\n// Checks to make sure the user entered a contact name.\n// If no name value detected, returns an error message.\nconst errorMessage = \"Don't forget to name your new contact!\";\nif (!contactName && event.dialogEventType === \"SUBMIT_DIALOG\") {\n return { actionResponse: {\n type: \"DIALOG\",\n dialogAction: { actionStatus: {\n statusCode: \"INVALID_ARGUMENT\",\n userFacingMessage: errorMessage\n }}\n }};\n}\n```\n\nExample:\n```text\ncontact_name = event.get('common').get('parameters')[\"contactName\"]\n# Checks to make sure the user entered a contact name.\n# If no name value detected, returns an error message.\nerror_message = \"Don't forget to name your new contact!\"\nif contact_name == \"\" and \"SUBMIT_DIALOG\" == event.get('dialogEventType'):\n return { 'actionResponse': {\n 'type': \"DIALOG\",\n 'dialogAction': { 'actionStatus': {\n 'statusCode': \"INVALID_ARGUMENT\",\n 'userFacingMessage': error_message\n }}\n }}\n```\n\nExample:\n```text\nString contactName = event.at(\"/common/parameters/contactName\").asText();\n// Checks to make sure the user entered a contact name.\n// If no name value detected, returns an error message.\nString errorMessage = \"Don't forget to name your new contact!\";\nif (contactName.isEmpty() && event.at(\"/dialogEventType\") != null && \"SUBMIT_DIALOG\".equals(event.at(\"/dialogEventType\").asText())) {\n return new Message().setActionResponse(new ActionResponse()\n .setType(\"DIALOG\")\n .setDialogAction(new DialogAction().setActionStatus(new ActionStatus()\n .setStatusCode(\"INVALID_ARGUMENT\")\n .setUserFacingMessage(errorMessage))));\n}\n```\n\nExample:\n```text\nreturn {\n actionResponse: { type: \"NEW_MESSAGE\" },\n privateMessageViewer: event.user,\n text: confirmationMessage\n};\n```\n\nExample:\n```text\nreturn {\n 'actionResponse': { 'type': \"NEW_MESSAGE\" },\n 'privateMessageViewer': event.get('user'),\n 'text': confirmation_message\n}\n```\n\nExample:\n```text\nreturn new Message()\n .setActionResponse(new ActionResponse().setType(\"NEW_MESSAGE\"))\n .setPrivateMessageViewer(new User().setName(event.at(\"/user/name\").asText()))\n .setText(confirmationMessage);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.441Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":464,"estimatedTokens":3978}}639{"id":"doc-manage_coursework_google_classroom_google_for_de-1e2b5ab6","source":"documentation","title":"Manage CourseWork | Google Classroom | Google for Developers","url":"https://developers.google.com/workspace/classroom/guides/manage-coursework","text":"Example:\n```text\nCourseWork courseWork = null;\ntry {\n // Create a link to add as a material on course work.\n Link articleLink =\n new Link()\n .setTitle(\"SR-71 Blackbird\")\n .setUrl(\"https://www.lockheedmartin.com/en-us/news/features/history/blackbird.html\");\n\n // Create a list of Materials to add to course work.\n List<Material> materials = Arrays.asList(new Material().setLink(articleLink));\n\n /* Create new CourseWork object with the material attached.\n Set workType to `ASSIGNMENT`. Possible values of workType can be found here:\n https://developers.google.com/classroom/reference/rest/v1/CourseWorkType\n Set state to `PUBLISHED`. Possible values of state can be found here:\n https://developers.google.com/classroom/reference/rest/v1/courses.courseWork#courseworkstate */\n CourseWork content =\n new CourseWork()\n .setTitle(\"Supersonic aviation\")\n .setDescription(\n \"Read about how the SR-71 Blackbird, the world’s fastest and \"\n + \"highest-flying manned aircraft, was built.\")\n .setMaterials(materials)\n .setWorkType(\"ASSIGNMENT\")\n .setState(\"PUBLISHED\");\n\n courseWork = service.courses().courseWork().create(courseId, content).execute();\n\n /* Prints the created courseWork. */\n System.out.printf(\"CourseWork created: %s\\n\", courseWork.getTitle());\n} catch (GoogleJsonResponseException e) {\n // TODO (developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 404) {\n System.out.printf(\"The courseId does not exist: %s.\\n\", courseId);\n } else {\n throw e;\n }\n throw e;\n} catch (Exception e) {\n throw e;\n}\nreturn courseWork;\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef classroom_create_coursework(course_id):\n \"\"\"\n Creates the coursework the user has access to.\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n\n creds, _ = google.auth.default()\n # pylint: disable=maybe-no-member\n\n try:\n service = build(\"classroom\", \"v1\", credentials=creds)\n coursework = {\n \"title\": \"Ant colonies\",\n \"description\": \"\"\"Read the article about ant colonies\n and complete the quiz.\"\"\",\n \"materials\": [\n {\"link\": {\"url\": \"http://example.com/ant-colonies\"}},\n {\"link\": {\"url\": \"http://example.com/ant-quiz\"}},\n ],\n \"workType\": \"ASSIGNMENT\",\n \"state\": \"PUBLISHED\",\n }\n coursework = (\n service.courses()\n .courseWork()\n .create(courseId=course_id, body=coursework)\n .execute()\n )\n print(f\"Assignment created with ID {coursework.get('id')}\")\n return coursework\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return error\n\n\nif __name__ == \"__main__\":\n # Put the course_id of course whose coursework needs to be created,\n # the user has access to.\n classroom_create_coursework(453686957652)\n```\n\nExample:\n```text\nList<StudentSubmission> studentSubmissions = new ArrayList<>();\nString pageToken = null;\n\ntry {\n do {\n ListStudentSubmissionsResponse response =\n service\n .courses()\n .courseWork()\n .studentSubmissions()\n .list(courseId, courseWorkId)\n .setPageToken(pageToken)\n .execute();\n\n /* Ensure that the response is not null before retrieving data from it to avoid errors. */\n if (response.getStudentSubmissions() != null) {\n studentSubmissions.addAll(response.getStudentSubmissions());\n pageToken = response.getNextPageToken();\n }\n } while (pageToken != null);\n\n if (studentSubmissions.isEmpty()) {\n System.out.println(\"No student submission found.\");\n } else {\n for (StudentSubmission submission : studentSubmissions) {\n System.out.printf(\n \"Student id (%s), student submission id (%s)\\n\",\n submission.getUserId(), submission.getId());\n }\n }\n} catch (GoogleJsonResponseException e) {\n // TODO (developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 404) {\n System.out.printf(\n \"The courseId (%s) or courseWorkId (%s) does not exist.\\n\", courseId, courseWorkId);\n } else {\n throw e;\n }\n} catch (Exception e) {\n throw e;\n}\nreturn studentSubmissions;\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef classroom_list_submissions(course_id, coursework_id):\n \"\"\"\n Creates the courses the user has access to.\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n\n creds, _ = google.auth.default()\n # pylint: disable=maybe-no-member\n submissions = []\n page_token = None\n\n try:\n service = build(\"classroom\", \"v1\", credentials=creds)\n while True:\n coursework = service.courses().courseWork()\n response = (\n coursework.studentSubmissions()\n .list(\n pageToken=page_token,\n courseId=course_id,\n courseWorkId=coursework_id,\n pageSize=10,\n )\n .execute()\n )\n submissions.extend(response.get(\"studentSubmissions\", []))\n page_token = response.get(\"nextPageToken\", None)\n if not page_token:\n break\n\n if not submissions:\n print(\"No student submissions found.\")\n\n print(\"Student Submissions:\")\n for submission in submissions:\n print(\n \"Submitted at:\"\n f\"{(submission.get('id'), submission.get('creationTime'))}\"\n )\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n submissions = None\n return submissions\n\n\nif __name__ == \"__main__\":\n # Put the course_id and coursework_id of course whose list needs to be\n # submitted.\n classroom_list_submissions(453686957652, 466086979658)\n```\n\nExample:\n```text\nList<StudentSubmission> studentSubmissions = new ArrayList<>();\nString pageToken = null;\n\ntry {\n do {\n // Set the userId as a query parameter on the request.\n ListStudentSubmissionsResponse response =\n service\n .courses()\n .courseWork()\n .studentSubmissions()\n .list(courseId, courseWorkId)\n .setPageToken(pageToken)\n .set(\"userId\", userId)\n .execute();\n\n /* Ensure that the response is not null before retrieving data from it to avoid errors. */\n if (response.getStudentSubmissions() != null) {\n studentSubmissions.addAll(response.getStudentSubmissions());\n pageToken = response.getNextPageToken();\n }\n } while (pageToken != null);\n\n if (studentSubmissions.isEmpty()) {\n System.out.println(\"No student submission found.\");\n } else {\n for (StudentSubmission submission : studentSubmissions) {\n System.out.printf(\"Student submission: %s.\\n\", submission.getId());\n }\n }\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef classroom_list_student_submissions(course_id, coursework_id, user_id):\n \"\"\"\n Creates the courses the user has access to.\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n\n creds, _ = google.auth.default()\n # pylint: disable=maybe-no-member\n submissions = []\n page_token = None\n\n try:\n service = build(\"classroom\", \"v1\", credentials=creds)\n while True:\n coursework = service.courses().courseWork()\n response = (\n coursework.studentSubmissions()\n .list(\n pageToken=page_token,\n courseId=course_id,\n courseWorkId=coursework_id,\n userId=user_id,\n )\n .execute()\n )\n submissions.extend(response.get(\"studentSubmissions\", []))\n page_token = response.get(\"nextPageToken\", None)\n if not page_token:\n break\n\n if not submissions:\n print(\"No student submissions found.\")\n\n print(\"Student Submissions:\")\n for submission in submissions:\n print(\n \"Submitted at:\"\n f\"{(submission.get('id'), submission.get('creationTime'))}\"\n )\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return submissions\n\n\nif __name__ == \"__main__\":\n # Put the course_id, coursework_id and user_id of course whose list needs\n # to be submitted.\n classroom_list_student_submissions(453686957652, 466086979658, \"me\")\n```\n\nExample:\n```text\nservice.courses().courseWork().studentSubmissions()\n .list(courseId, \"-\")\n .set(\"userId\", userId)\n .execute();\n```\n\nExample:\n```text\nservice.courses().courseWork().studentSubmissions().list(\n courseId=<course ID or alias>,\n courseWorkId='-',\n userId=<user ID>).execute()\n```\n\nExample:\n```text\nStudentSubmission studentSubmission = null;\ntry {\n // Create ModifyAttachmentRequest object that includes a new attachment with a link.\n Link link = new Link().setUrl(\"https://en.wikipedia.org/wiki/Irrational_number\");\n Attachment attachment = new Attachment().setLink(link);\n ModifyAttachmentsRequest modifyAttachmentsRequest =\n new ModifyAttachmentsRequest().setAddAttachments(Arrays.asList(attachment));\n\n // The modified studentSubmission object is returned with the new attachment added to it.\n studentSubmission =\n service\n .courses()\n .courseWork()\n .studentSubmissions()\n .modifyAttachments(courseId, courseWorkId, id, modifyAttachmentsRequest)\n .execute();\n\n /* Prints the modified student submission. */\n System.out.printf(\n \"Modified student submission attachments: '%s'.\\n\",\n studentSubmission.getAssignmentSubmission().getAttachments());\n} catch (GoogleJsonResponseException e) {\n // TODO (developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 404) {\n System.out.printf(\n \"The courseId (%s), courseWorkId (%s), or studentSubmissionId (%s) does \"\n + \"not exist.\\n\",\n courseId, courseWorkId, id);\n } else {\n throw e;\n }\n} catch (Exception e) {\n throw e;\n}\nreturn studentSubmission;\n```\n\nExample:\n```text\ndef classroom_add_attachment(course_id, coursework_id, submission_id):\n \"\"\"\n Adds attachment to existing course with specific course_id.\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n # pylint: disable=maybe-no-member\n request = {\n \"addAttachments\": [\n {\"link\": {\"url\": \"http://example.com/quiz-results\"}},\n {\"link\": {\"url\": \"http://example.com/quiz-reading\"}},\n ]\n }\n\n try:\n service = build(\"classroom\", \"v1\", credentials=creds)\n while True:\n coursework = service.courses().courseWork()\n coursework.studentSubmissions().modifyAttachments(\n courseId=course_id,\n courseWorkId=coursework_id,\n id=submission_id,\n body=request,\n ).execute()\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n\n\nif __name__ == \"__main__\":\n # Put the course_id, coursework_id and submission_id of course in which\n # attachment needs to be added.\n classroom_add_attachment(\"course_id\", \"coursework_id\", \"me\")\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.443Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":396,"estimatedTokens":2931}}640{"id":"doc-add_a_classroom_share_button_google_classroom_go-c8f083ec","source":"documentation","title":"Add a Classroom Share Button | Google Classroom | Google for Developers","url":"https://developers.google.com/workspace/classroom/guides/sharebutton","text":"Example:\n```text\n<script src=\"https://apis.google.com/js/platform.js\" async defer></script>\n<g:sharetoclassroom url=\"http://url-to-share\" size=\"32\"></g:sharetoclassroom>\n```\n\nExample:\n```text\n<script src=\"https://apis.google.com/js/platform.js\" async defer></script>\n<div class=\"g-sharetoclassroom\"\n data-url=\"https://developers.google.com/workspace/classroom/\"\n data-size=\"32\">\n</div>\n```\n\nExample:\n```text\n<script>\n window.___gcfg = {\n parsetags: 'onload'\n };\n</script>\n<script src=\"https://apis.google.com/js/platform.js\" async defer></script>\n```\n\nExample:\n```text\ngapi.sharetoclassroom.render(\n container,\n parameters\n)\n```\n\nExample:\n```text\ngapi.sharetoclassroom.go(\n opt_container\n)\n```\n\nExample:\n```text\n<html>\n <head>\n <title>Classroom demo: Basic page</title>\n <link href=\"http://www.example.com\" />\n <script src=\"https://apis.google.com/js/platform.js\" async defer>\n </script>\n </head>\n <body>\n <g:sharetoclassroom size=32 url=\"http://google.com\"></g:sharetoclassroom>\n </body>\n</html>\n```\n\nExample:\n```text\n<html>\n <head>\n <title>Demo: Explicit load of a Classroom share button</title>\n <link href=\"http://www.example.com\" />\n <script>\n window.___gcfg = {\n parsetags: 'explicit'\n };\n </script>\n <script src=\"https://apis.google.com/js/platform.js\">\n </script>\n </head>\n <body>\n <div id=\"content\">\n <div class=\"g-sharetoclassroom\" data-size=\"32\" data-url=\"...\" ></div>\n </div>\n <script>\n gapi.sharetoclassroom.go(\"content\");\n </script>\n </body>\n</html>\n```\n\nExample:\n```text\n<html>\n <head>\n <title>Demo: Explicit render of a Classroom share button</title>\n <link href=\"http://www.example.com\" />\n <script>\n window.___gcfg = {\n parsetags: 'explicit'\n };\n function renderWidget() {\n gapi.sharetoclassroom.render(\"widget-div\",\n {\"url\": \"http://www.google.com\"} );\n }\n </script>\n <script src=\"https://apis.google.com/js/platform.js\">\n </script>\n </head>\n <body>\n <a href=\"#\" onClick=\"renderWidget();\">Render the Classroom share button</a>\n <div id=\"widget-div\"></div>\n </body>\n</html>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.445Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":106,"estimatedTokens":544}}641{"id":"doc-respond_to_google_chat_app_commands_google_for_d-4eb12e8c","source":"documentation","title":"Respond to Google Chat app commands | Google for Developers","url":"https://developers.google.com/workspace/chat/commands","text":"Example:\n```text\n/**\n * Handles slash and quick commands.\n *\n * @param {Object} event - The Google Chat event.\n * @param {Object} res - The HTTP response object.\n */\nfunction handleAppCommands(event, res) {\n const {appCommandId, appCommandType} = event.appCommandMetadata;\n\n switch (appCommandId) {\n case ABOUT_COMMAND_ID:\n return res.send({\n privateMessageViewer: event.user,\n text: 'The Avatar app replies to Google Chat messages.'\n });\n case HELP_COMMAND_ID:\n return res.send({\n privateMessageViewer: event.user,\n text: 'The Avatar app replies to Google Chat messages.'\n });\n }\n}\n```\n\nExample:\n```text\n// Checks for the presence of a slash command in the message.\nif (event.message.slashCommand) {\n // Executes the slash command logic based on its ID.\n // Slash command IDs are set in the Google Chat API configuration.\n switch (event.message.slashCommand.commandId) {\n case ABOUT_COMMAND_ID:\n return {\n privateMessageViewer: event.user,\n text: 'The Avatar app replies to Google Chat messages.'\n };\n }\n}\n```\n\nExample:\n```text\ndef handle_app_commands(event: Mapping[str, Any]) -> Mapping[str, Any]:\n \"\"\"Handles slash and quick commands.\n\n Args:\n Mapping[str, Any] event: The Google Chat event.\n\n Returns:\n Mapping[str, Any]: the response\n \"\"\"\n app_command_id = event[\"appCommandMetadata\"][\"appCommandId\"]\n\n if app_command_id == ABOUT_COMMAND_ID:\n return {\n \"privateMessageViewer\": event[\"user\"],\n \"text\": \"The Avatar app replies to Google Chat messages.\",\n }\n elif app_command_id == HELP_COMMAND_ID:\n return {\n \"privateMessageViewer\": event[\"user\"],\n \"text\": \"The Avatar app replies to Google Chat messages.\",\n }\n return {}\n```\n\nExample:\n```text\n/**\n * Handles slash and quick commands.\n *\n * @param event The Google Chat event.\n * @param response The HTTP response object.\n */\nprivate void handleAppCommands(JsonObject event, HttpResponse response) throws Exception {\n int appCommandId = event.getAsJsonObject(\"appCommandMetadata\").get(\"appCommandId\").getAsInt();\n\n switch (appCommandId) {\n case ABOUT_COMMAND_ID:\n Message aboutMessage = new Message();\n aboutMessage.setText(\"The Avatar app replies to Google Chat messages.\");\n aboutMessage.setPrivateMessageViewer(new User()\n .setName(event.getAsJsonObject(\"user\").get(\"name\").getAsString()));\n response.getWriter().write(gson.toJson(aboutMessage));\n return;\n case HELP_COMMAND_ID:\n Message helpMessage = new Message();\n helpMessage.setText(\"The Avatar app replies to Google Chat messages.\");\n helpMessage.setPrivateMessageViewer(new User()\n .setName(event.getAsJsonObject(\"user\").get(\"name\").getAsString()));\n response.getWriter().write(gson.toJson(helpMessage));\n return;\n }\n}\n```\n\nExample:\n```text\n/**\n * Handles the APP_COMMAND event type. This function is triggered when a user\n * interacts with a quick command within the Google Chat app. It responds\n * based on the command ID.\n *\n * @param {Object} event The event object from Google Chat, containing details\n * about the app command interaction. It includes information like the\n * command ID and the user who triggered it.\n */\nfunction onAppCommand(event) {\n // Executes the quick command logic based on its ID.\n // Command IDs are set in the Google Chat API configuration.\n switch (event.appCommandMetadata.appCommandId) {\n case HELP_COMMAND_ID:\n return {\n privateMessageViewer: event.user,\n text: 'The Avatar app replies to Google Chat messages.'\n };\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event from Google Chat.\n *\n * @param {Object} event The interaction event from Google Chat.\n * @param {Object} res The HTTP response object.\n * @return {Object} The JSON response message with a confirmation.\n */\nfunction handleAppCommand(event, res) {\n // Collect the command ID and type from the event metadata.\n const {appCommandId, appCommandType} = event.appCommandMetadata;\n\n // Use appCommandType to detect message actions.\n if (appCommandType === 'MESSAGE_ACTION' &&\n appCommandId === REMIND_ME_COMMAND_ID) {\n\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n const messageText = event.message.text;\n\n // Return a response that includes details from the original message.\n return res.send({\n text: `Setting a reminder for this message: \"${messageText}\"`\n });\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event in Google Chat.\n *\n * @param {Object} event The interaction event from Google Chat.\n * @return {Object} The JSON response message with a confirmation.\n */\nfunction onAppCommand(event) {\n // Collect the command ID and type from the event metadata.\n const {appCommandId, appCommandType} = event.appCommandMetadata;\n\n if (appCommandType === 'MESSAGE_ACTION' &&\n appCommandId === REMIND_ME_COMMAND_ID) {\n\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n const messageText = event.message.text;\n\n // Return a response that includes details from the original message.\n return { \"text\": \"Setting a reminder for message: \" + messageText };\n }\n}\n```\n\nExample:\n```text\ndef handle_app_command(event):\n \"\"\"Responds to an APP_COMMAND interaction event from Google Chat.\n\n Args:\n event (dict): The interaction event from Google Chat.\n\n Returns:\n dict: The JSON response message with a confirmation.\n \"\"\"\n # Collect the command ID and type from the event metadata.\n metadata = event.get('appCommandMetadata', {})\n if metadata.get('appCommandType') == 'MESSAGE_ACTION' and \\\n metadata.get('appCommandId') == REMIND_ME_COMMAND_ID:\n\n # Message actions can access the context of the message they were\n # invoked on, such as the text or sender of that message.\n message_text = event.get('message', {}).get('text')\n\n # Return a response that includes details from the original message.\n return {\n \"text\": f'Setting a reminder for message: \"{message_text}\"'\n }\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event from Google Chat.\n *\n * @param event The interaction event from Google Chat.\n * @param response The HTTP response object.\n */\nvoid handleAppCommand(JsonObject event, HttpResponse response) throws Exception {\n // Collect the command ID and type from the event metadata.\n JsonObject metadata = event.getAsJsonObject(\"appCommandMetadata\");\n String appCommandType = metadata.get(\"appCommandType\").getAsString();\n\n if (appCommandType.equals(\"MESSAGE_ACTION\")) {\n int commandId = metadata.get(\"appCommandId\").getAsInt();\n if (commandId == REMIND_ME_COMMAND_ID) {\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n String messageText = event.getAsJsonObject(\"message\").get(\"text\").getAsString();\n\n // Return a response that includes details from the original message.\n JsonObject responseMessage = new JsonObject();\n responseMessage.addProperty(\"text\", \"Setting a reminder for message: \" + messageText);\n response.getWriter().write(responseMessage.toString());\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.447Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":232,"estimatedTokens":1884}}642{"id":"doc-overview_google_forms_google_for_developers-68b5f544","source":"documentation","title":"Overview | Google Forms | Google for Developers","url":"https://developers.google.com/workspace/forms/api","text":"Example:\n```text\n{\n \"formId\": \"FORM_ID\",\n \"info\": {\n \"title\": \"Famous Black Women\",\n \"description\": \"Please complete this quiz based off of this week's readings for class.\",\n \"documentTitle\": \"Famous Black Women\"\n },\n \"settings\": {\n \"quizSettings\": {\n \"isQuiz\": true\n }\n },\n \"revisionId\": \"00000021\",\n \"responderUri\": \"https://docs.google.com/forms/d/e/1FAIpQLSd0iBLPh4suZoGW938EU1WIxzObQv_jXto0nT2U8HH2KsI5dg/viewform\",\n \"items\": [\n {\n \"itemId\": \"5d9f9786\",\n \"imageItem\": {\n \"image\": {\n \"contentUri\": \"DIRECT_URL\",\n \"properties\": {\n \"alignment\": \"LEFT\"\n }\n }\n }\n },\n {\n \"itemId\": \"72b30353\",\n \"title\": \"Which African American woman authored \\\"I Know Why the Caged Bird Sings\\\"?\",\n \"questionItem\": {\n \"question\": {\n \"questionId\": \"25405d4e\",\n \"required\": true,\n \"grading\": {\n \"pointValue\": 2,\n \"correctAnswers\": {\n \"answers\": [\n {\n \"value\": \"Maya Angelou\"\n }\n ]\n }\n },\n \"choiceQuestion\": {\n \"type\": \"RADIO\",\n \"options\": [\n {\n \"value\": \"Maya Angelou\"\n },\n {\n \"value\": \"bell hooks\"\n },\n {\n \"value\": \"Alice Walker\"\n },\n {\n \"value\": \"Roxane Gay\"\n }\n ]\n }\n }\n }\n },\n {\n \"itemId\": \"0a4859c8\",\n \"title\": \"Who was the first Dominican-American woman elected to state office?\",\n \"questionItem\": {\n \"question\": {\n \"questionId\": \"37fff47a\",\n \"grading\": {\n \"pointValue\": 2,\n \"correctAnswers\": {\n \"answers\": [\n {\n \"value\": \"Grace Diaz\"\n }\n ]\n }\n },\n \"choiceQuestion\": {\n \"type\": \"RADIO\",\n \"options\": [\n {\n \"value\": \"Rosa Clemente\"\n },\n {\n \"value\": \"Grace Diaz\"\n },\n {\n \"value\": \"Juana Matias\"\n },\n {\n \"value\": \"Sabrina Matos\"\n }\n ]\n }\n }\n }\n }\n ],\n \"publishSettings\" : {\n \"isPublished\": true,\n \"isAcceptingResponses\": true\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.452Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":110,"estimatedTokens":632}}643{"id":"doc-work_with_the_meet_ecdn_on_premises_api_google_m-dd50d101","source":"documentation","title":"Work with the Meet eCDN On-Premises API | Google Meet | Google for Developers","url":"https://developers.google.com/workspace/meet/live-streaming/ecdn-on-premises-api","text":"Example:\n```text\nPOST /v1/get-peering-group\nContent-Type: application/json\n\nRequest body:\n{\n \"availableIPs\": []{\n \"format\": \"ipv4\"|\"ipv6\",\n \"address\": \"DETECTED_ADDRESS\"\n }\n}\n\nError response:\n{\n \"result\": null,\n \"error\": \"ERROR_MESSAGE\"\n}\n\nResponse body:\n{\n \"allowed\": boolean,\n \"result\": string,\n \"error\": null\n}\n```\n\nExample:\n```text\nLegacy response body:\n{\n \"result\": string,\n \"error\": null,\n}\n```\n\nExample:\n```text\nPOST /v1/encrypt-sdp\nContent-Type: application/json\n\nRequest body:\n{\n \"data\": \"SDP_DATA\"\n},\n\nError response:\n{\n \"result\": null,\n \"error\": \"ERROR_MESSAGE\"\n}\n\nResponse body:\n{\n \"result\": \"ENCRYPTED_DATA_STRING\",\n \"error\": null\n}\n```\n\nExample:\n```text\nPOST /v1/decrypt-sdp\nContent-Type: application/json\n\nRequest body:\n{\n \"data\": \"ENCRYPTED_DATA_STRING\"\n},\n\nError response:\n{\n \"result\": null,\n \"error\": \"ERROR_MESSAGE\"\n}\n\nResponse body:\n{\n \"result\": \"SDP_DATA\",\n \"error\": null\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.453Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":83,"estimatedTokens":235}}644{"id":"doc-configure_the_slides_mcp_server_google_slides_go-f58f0167","source":"documentation","title":"Configure the Slides MCP server | Google Slides | Google for Developers","url":"https://developers.google.com/workspace/slides/api/guides/configure-mcp-server","text":"Example:\n```text\ngcloud services enable slides.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable slidesmcp.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\n{\n \"mcpServers\": {\n \"slides\": {\n \"serverUrl\": \"https://slidesmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n }\n }\n}\n```\n\nExample:\n```text\nagy\n```\n\nExample:\n```text\n/mcp\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.456Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":36,"estimatedTokens":122}}645{"id":"doc-configure_the_calendar_mcp_server_google_calenda-84c85e34","source":"documentation","title":"Configure the Calendar MCP server | Google Calendar | Google for Developers","url":"https://developers.google.com/workspace/calendar/api/guides/configure-mcp-server","text":"Example:\n```text\ngcloud services enable calendar-json.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable calendarmcp.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\n{\n \"mcpServers\": {\n \"calendar\": {\n \"serverUrl\": \"https://calendarmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n }\n }\n}\n```\n\nExample:\n```text\nagy\n```\n\nExample:\n```text\n/mcp\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.457Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":36,"estimatedTokens":125}}646{"id":"doc-manage_courses_google_classroom_google_for_devel-67df043d","source":"documentation","title":"Manage Courses | Google Classroom | Google for Developers","url":"https://developers.google.com/workspace/classroom/guides/manage-courses","text":"Example:\n```text\nusing Google;\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Classroom.v1;\nusing Google.Apis.Classroom.v1.Data;\nusing Google.Apis.Services;\nusing System;\n\nnamespace ClassroomSnippets\n{ \n // Class to demonstrate the use of Classroom Create Course API\n public class CreateCourse\n {\n /// <summary>\n /// Creates a new course with description.\n /// </summary>\n /// <returns>newly created course</returns>\n public static Course ClassroomCreateCourse()\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(ClassroomService.Scope.ClassroomCourses);\n\n // Create Classroom API service.\n var service = new ClassroomService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Classroom API Snippets\"\n });\n\n // Create a new course with description.\n var course = new Course\n {\n Name = \"10th Grade Biology\",\n Section = \"Period 2\",\n DescriptionHeading = \"Welcome to 10th Grade Biology\",\n Description = \"We'll be learning about about the structure of living creatures \"\n + \"from a combination of textbooks, guest lectures, and lab work. Expect \"\n + \"to be excited!\",\n Room = \"301\",\n OwnerId = \"me\",\n CourseState = \"PROVISIONED\"\n };\n\n course = service.Courses.Create(course).Execute();\n // Prints the new created course Id and name.\n Console.WriteLine(\"Course created: {0} ({1})\", course.Name, course.Id);\n return course;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is GoogleApiException)\n { \n Console.WriteLine(\"OwnerId not specified.\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Creates 10th Grade Biology Course.\n * @see https://developers.google.com/classroom/reference/rest/v1/courses/create\n * return {string} Id of created course\n */\nfunction createCourse() {\n let course = {\n name: \"10th Grade Biology\",\n section: \"Period 2\",\n descriptionHeading: \"Welcome to 10th Grade Biology\",\n description:\n \"We'll be learning about the structure of living creatures from a combination \" +\n \"of textbooks, guest lectures, and lab work. Expect to be excited!\",\n room: \"301\",\n ownerId: \"me\",\n courseState: \"PROVISIONED\",\n };\n try {\n // Create the course using course details.\n course = Classroom.Courses.create(course);\n console.log(\"Course created: %s (%s)\", course.name, course.id);\n return course.id;\n } catch (err) {\n // TODO (developer) - Handle Courses.create() exception\n console.log(\n \"Failed to create course %s with an error %s\",\n course.name,\n err.message,\n );\n }\n}\n```\n\nExample:\n```text\nc := &classroom.Course{\n\tName: \"10th Grade Biology\",\n\tSection: \"Period 2\",\n\tDescriptionHeading: \"Welcome to 10th Grade Biology\",\n\tDescription: \"We'll be learning about about the structure of living creatures from a combination of textbooks, guest lectures, and lab work. Expect to be excited!\",\n\tRoom: \"301\",\n\tOwnerId: \"me\",\n\tCourseState: \"PROVISIONED\",\n}\ncourse, err := srv.Courses.Create(c).Do()\nif err != nil {\n\tlog.Fatalf(\"Course unable to be created %v\", err)\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.classroom.Classroom;\nimport com.google.api.services.classroom.ClassroomScopes;\nimport com.google.api.services.classroom.model.Course;\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\n/* Class to demonstrate the use of Classroom Create Course API */\npublic class CreateCourse {\n /* Scopes required by this API call. If modifying these scopes, delete your previously saved\n tokens/ folder. */\n static ArrayList<String> SCOPES =\n new ArrayList<>(Arrays.asList(ClassroomScopes.CLASSROOM_COURSES));\n\n /**\n * Creates a course\n *\n * @return newly created course\n * @throws IOException - if credentials file not found.\n * @throws GeneralSecurityException - if a new instance of NetHttpTransport was not created.\n */\n public static Course createCourse() throws GeneralSecurityException, IOException {\n\n // Create the classroom API client.\n final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();\n Classroom service =\n new Classroom.Builder(\n HTTP_TRANSPORT,\n GsonFactory.getDefaultInstance(),\n ClassroomCredentials.getCredentials(HTTP_TRANSPORT, SCOPES))\n .setApplicationName(\"Classroom samples\")\n .build();\n\n Course course = null;\n try {\n // Adding a new course with description. Set CourseState to `ACTIVE`. Possible values of\n // CourseState can be found here:\n // https://developers.google.com/classroom/reference/rest/v1/courses#coursestate\n course =\n new Course()\n .setName(\"10th Grade Biology\")\n .setSection(\"Period 2\")\n .setDescriptionHeading(\"Welcome to 10th Grade Biology\")\n .setDescription(\n \"We'll be learning about about the structure of living creatures \"\n + \"from a combination of textbooks, guest lectures, and lab work. Expect \"\n + \"to be excited!\")\n .setRoom(\"301\")\n .setOwnerId(\"me\")\n .setCourseState(\"ACTIVE\");\n course = service.courses().create(course).execute();\n // Prints the new created course Id and name\n System.out.printf(\"Course created: %s (%s)\\n\", course.getName(), course.getId());\n } catch (GoogleJsonResponseException e) {\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 400) {\n System.err.println(\"Unable to create course, ownerId not specified.\\n\");\n } else {\n throw e;\n }\n }\n return course;\n }\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Classroom;\nuse Google\\Service\\Classroom\\Course;\nuse Google\\Service\\Exception;\n\nfunction createCourse()\n{\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(\"https://www.googleapis.com/auth/classroom.courses\");\n $service = new Classroom($client);\n try {\n $course = new Course([\n 'name' => '10th Grade Biology',\n 'section' => 'Period 2',\n 'descriptionHeading' => 'Welcome to 10th Grade Biology',\n 'description' => 'We\\'ll be learning about about the structure of living ' .\n 'creatures from a combination of textbooks, guest ' .\n 'lectures, and lab work. Expect to be excited!',\n 'room' => '301',\n 'ownerId' => 'me',\n 'courseState' => 'PROVISIONED'\n ]);\n $course = $service->courses->create($course);\n printf(\"Course created: %s (%s)\\n\", $course->name, $course->id);\n return $course;\n } catch (Exception $e) {\n echo 'Message: ' . $e->getMessage();\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef classroom_create_course():\n \"\"\"\n Creates the courses the user has access to.\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n\n creds, _ = google.auth.default()\n # pylint: disable=maybe-no-member\n\n try:\n service = build(\"classroom\", \"v1\", credentials=creds)\n course = {\n \"name\": \"10th Grade Mathematics Probability-2\",\n \"section\": \"Period 3\",\n \"descriptionHeading\": \"Welcome to 10th Grade Mathematics\",\n \"description\": \"\"\"We'll be learning about about the\n polynomials from a\n combination of textbooks and guest lectures.\n Expect to be excited!\"\"\",\n \"room\": \"302\",\n \"ownerId\": \"me\",\n \"courseState\": \"PROVISIONED\",\n }\n # pylint: disable=maybe-no-member\n course = service.courses().create(body=course).execute()\n print(f\"Course created: {(course.get('name'), course.get('id'))}\")\n return course\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return error\n\n\nif __name__ == \"__main__\":\n classroom_create_course()\n```\n\nExample:\n```text\nusing Google;\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Classroom.v1;\nusing Google.Apis.Classroom.v1.Data;\nusing Google.Apis.Services;\nusing System;\n\nnamespace ClassroomSnippets\n{\n // Class to demonstrate the use of Classroom Get Course API\n public class GetCourse\n {\n /// <summary>\n /// Retrieve a single course's metadata.\n /// </summary>\n /// <param name=\"courseId\">Id of the course.</param>\n /// <returns>a course, null otherwise.</returns>\n public static Course ClassroomGetCourse(string courseId)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(ClassroomService.Scope.ClassroomCourses);\n\n // Create Classroom API service.\n var service = new ClassroomService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Classroom Snippets\"\n });\n\n // Get the course details using course id\n Course course = service.Courses.Get(courseId).Execute();\n Console.WriteLine(\"Course '{0}' found.\\n\", course.Name);\n return course;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is GoogleApiException)\n {\n Console.WriteLine(\"Course does not exist.\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Retrieves course by id.\n * @param {string} courseId\n * @see https://developers.google.com/classroom/reference/rest/v1/courses/get\n */\nfunction getCourse(courseId) {\n try {\n // Get the course details using course id\n const course = Classroom.Courses.get(courseId);\n console.log('Course \"%s\" found. ', course.name);\n } catch (err) {\n // TODO (developer) - Handle Courses.get() exception of Handle Classroom API\n console.log(\n \"Failed to found course %s with error %s \",\n courseId,\n err.message,\n );\n }\n}\n```\n\nExample:\n```text\nctx := context.Background()\nsrv, err := classroom.NewService(ctx, option.WithHTTPClient(client))\nif err != nil {\n\tlog.Fatalf(\"Unable to create classroom Client %v\", err)\n}\nid := \"123456\"\ncourse, err := srv.Courses.Get(id).Do()\nif err != nil {\n\tlog.Fatalf(\"Course unable to be retrieved %v\", err)\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.classroom.Classroom;\nimport com.google.api.services.classroom.ClassroomScopes;\nimport com.google.api.services.classroom.model.Course;\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\n/* Class to demonstrate the use of Classroom Get Course API */\npublic class GetCourse {\n\n /* Scopes required by this API call. If modifying these scopes, delete your previously saved\n tokens/ folder. */\n static ArrayList<String> SCOPES =\n new ArrayList<>(Arrays.asList(ClassroomScopes.CLASSROOM_COURSES));\n\n /**\n * Retrieve a single course's metadata.\n *\n * @param courseId - Id of the course to return.\n * @return a course\n * @throws IOException - if credentials file not found.\n * @throws GeneralSecurityException - if a new instance of NetHttpTransport was not created.\n */\n public static Course getCourse(String courseId) throws GeneralSecurityException, IOException {\n\n // Create the classroom API client.\n final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();\n Classroom service =\n new Classroom.Builder(\n HTTP_TRANSPORT,\n GsonFactory.getDefaultInstance(),\n ClassroomCredentials.getCredentials(HTTP_TRANSPORT, SCOPES))\n .setApplicationName(\"Classroom samples\")\n .build();\n\n Course course = null;\n try {\n course = service.courses().get(courseId).execute();\n System.out.printf(\"Course '%s' found.\\n\", course.getName());\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 404) {\n System.out.printf(\"Course with ID '%s' not found.\\n\", courseId);\n } else {\n throw e;\n }\n }\n return course;\n }\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Classroom;\nuse Google\\Service\\Exception;\n\nfunction getCourse($courseId)\n{\n /* Load pre-authorized user credentials from the environment.\n TODO (developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(\"https://www.googleapis.com/auth/classroom.courses\");\n $service = new Classroom($client);\n try {\n $course = $service->courses->get($courseId);\n printf(\"Course '%s' found.\\n\", $course->name);\n return $course;\n } catch (Exception $e) {\n if ($e->getCode() == 404) {\n printf(\"Course with ID '%s' not found.\\n\", $courseId);\n } else {\n throw $e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef classroom_get_course(course_id):\n \"\"\"\n Prints the name of the with specific course_id.\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n\n creds, _ = google.auth.default()\n # pylint: disable=maybe-no-member\n course = None\n try:\n service = build(\"classroom\", \"v1\", credentials=creds)\n course = service.courses().get(id=course_id).execute()\n print(f\"Course found : {course.get('name')}\")\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n print(f\"Course not found: {course_id}\")\n return error\n return course\n\n\nif __name__ == \"__main__\":\n # Put the course_id of course whose information needs to be fetched.\n classroom_get_course(\"course_id\")\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Classroom.v1;\nusing Google.Apis.Classroom.v1.Data;\nusing Google.Apis.Services;\nusing System;\nusing System.Collections.Generic;\n\nnamespace ClassroomSnippets\n{\n // Class to demonstrate the use of Classroom List Course API\n public class ListCourses\n {\n /// <summary>\n /// Retrieves all courses with metadata.\n /// </summary>\n /// <returns>list of courses with its metadata, null otherwise.</returns>\n public static List<Course> ClassroomListCourses()\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(ClassroomService.Scope.ClassroomCourses);\n\n // Create Classroom API service.\n var service = new ClassroomService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Classroom Snippets\"\n });\n\n string pageToken = null;\n var courses = new List<Course>();\n\n do\n {\n var request = service.Courses.List();\n request.PageSize = 100;\n request.PageToken = pageToken;\n var response = request.Execute();\n courses.AddRange(response.Courses);\n pageToken = response.NextPageToken;\n } while (pageToken != null);\n\n Console.WriteLine(\"Courses:\");\n foreach (var course in courses)\n {\n // Print the courses available in classroom\n Console.WriteLine(\"{0} ({1})\", course.Name, course.Id);\n } \n return courses;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is ArgumentNullException)\n {\n Console.WriteLine(\"No courses found.\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Lists all course names and ids.\n * @see https://developers.google.com/classroom/reference/rest/v1/courses/list\n */\nfunction listCourses() {\n let courses = [];\n const pageToken = null;\n const optionalArgs = {\n pageToken: pageToken,\n pageSize: 100,\n };\n try {\n const response = Classroom.Courses.list(optionalArgs);\n courses = response.courses;\n if (courses.length === 0) {\n console.log(\"No courses found.\");\n return;\n }\n // Print the courses available in classroom\n console.log(\"Courses:\");\n for (const course in courses) {\n console.log(\"%s (%s)\", courses[course].name, courses[course].id);\n }\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.classroom.Classroom;\nimport com.google.api.services.classroom.ClassroomScopes;\nimport com.google.api.services.classroom.model.Course;\nimport com.google.api.services.classroom.model.ListCoursesResponse;\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/* Class to demonstrate the use of Classroom List Course API */\npublic class ListCourses {\n\n /* Scopes required by this API call. If modifying these scopes, delete your previously saved\n tokens/ folder. */\n static ArrayList<String> SCOPES =\n new ArrayList<>(Arrays.asList(ClassroomScopes.CLASSROOM_COURSES));\n\n /**\n * Retrieves all courses with metadata\n *\n * @return list of courses with its metadata\n * @throws IOException - if credentials file not found.\n * @throws GeneralSecurityException - if a new instance of NetHttpTransport was not created.\n */\n public static List<Course> listCourses() throws GeneralSecurityException, IOException {\n\n // Create the classroom API client.\n final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();\n Classroom service =\n new Classroom.Builder(\n HTTP_TRANSPORT,\n GsonFactory.getDefaultInstance(),\n ClassroomCredentials.getCredentials(HTTP_TRANSPORT, SCOPES))\n .setApplicationName(\"Classroom samples\")\n .build();\n\n String pageToken = null;\n List<Course> courses = new ArrayList<>();\n\n try {\n do {\n ListCoursesResponse response =\n service.courses().list().setPageSize(100).setPageToken(pageToken).execute();\n courses.addAll(response.getCourses());\n pageToken = response.getNextPageToken();\n } while (pageToken != null);\n\n if (courses.isEmpty()) {\n System.out.println(\"No courses found.\");\n } else {\n System.out.println(\"Courses:\");\n for (Course course : courses) {\n System.out.printf(\"%s (%s)\\n\", course.getName(), course.getId());\n }\n }\n } catch (NullPointerException ne) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"No courses found.\\n\");\n }\n return courses;\n }\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Service\\Classroom;\nuse Google\\Client;\n\nfunction listCourses(): array\n{\n /* Load pre-authorized user credentials from the environment.\n TODO (developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(\"https://www.googleapis.com/auth/classroom.courses\");\n $service = new Classroom($client);\n $courses = [];\n $pageToken = '';\n\n do {\n $params = [\n 'pageSize' => 100,\n 'pageToken' => $pageToken\n ];\n $response = $service->courses->listCourses($params);\n $courses = array_merge($courses, $response->courses);\n $pageToken = $response->nextPageToken;\n } while (!empty($pageToken));\n\n if (count($courses) == 0) {\n print \"No courses found.\\n\";\n } else {\n print \"Courses:\\n\";\n foreach ($courses as $course) {\n printf(\"%s (%s)\\n\", $course->name, $course->id);\n }\n }\n return $courses;\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef classroom_list_courses():\n \"\"\"\n Prints the list of the courses the user has access to.\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n\n creds, _ = google.auth.default()\n try:\n service = build(\"classroom\", \"v1\", credentials=creds)\n courses = []\n page_token = None\n\n while True:\n # pylint: disable=maybe-no-member\n response = (\n service.courses().list(pageToken=page_token, pageSize=100).execute()\n )\n courses.extend(response.get(\"courses\", []))\n page_token = response.get(\"nextPageToken\", None)\n if not page_token:\n break\n\n if not courses:\n print(\"No courses found.\")\n return\n print(\"Courses:\")\n for course in courses:\n print(f\"{course.get('name'), course.get('id')}\")\n return courses\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return error\n\n\nif __name__ == \"__main__\":\n print(\"Courses available are-------\")\n classroom_list_courses()\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Classroom.v1;\nusing Google.Apis.Classroom.v1.Data;\nusing Google.Apis.Services;\nusing System;\nusing System.Net;\nusing Google;\n\nnamespace ClassroomSnippets\n{\n // Class to demonstrate the use of Classroom Update Course API\n public class UpdateCourse\n {\n /// <summary>\n /// Update one field of course \n /// </summary>\n /// <param name=\"courseId\"></param>\n /// <returns></returns>\n /// <exception cref=\"GoogleApiException\"></exception>\n public static Course ClassroomUpdateCourse(string courseId)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(ClassroomService.Scope.ClassroomCourses);\n\n // Create Classroom API service.\n var service = new ClassroomService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Classroom API Snippet\"\n });\n\n Course course = service.Courses.Get(courseId).Execute();\n course.Section = \"Period 3\";\n course.Room = \"302\";\n course = service.Courses.Update(course, courseId).Execute();\n Console.WriteLine(\"Course '{0}' updated.\\n\", course.Name);\n return course;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is GoogleApiException)\n {\n Console.WriteLine(\"Failed to update the course. Error message: {0}\", e.Message);\n }\n else\n {\n throw;\n }\n }\n\n return null;\n\n }\n }\n\n}\n```\n\nExample:\n```text\n/**\n * Updates the section and room of Google Classroom.\n * @param {string} courseId\n * @see https://developers.google.com/classroom/reference/rest/v1/courses/update\n */\nfunction courseUpdate(courseId) {\n try {\n // Get the course using course ID\n let course = Classroom.Courses.get(courseId);\n course.section = \"Period 3\";\n course.room = \"302\";\n // Update the course\n course = Classroom.Courses.update(course, courseId);\n console.log('Course \"%s\" updated.', course.name);\n } catch (e) {\n // TODO (developer) - Handle exception\n console.log(\"Failed to update the course with error %s\", e.message);\n }\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.classroom.Classroom;\nimport com.google.api.services.classroom.ClassroomScopes;\nimport com.google.api.services.classroom.model.Course;\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\n/* Class to demonstrate the use of Classroom Update Course API */\npublic class UpdateCourse {\n\n /* Scopes required by this API call. If modifying these scopes, delete your previously saved\n tokens/ folder. */\n static ArrayList<String> SCOPES =\n new ArrayList<>(Arrays.asList(ClassroomScopes.CLASSROOM_COURSES));\n /**\n * Updates a course's metadata.\n *\n * @param courseId - Id of the course to update.\n * @return updated course\n * @throws IOException - if credentials file not found.\n * @throws GeneralSecurityException - if a new instance of NetHttpTransport was not created.\n */\n public static Course updateCourse(String courseId) throws GeneralSecurityException, IOException {\n\n // Create the classroom API client.\n final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();\n Classroom service =\n new Classroom.Builder(\n HTTP_TRANSPORT,\n GsonFactory.getDefaultInstance(),\n ClassroomCredentials.getCredentials(HTTP_TRANSPORT, SCOPES))\n .setApplicationName(\"Classroom samples\")\n .build();\n\n Course course = null;\n try {\n // Updating the section and room in a course\n course = service.courses().get(courseId).execute();\n course.setSection(\"Period 3\");\n course.setRoom(\"302\");\n course = service.courses().update(courseId, course).execute();\n // Prints the updated course\n System.out.printf(\"Course '%s' updated.\\n\", course.getName());\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 404) {\n System.err.println(\"Course does not exist.\\n\");\n } else {\n throw e;\n }\n }\n return course;\n }\n}\n```\n\nExample:\n```text\n<?php\n\nuse Google\\Client;\nuse Google\\Service\\Classroom;\n\nfunction updateCourse($courseId)\n{\n /* Load pre-authorized user credentials from the environment.\n TODO (developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(\"https://www.googleapis.com/auth/classroom.courses\");\n $service = new Classroom($client);\n $course = $service->courses->get($courseId);\n $course->section = 'Period 3';\n $course->room = '302';\n $course = $service->courses->update($courseId, $course);\n printf(\"Course '%s' updated.\\n\", $course->name);\n return $course;\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef classroom_update_course(course_id):\n \"\"\"\n Updates the courses names the user has access to.\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n # pylint: disable=maybe-no-member\n\n creds, _ = google.auth.default()\n\n try:\n service = build(\"classroom\", \"v1\", credentials=creds)\n\n # Updates the section and room of Google Classroom.\n course = service.courses().get(id=course_id).execute()\n course[\"name\"] = \"10th Grade Physics - Light\"\n course[\"section\"] = \"Period 4\"\n course[\"room\"] = \"410\"\n course = service.courses().update(id=course_id, body=course).execute()\n print(f\" Updated Course is: {course.get('name')}\")\n return course\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return error\n\n\nif __name__ == \"__main__\":\n # Put the course_id of course whose course needs to be updated.\n classroom_update_course(\"course_id\")\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Classroom.v1;\nusing Google.Apis.Classroom.v1.Data;\nusing Google.Apis.Services;\nusing System;\nusing Google;\n\nnamespace ClassroomSnippets\n{\n // Class to demonstrate the use of Classroom Patch Course API\n public class PatchUpdate\n {\n /// <summary>\n /// Updates one or more fields in a course.\n /// </summary>\n /// <param name=\"courseId\"></param>\n /// <returns></returns>\n /// <exception cref=\"GoogleApiException\"></exception>\n public static Course ClassroomPatchUpdate(string courseId)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(ClassroomService.Scope.ClassroomCourses);\n\n // Create Classroom API service.\n var service = new ClassroomService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Classroom API Snippet\"\n });\n\n var course = new Course\n {\n Section = \"Period 3\",\n Room = \"302\"\n };\n // Updates one or more fields of course.\n var request = service.Courses.Patch(course, courseId);\n request.UpdateMask = \"section,room\";\n course = request.Execute();\n Console.WriteLine(\"Course '{0}' updated.\\n\", course.Name);\n return course;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is GoogleApiException)\n {\n Console.WriteLine(\"Failed to update the course. Error message: {0}\", e.Message);\n }\n else\n {\n throw ;\n }\n }\n\n return null;\n\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Updates the section and room of Google Classroom.\n * @param {string} courseId\n * @see https://developers.google.com/classroom/reference/rest/v1/courses/patch\n */\nfunction coursePatch(courseId) {\n const course = {\n section: \"Period 3\",\n room: \"302\",\n };\n const options = {\n updateMask: \"section,room\",\n };\n // Update section and room in course.\n const updatedCourse = Classroom.Courses.patch(course, courseId, options);\n console.log(`Course \"${updatedCourse.name}\" updated.`);\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.classroom.Classroom;\nimport com.google.api.services.classroom.ClassroomScopes;\nimport com.google.api.services.classroom.model.Course;\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\n\n/* Class to demonstrate the use of Classroom Patch Course API */\npublic class PatchCourse {\n\n /* Scopes required by this API call. If modifying these scopes, delete your previously saved\n tokens/ folder. */\n static ArrayList<String> SCOPES =\n new ArrayList<>(Arrays.asList(ClassroomScopes.CLASSROOM_COURSES));\n\n /**\n * Updates one or more fields in a course.\n *\n * @param courseId - Id of the course to update.\n * @return updated course\n * @throws IOException - if credentials file not found.\n * @throws GeneralSecurityException - if a new instance of NetHttpTransport was not created.\n */\n public static Course patchCourse(String courseId) throws GeneralSecurityException, IOException {\n\n // Create the classroom API client.\n final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();\n Classroom service =\n new Classroom.Builder(\n HTTP_TRANSPORT,\n GsonFactory.getDefaultInstance(),\n ClassroomCredentials.getCredentials(HTTP_TRANSPORT, SCOPES))\n .setApplicationName(\"Classroom samples\")\n .build();\n\n Course course = null;\n try {\n course = new Course().setSection(\"Period 3\").setRoom(\"302\");\n course = service.courses().patch(courseId, course).setUpdateMask(\"section,room\").execute();\n System.out.printf(\"Course '%s' updated.\\n\", course.getName());\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 404) {\n System.err.println(\"Course does not exist.\\n\");\n } else {\n throw e;\n }\n }\n return course;\n }\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Service\\Classroom;\nuse Google\\Service\\Classroom\\Course;\nuse Google\\Client;\n\nfunction patchCourse($courseId)\n{\n /* Load pre-authorized user credentials from the environment.\n TODO (developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(\"https://www.googleapis.com/auth/classroom.courses\");\n $service = new Classroom($client);\n\n try {\n $course = new Course([\n 'section' => 'Period 3',\n 'room' => '302'\n ]);\n $params = ['updateMask' => 'section,room'];\n $course = $service->courses->patch($courseId, $course, $params);\n printf(\"Course '%s' updated.\\n\", $course->name);\n return $course;\n } catch (Exception $e) {\n echo 'Message: ' . $e->getMessage();\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef classroom_patch_course(course_id):\n \"\"\"\n Patch new course with existing course in the account the user has access to.\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n # pylint: disable=maybe-no-member\n\n creds, _ = google.auth.default()\n\n try:\n service = build(\"classroom\", \"v1\", credentials=creds)\n course = {\"section\": \"Period 3\", \"room\": \"313\"}\n course = (\n service.courses()\n .patch(id=course_id, updateMask=\"section,room\", body=course)\n .execute()\n )\n print(f\" Course updated are: {course.get('name')}\")\n return course\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n\n\nif __name__ == \"__main__\":\n # Put the course_id of course with whom we need to patch some extra\n # information.\n classroom_patch_course(\"course_id\")\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.460Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":27,"totalLines":1229,"estimatedTokens":9967}}647{"id":"doc-introduction_google_slides_google_for_developers-ac837c27","source":"documentation","title":"Introduction | Google Slides | Google for Developers","url":"https://developers.google.com/workspace/slides/api/guides/overview","text":"Example:\n```text\nhttps://docs.google.com/presentation/d/presentationId/edit\n```\n\nExample:\n```text\n/presentation/d/([a-zA-Z0-9-_]+)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.462Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":37}}648{"id":"doc-groups_settings_api_overview_admin_console_googl-db9e48de","source":"documentation","title":"Groups Settings API overview | Admin console | Google for Developers","url":"https://developers.google.com/workspace/admin/groups-settings","text":"Example:\n```text\nGET https://www.googleapis.com/groups/v1/groups/salesgroup@example.com?alt=json\n```\n\nExample:\n```text\nGET https://www.googleapis.com/groups/v1/groups/salesgroup@example.com?alt=atom\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.463Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":54}}649{"id":"doc-add_and_remove_note_collaborators_with_permissio-4a7f3653","source":"documentation","title":"Add and remove note collaborators with permissions | Google Keep | Google for Developers","url":"https://developers.google.com/workspace/keep/api/guides/modify-permissions","text":"Example:\n```text\n/**\n * Grants write access to a user and to a Google group for the given note.\n *\n * @param note The note whose permissions will be updated.\n * @param userEmail Email address of the user that will be added to the\n * permissions of the note.\n * @param groupEmail Email address of the Google Groups that will be\n * added to the permissions of the note.\n * @throws IOException\n * @return The response of the create permissions request.\n */\nprivate BatchCreatePermissionsResponse addPermissions(\n Note note, String userEmail, String groupEmail) throws IOException {\n String noteName = note.getName();\n CreatePermissionRequest userPermission =\n new CreatePermissionRequest()\n .setParent(noteName)\n .setPermission(new Permission().setEmail(userEmail).setRole(\"WRITER\"));\n\n CreatePermissionRequest groupPermission =\n new CreatePermissionRequest()\n .setParent(noteName)\n .setPermission(new Permission().setEmail(groupEmail).setRole(\"WRITER\"));\n\n BatchCreatePermissionsRequest batchCreatePermissionsRequest =\n new BatchCreatePermissionsRequest()\n .setRequests(Arrays.asList(userPermission, groupPermission));\n\n return keepService\n .notes()\n .permissions()\n .batchCreate(noteName, batchCreatePermissionsRequest)\n .execute();\n}\n```\n\nExample:\n```text\n/**\n * Deletes all permissions of a given note excluding the owner. The owner\n * can't be removed from a note's permissions.\n *\n * @param note The note whose permissions will be deleted.\n * @throws IOException\n */\nprivate void deletePermissions(Note note) throws IOException {\n List<Permission> notePermissions =\n keepService.notes().get(note.getName()).execute().getPermissions();\n\n // List of users, groups or families that will be deleted from the\n // permissions of the note.\n List<String> permissionsToDelete = new ArrayList<>();\n\n for (Permission permission : notePermissions) {\n // The note owner can't be removed from the permissions. Trying to\n // remove the owner causes an exception.\n if (!permission.getRole().equals(\"OWNER\")) {\n permissionsToDelete.add(permission.getName());\n }\n }\n\n keepService\n .notes()\n .permissions()\n .batchDelete(\n note.getName(),\n new BatchDeletePermissionsRequest().setNames(permissionsToDelete))\n .execute();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.463Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":74,"estimatedTokens":597}}650{"id":"doc-create_a_search_interface_with_the_query_api_clo-743e335a","source":"documentation","title":"Create a search interface with the Query API | Cloud Search | Google for Developers","url":"https://developers.google.com/workspace/cloud-search/docs/guides/query-guide","text":"Example:\n```text\n{\n \"query\": \"titanic\",\n \"requestOptions\": {\n \"searchApplicationId\": \"searchapplications/<search_app_id>\"\n }\n}\n```\n\nExample:\n```text\n{\n \"results\": [...],\n \"structuredResults\": [{\n \"person\": {...}\n }]\n}\n```\n\nExample:\n```text\nfunction highlightSnippet(snippet) {\n let text = snippet.snippet;\n let formattedText = text;\n if (snippet.matchRanges) {\n let parts = [];\n let index = 0;\n for (let match of snippet.matchRanges) {\n let start = match.start || 0; // Default to 0 if omitted\n let end = match.end;\n if (index < start) { // Include any leading text before/between ranges\n parts.push(text.slice(index, start));\n }\n parts.push('<span class=\"highlight\">');\n parts.push(text.slice(start, end));\n parts.push('</span>');\n index = end;\n }\n parts.push(text.slice(index)); // Include any trailing text after last range\n formattedText = parts.join('');\n }\n return formattedText;\n}\n```\n\nExample:\n```text\n{\n \"snippet\": \"This is an example snippet...\",\n \"matchRanges\": [\n {\n \"start\": 11,\n \"end\": 18\n }\n ]\n}\n```\n\nExample:\n```text\nThis is an <span class=\"highlight\">example</span> snippet...\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.464Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":65,"estimatedTokens":303}}651{"id":"doc-list_notes_google_keep_google_for_developers-728ce1c2","source":"documentation","title":"List notes | Google Keep | Google for Developers","url":"https://developers.google.com/workspace/keep/api/guides/list-notes","text":"Example:\n```text\n/** Lists notes using different filtering and pagination options. */\nprivate void listNotes() throws IOException {\n // Lists 3 notes that were created after a specified timestamp and that are\n // not trashed. Results are ordered by most recently modified first.\n ListNotesResponse response =\n keepService\n .notes()\n .list()\n .setFilter(\"create_time > \\\"2021-01-01T00:00:00Z\\\"\")\n .setFilter(\"-trashed\")\n .setPageSize(3)\n .execute();\n\n System.out.println(\"List notes response: \" + response);\n\n // Lists notes using a pagination token.\n ListNotesResponse firstPageResponse =\n keepService.notes().list().setPageSize(1).execute();\n String nextPageToken = firstPageResponse.getNextPageToken();\n\n for (int i = 0; i < 5; i++) {\n // Uses the page token returned by the previous page's next page token.\n ListNotesResponse pagedResponse =\n keepService\n .notes()\n .list()\n .setPageSize(1)\n .setPageToken(nextPageToken)\n .execute();\n System.out.println(\"Listing note:\" + pagedResponse);\n nextPageToken = pagedResponse.getNextPageToken();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.464Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":38,"estimatedTokens":294}}652{"id":"doc-retrieve_notes_and_attachments_google_keep_googl-8308a10e","source":"documentation","title":"Retrieve notes and attachments | Google Keep | Google for Developers","url":"https://developers.google.com/workspace/keep/api/guides/retrieve-notes","text":"Example:\n```text\n/**\n * Gets and downloads the attachment of a note.\n *\n * @param note The note whose attachment will be downloaded.\n * @throws IOException\n */\nprivate void getNoteAttachment(Note note) throws IOException {\n // First call is to get the attachment resources on the note.\n List<Attachment> attachments =\n keepService.notes().get(note.getName()).execute().getAttachments();\n\n if (!attachments.isEmpty()) {\n Attachment attachment = attachments.get(0);\n String mimeType = attachment.getMimeType().get(0);\n // Make a second call to download the attachment with the specified\n // mimeType.\n OutputStream outputStream =\n new FileOutputStream(\"attachmentFile.\" + mimeType.split(\"/\")[1]);\n keepService\n .media()\n .download(attachment.getName())\n .setMimeType(mimeType)\n .executeMediaAndDownloadTo(outputStream);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.465Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":30,"estimatedTokens":226}}653{"id":"doc-integrate_i_mobile_with_mediation_flutter_google-98719ac4","source":"documentation","title":"Integrate i-mobile with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/imobile","text":"Example:\n```text\ndependencies:\n gma_mediation_imobile: ^1.0.4\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_imobile:\n path: path/to/local/package\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.470Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":14,"estimatedTokens":44}}654{"id":"doc-set_up_admob_mediation_flutter_google_for_develo-97ee8317","source":"documentation","title":"Set up AdMob Mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation","text":"Example:\n```text\nvoid main() {\n WidgetsFlutterBinding.ensureInitialized();\n \n MobileAds.instance.initialize()\n .then((initializationStatus) {\n initializationStatus.adapterStatuses.forEach((key, value) {\n debugPrint('Adapter status for $key: ${value.description}');\n });\n });\n \n runApp(MyApp());\n}\n```\n\nExample:\n```text\ndef flutterSdkPath = {\n def properties = new Properties()\n file(\"local.properties\").withInputStream { properties.load(it) }\n def flutterSdkPath = properties.getProperty(\"flutter.sdk\")\n assert flutterSdkPath != null, \"flutter.sdk not set in local.properties\"\n return flutterSdkPath\n}()\n\nincludeBuild(\"$flutterSdkPath/packages/flutter_tools/gradle\")\n```\n\nExample:\n```text\nfinal bannerAd = BannerAd(\n size: AdSize.banner,\n adUnitId: '<your-ad-unit>',\n listener: BannerAdListener(\n onAdLoaded: (ad) {\n debugPrint('$ad loaded: ${ad.responseInfo?.mediationAdapterClassName}');\n },\n ),\n request: AdRequest(),\n );\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.471Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":44,"estimatedTokens":259}}655{"id":"doc-integrate_dt_exchange_with_mediation_flutter_goo-86eb337b","source":"documentation","title":"Integrate DT Exchange with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/dt-exchange","text":"Example:\n```text\ndependencies:\n gma_mediation_dtexchange: ^1.3.5\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_dtexchange:\n path: path/to/local/package\n```\n\nExample:\n```text\ncom.google.ads.mediation.fyber.FyberMediationAdapter\n```\n\nExample:\n```text\nGADMediationAdapterFyber\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.472Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":24,"estimatedTokens":76}}656{"id":"doc-integrate_inmobi_with_mediation_flutter_google_f-97fcda38","source":"documentation","title":"Integrate InMobi with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/inmobi","text":"Example:\n```text\ndependencies:\n gma_mediation_inmobi: ^2.3.0\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_inmobi:\n path: path/to/local/package\n```\n\nExample:\n```text\n<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />\n```\n\nExample:\n```text\ncom.google.ads.mediation.inmobi.InMobiAdapter\ncom.google.ads.mediation.inmobi.InMobiMediationAdapter\n```\n\nExample:\n```text\nGADMAdapterInMobi\nGADMediationAdapterInMobi\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.472Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":33,"estimatedTokens":151}}657{"id":"doc-integrate_chartboost_with_mediation_flutter_goog-491b70b5","source":"documentation","title":"Integrate Chartboost with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/chartboost","text":"Example:\n```text\ndependencies:\n gma_mediation_chartboost: ^1.7.0\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_chartboost:\n path: path/to/local/package\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.Chartboost;\n// ...\n\nChartboost.AddDataUseConsent(CBCCPADataUseConsent.OptInSale);\n```\n\nExample:\n```text\n<uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n```\n\nExample:\n```text\ncom.google.ads.mediation.chartboost.ChartboostAdapter\ncom.google.ads.mediation.chartboost.ChartboostMediationAdapter\n```\n\nExample:\n```text\nGADMAdapterChartboost\nGADMediationAdapterChartboost\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.474Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":39,"estimatedTokens":157}}658{"id":"doc-integrate_applovin_with_mediation_flutter_google-8f1a4d9d","source":"documentation","title":"Integrate AppLovin with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/applovin","text":"Example:\n```text\ndependencies:\n gma_mediation_applovin: ^2.6.2\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_applovin:\n path: path/to/local/package\n```\n\nExample:\n```text\nimport 'package:gma_mediation_applovin/gma_mediation_applovin.dart';\n// ...\n\nGmaMediationApplovin.setHasUserConsent(true);\nGmaMediationApplovin.setIsAgeRestrictedUser(true);\n```\n\nExample:\n```text\nimport 'package:gma_mediation_applovin/gma_mediation_applovin.dart';\n// ...\n\nGmaMediationApplovin.setDoNotSell(true);\n```\n\nExample:\n```text\nAppLovinMediationExtras applovinExtras = AppLovinMediationExtras(isMuted: true)\n\nAdRequest request = AdRequest(\n keywords: <String>['foo', 'bar'],\n contentUrl: 'http://foo.com/bar.html',\n mediationExtras: [applovinExtras],\n);\n```\n\nExample:\n```text\ncom.google.ads.mediation.applovin.mediation.ApplovinAdapter\ncom.google.ads.mediation.applovin.AppLovinMediationAdapter\n```\n\nExample:\n```text\nGADMAdapterAppLovin\nGADMAdapterAppLovinRewardBasedVideoAd\nGADMediationAdapterAppLovin\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.475Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":55,"estimatedTokens":255}}659{"id":"doc-integrate_mintegral_with_mediation_flutter_googl-1fdc1b72","source":"documentation","title":"Integrate Mintegral with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/mintegral","text":"Example:\n```text\ndependencies:\n gma_mediation_mintegral: ^2.1.2\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_mintegral:\n path: path/to/local/package\n```\n\nExample:\n```text\ncom.mbridge.msdk\ncom.google.ads.mediation.mintegral.MintegralMediationAdapter\n```\n\nExample:\n```text\nGADMediationAdapterMintegral\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.476Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":25,"estimatedTokens":82}}660{"id":"doc-integrate_pubmatic_with_mediation_flutter_google-8d9f9c6f","source":"documentation","title":"Integrate PubMatic with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/pubmatic","text":"Example:\n```text\nrepositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://repo.pubmatic.com/artifactory/public-repos\")\n }\n }\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_pubmatic: ^2.3.0\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_pubmatic:\n path: path/to/local/package\n```\n\nExample:\n```text\ncom.pubmatic.sdk\ncom.google.ads.mediation.pubmatic\n```\n\nExample:\n```text\nGADMediationAdapterPubMatic\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.477Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":36,"estimatedTokens":118}}661{"id":"doc-integrate_meta_audience_network_with_bidding_flu-1bf51da6","source":"documentation","title":"Integrate Meta Audience Network with bidding | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/meta","text":"Example:\n```text\ndependencies:\n gma_mediation_meta: ^1.6.0\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_meta:\n path: path/to/local/package\n```\n\nExample:\n```text\ncom.google.ads.mediation.facebook.FacebookAdapter\ncom.google.ads.mediation.facebook.FacebookMediationAdapter\n```\n\nExample:\n```text\nGADMAdapterFacebook\nGADMediationAdapterFacebook\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.478Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":26,"estimatedTokens":92}}662{"id":"doc-integrate_ironsource_ads_with_mediation_flutter_-f4661d40","source":"documentation","title":"Integrate ironSource Ads with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/ironsource","text":"Example:\n```text\nrepositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://android-sdk.is.com/\")\n }\n }\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_ironsource: ^2.5.0\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_ironsource:\n path: path/to/local/package\n```\n\nExample:\n```text\nimport 'package:gma_mediation_ironsource/gma_mediation_ironsource.dart';\n// ...\n\nGmaMediationIronsource().setDoNotSell(true);\n```\n\nExample:\n```text\n@Override\npublic void onResume() {\n super.onResume();\n IronSource.onResume(this);\n}\n\n@Override\npublic void onPause() {\n super.onPause();\n IronSource.onPause(this);\n}\n```\n\nExample:\n```text\npublic override fun onResume() {\n super.onResume()\n IronSource.onResume(this)\n}\n\npublic override fun onPause() {\n super.onPause()\n IronSource.onPause(this)\n}\n```\n\nExample:\n```text\ncom.google.ads.mediation.ironsource.IronSourceAdapter\ncom.google.ads.mediation.ironsource.IronSourceRewardedAdapter\n```\n\nExample:\n```text\nGADMAdapterIronSource\nGADMAdapterIronSourceRewarded\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.479Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":73,"estimatedTokens":271}}663{"id":"doc-integrate_unity_ads_with_mediation_flutter_googl-1cefd618","source":"documentation","title":"Integrate Unity Ads with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/unity","text":"Example:\n```text\ndependencies:\n gma_mediation_unity: ^1.9.0\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_unity:\n path: path/to/local/package\n```\n\nExample:\n```text\nimport 'package:gma_mediation_unity/gma_mediation_unity.dart';\n// ...\n\nGmaMediationUnity.setGDPRConsent(true);\n```\n\nExample:\n```text\nimport 'package:gma_mediation_unity/gma_mediation_unity.dart';\n// ...\n\nGmaMediationUnity.setCCPAConsent(true);\n```\n\nExample:\n```text\ncom.google.ads.mediation.unity.UnityAdapter\ncom.google.ads.mediation.unity.UnityMediationAdapter\n```\n\nExample:\n```text\nGADMAdapterUnity\nGADMediationAdapterUnity\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.480Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":42,"estimatedTokens":155}}664{"id":"doc-integrate_maio_with_mediation_flutter_google_for-73640ca7","source":"documentation","title":"Integrate maio with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/maio","text":"Example:\n```text\ndependencies:\n gma_mediation_maio: ^1.1.6\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_maio:\n path: path/to/local/package\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.481Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":14,"estimatedTokens":42}}665{"id":"doc-integrate_liftoff_monetize_with_mediation_flutte-630bf72b","source":"documentation","title":"Integrate Liftoff Monetize with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/liftoff-monetize","text":"Example:\n```text\ndependencies:\n gma_mediation_liftoffmonetize: ^1.5.2\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_liftoffmonetize:\n path: path/to/local/package\n```\n\nExample:\n```text\nimport 'package:gma_mediation_liftoffmonetize/gma_mediation_liftoffmonetize.dart';\n// ...\n\nGmaMediationLiftoffmonetize.setCCPAStatus(true);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.482Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":22,"estimatedTokens":88}}666{"id":"doc-integrate_moloco_with_mediation_flutter_google_f-67500a34","source":"documentation","title":"Integrate Moloco with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/moloco","text":"Example:\n```text\ndependencies:\n gma_mediation_moloco: ^3.6.0\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_moloco:\n path: path/to/local/package\n```\n\nExample:\n```text\ncom.moloco.sdk\ncom.google.ads.mediation.moloco.MolocoMediationAdapter\n```\n\nExample:\n```text\nMolocoSDK.MolocoError\nGADMediationAdapterMoloco\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.482Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":26,"estimatedTokens":84}}667{"id":"doc-integrate_mytarget_with_mediation_flutter_google-00438731","source":"documentation","title":"Integrate myTarget with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/mytarget","text":"Example:\n```text\ndependencies:\n gma_mediation_mytarget: ^1.12.0\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_mytarget:\n path: path/to/local/package\n```\n\nExample:\n```text\ncom.google.ads.mediation.mytarget.MyTargetAdapter\ncom.google.ads.mediation.mytarget.MyTargetNativeAdapter\ncom.google.ads.mediation.mytarget.MyTargetRewardedAdapter\n```\n\nExample:\n```text\nGADMAdapterMyTarget\nGADMediationAdapterMyTargetNative\nGADMediationAdapterMyTargetRewarded\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.483Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":28,"estimatedTokens":119}}668{"id":"doc-network_specific_request_parameters_flutter_goog-a1c7db23","source":"documentation","title":"Network specific request parameters | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/network-specific-parameters","text":"Example:\n```text\nclass MyMediationNetworkExtrasProvider implements MediationNetworkExtrasProvider {\n\n @Override\n public Map<Class<? extends MediationExtrasReceiver>, Bundle> getMediationExtras(\n String adUnitId, @Nullable String identifier) {\n // This example passes extras to the AppLovin adapter.\n // This method is called with the ad unit of the associated ad request, and\n // an optional string parameter which comes from the dart ad request object.\n Bundle appLovinBundle = new AppLovinExtras.Builder().setMuteAudio(true).build();\n Map<Class<? extends MediationExtrasReceiver>, Bundle> extras = new HashMap<>();\n extras.put(ApplovinAdapter.class, appLovinBundle);\n // Note: You can pass extras to multiple adapters by adding more entries.\n return extras;\n }\n}\n```\n\nExample:\n```text\n// Register a MediationNetworkExtrasProvider with the plugin.\npublic class MainActivity extends FlutterActivity {\n\n @Override\n public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {\n super.configureFlutterEngine(flutterEngine);\n\n // Register your MediationNetworkExtrasProvider to provide network extras to ad requests.\n GoogleMobileAdsPlugin.registerMediationNetworkExtrasProvider(\n flutterEngine, new MyMediationNetworkExtrasProvider());\n }\n}\n```\n\nExample:\n```text\n@implementation MyFLTMediationNetworkExtrasProvider\n\n- (NSArray<id<GADAdNetworkExtras>> *_Nullable)getMediationExtras:(NSString *_Nonnull)adUnitId\n mediationExtrasIdentifier:\n (NSString *_Nullable)mediationExtrasIdentifier {\n // This example passes extras to the AppLovin adapter.\n // This method is called with the ad unit of the associated ad request, and\n // an optional string parameter which comes from the dart ad request object.\n GADMAdapterAppLovinExtras *appLovinExtras = [[GADMAdapterAppLovinExtras alloc] init];\n appLovinExtras.muteAudio = NO;\n // Note: You can pass extras to multiple adapters by adding more entries.\n\n return @[ appLovinExtras ];\n}\n@end\n```\n\nExample:\n```text\n@implementation AppDelegate\n\n- (BOOL)application:(UIApplication *)application\n didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n [GeneratedPluginRegistrant registerWithRegistry:self];\n\n // Register your network extras provider if you want to provide\n // network extras to specific ad requests.\n MyFLTMediationNetworkExtrasProvider *networkExtrasProvider =\n [[MyFLTMediationNetworkExtrasProvider alloc] init];\n [FLTGoogleMobileAdsPlugin registerMediationNetworkExtrasProvider:networkExtrasProvider\n registry:self];\n return [super application:application didFinishLaunchingWithOptions:launchOptions];\n}\n\n@end\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.484Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":75,"estimatedTokens":703}}669{"id":"doc-integrate_ly_ads_network_with_mediation_flutter_-d27fd388","source":"documentation","title":"Integrate LY Ads Network with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/line","text":"Example:\n```text\ndependencies:\n gma_mediation_line: ^2.1.2\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_line:\n path: path/to/local/package\n```\n\nExample:\n```text\nLineMediationExtras lineExtras = LineMediationExtras(enableAdSound: true)\n\nAdRequest request = AdRequest(\n keywords: <String>['foo', 'bar'],\n contentUrl: 'http://foo.com/bar.html',\n mediationExtras: [lineExtras],\n);\n```\n\nExample:\n```text\ncom.line.ads\ncom.google.ads.mediation.line.LineMediationAdapter\n```\n\nExample:\n```text\nGADMediationAdapterLine\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.484Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":36,"estimatedTokens":137}}670{"id":"doc-use_network_specific_apis_flutter_google_for_dev-e9756a0f","source":"documentation","title":"Use network specific APIs | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/network-apis","text":"Example:\n```text\n/// Wraps a method channel that makes calls to AppLovin privacy APIs.\nclass MyMethodChannel {\n final MethodChannel _methodChannel =\n MethodChannel('com.example.mediationexample/mediation-channel');\n\n /// Sets whether the user is age restricted in AppLovin.\n Future<void> setAppLovinIsAgeRestrictedUser(bool isAgeRestricted) async {\n return _methodChannel.invokeMethod(\n 'setIsAgeRestrictedUser',\n {\n 'isAgeRestricted': isAgeRestricted,\n },\n );\n }\n\n /// Sets whether we have user consent for the user in AppLovin.\n Future<void> setHasUserConsent(bool hasUserConsent) async {\n return _methodChannel.invokeMethod(\n 'setHasUserConsent',\n {\n 'hasUserConsent': hasUserConsent,\n },\n );\n }\n}\n```\n\nExample:\n```text\npublic class MainActivity extends FlutterActivity {\n private static final String CHANNEL_NAME =\n \"com.example.mediationexample/mediation-channel\";\n\n @Override\n public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {\n super.configureFlutterEngine(flutterEngine);\n\n // Set up a method channel for calling APIs in the AppLovin SDK.\n new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)\n .setMethodCallHandler(\n (call, result) -> {\n switch (call.method) {\n case \"setIsAgeRestrictedUser\":\n AppLovinPrivacySettings.setIsAgeRestrictedUser(call.argument(\"isAgeRestricted\"), context);\n result.success(null);\n break;\n case \"setHasUserConsent\":\n AppLovinPrivacySettings.setHasUserConsent(call.argument(\"hasUserConsent\"), context);\n result.success(null);\n break;\n default:\n result.notImplemented();\n break;\n }\n }\n );\n }\n}\n```\n\nExample:\n```text\n@implementation AppDelegate\n\n- (BOOL)application:(UIApplication *)application\n didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n [GeneratedPluginRegistrant registerWithRegistry:self];\n\n // Set up a method channel for calling methods in 3P SDKs.\n FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;\n\n FlutterMethodChannel* methodChannel = [FlutterMethodChannel\n methodChannelWithName:@\"com.example.mediationexample/mediation-channel\"\n binaryMessenger:controller.binaryMessenger];\n [methodChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {\n if ([call.method isEqualToString:@\"setIsAgeRestrictedUser\"]) {\n [ALPrivacySettings setIsAgeRestrictedUser:call.arguments[@\"isAgeRestricted\"]];\n result(nil);\n } else if ([call.method isEqualToString:@\"setHasUserConsent\"]) {\n [ALPrivacySettings setHasUserConsent:call.arguments[@\"hasUserConsent\"]];\n result(nil);\n } else {\n result(FlutterMethodNotImplemented);\n }\n }];\n}\n@end\n```\n\nExample:\n```text\n/// An example widget for the home page of your app.\nclass HomePage extends StatefulWidget {\n @override\n _HomePageState createState() => _HomePageState();\n}\n\nclass _HomePageState extends State<HomePage> {\n \n // Keep a reference to your MyMethodChannel.\n static MyMethodChannel platform = MyMethodChannel();\n \n @override\n void initState() {\n super.initState();\n\n _updateAppLovinSettingsAndLoadAd();\n }\n \n Future<void> _updateAppLovinSettingsAndLoadAd() async {\n // Update the AppLovin settings before loading an ad.\n await platform.setAppLovinIsAgeRestrictedUser(true);\n await platform.setHasUserConsent(false);\n _loadAd();\n }\n \n void _loadAd() {\n // TODO: Load an ad.\n };\n\n @override\n Widget build(BuildContext context) {\n // TODO: Build your widget.\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.485Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":130,"estimatedTokens":965}}671{"id":"doc-integrate_pangle_with_mediation_flutter_google_f-51e148bc","source":"documentation","title":"Integrate Pangle with mediation | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/mediation/pangle","text":"Example:\n```text\nrepositories {\n google()\n mavenCentral()\n maven {\n url = uri(\"https://artifact.bytedance.com/repository/pangle/\")\n }\n }\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_pangle: ^4.1.0\n```\n\nExample:\n```text\ndependencies:\n gma_mediation_pangle:\n path: path/to/local/package\n```\n\nExample:\n```text\nusing GoogleMobileAds.Api.Mediation.Pangle;\n// ...\n\nPangle.SetPAConsent(0);\n```\n\nExample:\n```text\ncom.pangle.ads\ncom.google.ads.mediation.pangle.PangleMediationAdapter\n```\n\nExample:\n```text\nGADMediationAdapterPangle\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.486Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":44,"estimatedTokens":146}}672{"id":"doc-ad_load_errors_flutter_google_for_developers-3776d1f6","source":"documentation","title":"Ad load errors | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/ad-load-errors","text":"Example:\n```text\nonAdFailedToLoad: (ad, loadAdError) {\n // Gets the domain from which the error came.\n String domain = loadAdError.domain;\n\n // Gets the error code. See\n // https://developers.google.com/admob/android/reference/com/google/android/gms/ads/AdRequest\n // and https://developers.google.com/admob/ios/api/reference/Enums/GADErrorCode\n // for a list of possible codes.\n int code = loadAdError.code;\n\n // A log friendly string summarizing the error.\n String message = loadAdError.message;\n\n // Get response information, which may include results of mediation requests.\n ResponseInfo? responseInfo = loadAdError.responseInfo;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.486Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":21,"estimatedTokens":166}}673{"id":"doc-test_ad_units_flutter_google_for_developers-ba7cdd02","source":"documentation","title":"Test ad units | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/ad-inspector/test-ad-units","text":"Example:\n```text\nAd Unit has no applicable adapter for single ad source testing on network: AD_SOURCE_ADAPTER_CLASS_NAME\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.487Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":35}}674{"id":"doc-launch_ad_inspector_flutter_google_for_developer-4167c9fe","source":"documentation","title":"Launch ad inspector | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/ad-inspector/launch-ad-inspector","text":"Example:\n```text\nMobileAds.instance.openAdInspector((error) {\n // Error will be non-null if ad inspector closed due to an error.\n});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.488Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":8,"estimatedTokens":38}}675{"id":"doc-sign_in_with_google_javascript_api_reference_web-02ae811c","source":"documentation","title":"Sign in with Google JavaScript API reference | Web guides | Google for Developers","url":"https://developers.google.com/identity/gsi/web/reference/js-reference","text":"Example:\n```text\ngoogle.accounts.id.initialize(IdConfiguration)\n```\n\nExample:\n```text\n<script>\n window.onload = function () {\n google.accounts.id.initialize({\n client_id: 'YOUR_GOOGLE_CLIENT_ID',\n callback: handleCredentialResponse\n });\n google.accounts.id.prompt();\n };\n</script>\n```\n\nExample:\n```text\ngoogle.accounts.id.prompt(/**\n @type{(function(!PromptMomentNotification):void)=} */ momentListener)\n```\n\nExample:\n```text\n<script>\n window.onload = function () {\n google.accounts.id.initialize(...);\n google.accounts.id.prompt((notification) => {\n if (notification.isNotDisplayed() || notification.isSkippedMoment()) {\n // continue with another identity provider.\n }\n });\n };\n</script>\n```\n\nExample:\n```text\nheader\n{\n \"alg\": \"RS256\",\n \"kid\": \"f05415b13acb9590f70df862765c655f5a7a019e\", // JWT signature\n \"typ\": \"JWT\"\n}\npayload\n{\n \"iss\": \"https://accounts.google.com\", // The JWT's issuer\n \"nbf\": 161803398874,\n \"aud\": \"314159265-pi.apps.googleusercontent.com\", // Your server's client ID\n \"sub\": \"3141592653589793238\", // The unique ID of the user's Google Account\n \"hd\": \"gmail.com\", // If present, the host domain of the user's Google Workspace email address\n \"auth_time\": 1748875426,\n \"amr\": [\"mfa\", \"pwd\", \"tel\"],\n \"email\": \"elisa.g.beckett@gmail.com\", // The user's email address\n \"email_verified\": true, // true, if Google has verified the email address\n \"azp\": \"314159265-pi.apps.googleusercontent.com\",\n \"name\": \"Elisa Beckett\",\n // If present, a URL to user's profile picture\n \"picture\": \"https://lh3.googleusercontent.com/a-/e2718281828459045235360uler\",\n \"given_name\": \"Elisa\",\n \"family_name\": \"Beckett\",\n \"iat\": 1596474000, // Unix timestamp of the assertion creation time\n \"exp\": 1596477600, // Unix timestamp of the assertion expiration time\n \"jti\": \"abc161803398874def\"\n}\n```\n\nExample:\n```text\ngoogle.accounts.id.renderButton(\n /** @type{!HTMLElement} */ parent,\n /** @type{!GsiButtonConfiguration} */ options\n )\n```\n\nExample:\n```text\ngoogle.accounts.id.renderButton(document.getElementById(\"signinDiv\"), {\n theme: 'outline',\n size: 'large',\n click_listener: onClickHandler\n });\n\n \n function onClickHandler(){\n console.log(\"Sign in with Google button clicked...\")\n }\n```\n\nExample:\n```text\ngoogle.accounts.id.disableAutoSelect()\n```\n\nExample:\n```text\n<script>\n function onSignout() {\n google.accounts.id.disableAutoSelect();\n }\n</script>\n```\n\nExample:\n```text\ngoogle.accounts.id.storeCredential(Credential, callback)\n```\n\nExample:\n```text\n<script>\n function onSignIn() {\n let cred = {id: '...', password: '...'};\n google.accounts.id.storeCredential(cred);\n }\n</script>\n```\n\nExample:\n```text\ngoogle.accounts.id.cancel()\n```\n\nExample:\n```text\n<script>\n function onNextButtonClicked() {\n google.accounts.id.cancel();\n showPasswordPage();\n }\n</script>\n```\n\nExample:\n```text\nwindow.onGoogleLibraryLoad = () => {\n ...\n};\n```\n\nExample:\n```text\n<script>\n window.onGoogleLibraryLoad = () => {\n google.accounts.id.initialize({\n ...\n });\n google.accounts.id.prompt();\n };\n</script>\n```\n\nExample:\n```text\ngoogle.accounts.id.revoke('1618033988749895', done => {\n console.log(done.error);\n });\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.492Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":162,"estimatedTokens":822}}676{"id":"doc-targeting_flutter_google_for_developers-df68fce7","source":"documentation","title":"Targeting | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/targeting","text":"Example:\n```text\nRequestConfiguration requestConfiguration = RequestConfiguration(\n // Indicates that ad requests should have child age treatment.\n ageRestrictedTreatment: AgeRestrictedTreatment.child,\n);\nMobileAds.instance.updateRequestConfiguration(requestConfiguration);request_configuration_snippets.dart\n```\n\nExample:\n```text\nfinal RequestConfiguration requestConfiguration = RequestConfiguration(\n tagForChildDirectedTreatment: TagForChildDirectedTreatment.yes);\nMobileAds.instance.updateRequestConfiguration(requestConfiguration);\n```\n\nExample:\n```text\nfinal RequestConfiguration requestConfiguration = RequestConfiguration(\n tagForUnderAgeOfConsent: TagForUnderAgeOfConsent.yes);\nMobileAds.instance.updateRequestConfiguration(requestConfiguration);\n```\n\nExample:\n```text\nfinal RequestConfiguration requestConfiguration = RequestConfiguration(\n maxAdContentRating: MaxAdContentRating.g);\nMobileAds.instance.updateRequestConfiguration(requestConfiguration);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.493Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":31,"estimatedTokens":247}}677{"id":"doc-sign_in_with_google_html_api_reference_web_guide-29fa58ac","source":"documentation","title":"Sign in with Google HTML API reference | Web guides | Google for Developers","url":"https://developers.google.com/identity/gsi/web/reference/html-reference","text":"Example:\n```text\n<script>\n function onClickHandler(){\n console.log(\"Sign in with Google button clicked...\")\n }\n </script>\n .....\n <div class=\"g_id_signin\"\n data-size=\"large\"\n data-theme=\"outline\"\n data-click_listener=\"onClickHandler\">\n </div>\n```\n\nExample:\n```text\nPOST /login HTTP/1.1\nContent-Type: application/x-www-form-urlencoded\nCookie: g_csrf_token=<RANDOM_STRING>\nHost: www.example.com\n\ncredential=<JWT_ENCODED_ID_TOKEN>&g_csrf_token=<RANDOM_STRING>\n```\n\nExample:\n```text\nheader\n{\n \"alg\": \"RS256\",\n \"kid\": \"f05415b13acb9590f70df862765c655f5a7a019e\", // JWT signature\n \"typ\": \"JWT\"\n}\npayload\n{\n \"iss\": \"https://accounts.google.com\", // The JWT's issuer\n \"nbf\": 161803398874,\n \"aud\": \"314159265-pi.apps.googleusercontent.com\", // Your server's client ID\n \"sub\": \"3141592653589793238\", // The unique ID of the user's Google Account\n \"hd\": \"gmail.com\", // If present, the host domain of the user's Google Workspace email address\n \"auth_time\": 1748875426,\n \"amr\": [\"mfa\", \"pwd\", \"tel\"],\n \"email\": \"elisa.g.beckett@gmail.com\", // The user's email address\n \"email_verified\": true, // true, if Google has verified the email address\n \"azp\": \"314159265-pi.apps.googleusercontent.com\",\n \"name\": \"Elisa Beckett\",\n // If present, a URL to user's profile picture\n \"picture\": \"https://lh3.googleusercontent.com/a-/e2718281828459045235360uler\",\n \"given_name\": \"Eliza\",\n \"family_name\": \"Beckett\",\n \"iat\": 1596474000, // Unix timestamp of the assertion creation time\n \"exp\": 1596477600, // Unix timestamp of the assertion expiration time\n \"jti\": \"abc161803398874def\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.495Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":57,"estimatedTokens":414}}678{"id":"doc-integrating_google_sign_in_into_your_web_app_web-12512546","source":"documentation","title":"Integrating Google Sign-In into your web app | Web guides | Google for Developers","url":"https://developers.google.com/identity/sign-in/web/sign-in","text":"Example:\n```text\n<script src=\"https://apis.google.com/js/platform.js\" async defer></script>\n```\n\nExample:\n```text\n<meta name=\"google-signin-client_id\" content=\"YOUR_CLIENT_ID.apps.googleusercontent.com\">\n```\n\nExample:\n```text\n<div class=\"g-signin2\" data-onsuccess=\"onSignIn\"></div>\n```\n\nExample:\n```text\nfunction onSignIn(googleUser) {\n var profile = googleUser.getBasicProfile();\n console.log('ID: ' + profile.getId()); // Do not send to your backend! Use an ID token instead.\n console.log('Name: ' + profile.getName());\n console.log('Image URL: ' + profile.getImageUrl());\n console.log('Email: ' + profile.getEmail()); // This is null if the 'email' scope is not present.\n}\n```\n\nExample:\n```text\n<a href=\"#\" onclick=\"signOut();\">Sign out</a>\n<script>\n function signOut() {\n var auth2 = gapi.auth2.getAuthInstance();\n auth2.signOut().then(function () {\n console.log('User signed out.');\n });\n }\n</script>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.495Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":40,"estimatedTokens":236}}679{"id":"doc-integrate_the_webview_api_for_ads_flutter_google-fe68c4a8","source":"documentation","title":"Integrate the WebView API for Ads | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/webview","text":"Example:\n```text\n<!-- Bypass APPLICATION_ID check for WebView API for Ads -->\n<meta-data\n android:name=\"com.google.android.gms.ads.INTEGRATION_MANAGER\"\n android:value=\"webview\"/>\n```\n\nExample:\n```text\n<!-- Bypass GADApplicationIdentifier check for WebView API for Ads -->\n<key>GADIntegrationManager</key>\n<string>webview</string>\n```\n\nExample:\n```text\nimport 'package:google_mobile_ads/google_mobile_ads.dart';\nimport 'package:webview_flutter/webview_flutter.dart';\nimport 'package:webview_flutter_android/webview_flutter_android.dart';\n\n@override\nclass WebViewExampleState extends State<WebViewExample> {\n late final WebViewController controller;\n\n @override\n void initState() {\n super.initState();\n\n createWebView();\n }\n\n void createWebView() async {\n controller = WebViewController();\n // 1. Enable JavaScript in the web view.\n await controller.setJavaScriptMode(JavaScriptMode.unrestricted);\n\n // 2. Enable third-party cookies for Android.\n if (controller.platform is AndroidWebViewController) {\n AndroidWebViewCookieManager cookieManager = AndroidWebViewCookieManager(\n const PlatformWebViewCookieManagerCreationParams());\n await cookieManager.setAcceptThirdPartyCookies(\n controller.platform as AndroidWebViewController, true);\n }\n\n // 3. Register the web view.\n await MobileAds.instance.registerWebView(controller);\n }\n}\n```\n\nExample:\n```text\nimport 'package:google_mobile_ads/google_mobile_ads.dart';\nimport 'package:webview_flutter/webview_flutter.dart';\nimport 'package:webview_flutter_android/webview_flutter_android.dart';\n\n@override\nclass WebViewExampleState extends State<WebViewExample> {\n late final WebViewController controller;\n\n @override\n void initState() {\n super.initState();\n\n createWebView();\n }\n\n void createWebView() async {\n controller = WebViewController();\n // 1. Enable JavaScript in the web view.\n await controller.setJavaScriptMode(JavaScriptMode.unrestricted);\n\n // 2. Enable third-party cookies for Android.\n if (controller.platform is AndroidWebViewController) {\n AndroidWebViewCookieManager cookieManager = AndroidWebViewCookieManager(\n const PlatformWebViewCookieManagerCreationParams());\n await cookieManager.setAcceptThirdPartyCookies(\n controller.platform as AndroidWebViewController, true);\n }\n\n // 3. Register the web view.\n await MobileAds.instance.registerWebView(controller);\n\n // 4. Load the URL.\n await controller.loadRequest(Uri.parse('https://google.github.io/webview-ads/test/'));\n }\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.496Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":90,"estimatedTokens":646}}680{"id":"doc-interstitial_ads_c_google_for_developers-0062c194","source":"documentation","title":"Interstitial ads | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/interstitial","text":"Example:\n```text\n#include \"firebase/gma/interstial_ad.h\"\n```\n\nExample:\n```text\nfirebase::gma::InterstitialAd* interstitial_ad;\n interstitial_ad = new firebase::gma::InterstitialAd();\n```\n\nExample:\n```text\n// my_ad_parent is a jobject reference to an Android Activity or\n// a pointer to an iOS UIView.\nfirebase::gma::AdParent ad_parent =\n static_cast<firebase::gma::AdParent>(my_ad_parent);\nfirebase::Future<void> result = interstitial_ad->Initialize(ad_parent);\n```\n\nExample:\n```text\n// Monitor the status of the future in your game loop:\nfirebase::Future<void> result = interstitial_ad->InitializeLastResult();\nif (result.status() == firebase::kFutureStatusComplete) {\n // Initialization completed.\n if(future.error() == firebase::gma::kAdErrorCodeNone) {\n // Initialization successful.\n } else {\n // An error has occurred.\n }\n} else {\n // Initialization on-going.\n}\n```\n\nExample:\n```text\nfirebase::gma::AdRequest ad_request;\nfirebase::Future<firebase::gma::AdResult> load_ad_result;\nload_ad_result = interstitial_ad->LoadAd(interstitial_ad_unit_id, ad_request);\n```\n\nExample:\n```text\nclass ExampleFullScreenContentListener\n : public firebase::gma::FullScreenContentListener {\n\n public:\n ExampleFullScreenContentListener() {}\n\n void OnAdClicked() override {\n // This method is invoked when the user clicks the ad.\n }\n\n void OnAdDismissedFullScreenContent() override {\n // This method is invoked when the ad dismisses full screen content.\n }\n\n void OnAdFailedToShowFullScreenContent(const AdError& error) override {\n // This method is invoked when the ad failed to show full screen content.\n // Details about the error are contained within the AdError parameter.\n }\n\n void OnAdImpression() override {\n // This method is invoked when an impression is recorded for an ad.\n }\n\n void OnAdShowedFullScreenContent() override {\n // This method is invoked when the ad showed its full screen content.\n }\n };\n\n ExampleFullScreenContentListener* full_screen_content_listener =\n new ExampleFullScreenContentListener();\n interstitial_ad->SetFullScreenContentListener(full_screen_content_listener);\n```\n\nExample:\n```text\nfirebase::Future<void> result = interstitial_ad->Show();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.497Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":84,"estimatedTokens":567}}681{"id":"doc-server_side_verification_flutter_google_for_deve-ad95f24d","source":"documentation","title":"Server-side verification | Flutter | Google for Developers","url":"https://developers.google.com/admob/flutter/ssv","text":"Example:\n```text\nRewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()\n .fetchVerifyingPublicKeysWith(\n RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)\n .build();\nString rewardUrl = ...;\nverifier.verify(rewardUrl);\n```\n\nExample:\n```readonly\nRewardedAd.load(\n adUnitId: \"_adUnitId\",\n request: AdRequest(),\n rewardedAdLoadCallback: RewardedAdLoadCallback(\n onAdLoaded: (ad) {\n ServerSideVerificationOptions _options =\n ServerSideVerificationOptions(\n customData: 'SAMPLE_CUSTOM_DATA_STRING',\n );\n ad.setServerSideOptions(_options);\n _rewardedAd = ad;\n },\n onAdFailedToLoad: (error) {},\n ),\n);rewarded_ad_snippets.dart\n```\n\nExample:\n```text\n{\n \"keys\": [\n {\n keyId: 1916455855,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...YTPcw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFkwEwYHKoZIzj0CAQYI...ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw==\"\n },\n {\n keyId: 3901585526,\n pem: \"-----BEGIN PUBLIC KEY-----\\nMF...aDUsw==\\n-----END PUBLIC KEY-----\"\n base64: \"MFYwEAYHKoZIzj0CAQYF...4akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw==\"\n },\n ],\n}\n```\n\nExample:\n```text\nString url = ...;\nNetHttpTransport httpTransport = new NetHttpTransport.Builder().build();\nHttpRequest httpRequest =\n httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(url));\nHttpResponse httpResponse = httpRequest.execute();\nif (httpResponse.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) {\n throw new IOException(\"Unexpected status code = \" + httpResponse.getStatusCode());\n}\nString data;\nInputStream contentStream = httpResponse.getContent();\ntry {\n InputStreamReader reader = new InputStreamReader(contentStream, UTF_8);\n data = readerToString(reader);\n} finally {\n contentStream.close();\n}\n```\n\nExample:\n```text\nprivate static Map<Integer, ECPublicKey> parsePublicKeysJson(String publicKeysJson)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = new HashMap<>();\n try {\n JSONArray keys = new JSONObject(publicKeysJson).getJSONArray(\"keys\");\n for (int i = 0; i < keys.length(); i++) {\n JSONObject key = keys.getJSONObject(i);\n publicKeys.put(\n key.getInt(\"keyId\"),\n EllipticCurves.getEcPublicKey(Base64.decode(key.getString(\"base64\"))));\n }\n } catch (JSONException e) {\n throw new GeneralSecurityException(\"failed to extract trusted signing public keys\", e);\n }\n if (publicKeys.isEmpty()) {\n throw new GeneralSecurityException(\"No trusted keys are available.\");\n }\n return publicKeys;\n}\n```\n\nExample:\n```devsite-click-to-copy\nhttps://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins\n×tamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887\n```\n\nExample:\n```text\npublic static final String SIGNATURE_PARAM_NAME = \"signature=\";\n...\nURI uri;\ntry {\n uri = new URI(rewardUrl);\n} catch (URISyntaxException ex) {\n throw new GeneralSecurityException(ex);\n}\nString queryString = uri.getQuery();\nint i = queryString.indexOf(SIGNATURE_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a signature query parameter\");\n}\nbyte[] queryParamContentData =\n queryString\n .substring(0, i - 1)\n // i - 1 instead of i because of & in the query string\n .getBytes(Charset.forName(\"UTF-8\"));\n```\n\nExample:\n```text\npublic static final String KEY_ID_PARAM_NAME = \"key_id=\";\n...\nString sigAndKeyId = queryString.substring(i);\ni = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME);\nif (i == -1) {\n throw new GeneralSecurityException(\"needs a key_id query parameter\");\n}\nString sig =\n sigAndKeyId.substring(\n SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */);\nint keyId = Integer.valueOf(sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length()));\n```\n\nExample:\n```text\nprivate void verify(final byte[] dataToVerify, int keyId, final byte[] signature)\n throws GeneralSecurityException {\n Map<Integer, ECPublicKey> publicKeys = parsePublicKeysJson();\n if (publicKeys.containsKey(keyId)) {\n foundKeyId = true;\n ECPublicKey publicKey = publicKeys.get(keyId);\n EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, HashType.SHA256, EcdsaEncoding.DER);\n verifier.verify(signature, dataToVerify);\n } else {\n throw new GeneralSecurityException(\"cannot find verifying key with key ID: \" + keyId);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.498Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":150,"estimatedTokens":1107}}682{"id":"doc-rewarded_ads_c_google_for_developers-882405d0","source":"documentation","title":"Rewarded ads | C++ | Google for Developers","url":"https://developers.google.com/admob/cpp/rewarded","text":"Example:\n```text\n#include \"firebase/gma/rewarded_ad.h\"\n```\n\nExample:\n```text\nfirebase::gma::RewardedAd* rewarded_ad;\n rewarded_ad = new firebase::gma::RewardedAd();\n```\n\nExample:\n```text\n// my_ad_parent is a jobject reference to an Android Activity or\n// a pointer to an iOS UIView.\nfirebase::gma::AdParent ad_parent =\n static_cast<firebase::gma::AdParent>(my_ad_parent);\nfirebase::Future<void> result = rewarded_ad->Initialize(ad_parent);\n```\n\nExample:\n```text\n// Monitor the status of the future in your game loop:\nfirebase::Future<void> result = rewarded_ad->InitializeLastResult();\nif (result.status() == firebase::kFutureStatusComplete) {\n // Initialization completed.\n if(future.error() == firebase::gma::kAdErrorCodeNone) {\n // Initialization successful.\n } else {\n // An error has occurred.\n }\n} else {\n // Initialization on-going.\n}\n```\n\nExample:\n```text\nfirebase::gma::AdRequest ad_request;\nfirebase::Future<firebase::gma::AdResult> load_ad_result;\nload_ad_result = rewarded_ad->LoadAd(rewarded_ad_unit_id, ad_request);\n```\n\nExample:\n```text\nclass ExampleFullScreenContentListener\n : public firebase::gma::FullScreenContentListener {\n\n public:\n ExampleFullScreenContentListener() {}\n\n void OnAdClicked() override {\n // This method is invoked when the user clicks the ad.\n }\n\n void OnAdDismissedFullScreenContent() override {\n // This method is invoked when the ad dismisses full screen content.\n }\n\n void OnAdFailedToShowFullScreenContent(const AdError& error) override {\n // This method is invoked when the ad failed to show full screen content.\n // Details about the error are contained within the AdError parameter.\n }\n\n void OnAdImpression() override {\n // This method is invoked when an impression is recorded for an ad.\n }\n\n void OnAdShowedFullScreenContent() override {\n // This method is invoked when the ad showed its full screen content.\n }\n };\n\n ExampleFullScreenContentListener* example_full_screen_content_listener =\n new ExampleFullScreenContentListener();\n rewarded_ad->SetFullScreenContentListener(example_full_screen_content_listener);\n```\n\nExample:\n```text\n// A simple listener track UserEarnedReward events.\nclass ExampleUserEarnedRewardListener :\n public firebase::gma::UserEarnedRewardListener {\n public:\n ExampleUserEarnedRewardListener() { }\n\n void OnUserEarnedReward(const firebase::gma::AdReward& reward) override {\n // Reward the user!\n }\n};\n\nExampleUserEarnedRewardListener* user_earned_reward_listener =\n new ExampleUserEarnedRewardListener();\nfirebase::Future<void> result = rewarded_ad->Show(user_earned_reward_listener);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.500Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":97,"estimatedTokens":670}}683{"id":"doc-build_a_google_workspace_add_on_with_node_js_goo-18b392dd","source":"documentation","title":"Build a Google Workspace add-on with Node.js | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/quickstart/alternate-runtimes","text":"Example:\n```text\ngcloud services enable cloudfunctions.googleapis.com \\\n cloudbuild.googleapis.com \\\n gsuiteaddons.googleapis.com \\\n compute.googleapis.com \\\n run.googleapis.com\n```\n\nExample:\n```text\n/**\n * Cloud Run function that loads the homepage for a\n * Google Workspace add-on.\n *\n * @param {Object} req Request sent from Google\n * @param {Object} res Response to send back\n */\nexports.loadHomePage = function addonsHomePage (req, res) {\n res.send(createAction());\n};\n\n/** Creates a card with two widgets. */\nfunction createAction() {\n return {\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"header\": {\n \"title\": \"Cats!\"\n },\n \"sections\": [\n {\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"Your random cat:\"\n }\n },\n {\n \"image\": {\n \"imageUrl\": \"https://cataas.com/cat\"\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n };\n}\n```\n\nExample:\n```text\n{\n \"dependencies\": {\n \"@google-cloud/functions-framework\": \"^3.0.0\"\n }\n}\n```\n\nExample:\n```text\nexport PROJECT_ID=$(gcloud config get project)\nexport SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe \\\n --format=\"value(defaultServiceAccount)\")\n```\n\nExample:\n```text\ngcloud projects add-iam-policy-binding $PROJECT_ID \\\n --member=\"serviceAccount:$SERVICE_ACCOUNT_NAME\" \\\n --role=\"roles/cloudbuild.builds.builder\"\n```\n\nExample:\n```text\ngcloud run deploy loadHomePage --runtime nodejs22 --trigger-http\n```\n\nExample:\n```text\ngcloud workspace-add-ons get-authorization\n```\n\nExample:\n```text\ngcloud run services add-iam-policy-binding loadHomePage \\\n --role roles/roles/run.invoker \\\n --member serviceAccount:SERVICE_ACCOUNT_EMAIL\n```\n\nExample:\n```text\ngcloud run services describe loadHomePage\n```\n\nExample:\n```text\n{\n \"oauthScopes\": [\"https://www.googleapis.com/auth/gmail.addons.execute\"],\n \"addOns\": {\n \"common\": {\n \"name\": \"My HTTP Add-on\",\n \"logoUrl\": \"https://raw.githubusercontent.com/webdog/octicons-png/main/black/beaker.png\",\n \"homepageTrigger\": {\n \"runFunction\": \"URL\"\n }\n },\n \"gmail\": {},\n \"drive\": {},\n \"calendar\": {},\n \"docs\": {},\n \"sheets\": {},\n \"slides\": {},\n \"httpOptions\": {\n \"granularOauthPermissionSupport\": \"OPT_IN\"\n }\n }\n}\n```\n\nExample:\n```text\ngcloud workspace-add-ons deployments create quickstart \\\n --deployment-file=deployment.json\n```\n\nExample:\n```text\ngcloud workspace-add-ons deployments install quickstart\n```\n\nExample:\n```text\ngcloud workspace-add-ons deployments uninstall quickstart\n```\n\nExample:\n```text\ngcloud projects delete PROJECT_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.504Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":148,"estimatedTokens":735}}684{"id":"doc-scopes_google_workspace_add_ons_google_for_devel-28af3867","source":"documentation","title":"Scopes | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/concepts/workspace-scopes","text":"Example:\n```text\n{\n ...\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/gmail.addons.current.message.metadata\",\n \"https://www.googleapis.com/auth/userinfo.email\"\n ],\n ...\n }\n```\n\nExample:\n```text\nfunction readSender(e) {\n var accessToken = e.gmail.accessToken;\n var messageId = e.gmail.messageId;\n\n // The following function enables short-lived access to the current\n // message in Gmail. Access to other Gmail messages or data isn't\n // permitted.\n GmailApp.setCurrentMessageAccessToken(accessToken);\n var mailMessage = GmailApp.getMessageById(messageId);\n return mailMessage.getFrom();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.506Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":28,"estimatedTokens":159}}685{"id":"doc-extend_the_message_ui_google_workspace_add_ons_g-d2a7c43c","source":"documentation","title":"Extend the message UI | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/gmail/extending-message-ui","text":"Example:\n```text\n{\n ...\n \"addOns\": {\n\n \"common\": {\n ...\n },\n \"gmail\": {\n \"contextualTriggers\": [\n {\n \"unconditional\": {},\n \"onTriggerFunction\": \"onGmailMessageOpen\"\n }\n ],\n ...\n },\n ...\n }\n ...\n}\n```\n\nExample:\n```text\n// Activate temporary Gmail scopes, in this case to allow\n// the add-on to read message metadata and content.\nvar accessToken = e.gmail.accessToken;\nGmailApp.setCurrentMessageAccessToken(accessToken);\n\n// Read message metadata and content. This requires the Gmail scope\n// https://www.googleapis.com/auth/gmail.addons.current.message.readonly.\nvar messageId = e.gmail.messageId;\nvar message = GmailApp.getMessageById(messageId);\nvar subject = message.getSubject();\nvar sender = message.getFrom();\nvar body = message.getPlainBody();\nvar messageDate = message.getDate();\n\n// Setting the access token with a gmail.addons.current.message.readonly\n// scope also allows read access to the other messages in the thread.\nvar thread = message.getThread();\nvar threadMessages = thread.getMessages();\n\n// Using this link can avoid the need to copy message or thread content\nvar threadLink = thread.getPermalink();\n```\n\nExample:\n```text\nfunction onGmailMessageOpen(e) {\n // Activate temporary Gmail scopes, in this case to allow\n // message metadata to be read.\n var accessToken = e.gmail.accessToken;\n GmailApp.setCurrentMessageAccessToken(accessToken);\n\n var messageId = e.gmail.messageId;\n var message = GmailApp.getMessageById(messageId);\n var subject = message.getSubject();\n var sender = message.getFrom();\n\n // Create a card with a single card section and two widgets.\n // Be sure to execute build() to finalize the card construction.\n var exampleCard = CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader()\n .setTitle('Example card'))\n .addSection(CardService.newCardSection()\n .addWidget(CardService.newDecoratedText()\n .setTopLabel('Subject')\n .setText(subject))\n .addWidget(CardService.newDecoratedText()\n .setTopLabel('From')\n .setText(sender)))\n .build(); // Don't forget to build the Card!\n return [exampleCard];\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.507Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":80,"estimatedTokens":562}}686{"id":"doc-manifests_for_google_workspace_add_ons_google_fo-cbce63ab","source":"documentation","title":"Manifests for Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/concepts/workspace-manifests","text":"Example:\n```text\n{\n \"addOns\": {\n \"calendar\": {\n \"createSettingsUrlFunction\": \"getConferenceSettingsPageUrl\",\n \"conferenceSolution\": [{\n \"id\": \"my-video-conf\",\n \"logoUrl\": \"https://lh3.googleusercontent.com/...\",\n \"name\": \"My Video Conference\",\n \"onCreateFunction\": \"onCreateMyVideoConference\"\n }, {\n \"id\": \"my-streamed-conf\",\n \"logoUrl\": \"https://lh3.googleusercontent.com/...\",\n \"name\": \"My Streamed Conference\",\n \"onCreateFunction\": \"onCreateMyStreamedConference\"\n }],\n \"currentEventAccess\": \"READ_WRITE\",\n \"eventOpenTrigger\": {\n \"runFunction\": \"onCalendarEventOpen\"\n },\n \"eventUpdateTrigger\": {\n \"runFunction\": \"onCalendarEventUpdate\"\n },\n \"eventAttachmentTrigger\": {\n \"label\": \"My Event Attachment\",\n \"runFunction\": \"onCalendarEventAddAttachment\"\n },\n \"homepageTrigger\": {\n \"runFunction\": \"onCalendarHomePageOpen\",\n \"enabled\": true\n }\n },\n \"common\": {\n \"homepageTrigger\": {\n \"runFunction\": \"onDefaultHomePageOpen\",\n \"enabled\": true\n },\n \"layoutProperties\": {\n \"primaryColor\": \"#ff392b\",\n \"secondaryColor\": \"#d68617\"\n },\n \"logoUrl\": \"https://ssl.gstatic.com/docs/script/images/logo/script-64.png\",\n \"name\": \"Demo Google Workspace add-on\",\n \"openLinkUrlPrefixes\": [\n \"https://mail.google.com/\",\n \"https://script.google.com/a/google.com/d/\",\n \"https://drive.google.com/a/google.com/file/d/\",\n \"https://www.example.com/\"\n ],\n \"universalActions\": [{\n \"label\": \"Open settings\",\n \"runFunction\": \"getSettingsCard\"\n }, {\n \"label\": \"Open Help URL\",\n \"openLink\": \"https://www.example.com/help\"\n }],\n \"useLocaleFromApp\": true\n },\n \"drive\": {\n \"homepageTrigger\": {\n \"runFunction\": \"onDriveHomePageOpen\",\n \"enabled\": true\n },\n \"onItemsSelectedTrigger\": {\n \"runFunction\": \"onDriveItemsSelected\"\n }\n },\n \"gmail\": {\n \"composeTrigger\": {\n \"selectActions\": [\n {\n \"text\": \"Add images to email\",\n \"runFunction\": \"getInsertImageComposeCards\"\n }\n ],\n \"draftAccess\": \"METADATA\"\n },\n \"contextualTriggers\": [\n {\n \"unconditional\": {},\n \"onTriggerFunction\": \"onGmailMessageOpen\"\n }\n ]\n },\n \"docs\": {\n \"homepageTrigger\": {\n \"runFunction\": \"onEditorsHomepage\"\n },\n \"onFileScopeGrantedTrigger\": {\n \"runFunction\": \"onFileScopeGrantedEditors\"\n },\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"onLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"example-path\"\n }\n ],\n \"labelText\": \"Link preview\",\n \"localizedLabelText\": {\n \"es\": \"Link preview localized in Spanish\"\n },\n \"logoUrl\": \"https://www.example.com/images/smart-chip-icon.png\"\n }\n ],\n \"createActionTriggers\": [\n {\n \"id\": \"exampleId\",\n \"labelText\": \"Example label text\",\n \"localizedLabelText\": {\n \"es\": \"Label text localized in Spanish\"\n },\n \"runFunction\": \"exampleFunction\",\n \"logoUrl\": \"https://www.example.com/images/case.png\"\n }\n ]\n },\n \"sheets\": {\n \"homepageTrigger\": {\n \"runFunction\": \"onEditorsHomepage\"\n },\n \"onFileScopeGrantedTrigger\": {\n \"runFunction\": \"onFileScopeGrantedEditors\"\n }\n },\n \"slides\": {\n \"homepageTrigger\": {\n \"runFunction\": \"onEditorsHomepage\"\n },\n \"onFileScopeGrantedTrigger\": {\n \"runFunction\": \"onFileScopeGrantedEditors\"\n }\n },\n \"meet\": {\n \"homepageTrigger\",\n \"Web\": [\n {\n \"sidePanelUrl\": \"https://myownpersonaldomain.com/sidePanelUrl\",\n \"supportsScreenSharing\": true,\n \"addOnOrigins\": [\n \"https://www.myownpersonaldomain.com\",\n \"https://www.myownpersonaldomain.com:443\"\n ],\n \"logoUrl\": \"https://myownpersonaldomain.com/logoUrl\",\n \"darkModeLogoUrl\": \"https://myownpersonaldomain.com/darkModeLogoUrl\"\n }\n },\n },\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/calendar.addons.execute\",\n \"https://www.googleapis.com/auth/calendar.addons.current.event.read\",\n \"https://www.googleapis.com/auth/calendar.addons.current.event.write\",\n \"https://www.googleapis.com/auth/drive.addons.metadata.readonly\",\n \"https://www.googleapis.com/auth/gmail.addons.current.action.compose\",\n \"https://www.googleapis.com/auth/gmail.addons.current.message.metadata\",\n \"https://www.googleapis.com/auth/userinfo.email\",\n \"https://www.googleapis.com/auth/script.external_request\",\n \"https://www.googleapis.com/auth/script.locale\",\n \"https://www.googleapis.com/auth/script.scriptapp\",\n \"https://www.googleapis.com/auth/drive.file\",\n \"https://www.googleapis.com/auth/documents.currentonly\",\n \"https://www.googleapis.com/auth/spreadsheets.currentonly\",\n \"https://www.googleapis.com/auth/presentations.currentonly\",\n \"https://www.googleapis.com/auth/workspace.linkpreview\"\n ],\n \"urlFetchWhitelist\": [\n \"https://www.example.com/myendpoint/\"\n ]\n}\n```\n\nExample:\n```text\n{\n \"addOns\": {\n \"calendar\": {\n \"currentEventAccess\": \"READ_WRITE\",\n \"eventOpenTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onCalendarEventOpen\"\n },\n \"eventUpdateTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onCalendarEventUpdate\"\n },\n \"eventAttachmentTrigger\": {\n \"label\": \"My Event Attachment\",\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onCalendarEventAddAttachment\"\n },\n \"homepageTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onCalendarHomePageOpen\",\n \"enabled\": true\n }\n },\n \"common\": {\n \"homepageTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onDefaultHomePageOpen\",\n \"enabled\": true\n },\n \"layoutProperties\": {\n \"primaryColor\": \"#ff392b\",\n \"secondaryColor\": \"#d68617\"\n },\n \"logoUrl\": \"https://ssl.gstatic.com/docs/script/images/logo/script-64.png\",\n \"name\": \"Demo Google Workspace add-on\",\n \"openLinkUrlPrefixes\": [\n \"https://mail.google.com/\",\n \"https://script.google.com/a/google.com/d/\",\n \"https://drive.google.com/a/google.com/file/d/\",\n \"https://www.example.com/\"\n ],\n \"universalActions\": [{\n \"label\": \"Open settings\",\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=getSettingsCard\"\n }, {\n \"label\": \"Open Help URL\",\n \"openLink\": \"https://www.example.com/help\"\n }],\n \"useLocaleFromApp\": true\n },\n \"drive\": {\n \"homepageTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onDriveHomePageOpen\",\n \"enabled\": true\n },\n \"onItemsSelectedTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onDriveItemsSelected\"\n }\n },\n \"gmail\": {\n \"composeTrigger\": {\n \"actions\": [\n {\n \"label\": \"Add images to email\",\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=getInsertImageComposeCards\"\n }\n ],\n \"draftAccess\": \"METADATA\"\n },\n \"contextualTriggers\": [\n {\n \"unconditional\": {},\n \"onTriggerFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onGmailMessageOpen\"\n }\n ]\n },\n \"docs\": {\n \"homepageTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onEditorsHomepage\"\n },\n \"onFileScopeGrantedTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onFileScopeGrantedEditors\"\n },\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"example-path\"\n }\n ],\n \"labelText\": \"Link preview\",\n \"localizedLabelText\": {\n \"es\": \"Link preview localized in Spanish\"\n },\n \"logoUrl\": \"https://www.example.com/images/smart-chip-icon.png\"\n }\n ],\n \"createActionTriggers\": [\n {\n \"id\": \"exampleId\",\n \"labelText\": \"Example label text\",\n \"localizedLabelText\": {\n \"es\": \"Label text localized in Spanish\"\n },\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onCreateAction\",\n \"logoUrl\": \"https://www.example.com/images/case.png\"\n }\n ]\n },\n \"sheets\": {\n \"homepageTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onEditorsHomepage\"\n },\n \"onFileScopeGrantedTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onFileScopeGrantedEditors\"\n }\n },\n \"slides\": {\n \"homepageTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onEditorsHomepage\"\n },\n \"onFileScopeGrantedTrigger\": {\n \"runFunction\": \"https://myownpersonaldomain.com/mypage?trigger=onFileScopeGrantedEditors\"\n }\n },\n \"meet\": {\n \"homepageTrigger\",\n \"Web\": [\n {\n \"sidePanelUrl\": \"https://myownpersonaldomain.com/sidePanelUrl\",\n \"supportsScreenSharing\": true,\n \"addOnOrigins\": [\n \"https://www.myownpersonaldomain.com\",\n \"https://www.myownpersonaldomain.com:443\"\n ],\n \"logoUrl\": \"https://myownpersonaldomain.com/meetWebLogoUrl\",\n \"darkModeLogoUrl\": \"https://myownpersonaldomain.com/darkModeLogoUrl\"\n }\n ]\n },\n \"httpOptions\": {\n \"authorizationHeader\": \"SYSTEM_ID_TOKEN\",\n \"granularOauthPermissionSupport\": \"OPT_IN\"\n }\n },\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/calendar.addons.execute\",\n \"https://www.googleapis.com/auth/calendar.addons.current.event.read\",\n \"https://www.googleapis.com/auth/calendar.addons.current.event.write\",\n \"https://www.googleapis.com/auth/drive.addons.metadata.readonly\",\n \"https://www.googleapis.com/auth/gmail.addons.current.action.compose\",\n \"https://www.googleapis.com/auth/gmail.addons.current.message.metadata\",\n \"https://www.googleapis.com/auth/userinfo.email\",\n \"https://www.googleapis.com/auth/script.external_request\",\n \"https://www.googleapis.com/auth/script.locale\",\n \"https://www.googleapis.com/auth/script.scriptapp\",\n \"https://www.googleapis.com/auth/drive.file\",\n \"https://www.googleapis.com/auth/documents.currentonly\",\n \"https://www.googleapis.com/auth/spreadsheets.currentonly\",\n \"https://www.googleapis.com/auth/presentations.currentonly\",\n \"https://www.googleapis.com/auth/workspace.linkpreview\"\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.508Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":339,"estimatedTokens":2805}}687{"id":"doc-compose_draft_messages_google_workspace_add_ons_-78fac142","source":"documentation","title":"Compose draft messages | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/gmail/compose","text":"Example:\n```text\nvar composeAction = CardService.newAction()\n .setFunctionName('createReplyDraft');\nvar composeButton = CardService.newTextButton()\n .setText('Compose Reply')\n .setComposeAction(\n composeAction,\n CardService.ComposedEmailType.REPLY_AS_DRAFT);\n\n// ...\n\n/**\n * Creates a draft email (with an attachment and inline image)\n * as a reply to an existing message.\n * @param {Object} e An event object passed by the action.\n * @return {ComposeActionResponse}\n */\nfunction createReplyDraft(e) {\n // Activate temporary Gmail scopes, in this case to allow\n // a reply to be drafted.\n var accessToken = e.gmail.accessToken;\n GmailApp.setCurrentMessageAccessToken(accessToken);\n\n // Creates a draft reply.\n var messageId = e.gmail.messageId;\n var message = GmailApp.getMessageById(messageId);\n var draft = message.createDraftReply('',\n {\n htmlBody: \"Kitten! <img src='cid:kitten'/>\",\n attachments: [\n UrlFetchApp.fetch('https://example.com/images/myDog.jpg')\n .getBlob()\n ],\n inlineImages: {\n \"kitten\": UrlFetchApp.fetch('https://example.com/images/myKitten.jpg')\n .getBlob()\n }\n }\n );\n\n // Return a built draft response. This causes Gmail to present a\n // compose window to the user, pre-filled with the content previously\n // specified.\n return CardService.newComposeActionResponseBuilder()\n .setGmailDraft(draft).build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.509Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":50,"estimatedTokens":378}}688{"id":"doc-build_a_google_workspace_add_on_with_apps_script-8fb9866c","source":"documentation","title":"Build a Google Workspace add-on with Apps Script | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/quickstart/cats-quickstart","text":"Example:\n```text\n/**\n * This simple Google Workspace add-on shows a random image of a cat in the\n * sidebar. When opened manually (the homepage card), some static text is\n * overlayed on the image, but when contextual cards are opened a new cat image\n * is shown with the text taken from that context (such as a message's subject\n * line) overlaying the image. There is also a button that updates the card with\n * a new random cat image.\n *\n * Click \"File > Make a copy...\" to copy the script, and \"Publish > Deploy from\n * manifest > Install add-on\" to install it.\n */\n\n/**\n * The maximum number of characters that can fit in the cat image.\n */\nvar MAX_MESSAGE_LENGTH = 40;\n\n/**\n * Callback for rendering the homepage card.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction onHomepage(e) {\n console.log(e);\n var hour = Number(Utilities.formatDate(new Date(), e.userTimezone.id, 'H'));\n var message;\n if (hour >= 6 && hour < 12) {\n message = 'Good morning';\n } else if (hour >= 12 && hour < 18) {\n message = 'Good afternoon';\n } else {\n message = 'Good night';\n }\n message += ' ' + e.hostApp;\n return createCatCard(message, true);\n}\n\n/**\n * Creates a card with an image of a cat, overlayed with the text.\n * @param {String} text The text to overlay on the image.\n * @param {Boolean} isHomepage True if the card created here is a homepage;\n * false otherwise. Defaults to false.\n * @return {CardService.Card} The assembled card.\n */\nfunction createCatCard(text, isHomepage) {\n // Explicitly set the value of isHomepage as false if null or undefined.\n if (!isHomepage) {\n isHomepage = false;\n }\n\n // Use the \"Cat as a service\" API to get the cat image. Add a \"time\" URL\n // parameter to act as a cache buster.\n var now = new Date();\n // Replace forward slashes in the text, as they break the CataaS API.\n var caption = text.replace(/\\//g, ' ');\n var imageUrl =\n Utilities.formatString('https://cataas.com/cat/says/%s?time=%s',\n encodeURIComponent(caption), now.getTime());\n var image = CardService.newImage()\n .setImageUrl(imageUrl)\n .setAltText('Meow')\n\n // Create a button that changes the cat image when pressed.\n // Note: Action parameter keys and values must be strings.\n var action = CardService.newAction()\n .setFunctionName('onChangeCat')\n .setParameters({text: text, isHomepage: isHomepage.toString()});\n var button = CardService.newTextButton()\n .setText('Change cat')\n .setOnClickAction(action)\n .setTextButtonStyle(CardService.TextButtonStyle.FILLED);\n var buttonSet = CardService.newButtonSet()\n .addButton(button);\n\n // Create a footer to be shown at the bottom.\n var footer = CardService.newFixedFooter()\n .setPrimaryButton(CardService.newTextButton()\n .setText('Powered by cataas.com')\n .setOpenLink(CardService.newOpenLink()\n .setUrl('https://cataas.com')));\n\n // Assemble the widgets and return the card.\n var section = CardService.newCardSection()\n .addWidget(image)\n .addWidget(buttonSet);\n var card = CardService.newCardBuilder()\n .addSection(section)\n .setFixedFooter(footer);\n\n if (!isHomepage) {\n // Create the header shown when the card is minimized,\n // but only when this card is a contextual card. Peek headers\n // are never used by non-contexual cards like homepages.\n var peekHeader = CardService.newCardHeader()\n .setTitle('Contextual Cat')\n .setImageUrl('https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png')\n .setSubtitle(text);\n card.setPeekCardHeader(peekHeader)\n }\n\n return card.build();\n}\n\n/**\n * Callback for the \"Change cat\" button.\n * @param {Object} e The event object, documented {@link\n * https://developers.google.com/gmail/add-ons/concepts/actions#action_event_objects\n * here}.\n * @return {CardService.ActionResponse} The action response to apply.\n */\nfunction onChangeCat(e) {\n console.log(e);\n // Get the text that was shown in the current cat image. This was passed as a\n // parameter on the Action set for the button.\n var text = e.parameters.text;\n\n // The isHomepage parameter is passed as a string, so convert to a Boolean.\n var isHomepage = e.parameters.isHomepage === 'true';\n\n // Create a new card with the same text.\n var card = createCatCard(text, isHomepage);\n\n // Create an action response that instructs the add-on to replace\n // the current card with the new one.\n var navigation = CardService.newNavigation()\n .updateCard(card);\n var actionResponse = CardService.newActionResponseBuilder()\n .setNavigation(navigation);\n return actionResponse.build();\n}\n\n/**\n * Truncate a message to fit in the cat image.\n * @param {string} message The message to truncate.\n * @return {string} The truncated message.\n */\nfunction truncate(message) {\n if (message.length > MAX_MESSAGE_LENGTH) {\n message = message.slice(0, MAX_MESSAGE_LENGTH);\n message = message.slice(0, message.lastIndexOf(' ')) + '...';\n }\n return message;\n}\n```\n\nExample:\n```text\n/**\n * Callback for rendering the card for a specific Gmail message.\n * @param {Object} e The event object.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction onGmailMessage(e) {\n console.log(e);\n // Get the ID of the message the user has open.\n var messageId = e.gmail.messageId;\n\n // Get an access token scoped to the current message and use it for GmailApp\n // calls.\n var accessToken = e.gmail.accessToken;\n GmailApp.setCurrentMessageAccessToken(accessToken);\n\n // Get the subject of the email.\n var message = GmailApp.getMessageById(messageId);\n var subject = message.getThread().getFirstMessageSubject();\n\n // Remove labels and prefixes.\n subject = subject\n .replace(/^([rR][eE]|[fF][wW][dD])\\:\\s*/, '')\n .replace(/^\\[.*?\\]\\s*/, '');\n\n // If neccessary, truncate the subject to fit in the image.\n subject = truncate(subject);\n\n return createCatCard(subject);\n}\n\n/**\n * Callback for rendering the card for the compose action dialog.\n * @param {Object} e The event object.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction onGmailCompose(e) {\n console.log(e);\n var header = CardService.newCardHeader()\n .setTitle('Insert cat')\n .setSubtitle('Add a custom cat image to your email message.');\n // Create text input for entering the cat's message.\n var input = CardService.newTextInput()\n .setFieldName('text')\n .setTitle('Caption')\n .setHint('What do you want the cat to say?');\n // Create a button that inserts the cat image when pressed.\n var action = CardService.newAction()\n .setFunctionName('onGmailInsertCat');\n var button = CardService.newTextButton()\n .setText('Insert cat')\n .setOnClickAction(action)\n .setTextButtonStyle(CardService.TextButtonStyle.FILLED);\n var buttonSet = CardService.newButtonSet()\n .addButton(button);\n // Assemble the widgets and return the card.\n var section = CardService.newCardSection()\n .addWidget(input)\n .addWidget(buttonSet);\n var card = CardService.newCardBuilder()\n .setHeader(header)\n .addSection(section);\n return card.build();\n}\n\n/**\n * Callback for inserting a cat into the Gmail draft.\n * @param {Object} e The event object.\n * @return {CardService.UpdateDraftActionResponse} The draft update response.\n */\nfunction onGmailInsertCat(e) {\n console.log(e);\n // Get the text that was entered by the user.\n var text = e.formInput.text;\n // Use the \"Cat as a service\" API to get the cat image. Add a \"time\" URL\n // parameter to act as a cache buster.\n var now = new Date();\n var imageUrl = 'https://cataas.com/cat';\n if (text) {\n // Replace forward slashes in the text, as they break the CataaS API.\n var caption = text.replace(/\\//g, ' ');\n imageUrl += Utilities.formatString('/says/%s?time=%s',\n encodeURIComponent(caption), now.getTime());\n }\n var imageHtmlContent = '<img style=\"display: block; max-height: 300px;\" src=\"'\n + imageUrl + '\"/>';\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftBodyAction(CardService.newUpdateDraftBodyAction()\n .addUpdateContent(imageHtmlContent,CardService.ContentType.MUTABLE_HTML)\n .setUpdateType(CardService.UpdateDraftBodyType.IN_PLACE_INSERT))\n .build();\n return response;\n}\n```\n\nExample:\n```text\n/**\n * Callback for rendering the card for a specific Calendar event.\n * @param {Object} e The event object.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction onCalendarEventOpen(e) {\n console.log(e);\n var calendar = CalendarApp.getCalendarById(e.calendar.calendarId);\n // The event metadata doesn't include the event's title, so using the\n // calendar.readonly scope and fetching the event by it's ID.\n var event = calendar.getEventById(e.calendar.id);\n if (!event) {\n // This is a new event still being created.\n return createCatCard('A new event! Am I invited?');\n }\n var title = event.getTitle();\n // If necessary, truncate the title to fit in the image.\n title = truncate(title);\n return createCatCard(title);\n}\n```\n\nExample:\n```text\n/**\n * Callback for rendering the card for specific Drive items.\n * @param {Object} e The event object.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction onDriveItemsSelected(e) {\n console.log(e);\n var items = e.drive.selectedItems;\n // Include at most 5 items in the text.\n items = items.slice(0, 5);\n var text = items.map(function(item) {\n var title = item.title;\n // If neccessary, truncate the title to fit in the image.\n title = truncate(title);\n return title;\n }).join('\\n');\n return createCatCard(text);\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/New_York\",\n \"dependencies\": {\n },\n \"exceptionLogging\": \"STACKDRIVER\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/calendar.addons.execute\",\n \"https://www.googleapis.com/auth/calendar.readonly\",\n \"https://www.googleapis.com/auth/drive.addons.metadata.readonly\",\n \"https://www.googleapis.com/auth/gmail.addons.current.action.compose\",\n \"https://www.googleapis.com/auth/gmail.addons.current.message.readonly\",\n \"https://www.googleapis.com/auth/gmail.addons.execute\",\n \"https://www.googleapis.com/auth/script.locale\"],\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Cats\",\n \"logoUrl\": \"https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png\",\n \"useLocaleFromApp\": true,\n \"homepageTrigger\": {\n \"runFunction\": \"onHomepage\",\n \"enabled\": true\n },\n \"universalActions\": [{\n \"label\": \"Learn more about Cataas\",\n \"openLink\": \"https://cataas.com\"\n }]\n },\n \"gmail\": {\n \"contextualTriggers\": [{\n \"unconditional\": {\n },\n \"onTriggerFunction\": \"onGmailMessage\"\n }],\n \"composeTrigger\": {\n \"selectActions\": [{\n \"text\": \"Insert cat\",\n \"runFunction\": \"onGmailCompose\"\n }],\n \"draftAccess\": \"NONE\"\n }\n },\n \"drive\": {\n \"onItemsSelectedTrigger\": {\n \"runFunction\": \"onDriveItemsSelected\"\n }\n },\n \"calendar\": {\n \"eventOpenTrigger\": {\n \"runFunction\": \"onCalendarEventOpen\"\n }\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.510Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":347,"estimatedTokens":2831}}689{"id":"doc-autocomplete_suggestions_for_text_inputs_google_-4ba25679","source":"documentation","title":"Autocomplete suggestions for text inputs | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/how-tos/suggestions","text":"Example:\n```text\n// Create an input with a static suggestion list.\nvar textInput1 = CardService.newTextInput()\n .setFieldName('colorInput')\n .setTitle('Color choice')\n .setSuggestions(CardService.newSuggestions()\n .addSuggestion('Red')\n .addSuggestion('Yellow')\n .addSuggestions(['Blue', 'Black', 'Green']));\n\n// Create an input with a dynamic suggestion list.\nvar action = CardService.newAction()\n .setFunctionName('refreshSuggestions');\nvar textInput2 = CardService.newTextInput()\n .setFieldName('emailInput')\n .setTitle('Email')\n .setSuggestionsAction(action);\n\n// ...\n\n/**\n * Build and return a suggestion response. In this case, the suggestions\n * are a list of emails taken from the To: and CC: lists of the open\n * message in Gmail, filtered by the text that the user has already\n * entered. This method assumes the Google Workspace\n * add-on extends Gmail; the add-on only calls this method for cards\n * displayed when the user has entered a message context.\n *\n * @param {Object} e the event object containing data associated with\n * this text input widget.\n * @return {SuggestionsResponse}\n */\n function refreshSuggestions(e) {\n // Activate temporary Gmail scopes, in this case so that the\n // open message metadata can be read.\n var accessToken = e.gmail.accessToken;\n GmailApp.setCurrentMessageAccessToken(accessToken);\n\n var userInput = e && e.formInput['emailInput'].toLowerCase();\n var messageId = e.gmail.messageId;\n var message = GmailApp.getMessageById(messageId);\n\n // Combine the comma-separated returned by these methods.\n var addresses = message.getTo() + ',' + message.getCc();\n\n // Filter the address list to those containing the text the user\n // has already entered.\n var suggestionList = [];\n addresses.split(',').forEach(function(email) {\n if (email.toLowerCase().indexOf(userInput) !== -1) {\n suggestionList.push(email);\n }\n });\n suggestionList.sort();\n\n return CardService.newSuggestionsResponseBuilder()\n .setSuggestions(CardService.newSuggestions()\n .addSuggestions(suggestionList))\n .build(); // Don't forget to build the response!\n }\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.511Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":64,"estimatedTokens":553}}690{"id":"doc-event_objects_google_workspace_add_ons_google_fo-b5adb629","source":"documentation","title":"Event objects | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/concepts/event-objects","text":"Example:\n```text\n\"docs\" : {\n \"matchedUrl\" : {\n \"url\" : \"https://www.example.com/12345\"\n }\n}\n```\n\nExample:\n```text\n\"sheets\" : {\n \"matchedUrl\" : {\n \"url\" : \"https://www.example.com/12345\"\n }\n}\n```\n\nExample:\n```text\n\"slides\" : {\n \"matchedUrl\" : {\n \"url\" : \"https://www.example.com/12345\"\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.515Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":28,"estimatedTokens":82}}691{"id":"doc-calendar_actions_google_workspace_add_ons_google-aff768d5","source":"documentation","title":"Calendar actions | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/calendar/calendar-actions","text":"Example:\n```text\n/**\n * Build a basic card with a button that sends a notification.\n * This function is called as part of the eventOpenTrigger that builds\n * a UI when the user opens an event.\n *\n * @param e The event object passed to eventOpenTrigger function.\n * @return {Card}\n */\nfunction buildSimpleCard(e) {\n var buttonAction = CardService.newAction()\n .setFunctionName('onAddAttendeesButtonClicked');\n var button = CardService.newTextButton()\n .setText('Add new attendee')\n .setOnClickAction(buttonAction);\n\n // Check the event object to determine if the user can add\n // attendees and disable the button if not.\n if (!e.calendar.capabilities.canAddAttendees) {\n button.setDisabled(true);\n }\n\n // ...continue creating card sections and widgets, then create a Card\n // object to add them to. Return the built Card object.\n}\n\n/**\n * Callback function for a button action. Adds attendees to the\n * Calendar event being edited.\n *\n * @param {Object} e The action event object.\n * @return {CalendarEventActionResponse}\n */\nfunction onAddAttendeesButtonClicked (e) {\n return CardService.newCalendarEventActionResponseBuilder()\n .addAttendees([\"aiko@example.com\", \"malcom@example.com\"])\n .build();\n}\n```\n\nExample:\n```text\n/**\n * Build a basic card with a button that sends a notification.\n * This function is called as part of the eventOpenTrigger that builds\n * a UI when the user opens a Calendar event.\n *\n * @param e The event object passed to eventOpenTrigger function.\n * @return {Card}\n */\nfunction buildSimpleCard(e) {\n var buttonAction = CardService.newAction()\n .setFunctionName('onSaveConferenceOptionsButtonClicked')\n .setParameters(\n {'phone': \"1555123467\", 'adminEmail': \"joyce@example.com\"});\n var button = CardService.newTextButton()\n .setText('Add new attendee')\n .setOnClickAction(buttonAction);\n\n // Check the event object to determine if the user can set\n // conference data and disable the button if not.\n if (!e.calendar.capabilities.canSetConferenceData) {\n button.setDisabled(true);\n }\n\n // ...continue creating card sections and widgets, then create a Card\n // object to add them to. Return the built Card object.\n}\n\n/**\n * Callback function for a button action. Sets conference data for the\n * Calendar event being edited.\n *\n * @param {Object} e The action event object.\n * @return {CalendarEventActionResponse}\n */\nfunction onSaveConferenceOptionsButtonClicked(e) {\n var parameters = e.commonEventObject.parameters;\n\n // Create an entry point and a conference parameter.\n var phoneEntryPoint = ConferenceDataService.newEntryPoint()\n .setEntryPointType(ConferenceDataService.EntryPointType.PHONE)\n .setUri('tel:' + parameters['phone']);\n\n var adminEmailParameter = ConferenceDataService.newConferenceParameter()\n .setKey('adminEmail')\n .setValue(parameters['adminEmail']);\n\n // Create a conference data object to set to this Calendar event.\n var conferenceData = ConferenceDataService.newConferenceDataBuilder()\n .addEntryPoint(phoneEntryPoint)\n .addConferenceParameter(adminEmailParameter)\n .setConferenceSolutionId('myWebScheduledMeeting')\n .build();\n\n return CardService.newCalendarEventActionResponseBuilder()\n .setConferenceData(conferenceData)\n .build();\n}\n```\n\nExample:\n```text\n/**\n * Build a basic card with a button that creates a new attachment.\n * This function is called as part of the eventAttachmentTrigger that\n * builds a UI when the user goes through the add-attachments flow.\n *\n * @param e The event object passed to eventAttachmentTrigger function.\n * @return {Card}\n */\nfunction buildSimpleCard(e) {\n var buttonAction = CardService.newAction()\n .setFunctionName('onAddAttachmentButtonClicked');\n var button = CardService.newTextButton()\n .setText('Add a custom attachment')\n .setOnClickAction(buttonAction);\n\n // Check the event object to determine if the user can add\n // attachments and disable the button if not.\n if (!e.calendar.capabilities.canAddAttachments) {\n button.setDisabled(true);\n }\n\n // ...continue creating card sections and widgets, then create a Card\n // object to add them to. Return the built Card object.\n}\n\n/**\n * Callback function for a button action. Adds attachments to the\n * Calendar event being edited.\n *\n * @param {Object} e The action event object.\n * @return {CalendarEventActionResponse}\n */\nfunction onAddAttachmentButtonClicked(e) {\n return CardService.newCalendarEventActionResponseBuilder()\n .addAttachments([\n CardService.newAttachment()\n .setResourceUrl(\"https://example.com/test\")\n .setTitle(\"Custom attachment\")\n .setMimeType(\"text/html\")\n .setIconUrl(\"https://example.com/test.png\")\n ])\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.517Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":150,"estimatedTokens":1214}}692{"id":"doc-universal_actions_google_workspace_add_ons_googl-99d1f658","source":"documentation","title":"Universal actions | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/how-tos/universal-actions","text":"Example:\n```text\n\"oauthScopes\": [\n \"https://www.googleapis.com/auth/gmail.addons.current.message.metadata\"\n],\n\"addOns\": {\n \"common\": {\n \"name\": \"Universal Actions Only Addon\",\n \"logoUrl\": \"https://www.example.com/hosted/images/2x/my-icon.png\",\n \"openLinkUrlPrefixes\": [\n \"https://www.google.com\",\n \"https://www.example.com/urlbase\"\n ],\n \"universalActions\": [{\n \"label\": \"Open google.com\",\n \"openLink\": \"https://www.google.com\"\n }, {\n \"label\": \"Open contact URL\",\n \"runFunction\": \"openContactURL\"\n }, {\n \"label\": \"Open settings\",\n \"runFunction\": \"createSettingsResponse\"\n }, {\n \"label\": \"Run background sync\",\n \"runFunction\": \"runBackgroundSync\"\n }],\n ...\n },\n \"gmail\": {\n \"contextualTriggers\": [\n {\n \"unconditional\": {},\n \"onTriggerFunction\": \"getContextualAddOn\"\n }\n ]\n },\n ...\n},\n...\n```\n\nExample:\n```text\n/**\n * Open a contact URL.\n * @param {Object} e an event object\n * @return {UniversalActionResponse}\n */\nfunction openContactURL(e) {\n // Activate temporary Gmail scopes, in this case so that the\n // open message metadata can be read.\n var accessToken = e.gmail.accessToken;\n GmailApp.setCurrentMessageAccessToken(accessToken);\n\n // Build URL to open based on a base URL and the sender's email.\n // This URL must be included in the openLinkUrlPrefixes whitelist.\n var messageId = e.gmail.messageId;\n var message = GmailApp.getMessageById(messageId);\n var sender = message.getFrom();\n var url = \"https://www.example.com/urlbase/\" + sender;\n return CardService.newUniversalActionResponseBuilder()\n .setOpenLink(CardService.newOpenLink()\n .setUrl(url))\n .build();\n}\n\n/**\n * Create a collection of cards to control the add-on\n * settings and present other information. These cards are displayed in a list\n * when the user selects the associated \"Open settings\" universal action.\n *\n * @param {Object} e an event object\n * @return {UniversalActionResponse}\n */\nfunction createSettingsResponse(e) {\n return CardService.newUniversalActionResponseBuilder()\n .displayAddOnCards(\n [createSettingCard(), createAboutCard()])\n .build();\n}\n\n/**\n * Create and return a built settings card.\n * @return {Card}\n */\nfunction createSettingCard() {\n return CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader().setTitle('Settings'))\n .addSection(CardService.newCardSection()\n .addWidget(CardService.newSelectionInput()\n .setType(CardService.SelectionInputType.CHECK_BOX)\n .addItem(\"Ask before deleting contact\", \"contact\", false)\n .addItem(\"Ask before deleting cache\", \"cache\", false)\n .addItem(\"Preserve contact ID after deletion\", \"contactId\", false))\n // ... continue adding widgets or other sections here ...\n ).build(); // Don't forget to build the card!\n}\n\n/**\n * Create and return a built 'About' informational card.\n * @return {Card}\n */\nfunction createAboutCard() {\n return CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader().setTitle('About'))\n .addSection(CardService.newCardSection()\n .addWidget(CardService.newTextParagraph()\n .setText('This add-on manages contact information. For more '\n + 'details see the <a href=\"https://www.example.com/help\">help page</a>.'))\n // ... add other information widgets or sections here ...\n ).build(); // Don't forget to build the card!\n}\n\n/**\n * Run background tasks, none of which should alter the UI.\n * Also records the time of sync in the script properties.\n *\n * @param {Object} e an event object\n */\nfunction runBackgroundSync(e) {\n var props = PropertiesService.getUserProperties();\n props.setProperty(\"syncTime\", new Date().toString());\n\n syncWithContacts(); // Not shown.\n updateCache(); // Not shown.\n validate(); // Not shown.\n\n // no return value tells the UI to keep showing the current card.\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.518Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":132,"estimatedTokens":1013}}693{"id":"doc-add_a_web_conferencing_service_to_google_calenda-892c352a","source":"documentation","title":"Add a web conferencing service to Google Calendar | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/samples/conferencing-sample","text":"Example:\n```text\n/**\n * Creates a conference, then builds and returns a ConferenceData object\n * with the corresponding conference information. This method is called\n * when a user selects a conference solution defined by the add-on that\n * uses this function as its 'onCreateFunction' in the add-on manifest.\n *\n * @param {Object} arg The default argument passed to a 'onCreateFunction';\n * it carries information about the Google Calendar event.\n * @return {ConferenceData}\n */\nfunction createConference(arg) {\n const eventData = arg.eventData;\n const calendarId = eventData.calendarId;\n const eventId = eventData.eventId;\n\n // Retrieve the Calendar event information using the Calendar\n // Advanced service.\n var calendarEvent;\n try {\n calendarEvent = Calendar.Events.get(calendarId, eventId);\n } catch (err) {\n // The calendar event does not exist just yet; just proceed with the\n // given event ID and allow the event details to sync later.\n console.log(err);\n calendarEvent = {\n id: eventId,\n };\n }\n\n // Create a conference on the third-party service and return the\n // conference data or errors in a custom JSON object.\n var conferenceInfo = create3rdPartyConference(calendarEvent);\n\n // Build and return a ConferenceData object, either with conference or\n // error information.\n var dataBuilder = ConferenceDataService.newConferenceDataBuilder();\n\n if (!conferenceInfo.error) {\n // No error, so build the ConferenceData object from the\n // returned conference info.\n\n var phoneEntryPoint = ConferenceDataService.newEntryPoint()\n .setEntryPointType(ConferenceDataService.EntryPointType.PHONE)\n .setUri('tel:+' + conferenceInfo.phoneNumber)\n .setPin(conferenceInfo.phonePin);\n\n var adminEmailParameter = ConferenceDataService.newConferenceParameter()\n .setKey('adminEmail')\n .setValue(conferenceInfo.adminEmail);\n\n dataBuilder.setConferenceId(conferenceInfo.id)\n .addEntryPoint(phoneEntryPoint)\n .addConferenceParameter(adminEmailParameter)\n .setNotes(conferenceInfo.conferenceLegalNotice);\n\n if (conferenceInfo.videoUri) {\n var videoEntryPoint = ConferenceDataService.newEntryPoint()\n .setEntryPointType(ConferenceDataService.EntryPointType.VIDEO)\n .setUri(conferenceInfo.videoUri)\n .setPasscode(conferenceInfo.videoPasscode);\n dataBuilder.addEntryPoint(videoEntryPoint);\n }\n\n // Since the conference creation request succeeded, make sure that\n // syncing has been enabled.\n initializeSyncing(calendarId, eventId, conferenceInfo.id);\n\n } else if (conferenceInfo.error === 'AUTH') {\n // Authenentication error. Implement a function to build the correct\n // authenication URL for the third-party conferencing system.\n var authenticationUrl = getAuthenticationUrl();\n var error = ConferenceDataService.newConferenceError()\n .setConferenceErrorType(\n ConferenceDataService.ConferenceErrorType.AUTHENTICATION)\n .setAuthenticationUrl(authenticationUrl);\n dataBuilder.setError(error);\n\n } else {\n // Other error type;\n var error = ConferenceDataService.newConferenceError()\n .setConferenceErrorType(\n ConferenceDataService.ConferenceErrorType.TEMPORARY);\n dataBuilder.setError(error);\n }\n\n // Don't forget to build the ConferenceData object.\n return dataBuilder.build();\n}\n\n\n/**\n * Contact the third-party conferencing system to create a conference there,\n * using the provided calendar event information. Collects and retuns the\n * conference data returned by the third-party system in a custom JSON object\n * with the following fields:\n *\n * data.adminEmail - the conference administrator's email\n * data.conferenceLegalNotice - the conference legal notice text\n * data.error - Only present if there was an error during\n * conference creation. Equal to 'AUTH' if the add-on user needs to\n * authorize on the third-party system.\n * data.id - the conference ID\n * data.phoneNumber - the conference phone entry point phone number\n * data.phonePin - the conference phone entry point PIN\n * data.videoPasscode - the conference video entry point passcode\n * data.videoUri - the conference video entry point URI\n *\n * The above fields are specific to this example; which conference information\n * your add-on needs is dependent on the third-party conferencing system\n * requirements.\n *\n * @param {Object} calendarEvent A Calendar Event resource object returned by\n * the Google Calendar API.\n * @return {Object}\n */\nfunction create3rdPartyConference(calendarEvent) {\n var data = {};\n\n // Implementation details dependent on the third-party system API.\n // Typically one or more API calls are made to create the conference and\n // acquire its relevant data, which is then put in to the returned JSON\n // object.\n\n return data;\n}\n\n/**\n * Return the URL used to authenticate the user with the third-party\n * conferencing system.\n *\n * @return {String}\n */\nfunction getAuthenticationUrl() {\n var url;\n // Implementation details dependent on the third-party system.\n\n return url;\n}\n```\n\nExample:\n```text\n/**\n * Initializes syncing of conference data by creating a sync trigger and\n * sync token if either does not exist yet.\n *\n * @param {String} calendarId The ID of the Google Calendar.\n */\nfunction initializeSyncing(calendarId) {\n // Create a syncing trigger if it doesn't exist yet.\n createSyncTrigger(calendarId);\n\n // Perform an event sync to create the initial sync token.\n syncEvents({'calendarId': calendarId});\n}\n\n/**\n * Creates a sync trigger if it does not exist yet.\n *\n * @param {String} calendarId The ID of the Google Calendar.\n */\nfunction createSyncTrigger(calendarId) {\n // Check to see if the trigger already exists; if does, return.\n var allTriggers = ScriptApp.getProjectTriggers();\n for (var i = 0; i < allTriggers.length; i++) {\n var trigger = allTriggers[i];\n if (trigger.getTriggerSourceId() == calendarId) {\n return;\n }\n }\n\n // Trigger does not exist, so create it. The trigger calls the\n // 'syncEvents()' trigger function when it fires.\n var trigger = ScriptApp.newTrigger('syncEvents')\n .forUserCalendar(calendarId)\n .onEventUpdated()\n .create();\n}\n\n/**\n * Sync events for the given calendar; this is the syncing trigger\n * function. If a sync token already exists, this retrieves all events\n * that have been modified since the last sync, then checks each to see\n * if an associated conference needs to be updated and makes any required\n * changes. If the sync token does not exist or is invalid, this\n * retrieves future events modified in the last 24 hours instead. In\n * either case, a new sync token is created and stored.\n *\n * @param {Object} e If called by a event updated trigger, this object\n * contains the Google Calendar ID, authorization mode, and\n * calling trigger ID. Only the calendar ID is actually used here,\n * however.\n */\nfunction syncEvents(e) {\n var calendarId = e.calendarId;\n var properties = PropertiesService.getUserProperties();\n var syncToken = properties.getProperty('syncToken');\n\n var options;\n if (syncToken) {\n // There's an existing sync token, so configure the following event\n // retrieval request to only get events that have been modified\n // since the last sync.\n options = {\n syncToken: syncToken\n };\n } else {\n // No sync token, so configure to do a 'full' sync instead. In this\n // example only recently updated events are retrieved in a full sync.\n // A larger time window can be examined during a full sync, but this\n // slows down the script execution. Consider the trade-offs while\n // designing your add-on.\n var now = new Date();\n var yesterday = new Date();\n yesterday.setDate(now.getDate() - 1);\n options = {\n timeMin: now.toISOString(), // Events that start after now...\n updatedMin: yesterday.toISOString(), // ...and were modified recently\n maxResults: 50, // Max. number of results per page of responses\n orderBy: 'updated'\n }\n }\n\n // Examine the list of updated events since last sync (or all events\n // modified after yesterday if the sync token is missing or invalid), and\n // update any associated conferences as required.\n var events;\n var pageToken;\n do {\n try {\n options.pageToken = pageToken;\n events = Calendar.Events.list(calendarId, options);\n } catch (err) {\n // Check to see if the sync token was invalidated by the server;\n // if so, perform a full sync instead.\n if (err.message ===\n \"Sync token is no longer valid, a full sync is required.\") {\n properties.deleteProperty('syncToken');\n syncEvents(e);\n return;\n } else {\n throw new Error(err.message);\n }\n }\n\n // Read through the list of returned events looking for conferences\n // to update.\n if (events.items && events.items.length > 0) {\n for (var i = 0; i < events.items.length; i++) {\n var calEvent = events.items[i];\n // Check to see if there is a record of this event has a\n // conference that needs updating.\n if (eventHasConference(calEvent)) {\n updateConference(calEvent, calEvent.conferenceData.conferenceId);\n }\n }\n }\n\n pageToken = events.nextPageToken;\n } while (pageToken);\n\n // Record the new sync token.\n if (events.nextSyncToken) {\n properties.setProperty('syncToken', events.nextSyncToken);\n }\n}\n\n/**\n * Returns true if the specified event has an associated conference\n * of the type managed by this add-on; retuns false otherwise.\n *\n * @param {Object} calEvent The Google Calendar event object, as defined by\n * the Calendar API.\n * @return {boolean}\n */\nfunction eventHasConference(calEvent) {\n var name = calEvent.conferenceData.conferenceSolution.name || null;\n\n // This version checks if the conference data solution name matches the\n // one of the solution names used by the add-on. Alternatively you could\n // check the solution's entry point URIs or other solution-specific\n // information.\n if (name) {\n if (name === \"My Web Conference\" ||\n name === \"My Recorded Web Conference\") {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Update a conference based on new Google Calendar event information.\n * The exact implementation of this function is highly dependant on the\n * details of the third-party conferencing system, so only a rough outline\n * is shown here.\n *\n * @param {Object} calEvent The Google Calendar event object, as defined by\n * the Calendar API.\n * @param {String} conferenceId The ID used to identify the conference on\n * the third-party conferencing system.\n */\nfunction updateConference(calEvent, conferenceId) {\n // Check edge case: the event was cancelled\n if (calEvent.status === 'cancelled' || eventHasConference(calEvent)) {\n // Use the third-party API to delete the conference too.\n\n\n } else {\n // Extract any necessary information from the event object, then\n // make the appropriate third-party API requests to update the\n // conference with that information.\n\n }\n}\n```\n\nExample:\n```text\n{\n \"addOns\": {\n \"calendar\": {\n \"conferenceSolution\": [{\n \"id\": 1,\n \"name\": \"My Web Conference\",\n \"logoUrl\": \"https://lh3.googleusercontent.com/...\",\n \"onCreateFunction\": \"createConference\"\n }],\n \"currentEventAccess\": \"READ_WRITE\"\n },\n \"common\": {\n \"homepageTrigger\": {\n \"enabled\": false\n },\n \"logoUrl\": \"https://lh3.googleusercontent.com/...\",\n \"name\": \"My Web Conferencing\"\n }\n },\n \"timeZone\": \"America/New_York\",\n \"dependencies\": {\n \"enabledAdvancedServices\": [\n {\n \"userSymbol\": \"Calendar\",\n \"serviceId\": \"calendar\",\n \"version\": \"v3\"\n }\n ]\n },\n \"webapp\": {\n \"access\": \"ANYONE\",\n \"executeAs\": \"USER_ACCESSING\"\n },\n \"exceptionLogging\": \"STACKDRIVER\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/calendar.addons.execute\",\n \"https://www.googleapis.com/auth/calendar.events.readonly\",\n \"https://www.googleapis.com/auth/calendar.addons.current.event.read\",\n \"https://www.googleapis.com/auth/calendar.addons.current.event.write\",\n \"https://www.googleapis.com/auth/script.external_request\",\n \"https://www.googleapis.com/auth/script.scriptapp\"\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.520Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":367,"estimatedTokens":3125}}694{"id":"doc-build_interactive_cards_google_workspace_add_ons-4f4402bf","source":"documentation","title":"Build interactive cards | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/how-tos/interactions","text":"Example:\n```text\n/**\n * Build a card with a button that sends a notification.\n * @return {Card}\n */\nfunction buildSimpleCard() {\n var buttonAction = CardService.newAction()\n .setFunctionName('notifyUser')\n .setParameters({'notifyText': 'Button clicked!'});\n var button = CardService.newTextButton()\n .setText('Notify')\n .setOnClickAction(buttonAction);\n\n // ...continue creating widgets, then create a Card object\n // to add them to. Return the built Card object.\n}\n\n/**\n * Callback function for a button action. Constructs a\n * notification action response and returns it.\n * @param {Object} e the action event object\n * @return {ActionResponse}\n */\nfunction notifyUser(e) {\n var parameters = e.parameters;\n var notificationText = parameters['notifyText'];\n return CardService.newActionResponseBuilder()\n .setNotification(CardService.newNotification()\n .setText(notificationText))\n .build(); // Don't forget to build the response!\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.520Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":35,"estimatedTokens":251}}695{"id":"doc-extend_the_compose_ui_with_compose_actions_googl-73668ecd","source":"documentation","title":"Extend the compose UI with compose actions | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/gmail/extending-compose-ui","text":"Example:\n```text\n/**\n * Compose trigger function that fires when the compose UI is\n * requested. Builds and returns a compose UI for inserting images.\n *\n * @param {event} e The compose trigger event object. Not used in\n * this example.\n * @return {Card[]}\n */\nfunction getComposeUI(e) {\n return [buildComposeCard()];\n}\n\n/**\n * Build a card to display interactive buttons to allow the user to\n * update the subject, and To, Cc, Bcc recipients.\n *\n * @return {Card}\n */\nfunction buildComposeCard() {\n\n var card = CardService.newCardBuilder();\n var cardSection = CardService.newCardSection().setHeader('Update email');\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update subject')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('applyUpdateSubjectAction')));\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update To recipients')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('updateToRecipients')));\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update Cc recipients')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('updateCcRecipients')));\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update Bcc recipients')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('updateBccRecipients')));\n return card.addSection(cardSection).build();\n}\n\n/**\n * Updates the subject field of the current email when the user clicks\n * on \"Update subject\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateSubjectAction() {\n // Get the new subject field of the email.\n // This function is not shown in this example.\n var subject = getSubject();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftSubjectAction(CardService.newUpdateDraftSubjectAction()\n .addUpdateSubject(subject))\n .build();\n return response;\n}\n\n/**\n * Updates the To recipients of the current email when the user clicks\n * on \"Update To recipients\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateToRecipientsAction() {\n // Get the new To recipients of the email.\n // This function is not shown in this example.\n var toRecipients = getToRecipients();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftToRecipientsAction(CardService.newUpdateDraftToRecipientsAction()\n .addUpdateToRecipients(toRecipients))\n .build();\n return response;\n}\n\n/**\n * Updates the Cc recipients of the current email when the user clicks\n * on \"Update Cc recipients\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateCcRecipientsAction() {\n // Get the new Cc recipients of the email.\n // This function is not shown in this example.\n var ccRecipients = getCcRecipients();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftCcRecipientsAction(CardService.newUpdateDraftCcRecipientsAction()\n .addUpdateToRecipients(ccRecipients))\n .build();\n return response;\n}\n\n/**\n * Updates the Bcc recipients of the current email when the user clicks\n * on \"Update Bcc recipients\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateBccRecipientsAction() {\n // Get the new Bcc recipients of the email.\n // This function is not shown in this example.\n var bccRecipients = getBccRecipients();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftBccRecipientsAction(CardService.newUpdateDraftBccRecipientsAction()\n .addUpdateToRecipients(bccRecipients))\n .build();\n return response;\n}\n```\n\nExample:\n```text\n/**\n * Compose trigger function that fires when the compose UI is\n * requested. Builds and returns a compose UI for inserting images.\n *\n * @param {event} e The compose trigger event object. Not used in\n * this example.\n * @return {Card[]}\n */\nfunction getInsertImageComposeUI(e) {\n return [buildImageComposeCard()];\n}\n\n/**\n * Build a card to display images from a third-party source.\n *\n * @return {Card}\n */\nfunction buildImageComposeCard() {\n // Get a short list of image URLs to display in the UI.\n // This function is not shown in this example.\n var imageUrls = getImageUrls();\n\n var card = CardService.newCardBuilder();\n var cardSection = CardService.newCardSection().setHeader('My Images');\n for (var i = 0; i < imageUrls.length; i++) {\n var imageUrl = imageUrls[i];\n cardSection.addWidget(\n CardService.newImage()\n .setImageUrl(imageUrl)\n .setOnClickAction(CardService.newAction()\n .setFunctionName('applyInsertImageAction')\n .setParameters({'url' : imageUrl})));\n }\n return card.addSection(cardSection).build();\n}\n\n/**\n * Adds an image to the current draft email when the image is clicked\n * in the compose UI. The image is inserted at the current cursor\n * location. If any content of the email draft is currently selected,\n * it is deleted and replaced with the image.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @param {event} e The incoming event object.\n * @return {UpdateDraftActionResponse}\n */\nfunction applyInsertImageAction(e) {\n var imageUrl = e.parameters.url;\n var imageHtmlContent = '<img style=\\\"display: block\\\" src=\\\"'\n + imageUrl + '\\\"/>';\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftBodyAction(CardService.newUpdateDraftBodyAction()\n .addUpdateContent(\n imageHtmlContent,\n CardService.ContentType.MUTABLE_HTML)\n .setUpdateType(\n CardService.UpdateDraftBodyType.IN_PLACE_INSERT))\n .build();\n return response;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.521Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":195,"estimatedTokens":1649}}696{"id":"doc-card_navigation_google_workspace_add_ons_google_-b97a86fa","source":"documentation","title":"Card navigation | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/how-tos/navigation","text":"Example:\n```text\n/**\n * Create the top-level card, with buttons leading to each of three\n * 'children' cards, as well as buttons to backtrack and return to the\n * root card of the stack.\n * @return {Card}\n */\nfunction createNavigationCard() {\n // Create a button set with actions to navigate to 3 different\n // 'children' cards.\n var buttonSet = CardService.newButtonSet();\n for(var i = 1; i <= 3; i++) {\n buttonSet.addButton(createToCardButton(i));\n }\n\n // Build the card with all the buttons (two rows)\n var card = CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader().setTitle('Navigation'))\n .addSection(CardService.newCardSection()\n .addWidget(buttonSet)\n .addWidget(buildPreviousAndRootButtonSet()));\n return card.build();\n}\n\n/**\n * Create a button that navigates to the specified child card.\n * @return {TextButton}\n */\nfunction createToCardButton(id) {\n var action = CardService.newAction()\n .setFunctionName('gotoChildCard')\n .setParameters({'id': id.toString()});\n var button = CardService.newTextButton()\n .setText('Card ' + id)\n .setOnClickAction(action);\n return button;\n}\n\n/**\n * Create a ButtonSet with two buttons: one that backtracks to the\n * last card and another that returns to the original (root) card.\n * @return {ButtonSet}\n */\nfunction buildPreviousAndRootButtonSet() {\n var previousButton = CardService.newTextButton()\n .setText('Back')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('gotoPreviousCard'));\n var toRootButton = CardService.newTextButton()\n .setText('To Root')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('gotoRootCard'));\n\n // Return a new ButtonSet containing these two buttons.\n return CardService.newButtonSet()\n .addButton(previousButton)\n .addButton(toRootButton);\n}\n\n/**\n * Create a child card, with buttons leading to each of the other\n * child cards, and then navigate to it.\n * @param {Object} e object containing the ID of the card to build.\n * @return {ActionResponse}\n */\nfunction gotoChildCard(e) {\n var id = parseInt(e.parameters.id); // Current card ID\n var id2 = (id==3) ? 1 : id + 1; // 2nd card ID\n var id3 = (id==1) ? 3 : id - 1; // 3rd card ID\n var title = 'CARD ' + id;\n\n // Create buttons that go to the other two child cards.\n var buttonSet = CardService.newButtonSet()\n .addButton(createToCardButton(id2))\n .addButton(createToCardButton(id3));\n\n // Build the child card.\n var card = CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader().setTitle(title))\n .addSection(CardService.newCardSection()\n .addWidget(buttonSet)\n .addWidget(buildPreviousAndRootButtonSet()))\n .build();\n\n // Create a Navigation object to push the card onto the stack.\n // Return a built ActionResponse that uses the navigation object.\n var nav = CardService.newNavigation().pushCard(card);\n return CardService.newActionResponseBuilder()\n .setNavigation(nav)\n .build();\n}\n\n/**\n * Pop a card from the stack.\n * @return {ActionResponse}\n */\nfunction gotoPreviousCard() {\n var nav = CardService.newNavigation().popCard();\n return CardService.newActionResponseBuilder()\n .setNavigation(nav)\n .build();\n}\n\n/**\n * Return to the initial add-on card.\n * @return {ActionResponse}\n */\nfunction gotoRootCard() {\n var nav = CardService.newNavigation().popToRoot();\n return CardService.newActionResponseBuilder()\n .setNavigation(nav)\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.523Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":117,"estimatedTokens":894}}697{"id":"doc-build_a_dialogflow_cx_add_on_that_extends_google-c4d1fcfb","source":"documentation","title":"Build a Dialogflow CX add-on that extends Google Chat that understands and responds with natural language | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/quickstart-dialogflow-cx","text":"Example:\n```text\n{ \"hostAppDataAction\": { \"chatDataAction\": { \"createMessageAction\": {\n \"message\": { \"cardsV2\": [{\n \"cardId\": \"createCardMessage\",\n \"card\": {\n \"header\": {\n \"title\": \"A card message!\",\n \"subtitle\": \"Sent from Dialogflow\",\n \"imageUrl\": \"https://developers.google.com/chat/images/chat-product-icon.png\",\n \"imageType\": \"CIRCLE\"\n },\n \"sections\": [{ \"widgets\": [{ \"buttonList\": { \"buttons\": [{\n \"text\": \"Read the docs!\",\n \"onClick\": { \"openLink\": {\n \"url\": \"https://developers.google.com/workspace/chat\"\n }}\n }]}}]}]\n }\n }]}\n}}}}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.525Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":24,"estimatedTokens":162}}698{"id":"doc-build_google_editor_interfaces_google_workspace_-cc38f8a7","source":"documentation","title":"Build Google editor interfaces | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/editors/gsao/building-editor-interfaces","text":"Example:\n```text\n{\n \"addOns\": {\n \"common\": {\n \"name\": \"Translate\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/product/1x/translate_24dp.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#2772ed\"\n },\n \"homepageTrigger\": {\n \"runFunction\": \"onHomepage\"\n }\n },\n \"docs\": {},\n \"sheets\": {},\n \"slides\": {}\n }\n}\n```\n\nExample:\n```text\n\"addOns\": {\n \"common\": {\n \"name\": \"Translate\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/product/1x/translate_24dp.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#2772ed\"\n },\n \"homepageTrigger\": {\n \"runFunction\": \"onHomepage\"\n }\n },\n \"docs\": {},\n \"slides\": {},\n \"sheets\": {\n \"homepageTrigger\": {\n \"runFunction\": \"onSheetsHomepage\"\n },\n }\n}\n```\n\nExample:\n```text\n{\n \"addOns\": {\n \"common\": {\n \"name\": \"Productivity add-on\",\n \"logoUrl\": \"https://www.gstatic.com/images/icons/material/system_gm/1x/work_outline_black_18dp.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#669df6\",\n \"secondaryColor\": \"#ee675c\"\n }\n },\n \"sheets\": {\n \"homepageTrigger\": {\n \"runFunction\": \"onEditorsHomepage\"\n },\n \"onFileScopeGrantedTrigger\": {\n \"runFunction\": \"onFileScopeGrantedSheets\"\n }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"commonEventObject\": { ... },\n \"sheets\": {\n \"addonHasFileScopePermission\": true,\n \"id\":\"A_24Q3CDA23112312ED52\",\n \"title\":\"How to get started with Sheets\"\n },\n ...\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.526Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":82,"estimatedTokens":388}}699{"id":"doc-build_a_google_chat_add_on_with_dialogflow_es_go-8f2ad1f6","source":"documentation","title":"Build a Google Chat add-on with Dialogflow ES | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/quickstart-dialogflow-es","text":"Example:\n```text\n\"fulfillmentMessages\": [\n{\n \"text\": {\n \"text\": [\n \"This is a test.\"\n ]\n},\n \"platform\": \"GOOGLE_HANGOUTS\"\n},\n```\n\nExample:\n```text\n{ \"hangouts\": { \"hostAppDataAction\": { \"chatDataAction\": {\n \"createMessageAction\": { \"message\": { \"cardsV2\": [{\n \"cardId\": \"pizza\",\n \"card\": {\n \"header\": {\n \"title\": \"Pizza Delivery Customer Support\",\n \"subtitle\": \"pizzadelivery@example.com\",\n \"imageUrl\": \"https://goo.gl/aeDtrS\"\n },\n \"sections\": [{ \"widgets\": [{ \"textParagraph\": {\n \"text\": \" Your pizza is here!\"\n }}]}]\n }\n }]}}\n}}}}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.528Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":33,"estimatedTokens":156}}700{"id":"doc-editor_actions_google_workspace_add_ons_google_f-9e676958","source":"documentation","title":"Editor actions | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/editors/gsao/editor-actions","text":"Example:\n```text\n/**\n * Adds a section to the Card Builder that displays a \"REQUEST PERMISSION\"\n * button. When it's clicked, the callback triggers file scope permission flow.\n * This is used in the add-on when the home-page displays basic data.\n */\nfunction addRequestFileScopeButtonToBuilder(cardBuilder) {\n var buttonSection = CardService.newCardSection();\n // If the add-on does not have access permission, add a button that\n // lets the user provide that permission on a per-file basis.\n var buttonAction = CardService.newAction()\n .setFunctionName(\"onRequestFileScopeButtonClickedInEditor\");\n\n var button = CardService.newTextButton()\n .setText(\"Request permission\")\n .setBackgroundColor(\"#4285f4\")\n .setTextButtonStyle(CardService.TextButtonStyle.FILLED)\n .setOnClickAction(buttonAction);\n\n buttonSection.addWidget(button);\n cardBuilder.addSection(buttonSection);\n}\n\n/**\n * Callback function for a button action. Instructs Docs to display a\n * permissions dialog to the user, requesting `drive.file` scope for the \n * current file on behalf of this add-on.\n *\n * @param {Object} e The parameters object that contains the document's ID\n * @return {editorFileScopeActionResponse}\n */\nfunction onRequestFileScopeButtonClickedInEditor(e) {\n return CardService.newEditorFileScopeActionResponseBuilder()\n .requestFileScopeForActiveDocument().build();\n}\n```\n\nExample:\n```text\n/**\n * Build a card that checks selected items' quota usage. Checking\n * quota usage requires user-permissions, so this add-on provides a button\n * to request `drive.file` scope for items the add-on doesn't yet have\n * permission to access.\n *\n * @param e The event object passed containing information about the\n * current document.\n * @return {Card}\n */\nfunction onDocsHomepage(e) {\n return createAddOnView(e);\n}\n\nfunction onFileScopeGranted(e) {\n return createAddOnView(e);\n}\n\n/**\n * For the current document, display either its quota information or\n * a button that lets the user provide permission to access that\n * file to retrieve its quota details.\n *\n * @param e The event containing information about the current document\n * @return {Card}\n */\nfunction createAddOnView(e) {\n var docsEventObject = e['docs'];\n var builder = CardService.newCardBuilder();\n\n var cardSection = CardService.newCardSection();\n if (docsEventObject['addonHasFileScopePermission']) {\n cardSection.setHeader(docsEventObject['title']);\n // This add-on uses the recommended, limited-permission `drive.file`\n // scope to get granular per-file access permissions.\n // See: https://developers.google.com/drive/api/v2/about-auth\n // If the add-on has access permission, read and display its quota.\n cardSection.addWidget(\n CardService.newTextParagraph().setText(\n \"This file takes up: \" +\n getQuotaBytesUsed(docsEventObject['id'])));\n } else {\n // If the add-on does not have access permission, add a button that\n // lets the user provide that permission on a per-file basis.\n cardSection.addWidget(\n CardService.newTextParagraph().setText(\n \"The add-on needs permission to access this file's quota.\"));\n\n var buttonAction = CardService.newAction()\n .setFunctionName(\"onRequestFileScopeButtonClicked\");\n\n var button = CardService.newTextButton()\n .setText(\"Request permission\")\n .setOnClickAction(buttonAction);\n\n cardSection.addWidget(button);\n }\n return builder.addSection(cardSection).build();\n}\n\n/**\n * Callback function for a button action. Instructs Docs to\n * display a permissions dialog to the user, requesting `drive.file` scope for\n * the current file on behalf of this add-on.\n *\n * @param {Object} e The parameters object that contains the document's ID\n * @return {editorFileScopeActionResponse}\n */\nfunction onRequestFileScopeButtonClicked(e) {\n return CardService.newEditorFileScopeActionResponseBuilder()\n .requestFileScopeForActiveDocument().build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.529Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":115,"estimatedTokens":1000}}701{"id":"doc-google_drive_actions_google_workspace_add_ons_go-dd1abeb3","source":"documentation","title":"Google Drive actions | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/drive/drive-actions","text":"Example:\n```text\n/**\n * Builds a card that checks selected items' quota usage. Checking\n * quota usage requires user-permissions, so this\n * add-on provides a button to request\n * `drive.file` scope for items the add-on\n * doesn't yet have permission to access.\n *\n * @param e The event object passed containing contextual information about\n * the Drive items selected.\n * @return {Card}\n */\nfunction onDriveItemsSelected(e) {\n var builder = CardService.newCardBuilder();\n\n // For each item the user has selected in Drive, display\n // either its quota information or a button that lets the user provide\n // permission to access that file to retrieve its quota details.\n e['drive']['selectedItems'].forEach(\n function(item){\n var cardSection = CardService.newCardSection()\n .setHeader(item['title']);\n\n // This add-on uses the recommended, limited-permission `drive.file`\n // scope to get granular per-file access permissions.\n // See: https://developers.google.com/drive/api/v2/about-auth\n if (item['addonHasFileScopePermission']) {\n // If the add-on has access permission, read and display its\n // quota.\n cardSection.addWidget(\n CardService.newTextParagraph().setText(\n \"This file takes up: \" + getQuotaBytesUsed(item['id'])));\n } else {\n // If the add-on doesn't have access permission, add a button\n // that lets the user provide that permission on a per-file\n // basis.\n cardSection.addWidget(\n CardService.newTextParagraph().setText(\n \"The add-on needs permission to access this file's quota.\"));\n\n var buttonAction = CardService.newAction()\n .setFunctionName(\"onRequestFileScopeButtonClicked\")\n .setParameters({id: item.id});\n\n var button = CardService.newTextButton()\n .setText(\"Request permission\")\n .setOnClickAction(buttonAction);\n\n cardSection.addWidget(button);\n }\n\n builder.addSection(cardSection);\n });\n\n return builder.build();\n}\n\n/**\n * Callback function for a button action. Instructs Drive to\n * display a permissions dialog to the user, requesting `drive.file` scope\n * for a specific item on behalf of this add-on.\n *\n * @param {Object} e The parameters object that contains the item's\n * Drive ID.\n * @return {DriveItemsSelectedActionResponse}\n */\nfunction onRequestFileScopeButtonClicked (e) {\n var idToRequest = e.parameters.id;\n return CardService.newDriveItemsSelectedActionResponseBuilder()\n .requestFileScope(idToRequest).build();\n}\n\n/**\n * Use the Advanced Drive Service (See\n * https://developers.google.com/apps-script/advanced/drive), with\n * `drive.file` scope permissions to request the quota usage of a specific\n * Drive item.\n *\n * @param {string} itemId The ID of the item to check.\n * @return {string} A description of the item's quota usage, in bytes.\n */\nfunction getQuotaBytesUsed(itemId) {\n try {\n return Drive.Files.get(itemId,{fields: \"quotaBytesUsed\"})\n .quotaBytesUsed + \" bytes\";\n } catch (e) {\n return \"Error fetching how much quota this item uses. Error: \" + e;\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.529Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":93,"estimatedTokens":794}}702{"id":"doc-create_third_party_conferences_google_workspace_-9f1777c9","source":"documentation","title":"Create third-party conferences | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/calendar/conferencing/create-conference","text":"Example:\n```text\n/**\n * Creates a conference, then builds and returns a ConferenceData object\n * with the corresponding conference information. This method is called\n * when a user selects a conference solution defined by the add-on that\n * uses this function as its 'onCreateFunction' in the add-on manifest.\n *\n * @param {Object} arg The default argument passed to a 'onCreateFunction';\n * it carries information about the Google Calendar event.\n * @return {ConferenceData}\n */\nfunction createConference(arg) {\n const eventData = arg.eventData;\n const calendarId = eventData.calendarId;\n const eventId = eventData.eventId;\n\n // Retrieve the Calendar event information using the Calendar\n // Advanced service.\n var calendarEvent;\n try {\n calendarEvent = Calendar.Events.get(calendarId, eventId);\n } catch (err) {\n // The calendar event does not exist just yet; just proceed with the\n // given event ID and allow the event details to sync later.\n console.log(err);\n calendarEvent = {\n id: eventId,\n };\n }\n\n // Create a conference on the third-party service and return the\n // conference data or errors in a custom JSON object.\n var conferenceInfo = create3rdPartyConference(calendarEvent);\n\n // Build and return a ConferenceData object, either with conference or\n // error information.\n var dataBuilder = ConferenceDataService.newConferenceDataBuilder();\n\n if (!conferenceInfo.error) {\n // No error, so build the ConferenceData object from the\n // returned conference info.\n\n var phoneEntryPoint = ConferenceDataService.newEntryPoint()\n .setEntryPointType(ConferenceDataService.EntryPointType.PHONE)\n .setUri('tel:+' + conferenceInfo.phoneNumber)\n .setPin(conferenceInfo.phonePin);\n\n var adminEmailParameter = ConferenceDataService.newConferenceParameter()\n .setKey('adminEmail')\n .setValue(conferenceInfo.adminEmail);\n\n dataBuilder.setConferenceId(conferenceInfo.id)\n .addEntryPoint(phoneEntryPoint)\n .addConferenceParameter(adminEmailParameter)\n .setNotes(conferenceInfo.conferenceLegalNotice);\n\n if (conferenceInfo.videoUri) {\n var videoEntryPoint = ConferenceDataService.newEntryPoint()\n .setEntryPointType(ConferenceDataService.EntryPointType.VIDEO)\n .setUri(conferenceInfo.videoUri)\n .setPasscode(conferenceInfo.videoPasscode);\n dataBuilder.addEntryPoint(videoEntryPoint);\n }\n\n // Since the conference creation request succeeded, make sure that\n // syncing has been enabled.\n initializeSyncing(calendarId, eventId, conferenceInfo.id);\n\n } else if (conferenceInfo.error === 'AUTH') {\n // Authenentication error. Implement a function to build the correct\n // authenication URL for the third-party conferencing system.\n var authenticationUrl = getAuthenticationUrl();\n var error = ConferenceDataService.newConferenceError()\n .setConferenceErrorType(\n ConferenceDataService.ConferenceErrorType.AUTHENTICATION)\n .setAuthenticationUrl(authenticationUrl);\n dataBuilder.setError(error);\n\n } else {\n // Other error type;\n var error = ConferenceDataService.newConferenceError()\n .setConferenceErrorType(\n ConferenceDataService.ConferenceErrorType.TEMPORARY);\n dataBuilder.setError(error);\n }\n\n // Don't forget to build the ConferenceData object.\n return dataBuilder.build();\n}\n\n\n/**\n * Contact the third-party conferencing system to create a conference there,\n * using the provided calendar event information. Collects and retuns the\n * conference data returned by the third-party system in a custom JSON object\n * with the following fields:\n *\n * data.adminEmail - the conference administrator's email\n * data.conferenceLegalNotice - the conference legal notice text\n * data.error - Only present if there was an error during\n * conference creation. Equal to 'AUTH' if the add-on user needs to\n * authorize on the third-party system.\n * data.id - the conference ID\n * data.phoneNumber - the conference phone entry point phone number\n * data.phonePin - the conference phone entry point PIN\n * data.videoPasscode - the conference video entry point passcode\n * data.videoUri - the conference video entry point URI\n *\n * The above fields are specific to this example; which conference information\n * your add-on needs is dependent on the third-party conferencing system\n * requirements.\n *\n * @param {Object} calendarEvent A Calendar Event resource object returned by\n * the Google Calendar API.\n * @return {Object}\n */\nfunction create3rdPartyConference(calendarEvent) {\n var data = {};\n\n // Implementation details dependent on the third-party system API.\n // Typically one or more API calls are made to create the conference and\n // acquire its relevant data, which is then put in to the returned JSON\n // object.\n\n return data;\n}\n\n/**\n * Return the URL used to authenticate the user with the third-party\n * conferencing system.\n *\n * @return {String}\n */\nfunction getAuthenticationUrl() {\n var url;\n // Implementation details dependent on the third-party system.\n\n return url;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.530Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":143,"estimatedTokens":1300}}703{"id":"doc-build_google_drive_interfaces_google_workspace_a-bdcd4a19","source":"documentation","title":"Build Google Drive interfaces | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/drive/building-drive-interfaces","text":"Example:\n```text\n{\n \"commonEventObject\": { ... },\n \"drive\": {\n \"activeCursorItem\":{\n \"addonHasFileScopePermission\": true,\n \"id\":\"0B_sX1fXRRU6Ac3RhcnRlcl9maWxl\",\n \"iconUrl\": \"https://drive-thirdparty.googleusercontent.com...\",\n \"mimeType\":\"application/pdf\",\n \"title\":\"How to get started with Drive\"\n },\n \"selectedItems\": [\n {\n \"addonHasFileScopePermission\": true,\n \"id\":\"0B_sX1fXRRU6Ac3RhcnRlcl9maWxl\",\n \"iconUrl\":\"https://drive-thirdparty.googleusercontent.com...\",\n \"mimeType\":\"application/pdf\",\n \"title\":\"How to get started with Drive\"\n },\n ...\n ]\n },\n ...\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.531Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":28,"estimatedTokens":168}}704{"id":"doc-build_an_http_google_chat_app_google_workspace_a-064e3fb2","source":"documentation","title":"Build an HTTP Google Chat app | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/quickstart-http","text":"Example:\n```text\nimport { http } from '@google-cloud/functions-framework';\n\n// The ID of the slash command \"/about\".\n// You must use the same ID in the Google Chat API configuration.\nconst ABOUT_COMMAND_ID = 1;\n\n/**\n * Handle requests from Google Workspace add on\n *\n * @param {Object} req Request sent by Google Chat\n * @param {Object} res Response to be sent back to Google Chat\n */\nhttp('avatarApp', (req, res) => {\n const chatEvent = req.body.chat;\n let message;\n if (chatEvent.appCommandPayload) {\n message = handleAppCommand(chatEvent);\n } else {\n message = handleMessage(chatEvent);\n }\n res.send({ hostAppDataAction: { chatDataAction: { createMessageAction: {\n message: message\n }}}});\n});\n\n/**\n * Responds to an APP_COMMAND event in Google Chat.\n *\n * @param {Object} event the event object from Google Chat\n * @return the response message object.\n */\nfunction handleAppCommand(event) {\n switch (event.appCommandPayload.appCommandMetadata.appCommandId) {\n case ABOUT_COMMAND_ID:\n return {\n text: 'The Avatar app replies to Google Chat messages.'\n };\n }\n}\n\n/**\n * Responds to a MESSAGE event in Google Chat.\n *\n * @param {Object} event the event object from Google Chat\n * @return the response message object.\n */\nfunction handleMessage(event) {\n // Stores the Google Chat user as a variable.\n const chatUser = event.messagePayload.message.sender;\n const displayName = chatUser.displayName;\n const avatarUrl = chatUser.avatarUrl;\n return {\n text: 'Here\\'s your avatar',\n cardsV2: [{\n cardId: 'avatarCard',\n card: {\n name: 'Avatar Card',\n header: {\n title: `Hello ${displayName}!`,\n },\n sections: [{ widgets: [{\n textParagraph: { text: 'Your avatar picture: ' }\n }, {\n image: { imageUrl: avatarUrl }\n }]}]\n }\n }]\n };\n}\n```\n\nExample:\n```text\nfrom typing import Any, Mapping\n\nimport flask\nimport functions_framework\n\n# The ID of the slash command \"/about\".\n# You must use the same ID in the Google Chat API configuration.\nABOUT_COMMAND_ID = 1\n\n@functions_framework.http\ndef avatar_app(req: flask.Request) -> Mapping[str, Any]:\n \"\"\"Handle requests from Google Workspace add on\n\n Args:\n flask.Request req: the request sent by Google Chat\n\n Returns:\n Mapping[str, Any]: the response to be sent back to Google Chat\n \"\"\"\n chat_event = req.get_json(silent=True)[\"chat\"]\n if chat_event and \"appCommandPayload\" in chat_event:\n message = handle_app_command(chat_event)\n else:\n message = handle_message(chat_event)\n return { \"hostAppDataAction\": { \"chatDataAction\": { \"createMessageAction\": {\n \"message\": message\n }}}}\n\ndef handle_app_command(event: Mapping[str, Any]) -> Mapping[str, Any]:\n \"\"\"Responds to an APP_COMMAND event in Google Chat.\n\n Args:\n Mapping[str, Any] event: the event object from Google Chat\n\n Returns:\n Mapping[str, Any]: the response message object.\n \"\"\"\n if event[\"appCommandPayload\"][\"appCommandMetadata\"][\"appCommandId\"] == ABOUT_COMMAND_ID:\n return {\n \"text\": \"The Avatar app replies to Google Chat messages.\",\n }\n return {}\n\ndef handle_message(event: Mapping[str, Any]) -> Mapping[str, Any]:\n \"\"\"Responds to a MESSAGE event in Google Chat.\n\n Args:\n Mapping[str, Any] event: the event object from Google Chat\n\n Returns:\n Mapping[str, Any]: the response message object.\n \"\"\"\n # Stores the Google Chat user as a variable.\n chat_user = event[\"messagePayload\"][\"message\"][\"sender\"]\n display_name = chat_user.get(\"displayName\", \"\")\n avatar_url = chat_user.get(\"avatarUrl\", \"\")\n return {\n \"text\": \"Here's your avatar\",\n \"cardsV2\": [{\n \"cardId\": \"avatarCard\",\n \"card\": {\n \"name\": \"Avatar Card\",\n \"header\": {\n \"title\": f\"Hello {display_name}!\"\n },\n \"sections\": [{ \"widgets\": [\n { \"textParagraph\": { \"text\": \"Your avatar picture:\" }},\n { \"image\": { \"imageUrl\": avatar_url }},\n ]}]\n }\n }]\n }\n```\n\nExample:\n```text\npackage com.google.chat.avatar;\n\nimport com.google.api.services.chat.v1.model.CardWithId;\nimport com.google.api.services.chat.v1.model.GoogleAppsCardV1Card;\nimport com.google.api.services.chat.v1.model.GoogleAppsCardV1CardHeader;\nimport com.google.api.services.chat.v1.model.GoogleAppsCardV1Image;\nimport com.google.api.services.chat.v1.model.GoogleAppsCardV1Section;\nimport com.google.api.services.chat.v1.model.GoogleAppsCardV1TextParagraph;\nimport com.google.api.services.chat.v1.model.GoogleAppsCardV1Widget;\nimport com.google.api.services.chat.v1.model.Message;\nimport com.google.cloud.functions.HttpFunction;\nimport com.google.cloud.functions.HttpRequest;\nimport com.google.cloud.functions.HttpResponse;\nimport com.google.gson.Gson;\nimport com.google.gson.JsonObject;\nimport java.util.List;\n\npublic class App implements HttpFunction {\n // The ID of the slash command \"/about\".\n // You must use the same ID in the Google Chat API configuration.\n private static final int ABOUT_COMMAND_ID = 1;\n\n private static final Gson gson = new Gson();\n\n /**\n * Handle requests from Google Workspace add on\n * \n * @param request the request sent by Google Chat\n * @param response the response to be sent back to Google Chat\n */\n @Override\n public void service(HttpRequest request, HttpResponse response) throws Exception {\n JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);\n JsonObject chatEvent = event.getAsJsonObject(\"chat\");\n Message message;\n if (chatEvent.has(\"appCommandPayload\")) {\n message = handleAppCommand(chatEvent);\n } else {\n message = handleMessage(chatEvent);\n }\n JsonObject createMessageAction = new JsonObject();\n createMessageAction.add(\"message\", gson.fromJson(gson.toJson(message), JsonObject.class));\n JsonObject chatDataAction = new JsonObject();\n chatDataAction.add(\"createMessageAction\", createMessageAction);\n JsonObject hostAppDataAction = new JsonObject();\n hostAppDataAction.add(\"chatDataAction\", chatDataAction);\n JsonObject dataActions = new JsonObject();\n dataActions.add(\"hostAppDataAction\", hostAppDataAction);\n response.getWriter().write(gson.toJson(dataActions));\n }\n\n /**\n * Handles an APP_COMMAND event in Google Chat.\n *\n * @param event the event object from Google Chat\n * @return the response message object.\n */\n private Message handleAppCommand(JsonObject event) throws Exception {\n switch (event.getAsJsonObject(\"appCommandPayload\")\n .getAsJsonObject(\"appCommandMetadata\").get(\"appCommandId\").getAsInt()) {\n case ABOUT_COMMAND_ID:\n return new Message()\n .setText(\"The Avatar app replies to Google Chat messages.\");\n default:\n return null;\n }\n }\n\n /**\n * Handles a MESSAGE event in Google Chat.\n *\n * @param event the event object from Google Chat\n * @return the response message object.\n */\n private Message handleMessage(JsonObject event) throws Exception {\n // Stores the Google Chat user as a variable.\n JsonObject chatUser = event.getAsJsonObject(\"messagePayload\").getAsJsonObject(\"message\").getAsJsonObject(\"sender\");\n String displayName = chatUser.has(\"displayName\") ? chatUser.get(\"displayName\").getAsString() : \"\";\n String avatarUrl = chatUser.has(\"avatarUrl\") ? chatUser.get(\"avatarUrl\").getAsString() : \"\";\n return new Message()\n .setText(\"Here's your avatar\")\n .setCardsV2(List.of(new CardWithId()\n .setCardId(\"avatarCard\")\n .setCard(new GoogleAppsCardV1Card()\n .setName(\"Avatar Card\")\n .setHeader(new GoogleAppsCardV1CardHeader()\n .setTitle(String.format(\"Hello %s!\", displayName)))\n .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(\n new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()\n .setText(\"Your avatar picture:\")),\n new GoogleAppsCardV1Widget()\n .setImage(new GoogleAppsCardV1Image().setImageUrl(avatarUrl)))))))));\n }\n}\n```\n\nExample:\n```text\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n\n <groupId>com.google.chat</groupId>\n <artifactId>avatar-app</artifactId>\n <version>1.0-SNAPSHOT</version>\n\n <properties>\n <maven.compiler.target>17</maven.compiler.target>\n <maven.compiler.source>17</maven.compiler.source>\n </properties>\n\n <dependencies>\n <dependency>\n <groupId>com.google.cloud.functions</groupId>\n <artifactId>functions-framework-api</artifactId>\n <version>1.1.4</version>\n </dependency>\n <dependency>\n <groupId>com.google.code.gson</groupId>\n <artifactId>gson</artifactId>\n <version>2.9.1</version>\n </dependency>\n <dependency>\n <groupId>com.google.apis</groupId>\n <artifactId>google-api-services-chat</artifactId>\n <version>v1-rev20230115-2.0.0</version>\n </dependency>\n </dependencies>\n\n <build>\n <plugins>\n <plugin>\n <groupId>org.apache.maven.plugins</groupId>\n <artifactId>maven-compiler-plugin</artifactId>\n <version>3.8.1</version>\n <configuration>\n <excludes>\n <exclude>.google/</exclude>\n </excludes>\n </configuration>\n </plugin>\n </plugins>\n </build>\n</project>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.536Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":299,"estimatedTokens":2365}}705{"id":"doc-sync_calendar_conference_changes_google_workspac-d650d509","source":"documentation","title":"Sync calendar conference changes | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/calendar/conferencing/sync-calendar-changes","text":"Example:\n```text\n/**\n * Initializes syncing of conference data by creating a sync trigger and\n * sync token if either does not exist yet.\n *\n * @param {String} calendarId The ID of the Google Calendar.\n */\nfunction initializeSyncing(calendarId) {\n // Create a syncing trigger if it doesn't exist yet.\n createSyncTrigger(calendarId);\n\n // Perform an event sync to create the initial sync token.\n syncEvents({'calendarId': calendarId});\n}\n\n/**\n * Creates a sync trigger if it does not exist yet.\n *\n * @param {String} calendarId The ID of the Google Calendar.\n */\nfunction createSyncTrigger(calendarId) {\n // Check to see if the trigger already exists; if does, return.\n var allTriggers = ScriptApp.getProjectTriggers();\n for (var i = 0; i < allTriggers.length; i++) {\n var trigger = allTriggers[i];\n if (trigger.getTriggerSourceId() == calendarId) {\n return;\n }\n }\n\n // Trigger does not exist, so create it. The trigger calls the\n // 'syncEvents()' trigger function when it fires.\n var trigger = ScriptApp.newTrigger('syncEvents')\n .forUserCalendar(calendarId)\n .onEventUpdated()\n .create();\n}\n\n/**\n * Sync events for the given calendar; this is the syncing trigger\n * function. If a sync token already exists, this retrieves all events\n * that have been modified since the last sync, then checks each to see\n * if an associated conference needs to be updated and makes any required\n * changes. If the sync token does not exist or is invalid, this\n * retrieves future events modified in the last 24 hours instead. In\n * either case, a new sync token is created and stored.\n *\n * @param {Object} e If called by a event updated trigger, this object\n * contains the Google Calendar ID, authorization mode, and\n * calling trigger ID. Only the calendar ID is actually used here,\n * however.\n */\nfunction syncEvents(e) {\n var calendarId = e.calendarId;\n var properties = PropertiesService.getUserProperties();\n var syncToken = properties.getProperty('syncToken');\n\n var options;\n if (syncToken) {\n // There's an existing sync token, so configure the following event\n // retrieval request to only get events that have been modified\n // since the last sync.\n options = {\n syncToken: syncToken\n };\n } else {\n // No sync token, so configure to do a 'full' sync instead. In this\n // example only recently updated events are retrieved in a full sync.\n // A larger time window can be examined during a full sync, but this\n // slows down the script execution. Consider the trade-offs while\n // designing your add-on.\n var now = new Date();\n var yesterday = new Date();\n yesterday.setDate(now.getDate() - 1);\n options = {\n timeMin: now.toISOString(), // Events that start after now...\n updatedMin: yesterday.toISOString(), // ...and were modified recently\n maxResults: 50, // Max. number of results per page of responses\n orderBy: 'updated'\n }\n }\n\n // Examine the list of updated events since last sync (or all events\n // modified after yesterday if the sync token is missing or invalid), and\n // update any associated conferences as required.\n var events;\n var pageToken;\n do {\n try {\n options.pageToken = pageToken;\n events = Calendar.Events.list(calendarId, options);\n } catch (err) {\n // Check to see if the sync token was invalidated by the server;\n // if so, perform a full sync instead.\n if (err.message ===\n \"Sync token is no longer valid, a full sync is required.\") {\n properties.deleteProperty('syncToken');\n syncEvents(e);\n return;\n } else {\n throw new Error(err.message);\n }\n }\n\n // Read through the list of returned events looking for conferences\n // to update.\n if (events.items && events.items.length > 0) {\n for (var i = 0; i < events.items.length; i++) {\n var calEvent = events.items[i];\n // Check to see if there is a record of this event has a\n // conference that needs updating.\n if (eventHasConference(calEvent)) {\n updateConference(calEvent, calEvent.conferenceData.conferenceId);\n }\n }\n }\n\n pageToken = events.nextPageToken;\n } while (pageToken);\n\n // Record the new sync token.\n if (events.nextSyncToken) {\n properties.setProperty('syncToken', events.nextSyncToken);\n }\n}\n\n/**\n * Returns true if the specified event has an associated conference\n * of the type managed by this add-on; retuns false otherwise.\n *\n * @param {Object} calEvent The Google Calendar event object, as defined by\n * the Calendar API.\n * @return {boolean}\n */\nfunction eventHasConference(calEvent) {\n var name = calEvent.conferenceData.conferenceSolution.name || null;\n\n // This version checks if the conference data solution name matches the\n // one of the solution names used by the add-on. Alternatively you could\n // check the solution's entry point URIs or other solution-specific\n // information.\n if (name) {\n if (name === \"My Web Conference\" ||\n name === \"My Recorded Web Conference\") {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Update a conference based on new Google Calendar event information.\n * The exact implementation of this function is highly dependant on the\n * details of the third-party conferencing system, so only a rough outline\n * is shown here.\n *\n * @param {Object} calEvent The Google Calendar event object, as defined by\n * the Calendar API.\n * @param {String} conferenceId The ID used to identify the conference on\n * the third-party conferencing system.\n */\nfunction updateConference(calEvent, conferenceId) {\n // Check edge case: the event was cancelled\n if (calEvent.status === 'cancelled' || eventHasConference(calEvent)) {\n // Use the third-party API to delete the conference too.\n\n\n } else {\n // Extract any necessary information from the event object, then\n // make the appropriate third-party API requests to update the\n // conference with that information.\n\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.537Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":178,"estimatedTokens":1524}}706{"id":"doc-build_a_google_chat_app_that_uses_pub_sub_google-65f4484c","source":"documentation","title":"Build a Google Chat app that uses Pub/Sub | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/quickstart-pubsub","text":"Example:\n```text\nnpm init\n```\n\nExample:\n```text\nexport GOOGLE_APPLICATION_CREDENTIALS=SERVICE_ACCOUNT_FILE_PATH\n```\n\nExample:\n```text\nexport PROJECT_ID=PROJECT_ID\n```\n\nExample:\n```text\nexport SUBSCRIPTION_ID=SUBSCRIPTION_ID\n```\n\nExample:\n```text\n{\n \"name\": \"pub-sub-app\",\n \"version\": \"1.0.0\",\n \"description\": \"Google Chat App that listens for messages via Cloud Pub/Sub\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"start\": \"node index.js\",\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"dependencies\": {\n \"@google-apps/chat\": \"^0.4.0\",\n \"@google-cloud/pubsub\": \"^4.5.0\"\n },\n \"license\": \"Apache-2.0\"\n}\n```\n\nExample:\n```text\nconst {ChatServiceClient} = require('@google-apps/chat');\nconst {MessageReplyOption} = require('@google-apps/chat').protos.google.chat.v1.CreateMessageRequest;\nconst {PubSub} = require('@google-cloud/pubsub');\nconst {SubscriberClient} = require('@google-cloud/pubsub/build/src/v1');\n\n// Receives messages from a pull subscription.\nfunction receiveMessages() {\n const chat = new ChatServiceClient({\n keyFile: process.env.GOOGLE_APPLICATION_CREDENTIALS,\n scopes: ['https://www.googleapis.com/auth/chat.bot'],\n });\n\n const subscriptionPath = new SubscriberClient()\n .subscriptionPath(process.env.PROJECT_ID, process.env.SUBSCRIPTION_ID)\n const subscription = new PubSub()\n .subscription(subscriptionPath);\n\n // Handle incoming message, then acknowledge the received message\n const messageHandler = message => {\n console.log(`Id : ${message.id}`);\n const event = JSON.parse(message.data);\n console.log(`Data : ${JSON.stringify(event)}`);\n\n // Post the response to Google Chat.\n const request = formatRequest(event);\n if (request != null) {\n chat.createMessage(request);\n }\n\n // Acknowledge the message.\n message.ack();\n }\n\n subscription.on('message', messageHandler);\n console.log(`Listening for messages on ${subscriptionPath}`);\n\n // Keep main thread from exiting while waiting for messages\n setTimeout(() => {\n subscription.removeListener('message', messageHandler);\n console.log(`Stopped listening for messages.`);\n }, 60 * 1000);\n}\n\n// Send message to Google Chat based on the type of event\nfunction formatRequest(event) {\n const chatEvent = event.chat || {};\n\n // If the app was removed, we don't respond.\n if (chatEvent.removedFromSpacePayload) {\n console.log(`App removed from space.`);\n return null;\n }\n\n const payload = chatEvent.messagePayload || chatEvent.addedToSpacePayload;\n const spaceName = payload?.space?.name;\n\n if (!spaceName) {\n console.log('No space name in event.');\n return null;\n }\n\n if (chatEvent.addedToSpacePayload) {\n // An app can also be added to a space by @mentioning it in a\n // message. In that case, we fall through to the message case\n // and let the app respond. If the app was added using the\n // invite flow, we just post a thank you message in the space.\n return {\n parent: spaceName,\n message: { text: 'Thank you for adding me!' },\n };\n } else if (chatEvent.messagePayload) {\n // In case of message, post the response in the same thread.\n const message = chatEvent.messagePayload.message;\n return {\n parent: spaceName,\n messageReplyOption: MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD,\n message: {\n text: 'You said: `' + message.text + '`',\n thread: { name: message.thread.name },\n },\n };\n }\n}\n\nif (!process.env.PROJECT_ID) {\n console.log('Missing PROJECT_ID env var.');\n process.exit(1);\n}\nif (!process.env.SUBSCRIPTION_ID) {\n console.log('Missing SUBSCRIPTION_ID env var.');\n process.exit(1);\n}\nif (!process.env.GOOGLE_APPLICATION_CREDENTIALS) {\n console.log('Missing GOOGLE_APPLICATION_CREDENTIALS env var.');\n process.exit(1);\n}\n\nreceiveMessages();\n```\n\nExample:\n```text\ngoogle-cloud-pubsub>=2.23.0\ngoogle-apps-chat==0.1.9\n```\n\nExample:\n```text\nimport json\nimport logging\nimport os\nimport sys\nimport time\nfrom google.apps import chat_v1 as google_chat\nfrom google.cloud import pubsub_v1\nfrom google.oauth2.service_account import Credentials\n\ndef receive_messages():\n \"\"\"Receives messages from a pull subscription.\"\"\"\n\n scopes = ['https://www.googleapis.com/auth/chat.bot']\n service_account_key_path = os.environ.get('GOOGLE_APPLICATION_CREDENTIALS')\n creds = Credentials.from_service_account_file(service_account_key_path)\n chat = google_chat.ChatServiceClient(\n credentials=creds, client_options={'scopes': scopes}\n )\n\n project_id = os.environ.get('PROJECT_ID')\n subscription_id = os.environ.get('SUBSCRIPTION_ID')\n subscriber = pubsub_v1.SubscriberClient()\n subscription_path = subscriber.subscription_path(project_id, subscription_id)\n\n # Handle incoming message, then acknowledge the received message\n def callback(message):\n event = json.loads(message.data)\n logging.info('Data : %s', event)\n\n # Post the response to Google Chat.\n request = format_request(event)\n if request is not None:\n chat.create_message(request)\n\n # Acknowledge the message.\n message.ack()\n\n subscriber.subscribe(subscription_path, callback = callback)\n logging.info('Listening for messages on %s', subscription_path)\n\n # Keep main thread from exiting while waiting for messages\n while True:\n time.sleep(60)\n\ndef format_request(event):\n \"\"\"Send message to Google Chat based on the type of event.\n Args:\n event: A dictionary with the event data.\n \"\"\"\n chat_event = event.get('chat', {})\n\n # If the app was removed, we don't respond.\n if 'removedFromSpacePayload' in chat_event:\n logging.info('App removed from space.')\n return\n\n payload = chat_event.get('messagePayload') or chat_event.get(\n 'addedToSpacePayload'\n )\n space_name = payload.get('space', {}).get('name') if payload else None\n\n if not space_name:\n logging.warning('No space name in event.')\n return\n\n if 'addedToSpacePayload' in chat_event:\n # An app can also be added to a space by @mentioning it in a\n # message. In that case, we fall through to the message case\n # and let the app respond. If the app was added using the\n # invite flow, we just post a thank you message in the space.\n return google_chat.CreateMessageRequest(\n parent = space_name,\n message = {\n 'text': 'Thank you for adding me!'\n }\n )\n elif 'messagePayload' in chat_event:\n # In case of message, post the response in the same thread.\n message = chat_event['messagePayload']['message']\n return google_chat.CreateMessageRequest(\n parent = space_name,\n message_reply_option = google_chat.CreateMessageRequest.MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD,\n message = {\n 'text': 'You said: `' + message['text'] + '`',\n 'thread': {\n 'name': message['thread']['name']\n }\n }\n )\n\nif __name__ == '__main__':\n if 'PROJECT_ID' not in os.environ:\n logging.error('Missing PROJECT_ID env var.')\n sys.exit(1)\n\n if 'SUBSCRIPTION_ID' not in os.environ:\n logging.error('Missing SUBSCRIPTION_ID env var.')\n sys.exit(1)\n\n if 'GOOGLE_APPLICATION_CREDENTIALS' not in os.environ:\n logging.error('Missing GOOGLE_APPLICATION_CREDENTIALS env var.')\n sys.exit(1)\n\n logging.basicConfig(\n level=logging.INFO,\n style='{',\n format='{levelname:.1}{asctime} {filename}:{lineno}] {message}')\n receive_messages()\n```\n\nExample:\n```text\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n <modelVersion>4.0.0</modelVersion>\n\n <groupId>com.google.chat.addon</groupId>\n <artifactId>pubsub-addon-chat-app</artifactId>\n <version>0.1.0</version>\n\n <name>pubsub-addon-chat-app-java</name>\n\n <properties>\n <maven.compiler.release>11</maven.compiler.release>\n <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n </properties>\n\n <dependencyManagement>\n <dependencies>\n <dependency>\n <groupId>com.google.cloud</groupId>\n <artifactId>libraries-bom</artifactId>\n <version>26.41.0</version> <!-- Use a recent BOM version -->\n <type>pom</type>\n <scope>import</scope>\n </dependency>\n </dependencies>\n </dependencyManagement>\n\n <dependencies>\n <!-- Google Chat GAPIC library -->\n <dependency>\n <groupId>com.google.cloud</groupId>\n <artifactId>google-cloud-chat</artifactId>\n </dependency>\n <!-- Google Cloud Pub/Sub library -->\n <dependency>\n <groupId>com.google.cloud</groupId>\n <artifactId>google-cloud-pubsub</artifactId>\n </dependency>\n <!-- Google Apps Add-ons Event Object -->\n <dependency>\n <groupId>com.google.apps.addons.v1</groupId>\n <artifactId>google-apps-addons-v1-java</artifactId>\n <version>0.2.0</version> <!-- Check for latest version -->\n </dependency>\n <!-- Protobuf JSON utility -->\n <dependency>\n <groupId>com.google.protobuf</groupId>\n <artifactId>protobuf-java-util</artifactId>\n </dependency>\n <!-- Google Auth Library -->\n <dependency>\n <groupId>com.google.auth</groupId>\n <artifactId>google-auth-library-oauth2-http</artifactId>\n </dependency>\n <dependency>\n <groupId>com.google.api</groupId>\n <artifactId>gax</artifactId>\n </dependency>\n <!-- JSON utilities for PubSub message (if needed, though protobuf-java-util is primary for EventObject) -->\n <dependency>\n <groupId>com.fasterxml.jackson.core</groupId>\n <artifactId>jackson-databind</artifactId>\n <version>2.14.2</version>\n </dependency>\n <dependency>\n <groupId>org.slf4j</groupId>\n <artifactId>slf4j-jdk14</artifactId>\n <version>1.7.36</version>\n <scope>runtime</scope>\n </dependency>\n </dependencies>\n\n <build>\n <plugins>\n <plugin>\n <groupId>org.apache.maven.plugins</groupId>\n <artifactId>maven-compiler-plugin</artifactId>\n <version>3.13.0</version>\n <configuration>\n <source>11</source>\n <target>11</target>\n </configuration>\n </plugin>\n <plugin>\n <groupId>org.codehaus.mojo</groupId>\n <artifactId>exec-maven-plugin</artifactId>\n <version>3.3.0</version>\n <configuration>\n <mainClass>Main</mainClass>\n </configuration>\n </plugin>\n </plugins>\n </build>\n</project>\n```\n\nExample:\n```text\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.google.api.gax.core.FixedCredentialsProvider;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.ChatServiceSettings;\nimport com.google.chat.v1.CreateMessageRequest;\nimport com.google.chat.v1.CreateMessageRequest.MessageReplyOption;\nimport com.google.chat.v1.Message;\nimport com.google.chat.v1.Thread;\nimport com.google.cloud.pubsub.v1.AckReplyConsumer;\nimport com.google.cloud.pubsub.v1.MessageReceiver;\nimport com.google.cloud.pubsub.v1.Subscriber;\nimport com.google.pubsub.v1.ProjectSubscriptionName;\nimport com.google.pubsub.v1.PubsubMessage;\nimport java.io.FileInputStream;\nimport java.util.Collections;\n\npublic class Main {\n\n public static final String PROJECT_ID_ENV_PROPERTY = \"PROJECT_ID\";\n public static final String SUBSCRIPTION_ID_ENV_PROPERTY = \"SUBSCRIPTION_ID\";\n public static final String CREDENTIALS_PATH_ENV_PROPERTY = \"GOOGLE_APPLICATION_CREDENTIALS\";\n\n public static void main(String[] args) throws Exception {\n ProjectSubscriptionName subscriptionName =\n ProjectSubscriptionName.of(\n System.getenv(Main.PROJECT_ID_ENV_PROPERTY),\n System.getenv(Main.SUBSCRIPTION_ID_ENV_PROPERTY));\n\n // Instantiate app, which implements an asynchronous message receiver.\n EchoApp echoApp = new EchoApp();\n\n // Create a subscriber for <var>SUBSCRIPTION_ID</var> bound to the message receiver\n final Subscriber subscriber = Subscriber.newBuilder(subscriptionName, echoApp).build();\n System.out.println(\"Subscriber is listening to events...\");\n subscriber.startAsync();\n\n // Wait for termination\n subscriber.awaitTerminated();\n }\n}\n\n/**\n * A demo app which implements {@link MessageReceiver} to receive messages.\n * It echoes incoming messages.\n */\nclass EchoApp implements MessageReceiver {\n\n // Path to the private key JSON file of the service account to be used for posting response\n // messages to Google Chat.\n // In this demo, we are using the same service account for authorizing with Cloud Pub/Sub to\n // receive messages and authorizing with Google Chat to post messages. If you are using\n // different service accounts, set the path to the private key JSON file of the service\n // account used to post messages to Google Chat here.\n private static final String SERVICE_ACCOUNT_KEY_PATH =\n System.getenv(Main.CREDENTIALS_PATH_ENV_PROPERTY);\n\n // Developer code for Google Chat API scope.\n private static final String GOOGLE_CHAT_API_SCOPE = \"https://www.googleapis.com/auth/chat.bot\";\n\n private static final String ADDED_RESPONSE = \"Thank you for adding me!\";\n\n ChatServiceClient chatServiceClient;\n\n EchoApp() throws Exception {\n GoogleCredentials credential =\n GoogleCredentials.fromStream(new FileInputStream(SERVICE_ACCOUNT_KEY_PATH))\n .createScoped(Collections.singleton(GOOGLE_CHAT_API_SCOPE));\n\n // Create the ChatServiceSettings with the app credentials\n ChatServiceSettings chatServiceSettings =\n ChatServiceSettings.newBuilder()\n .setCredentialsProvider(FixedCredentialsProvider.create(credential))\n .build();\n\n // Set the Chat service client\n chatServiceClient = ChatServiceClient.create(chatServiceSettings);\n }\n\n // Called when a message is received by the subscriber.\n @Override\n public void receiveMessage(PubsubMessage pubsubMessage, AckReplyConsumer consumer) {\n System.out.println(\"Id : \" + pubsubMessage.getMessageId());\n // Handle incoming message, then acknowledge the received message\n try {\n ObjectMapper mapper = new ObjectMapper();\n JsonNode dataJson = mapper.readTree(pubsubMessage.getData().toStringUtf8());\n System.out.println(\"Data : \" + dataJson.toString());\n handle(dataJson);\n consumer.ack();\n } catch (Exception e) {\n System.out.println(e);\n // Negative acknowledgement makes Pub/Sub redeliver the message.\n consumer.nack();\n }\n }\n\n // Send message to Google Chat based on the type of event.\n public void handle(JsonNode eventJson) throws Exception {\n // Google Chat events for add-ons are wrapped in a 'chat' object.\n if (!eventJson.has(\"chat\")) {\n System.out.println(\"Ignored: Not a Chat event (missing 'chat' field).\");\n return;\n }\n\n JsonNode chatNode = eventJson.get(\"chat\");\n CreateMessageRequest createMessageRequest = null;\n\n if (chatNode.has(\"messagePayload\")) {\n // HANDLE MESSAGE\n JsonNode messagePayload = chatNode.get(\"messagePayload\");\n JsonNode message = messagePayload.get(\"message\");\n JsonNode space = messagePayload.get(\"space\");\n\n String spaceName = space.get(\"name\").asText();\n String userText = message.has(\"text\") ? message.get(\"text\").asText() : \"\";\n String threadName = message.has(\"thread\") ? message.get(\"thread\").get(\"name\").asText() : \"\";\n\n System.out.println(\"Received message in \" + spaceName + \": \" + userText);\n\n createMessageRequest =\n CreateMessageRequest.newBuilder()\n .setParent(spaceName)\n .setMessageReplyOption(MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD)\n .setMessage(\n Message.newBuilder()\n .setText(\"You said: `\" + userText + \"`\")\n .setThread(Thread.newBuilder().setName(threadName).build())\n .build())\n .build();\n\n } else if (chatNode.has(\"addedToSpacePayload\")) {\n // HANDLE ADDED TO SPACE\n JsonNode addedPayload = chatNode.get(\"addedToSpacePayload\");\n JsonNode space = addedPayload.get(\"space\");\n String spaceName = space.get(\"name\").asText();\n\n System.out.println(\"Added to space: \" + spaceName);\n\n createMessageRequest =\n CreateMessageRequest.newBuilder()\n .setParent(spaceName)\n .setMessage(Message.newBuilder().setText(ADDED_RESPONSE).build())\n .build();\n\n } else if (chatNode.has(\"removedFromSpacePayload\")) {\n System.out.println(\"Removed from space.\");\n return;\n } else {\n System.out.println(\"Ignored: Unhandled Chat event type.\");\n return;\n }\n\n if (createMessageRequest != null) {\n // Post the response to Google Chat.\n chatServiceClient.createMessage(createMessageRequest);\n System.out.println(\"Sent reply.\");\n }\n }\n}\n```\n\nExample:\n```text\nnpm install\nnpm start\n```\n\nExample:\n```text\npython -m venv env\nsource env/bin/activate\npip install -r requirements.txt -U\npython app.py\n```\n\nExample:\n```text\nmvn compile exec:java -Dexec.mainClass=Main\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.539Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":542,"estimatedTokens":4274}}707{"id":"doc-respond_to_google_chat_app_commands_google_works-569cbd6d","source":"documentation","title":"Respond to Google Chat app commands | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/commands","text":"Example:\n```text\n// The ID of the slash command \"/about\".\n// You must use the same ID in the Google Chat API configuration.\nconst ABOUT_COMMAND_ID = 1;\n\n/**\n * Handle requests from Google Workspace add on\n *\n * @param {Object} req Request sent by Google Chat\n * @param {Object} res Response to be sent back to Google Chat\n */\nhttp('avatarApp', (req, res) => {\n const chatEvent = req.body.chat;\n let message;\n if (chatEvent.appCommandPayload) {\n message = handleAppCommand(chatEvent);\n } else {\n message = handleMessage(chatEvent);\n }\n res.send({ hostAppDataAction: { chatDataAction: { createMessageAction: {\n message: message\n }}}});\n});\n\n/**\n * Responds to an APP_COMMAND event in Google Chat.\n *\n * @param {Object} event the event object from Google Chat\n * @return the response message object.\n */\nfunction handleAppCommand(event) {\n switch (event.appCommandPayload.appCommandMetadata.appCommandId) {\n case ABOUT_COMMAND_ID:\n return {\n text: 'The Avatar app replies to Google Chat messages.'\n };\n }\n}\n```\n\nExample:\n```text\n# The ID of the slash command \"/about\".\n# You must use the same ID in the Google Chat API configuration.\nABOUT_COMMAND_ID = 1\n\n@functions_framework.http\ndef avatar_app(req: flask.Request) -> Mapping[str, Any]:\n \"\"\"Handle requests from Google Workspace add on\n\n Args:\n flask.Request req: the request sent by Google Chat\n\n Returns:\n Mapping[str, Any]: the response to be sent back to Google Chat\n \"\"\"\n chat_event = req.get_json(silent=True)[\"chat\"]\n if chat_event and \"appCommandPayload\" in chat_event:\n message = handle_app_command(chat_event)\n else:\n message = handle_message(chat_event)\n return { \"hostAppDataAction\": { \"chatDataAction\": { \"createMessageAction\": {\n \"message\": message\n }}}}\n\ndef handle_app_command(event: Mapping[str, Any]) -> Mapping[str, Any]:\n \"\"\"Responds to an APP_COMMAND event in Google Chat.\n\n Args:\n Mapping[str, Any] event: the event object from Google Chat\n\n Returns:\n Mapping[str, Any]: the response message object.\n \"\"\"\n if event[\"appCommandPayload\"][\"appCommandMetadata\"][\"appCommandId\"] == ABOUT_COMMAND_ID:\n return {\n \"text\": \"The Avatar app replies to Google Chat messages.\",\n }\n return {}\n```\n\nExample:\n```text\n// The ID of the slash command \"/about\".\n// You must use the same ID in the Google Chat API configuration.\nprivate static final int ABOUT_COMMAND_ID = 1;\n\nprivate static final Gson gson = new Gson();\n\n/**\n * Handle requests from Google Workspace add on\n * \n * @param request the request sent by Google Chat\n * @param response the response to be sent back to Google Chat\n */\n@Override\npublic void service(HttpRequest request, HttpResponse response) throws Exception {\n JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);\n JsonObject chatEvent = event.getAsJsonObject(\"chat\");\n Message message;\n if (chatEvent.has(\"appCommandPayload\")) {\n message = handleAppCommand(chatEvent);\n } else {\n message = handleMessage(chatEvent);\n }\n JsonObject createMessageAction = new JsonObject();\n createMessageAction.add(\"message\", gson.fromJson(gson.toJson(message), JsonObject.class));\n JsonObject chatDataAction = new JsonObject();\n chatDataAction.add(\"createMessageAction\", createMessageAction);\n JsonObject hostAppDataAction = new JsonObject();\n hostAppDataAction.add(\"chatDataAction\", chatDataAction);\n JsonObject dataActions = new JsonObject();\n dataActions.add(\"hostAppDataAction\", hostAppDataAction);\n response.getWriter().write(gson.toJson(dataActions));\n}\n\n/**\n * Handles an APP_COMMAND event in Google Chat.\n *\n * @param event the event object from Google Chat\n * @return the response message object.\n */\nprivate Message handleAppCommand(JsonObject event) throws Exception {\n switch (event.getAsJsonObject(\"appCommandPayload\")\n .getAsJsonObject(\"appCommandMetadata\").get(\"appCommandId\").getAsInt()) {\n case ABOUT_COMMAND_ID:\n return new Message()\n .setText(\"The Avatar app replies to Google Chat messages.\");\n default:\n return null;\n }\n}\n```\n\nExample:\n```text\n// The ID of the slash command \"/about\".\n// You must use the same ID in the Google Chat API configuration.\nconst ABOUT_COMMAND_ID = 1;\n\n/**\n * Responds to an APP_COMMAND event in Google Chat.\n *\n * @param {Object} event the event object from Google Chat\n */\nfunction onAppCommand(event) {\n // Executes the app command logic based on ID.\n switch (event.chat.appCommandPayload.appCommandMetadata.appCommandId) {\n case ABOUT_COMMAND_ID:\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: 'The Avatar app replies to Google Chat messages.'\n }}}}};\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event from Google Chat.\n *\n * @param {Object} event The interaction event from Google Chat.\n * @param {Object} res The HTTP response object.\n * @return {Object} The JSON response message with a confirmation.\n */\nfunction onAppCommand(event, res) {\n // Collect the command ID and type from the event metadata.\n const {appCommandId, appCommandType} =\n event.chat.appCommandPayload.appCommandMetadata;\n\n if (appCommandType === 'MESSAGE_ACTION' &&\n appCommandId === REMIND_ME_COMMAND_ID) {\n\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n const messageText = event.chat.appCommandPayload.message.text;\n\n // Return a response that includes details from the original message.\n return res.json({\n \"hostAppDataAction\": {\n \"chatDataAction\": {\n \"createMessageAction\": {\n \"message\": {\n \"text\": `Setting a reminder for message: \"${messageText}\"`\n }\n }\n }\n }\n });\n }\n}\n```\n\nExample:\n```text\ndef on_app_command(event):\n \"\"\"Responds to an APP_COMMAND interaction event from Google Chat.\n\n Args:\n event (dict): The interaction event from Google Chat.\n\n Returns:\n dict: The JSON response message with a confirmation.\n \"\"\"\n # Collect the command ID and type from the event metadata.\n payload = event.get('chat', {}).get('appCommandPayload', {})\n metadata = payload.get('appCommandMetadata', {})\n if metadata.get('appCommandType') == 'MESSAGE_ACTION' and \\\n metadata.get('appCommandId') == REMIND_ME_COMMAND_ID:\n\n # Message actions can access the context of the message they were\n # invoked on, such as the text or sender of that message.\n message_text = payload.get('message', {}).get('text')\n\n # Return a response that includes details from the original message.\n return {\n \"hostAppDataAction\": {\n \"chatDataAction\": {\n \"createMessageAction\": {\n \"message\": {\n \"text\": f'Setting a reminder for message: \"{message_text}\"'\n }\n }\n }\n }\n }\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event from Google Chat.\n *\n * @param event The interaction event from Google Chat.\n * @param response The HTTP response object.\n */\nvoid onAppCommand(JsonObject event, HttpResponse response) throws Exception {\n // Collect the command ID and type from the event metadata.\n JsonObject payload = event.getAsJsonObject(\"chat\").getAsJsonObject(\"appCommandPayload\");\n JsonObject metadata = payload.getAsJsonObject(\"appCommandMetadata\");\n String appCommandType = metadata.get(\"appCommandType\").getAsString();\n\n if (appCommandType.equals(\"MESSAGE_ACTION\")) {\n int commandId = metadata.get(\"appCommandId\").getAsInt();\n if (commandId == REMIND_ME_COMMAND_ID) {\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n String messageText = payload.getAsJsonObject(\"message\").get(\"text\").getAsString();\n\n // Return a response that includes details from the original message.\n JsonObject responseMessage = new JsonObject();\n responseMessage.addProperty(\"text\", \"Setting a reminder for message: \" + messageText);\n\n JsonObject createMessageAction = new JsonObject();\n createMessageAction.add(\"message\", responseMessage);\n\n JsonObject chatDataAction = new JsonObject();\n chatDataAction.add(\"createMessageAction\", createMessageAction);\n\n JsonObject hostAppDataAction = new JsonObject();\n hostAppDataAction.add(\"chatDataAction\", chatDataAction);\n\n JsonObject finalResponse = new JsonObject();\n finalResponse.add(\"hostAppDataAction\", hostAppDataAction);\n\n response.getWriter().write(finalResponse.toString());\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event in Google Chat.\n *\n * @param {Object} event The interaction event from Google Chat.\n * @return {Object} The JSON response message with a confirmation.\n */\nfunction onAppCommand(event) {\n // Collect the command ID and type from the event metadata.\n const {appCommandId, appCommandType} =\n event.chat.appCommandPayload.appCommandMetadata;\n\n if (appCommandType === 'MESSAGE_ACTION' &&\n appCommandId === REMIND_ME_COMMAND_ID) {\n\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n const messageText = event.chat.appCommandPayload.message.text;\n\n // Return a response that includes details from the original message.\n return CardService.newChatResponseBuilder()\n .setText(\"Setting a reminder for message: \" + messageText)\n .build();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.540Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":301,"estimatedTokens":2422}}708{"id":"doc-build_a_step_google_workspace_add_ons_google_for-87ca7e14","source":"documentation","title":"Build a step | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/build-a-step","text":"Example:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Calculator\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"calculatorDemo\",\n \"state\": \"ACTIVE\",\n \"name\": \"Calculate\",\n \"description\": \"Asks the user for two values and a math operation, then performs the math operation on the values and outputs the result.\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"value1\",\n \"description\": \"value1\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n },\n {\n \"id\": \"value2\",\n \"description\": \"value2\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n },\n {\n \"id\": \"operation\",\n \"description\": \"operation\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"outputs\": [\n {\n \"id\": \"result\",\n \"description\": \"Calculated result\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfigCalculate\",\n \"onExecuteFunction\": \"onExecuteCalculate\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Generates and displays a configuration card for the sample calculation step.\n *\n * This function creates a card with input fields for two values and a drop-down\n * for selecting an arithmetic operation.\n *\n * The input fields are configured to let the user select outputs from previous\n * steps as input values using the `hostAppDataSource` property.\n */\nfunction onConfigCalculate() {\n const firstInput = CardService.newTextInput()\n .setFieldName(\"value1\")\n .setTitle(\"First Value\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n )\n );\n const secondInput = CardService.newTextInput()\n .setFieldName(\"value2\")\n .setTitle(\"Second Value\").setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n )\n );\n const selectionInput = CardService.newSelectionInput()\n .setTitle(\"operation\")\n .setFieldName(\"operation\")\n .setType(CardService.SelectionInputType.DROPDOWN)\n .addItem(\"+\", \"+\", false)\n .addItem(\"-\", \"-\", true)\n .addItem(\"x\", \"x\", false)\n .addItem(\"/\", \"/\", false);\n\n const sections = CardService.newCardSection()\n .setHeader(\"Action_sample: Calculate\")\n .setId(\"section_1\")\n .addWidget(firstInput)\n .addWidget(selectionInput)\n .addWidget(secondInput)\n\n var card = CardService.newCardBuilder()\n .addSection(sections)\n .build();\n\n return card;\n}\n\n/**\n* Returns output variables from a step.\n*\n* This function constructs an object that, when returned, sends the\n* provided variable values as output from the current step.\n* The variable values are logged to the console for debugging purposes.\n*/\nfunction outputVariables(variableDataMap) {\nconst workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()\n .setVariableDataMap(variableDataMap);\n\nconst hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\nconst renderAction = AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n\nreturn renderAction;\n}\n\n/**\n * Executes the calculation step based on the inputs from a flow event.\n *\n * This function retrieves input values and the operation from the flow event,\n * performs the calculation, and returns the result as an output variable.\n * The function logs the event for debugging purposes.\n */\nfunction onExecuteCalculate(event) {\n console.log(\"output: \" + JSON.stringify(event));\n var calculatedValue = 0;\n var value1 = event.workflow.actionInvocation.inputs[\"value1\"].integerValues[0];\n var value2 = event.workflow.actionInvocation.inputs[\"value2\"].integerValues[0];\n var operation = event.workflow.actionInvocation.inputs[\"operation\"].stringValues[0];\n\n if (operation == \"+\") {\n calculatedValue = value1 + value2;\n } else if (operation == \"-\") {\n calculatedValue = value1 - value2;\n } else if (operation == \"x\") {\n calculatedValue = value1 * value2;\n } else if (operation == \"/\") {\n calculatedValue = value1 / value2;\n }\n\n const variableDataMap = { \"result\": AddOnsResponseService.newVariableData().addIntegerValue(calculatedValue) };\n\n return outputVariables(variableDataMap);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.541Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":172,"estimatedTokens":1314}}709{"id":"doc-preview_links_with_smart_chips_google_workspace_-187d639d","source":"documentation","title":"Preview links with smart chips | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/guides/preview-links-smart-chips","text":"Example:\n```text\n{\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Preview support cases\",\n \"logoUrl\": \"https://www.example.com/images/company-logo.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"caseLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"logoUrl\": \"https://www.example.com/images/support-icon.png\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n }\n }\n ]\n },\n \"sheets\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"caseLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"logoUrl\": \"https://www.example.com/images/support-icon.png\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n }\n }\n ]\n },\n \"slides\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"caseLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"logoUrl\": \"https://www.example.com/images/support-icon.png\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"docs\": {\n \"matchedUrl\": {\n \"url\": \"https://www.example.com/support/cases/123456\"\n }\n }\n}\n```\n\nExample:\n```text\n/**\n* Entry point for a support case link preview.\n*\n* @param {!Object} event The event object.\n* @return {!Card} The resulting preview link card.\n*/\nfunction caseLinkPreview(event) {\n\n // If the event object URL matches a specified pattern for support case links.\n if (event.docs.matchedUrl.url) {\n\n // Uses the event object to parse the URL and identify the case details.\n const caseDetails = parseQuery(event.docs.matchedUrl.url);\n\n // Builds a preview card with the case name, and description\n const caseHeader = CardService.newCardHeader()\n .setTitle(`Case ${caseDetails[\"name\"][0]}`);\n const caseDescription = CardService.newTextParagraph()\n .setText(caseDetails[\"description\"][0]);\n\n // Returns the card.\n // Uses the text from the card's header for the title of the smart chip.\n return CardService.newCardBuilder()\n .setHeader(caseHeader)\n .addSection(CardService.newCardSection().addWidget(caseDescription))\n .build();\n }\n}\n\n/**\n* Extracts the URL parameters from the given URL.\n*\n* @param {!string} url The URL to parse.\n* @return {!Map} A map with the extracted URL parameters.\n*/\nfunction parseQuery(url) {\n const query = url.split(\"?\")[1];\n if (query) {\n return query.split(\"&\")\n .reduce(function(o, e) {\n var temp = e.split(\"=\");\n var key = temp[0].trim();\n var value = temp[1].trim();\n value = isNaN(value) ? value : Number(value);\n if (o[key]) {\n o[key].push(value);\n } else {\n o[key] = [value];\n }\n return o;\n }, {});\n }\n return null;\n}\n```\n\nExample:\n```text\n/**\n * \n * A support case link preview.\n *\n * @param {!URL} url The event object.\n * @return {!Card} The resulting preview link card.\n */\nfunction caseLinkPreview(url) {\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n // Parses the URL and identify the case details.\n const name = `Case ${url.searchParams.get(\"name\")}`;\n return {\n action: {\n linkPreview: {\n title: name,\n previewCard: {\n header: {\n title: name\n },\n sections: [{\n widgets: [{\n textParagraph: {\n text: url.searchParams.get(\"description\")\n }\n }]\n }]\n }\n }\n }\n };\n}\n```\n\nExample:\n```text\ndef case_link_preview(url):\n \"\"\"A support case link preview.\n Args:\n url: A matching URL.\n Returns:\n The resulting preview link card.\n \"\"\"\n\n # Parses the URL and identify the case details.\n query_string = parse_qs(url.query)\n name = f'Case {query_string[\"name\"][0]}'\n # Uses the text from the card's header for the title of the smart chip.\n return {\n \"action\": {\n \"linkPreview\": {\n \"title\": name,\n \"previewCard\": {\n \"header\": {\n \"title\": name\n },\n \"sections\": [{\n \"widgets\": [{\n \"textParagraph\": {\n \"text\": query_string[\"description\"][0]\n }\n }]\n }],\n }\n }\n }\n }\n```\n\nExample:\n```text\n/**\n * A support case link preview.\n *\n * @param url A matching URL.\n * @return The resulting preview link card.\n */\nJsonObject caseLinkPreview(URL url) throws UnsupportedEncodingException {\n // Parses the URL and identify the case details.\n Map<String, String> caseDetails = new HashMap<String, String>();\n for (String pair : url.getQuery().split(\"&\")) {\n caseDetails.put(URLDecoder.decode(pair.split(\"=\")[0], \"UTF-8\"), URLDecoder.decode(pair.split(\"=\")[1], \"UTF-8\"));\n }\n\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n JsonObject cardHeader = new JsonObject();\n String caseName = String.format(\"Case %s\", caseDetails.get(\"name\"));\n cardHeader.add(\"title\", new JsonPrimitive(caseName));\n\n JsonObject textParagraph = new JsonObject();\n textParagraph.add(\"text\", new JsonPrimitive(caseDetails.get(\"description\")));\n\n JsonObject widget = new JsonObject();\n widget.add(\"textParagraph\", textParagraph);\n\n JsonArray widgets = new JsonArray();\n widgets.add(widget);\n\n JsonObject section = new JsonObject();\n section.add(\"widgets\", widgets);\n\n JsonArray sections = new JsonArray();\n sections.add(section);\n\n JsonObject previewCard = new JsonObject();\n previewCard.add(\"header\", cardHeader);\n previewCard.add(\"sections\", sections);\n\n JsonObject linkPreview = new JsonObject();\n linkPreview.add(\"title\", new JsonPrimitive(caseName));\n linkPreview.add(\"previewCard\", previewCard);\n\n JsonObject action = new JsonObject();\n action.add(\"linkPreview\", linkPreview);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n return renderActions;\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/New_York\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\",\n \"https://www.googleapis.com/auth/workspace.linkcreate\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Manage support cases\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"caseLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n },\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ],\n \"createActionTriggers\": [\n {\n \"id\": \"createCase\",\n \"labelText\": \"Create support case\",\n \"localizedLabelText\": {\n \"es\": \"Crear caso de soporte\"\n },\n \"runFunction\": \"createCaseInputCard\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Preview support cases\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"URL\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n },\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to any HTTP request related to link previews.\n *\n * @param {Object} req An HTTP request context.\n * @param {Object} res An HTTP response context.\n */\nexports.createLinkPreview = (req, res) => {\n const event = req.body;\n if (event.docs.matchedUrl.url) {\n const url = event.docs.matchedUrl.url;\n const parsedUrl = new URL(url);\n // If the event object URL matches a specified pattern for preview links.\n if (parsedUrl.hostname === 'example.com') {\n if (parsedUrl.pathname.startsWith('/support/cases/')) {\n return res.json(caseLinkPreview(parsedUrl));\n }\n }\n }\n};\n\n\n/**\n * \n * A support case link preview.\n *\n * @param {!URL} url The event object.\n * @return {!Card} The resulting preview link card.\n */\nfunction caseLinkPreview(url) {\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n // Parses the URL and identify the case details.\n const name = `Case ${url.searchParams.get(\"name\")}`;\n return {\n action: {\n linkPreview: {\n title: name,\n previewCard: {\n header: {\n title: name\n },\n sections: [{\n widgets: [{\n textParagraph: {\n text: url.searchParams.get(\"description\")\n }\n }]\n }]\n }\n }\n }\n };\n}\n```\n\nExample:\n```text\nfrom typing import Any, Mapping\nfrom urllib.parse import urlparse, parse_qs\n\nimport flask\nimport functions_framework\n\n\n@functions_framework.http\ndef create_link_preview(req: flask.Request):\n \"\"\"Responds to any HTTP request related to link previews.\n Args:\n req: An HTTP request context.\n Returns:\n An HTTP response context.\n \"\"\"\n event = req.get_json(silent=True)\n if event[\"docs\"][\"matchedUrl\"][\"url\"]:\n url = event[\"docs\"][\"matchedUrl\"][\"url\"]\n parsed_url = urlparse(url)\n # If the event object URL matches a specified pattern for preview links.\n if parsed_url.hostname == \"example.com\":\n if parsed_url.path.startswith(\"/support/cases/\"):\n return case_link_preview(parsed_url)\n\n return {}\n\n\n\n\ndef case_link_preview(url):\n \"\"\"A support case link preview.\n Args:\n url: A matching URL.\n Returns:\n The resulting preview link card.\n \"\"\"\n\n # Parses the URL and identify the case details.\n query_string = parse_qs(url.query)\n name = f'Case {query_string[\"name\"][0]}'\n # Uses the text from the card's header for the title of the smart chip.\n return {\n \"action\": {\n \"linkPreview\": {\n \"title\": name,\n \"previewCard\": {\n \"header\": {\n \"title\": name\n },\n \"sections\": [{\n \"widgets\": [{\n \"textParagraph\": {\n \"text\": query_string[\"description\"][0]\n }\n }]\n }],\n }\n }\n }\n }\n```\n\nExample:\n```text\nimport com.google.cloud.functions.HttpFunction;\nimport com.google.cloud.functions.HttpRequest;\nimport com.google.cloud.functions.HttpResponse;\nimport com.google.gson.Gson;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URL;\nimport java.net.URLDecoder;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class CreateLinkPreview implements HttpFunction {\n private static final Gson gson = new Gson();\n\n /**\n * Responds to any HTTP request related to link previews.\n *\n * @param request An HTTP request context.\n * @param response An HTTP response context.\n */\n @Override\n public void service(HttpRequest request, HttpResponse response) throws Exception {\n JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);\n String url = event.getAsJsonObject(\"docs\")\n .getAsJsonObject(\"matchedUrl\")\n .get(\"url\")\n .getAsString();\n URL parsedURL = new URL(url);\n // If the event object URL matches a specified pattern for preview links.\n if (\"example.com\".equals(parsedURL.getHost())) {\n if (parsedURL.getPath().startsWith(\"/support/cases/\")) {\n response.getWriter().write(gson.toJson(caseLinkPreview(parsedURL)));\n return;\n }\n }\n\n response.getWriter().write(\"{}\");\n }\n\n\n /**\n * A support case link preview.\n *\n * @param url A matching URL.\n * @return The resulting preview link card.\n */\n JsonObject caseLinkPreview(URL url) throws UnsupportedEncodingException {\n // Parses the URL and identify the case details.\n Map<String, String> caseDetails = new HashMap<String, String>();\n for (String pair : url.getQuery().split(\"&\")) {\n caseDetails.put(URLDecoder.decode(pair.split(\"=\")[0], \"UTF-8\"), URLDecoder.decode(pair.split(\"=\")[1], \"UTF-8\"));\n }\n\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n JsonObject cardHeader = new JsonObject();\n String caseName = String.format(\"Case %s\", caseDetails.get(\"name\"));\n cardHeader.add(\"title\", new JsonPrimitive(caseName));\n\n JsonObject textParagraph = new JsonObject();\n textParagraph.add(\"text\", new JsonPrimitive(caseDetails.get(\"description\")));\n\n JsonObject widget = new JsonObject();\n widget.add(\"textParagraph\", textParagraph);\n\n JsonArray widgets = new JsonArray();\n widgets.add(widget);\n\n JsonObject section = new JsonObject();\n section.add(\"widgets\", widgets);\n\n JsonArray sections = new JsonArray();\n sections.add(section);\n\n JsonObject previewCard = new JsonObject();\n previewCard.add(\"header\", cardHeader);\n previewCard.add(\"sections\", sections);\n\n JsonObject linkPreview = new JsonObject();\n linkPreview.add(\"title\", new JsonPrimitive(caseName));\n linkPreview.add(\"previewCard\", previewCard);\n\n JsonObject action = new JsonObject();\n action.add(\"linkPreview\", linkPreview);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n return renderActions;\n }\n\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.543Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":609,"estimatedTokens":4090}}710{"id":"doc-quickstart_build_a_calculator_step_with_google_a-0ad95fe0","source":"documentation","title":"Quickstart: Build a calculator step with Google Apps Script | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/quickstart-calculator","text":"Example:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Calculator\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"actionElement\",\n \"state\": \"ACTIVE\",\n \"name\": \"Calculate\",\n \"description\": \"Asks the user for two values and a math operation, then performs the math operation on the values and outputs the result.\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"value1\",\n \"description\": \"value1\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n },\n {\n \"id\": \"value2\",\n \"description\": \"value2\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n },\n {\n \"id\": \"operation\",\n \"description\": \"operation\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"outputs\": [\n {\n \"id\": \"result\",\n \"description\": \"Calculated result\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfigCalculateFunction\",\n \"onExecuteFunction\": \"onExecuteCalculateFunction\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This script defines a custom step for Google Workspace Studio.\n * The step, named \"Calculate\", takes two numbers and an operation as input\n * and returns the result of the calculation.\n *\n * The script includes functions to:\n *\n * 1. Define the configuration UI for the step using Card objects:\n *\n * - `onConfigCalculateFunction()`: Generates the main configuration card.\n * - Helper functions like `pushCard()`, `saveButton()` to build card components.\n *\n * 2. Handle the execution of the step.\n *\n * - `onExecuteCalculateFunction()`: Retrieves inputs, performs the calculation,\n * and returns outputs.\n *\n * To learn more, see the following quickstart guide:\n * https://developers.google.com/workspace/add-ons/studio/quickstart\n */\n\n/**\n * Creates an action response to push a new card onto the card stack.\n *\n * This function generates an action object that, when returned, causes the\n * provided card to be pushed onto the card stack, making it the currently\n * displayed card in the configuration UI.\n * @param {Object} card The Card object to push.\n * @return {Object} The action response object.\n */\nfunction pushCard(card) {\n return {\n\n \"action\": {\n \"navigations\": [{\n \"push_card\": card\n }\n ]\n } }; \n}\n\n/**\n * Creates an action response to update the currently displayed card.\n *\n * This function generates an action object that, when returned, causes the\n * currently displayed card to be replaced with the provided card in the\n * configuration UI.\n * @param {Object} card The Card object to update.\n * @return {Object} The render actions object.\n */\nfunction updateCard(card) {\n return {\n \"render_actions\": {\n \"action\": {\n \"navigations\": [{\n \"update_card\": card\n }\n ]\n }\n }\n };\n}\n\n/**\n * Creates a button configuration object for saving the step.\n *\n * This function generates a button definition that, when clicked, triggers\n * a save action for the current step configuration.\n * @return {Object} The button widget object.\n */\nfunction saveButton() {\n return {\n \"text\": \"Save\",\n \"onClick\": {\n \"hostAppAction\" : {\n \"workflowAction\" : {\n \"saveWorkflowAction\" : {}\n }\n }\n },\n };\n}\n\n/**\n * Creates a button configuration object for a refresh action.\n *\n * This function generates a button definition that, when clicked, triggers\n * a function to refresh the current card.\n * @param {string} functionName The name of the Apps Script function to call on click.\n * @return {Object} The button widget object.\n */\nfunction refreshButton(functionName) {\n return {\n \"text\": \"Refresh\",\n \"onClick\": {\n \"action\" : {\n \"function\" : functionName\n }\n },\n };\n}\n\n\n/**\n * Generates and displays a configuration card for the sample calculation action.\n *\n * This function creates a card with input fields for two values and a dropdown\n * for selecting an arithmetic operation. The card also includes a \"Save\"\n * button to save the action configuration for the step.\n *\n * The input fields are configured to let the user select outputs from previous\n * steps as input values using the `hostAppDataSource` property.\n * This function is called when the user adds or edits the \"Calculate\" step in the UI.\n * @return {Object} The action response object containing the card to display.\n */\nfunction onConfigCalculateFunction() {\n var card = {\n \"sections\": [\n {\n \"header\": \"Action sample: Calculate\",\n \"widgets\": [\n {\n \"textInput\": {\n \"name\": \"value1\",\n \"label\": \"First value\",\n \"hostAppDataSource\" : {\n \"workflowDataSource\" : {\n \"includeVariables\" : true\n }\n }\n }\n },\n {\n \"selectionInput\": {\n \"name\": \"operation\",\n \"label\": \"Operation\",\n \"type\": \"DROPDOWN\",\n \"items\": [\n {\n \"text\": \"+\",\n \"value\": \"+\",\n },\n {\n \"text\": \"-\",\n \"value\": \"-\",\n },\n {\n \"text\": \"x\",\n \"value\": \"x\",\n },\n {\n \"text\": \"/\",\n \"value\": \"/\",\n }\n ]\n }\n },\n {\n \"textInput\": {\n \"name\": \"value2\",\n \"label\": \"Second value\",\n \"hostAppDataSource\" : {\n \"workflowDataSource\" : {\n \"includeVariables\" : true\n }\n }\n }\n }\n ]\n }\n ]\n };\n return pushCard(card);\n}\n\n/**\n * Gets an integer value from variable data, handling both string and integer formats.\n *\n * This function attempts to extract an integer value from the provided variable data.\n * It checks if the data contains string values and, if so, parses the first string\n * as an integer. If integer values are present, it returns the first integer.\n * @param {Object} variableData The variable data object from the event.\n * @return {number} The extracted integer value.\n */\nfunction getIntValue(variableData) {\n if (variableData.stringValues) {\n return parseInt(variableData.stringValues[0]);\n }\n return variableData.integerValues[0];\n}\n\n/**\n* Returns output variables from a step.\n*\n* This function constructs an object that, when returned, sends the\n* provided variable values as output from the current step.\n* The variable values are logged to the console for debugging purposes.\n*/\nfunction outputVariables(variableDataMap) {\n const workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()\n .setVariableDataMap(variableDataMap);\n\n const hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n const renderAction = AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n\n return renderAction;\n}\n\n/**\n * Executes the calculation action based on the inputs from an event.\n *\n * This function retrieves input values (\"value1\", \"value2\") and the \"operation\"\n * from the event, performs the calculation, and returns the \"result\" and\n * \"log\" as output variables.\n * This function is called when the flow reaches this custom step.\n * @param {Object} event The event object passed by the runtime.\n * @return {Object} The output variables object.\n */\nfunction onExecuteCalculateFunction(event) {\n console.log(\"output: \" + JSON.stringify(event));\n var calculatedValue = 0;\n var value1 = event.workflow.actionInvocation.inputs[\"value1\"].integerValues[0];\n var value2 = event.workflow.actionInvocation.inputs[\"value2\"].integerValues[0];\n var operation = event.workflow.actionInvocation.inputs[\"operation\"].stringValues[0];\n\n\n if (operation == \"+\") {\n calculatedValue = value1 + value2;\n } else if (operation == \"-\") {\n calculatedValue = value1 - value2;\n } else if (operation == \"x\") {\n calculatedValue = value1 * value2;\n } else if (operation == \"/\") {\n calculatedValue = value1 / value2;\n }\n\n const variableDataMap = { \"result\": AddOnsResponseService.newVariableData().addIntegerValue(calculatedValue) };\n\n return outputVariables(variableDataMap);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.544Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":315,"estimatedTokens":2325}}711{"id":"doc-send_google_chat_messages_google_workspace_add_o-2d3aa005","source":"documentation","title":"Send Google Chat messages | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/send-messages","text":"Example:\n```text\n{ \"hostAppDataAction\": { \"chatDataAction\": { \"createMessageAction\": {\n \"message\": MESSAGE\n}}}\n```\n\nExample:\n```text\n/**\n * Sends an onboarding message when the Chat app is added to a space.\n *\n * @param {Object} req The request object from Google Workspace add-on.\n * @param {Object} res The response object from the Chat app.\n */\nexports.cymbalApp = function cymbalApp(req, res) {\n const chatEvent = req.body.chat;\n // Send an onboarding message when added to a Chat space\n if (chatEvent.addedToSpacePayload) {\n res.json({ hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: 'Hi, Cymbal at your service. I help you manage your calendar' +\n 'from Google Chat. Take a look at your schedule today by typing' +\n '`/checkCalendar`, or schedule a meeting with `/scheduleMeeting`. ' +\n 'To learn what else I can do, type `/help`.'\n }}}}});\n }\n};\n```\n\nExample:\n```text\nfrom flask import Flask, request, json\napp = Flask(__name__)\n\n@app.route('/', methods=['POST'])\ndef cymbal_app():\n \"\"\"Sends an onboarding message when the Chat app is added to a space.\n\n Returns:\n Mapping[str, Any]: The response object from the Chat app.\n \"\"\"\n chat_event = request.get_json()[\"chat\"]\n if \"addedToSpacePayload\" in chat_event:\n return json.jsonify({ \"hostAppDataAction\": { \"chatDataAction\": {\n \"createMessageAction\": { \"message\": {\n \"text\": 'Hi, Cymbal at your service. I help you manage your calendar' +\n 'from Google Chat. Take a look at your schedule today by typing' +\n '`/checkCalendar`, or schedule a meeting with `/scheduleMeeting`. ' +\n 'To learn what else I can do, type `/help`.'\n }}\n }}})\n```\n\nExample:\n```text\n@SpringBootApplication\n@RestController\npublic class App {\n public static void main(String[] args) {\n SpringApplication.run(App.class, args);\n }\n\n /*\n * Sends an onboarding message when the Chat app is added to a space.\n *\n * @return The response object from the Chat app.\n */\n @PostMapping(\"/\")\n @ResponseBody\n public GenericJson onEvent(@RequestBody JsonNode event) throws Exception {\n JsonNode chatEvent = event.at(\"/chat\");\n if(!chatEvent.at(\"/addedToSpacePayload\").isEmpty()) {\n return new GenericJson() { {\n put(\"hostAppDataAction\", new GenericJson() { {\n put(\"chatDataAction\", new GenericJson() { {\n put(\"createMessageAction\", new GenericJson() { {\n put(\"message\", new Message().setText(\n \"Hi, Cymbal at your service. I help you manage your calendar\" +\n \"from Google Chat. Take a look at your schedule today by typing\" +\n \"`/checkCalendar`, or schedule a meeting with `/scheduleMeeting`. \" +\n \"To learn what else I can do, type `/help`.\"\n ));\n } });\n } });\n } });\n } };\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Sends an onboarding message when the Chat app is added to a space.\n *\n * @param {Object} event The event object from Chat API.\n * @return {Object} Response from the Chat app.\n */\nfunction onAddedToSpace(event) {\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: 'Hi, Cymbal at your service. I help you manage your calendar' +\n 'from Google Chat. Take a look at your schedule today by typing' +\n '`/checkCalendar`, or schedule a meeting with `/scheduleMeeting`. ' +\n 'To learn what else I can do, type `/help`.'\n }}}}};\n}\n```\n\nExample:\n```text\n{ \"hostAppDataAction\": { \"chatDataAction\": { \"updateMessageAction\": {\n \"message\": MESSAGE\n}}}}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.545Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":117,"estimatedTokens":913}}712{"id":"doc-update_and_manage_steps_with_versions_google_wor-f95ac3df","source":"documentation","title":"Update and manage steps with versions | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/versioning","text":"Example:\n```text\n...\n\"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"...\",\n \"state\": \"...\",\n \"name\": \"...\",\n \"description\": \"...\",\n \"version\" : {\n \"current_version\": 3,\n \"min_version\" : 1\n },\n...\n```\n\nExample:\n```text\n/**\n * Executes the step and handles different versions.\n * @param {Object} event The event object.\n */\nfunction onExecute(event) {\n // Get the version ID from the execution metadata.\n const versionId = event.workflow.executionMetadata.versionId;\n\n // Implement different behavior based on the version.\n if (versionId < 2) {\n // Handle earlier versions\n } else {\n // Handle current and newer versions\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.546Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":37,"estimatedTokens":182}}713{"id":"doc-convert_an_interactive_google_chat_app_to_a_goog-87143e82","source":"documentation","title":"Convert an interactive Google Chat app to a Google Workspace add-on | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/convert","text":"Example:\n```text\n{\n \"type\": \"ADDED_TO_SPACE\",\n \"space\": { ... }\n}\n```\n\nExample:\n```text\n{\n \"chat\": {\n \"addedToSpacePayload\": {\n \"space\": { ... }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"type\": \"REMOVED_FROM_SPACE\",\n \"space\": { ... }\n}\n```\n\nExample:\n```text\n{\n \"chat\": {\n \"removedFromSpacePayload\": {\n \"space\": { ... }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"type\": \"MESSAGE\",\n \"message\": { ... },\n \"space\": { ... },\n \"configCompleteRedirectUrl\": \"...\"\n}\n```\n\nExample:\n```text\n{\n \"chat\": {\n \"messagePayload\": {\n \"message\": { ... },\n \"space\": { ... },\n \"configCompleteRedirectUri\": \"...\"\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"type\": \"ADDED_TO_SPACE\",\n \"space\": { ... },\n \"message\": { ... }\n}\n```\n\nExample:\n```text\n{\n \"chat\": {\n \"addedToSpacePayload\": {\n \"space\": { ... },\n \"interactionAdd\": true\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"chat\": {\n \"messagePayload\": {\n \"message\": { ... },\n \"space\": { ... }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"type\": \"MESSAGE\",\n \"message\": { \"slashCommand\": { ... } },\n \"space\": { ... }\n}\n```\n\nExample:\n```text\n{\n \"chat\": {\n \"appCommandPayload\": {\n \"message\": { ... },\n \"space\": { ... },\n \"appCommandMetadata\": { ... }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"type\": \"ADDED_TO_SPACE\",\n \"space\": { ... },\n \"message\": { \"slashCommand\": { ... } }\n}\n```\n\nExample:\n```text\n{\n \"type\": \"CARD_CLICKED\",\n \"common\": { ... },\n \"space\": { ... },\n \"message\": { ... },\n \"isDialogEvent\": \"...\",\n \"dialogEventType\": \"...\"\n}\n```\n\nExample:\n```text\n{\n \"type\": \"CARD_CLICKED\",\n \"common\": {\n \"formInputs\": {\n \"contactName\": {\n \"\": { \"stringInputs\": { \"value\": [\"Kai 0\"] }}\n }\n }\n },\n \"space\": { ... },\n \"message\": { ... },\n \"isDialogEvent\": true,\n \"dialogEventType\": \"...\"\n}\n```\n\nExample:\n```text\n{\n \"commonEventObject\": { ... },\n \"chat\": {\n \"buttonClickedPayload\": {\n \"message\": { ... },\n \"space\": { ... },\n \"isDialogEvent\": \"...\",\n \"dialogEventType\": \"...\"\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"commonEventObject\": {\n \"formInputs\": {\n \"contactName\": {\n \"stringInputs\": {\n \"value\": [\"Kai 0\"]\n }\n }\n }\n },\n \"chat\": {\n \"buttonClickedPayload\": {\n \"message\": { ... },\n \"space\": { ... },\n \"isDialogEvent\": \"true\",\n \"dialogEventType\": \"...\"\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"type\": \"SUBMIT_FORM\",\n \"common\": { ... },\n \"space\": { ... },\n \"message\": { ... },\n \"isDialogEvent\": \"...\",\n \"dialogEventType\": \"...\"\n}\n```\n\nExample:\n```text\n{\n \"commonEventObject\": { ... },\n \"chat\": {\n \"buttonClickedPayload\": {\n \"message\": { ... },\n \"space\": { ... },\n \"isDialogEvent\": \"...\",\n \"dialogEventType\": \"SUBMIT_DIALOG\"\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"type\": \"APP_COMMAND\",\n \"space\": { ... },\n \"isDialogEvent\": \"...\",\n \"dialogEventType\": \"...\"\n}\n```\n\nExample:\n```text\n{\n \"type\": \"MESSAGE\",\n \"message\": {\n \"matchedUrl\": \"...\"\n },\n \"space\": { ... }\n}\n```\n\nExample:\n```text\n{\n \"chat\": {\n \"messagePayload\": {\n \"message\": {\n \"matchedUrl\": \"...\"\n },\n \"space\": { ... }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"type\": \"WIDGET_UPDATED\",\n \"space\": { ... },\n \"common\": { ... }\n}\n```\n\nExample:\n```text\n{\n \"commonEventObject\": { ... },\n \"chat\": {\n \"widgetUpdatedPayload\": {\n \"space\": { ... }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"actionResponse\": {\n \"type\": \"NEW_MESSAGE\"\n },\n \"text\": \"...\"\n}\n```\n\nExample:\n```text\n{\n \"hostAppDataAction\": {\n \"chatDataAction\": {\n \"createMessageAction\": {\n \"message\": {\n \"text\": \"...\"\n }\n }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"actionResponse\": {\n \"type\": \"UPDATE_MESSAGE\"\n },\n \"text\": \"...\"\n}\n```\n\nExample:\n```text\n{\n \"hostAppDataAction\": {\n \"chatDataAction\": {\n \"updateMessageAction\": {\n \"message\": {\n \"text\": \"...\"\n }\n }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"actionResponse\": {\n \"type\": \"UPDATE_USER_MESSAGE_CARDS\"\n },\n \"cardsV2\": [{ ... }]\n}\n```\n\nExample:\n```text\n{\n \"hostAppDataAction\": {\n \"chatDataAction\": {\n \"updateInlinePreviewAction\": {\n \"cardsV2\": [{ ... }]\n }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"actionResponse\": {\n \"type\": \"DIALOG\",\n \"dialogAction\": {\n \"dialog\": {\n \"body\": { /* Card object */ }\n }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"action\": {\n \"navigations\": [{\n \"pushCard\": { /* Card object */ }\n }]\n }\n}\n```\n\nExample:\n```text\n{\n \"onClick\": {\n \"action\": {\n \"function\": \"https://...\",\n \"parameters\": [{\n \"key\": \"clickedButton\",\n \"value\": \"submit\"\n }]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"actionResponse\": {\n \"type\": \"DIALOG\",\n \"dialogAction\": {\n \"actionStatus\": {\n \"userFacingMessage\": \"...\"\n }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"action\": {\n \"navigations\": [{\n \"endNavigation\": \"CLOSE_DIALOG\"\n }],\n \"notification\": { \"text\": \"...\"}\n }\n}\n```\n\nExample:\n```text\n{\n \"actionResponse\": {\n \"type\": \"REQUEST_CONFIG\",\n \"url\": \"...\"\n }\n}\n```\n\nExample:\n```text\n{\n \"basic_authorization_prompt\": {\n \"authorization_url\": \"...\",\n \"resource\": \"...\"\n }\n}\n```\n\nExample:\n```text\n{\n \"actionResponse\": {\n \"type\": \"UPDATE_WIDGET\",\n \"updatedWidget\": {\n \"suggestions\": {\n \"items\": [\"...\"]\n },\n \"widget\": \"widget_id\"\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"action\": {\n \"modifyOperations\": [{\n \"updateWidget\": {\n \"widgetId\": \"widget_id\",\n \"selectionInputWidgetSuggestions\": {\n \"suggestions\": [\"...\"]\n }\n }\n }]\n }\n}\n```\n\nExample:\n```text\n{\n \"onClick\": {\n \"action\": {\n \"function\": \"submit\"\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"onClick\": {\n \"action\": {\n \"function\": \"https://...\",\n \"parameters\": [{\n \"key\": \"method\",\n \"value\": \"submit\"\n }]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"commonEventObject\": {\n \"parameters\": {\n \"__action_method_name__\": \"submit\"\n }\n },\n \"chat\": {\n \"buttonClickedPayload\": { ... }\n }\n}\n```\n\nExample:\n```text\n{\n \"selectionInput\": {\n \"name\": \"contacts\",\n \"type\": \"MULTI_SELECT\",\n \"externalDataSource\": {\n \"function\": \"getContacts\"\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"commonEventObject\": {\n \"parameters\": {\n \"__action_method_name__\": \"getContacts\",\n }\n },\n \"chat\": {\n \"widgetUpdatedPayload\": { ... }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.547Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":43,"totalLines":534,"estimatedTokens":1614}}714{"id":"doc-preview_links_in_google_chat_messages_google_wor-b9badcae","source":"documentation","title":"Preview links in Google Chat messages | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/preview-links","text":"Example:\n```text\nmessage: {\n matchedUrl: {\n url: \"https://support.example.com/cases/case123\"\n },\n ... // other message attributes redacted\n}\n```\n\nExample:\n```text\n// Reply with a text message for URLs of the subdomain \"text\"\nif (chatMessage.matchedUrl.url.includes(\"text.example.com\")) {\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: 'event.chat.messagePayload.message.matchedUrl.url: ' + chatMessage.matchedUrl.url\n }}}}};\n}\n```\n\nExample:\n```text\n# Reply with a text message for URLs of the subdomain \"text\"\nif \"text.example.com\" in chatMessage.get('matchedUrl').get('url'):\n return { 'hostAppDataAction': { 'chatDataAction': { 'createMessageAction': { 'message': {\n 'text': 'event.chat.messagePayload.message.matchedUrl.url: ' + chatMessage.get('matchedUrl').get('url')\n }}}}}\n```\n\nExample:\n```text\n// Reply with a text message for URLs of the subdomain \"text\"\nif (chatMessage.at(\"/matchedUrl/url\").asText().contains(\"text.example.com\")) {\n return new GenericJson() {{\n put(\"hostAppDataAction\", new GenericJson() {{\n put(\"chatDataAction\", new GenericJson() {{\n put(\"createMessageAction\", new GenericJson() {{\n put(\"message\", new Message()\n .setText(\"event.chat.messagePayload.message.matchedUrl.url: \" + chatMessage.at(\"/matchedUrl/url\").asText()));\n }});\n }});\n }});\n }};\n}\n```\n\nExample:\n```text\n// Reply with a text message for URLs of the subdomain \"text\".\nif (chatMessage.matchedUrl.url.includes(\"text.example.com\")) {\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: 'event.chat.messagePayload.message.matchedUrl.url: ' + chatMessage.matchedUrl.url\n }}}}};\n}\n```\n\nExample:\n```text\n// Attach a card to the message for URLs of the subdomain \"support\"\nif (chatMessage.matchedUrl.url.includes(\"support.example.com\")) {\n // A hard-coded card is used in this example. In a real-life scenario,\n // the case information would be fetched and used to build the card.\n return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: { cardsV2: [{\n cardId: 'attachCard',\n card: {\n header: {\n title: 'Example Customer Service Case',\n subtitle: 'Case basics',\n },\n sections: [{ widgets: [\n { decoratedText: { topLabel: 'Case ID', text: 'case123'}},\n { decoratedText: { topLabel: 'Assignee', text: 'Charlie'}},\n { decoratedText: { topLabel: 'Status', text: 'Open'}},\n { decoratedText: { topLabel: 'Subject', text: 'It won\\'t turn on...' }},\n { buttonList: { buttons: [{\n text: 'OPEN CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123'\n }},\n }, {\n text: 'RESOLVE CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123?resolved=y',\n }},\n }, {\n text: 'ASSIGN TO ME',\n onClick: { action: { function: FUNCTION_URL }}\n }]}}\n ]}]\n }\n }]}}}};\n}\n```\n\nExample:\n```text\n# Attach a card to the message for URLs of the subdomain \"support\"\nif \"support.example.com\" in chatMessage.get('matchedUrl').get('url'):\n # A hard-coded card is used in this example. In a real-life scenario,\n # the case information would be fetched and used to build the card.\n return { 'hostAppDataAction': { 'chatDataAction': { 'updateInlinePreviewAction': { 'cardsV2': [{\n 'cardId': 'attachCard',\n 'card': {\n 'header': {\n 'title': 'Example Customer Service Case',\n 'subtitle': 'Case basics',\n },\n 'sections': [{ 'widgets': [\n { 'decoratedText': { 'topLabel': 'Case ID', 'text': 'case123'}},\n { 'decoratedText': { 'topLabel': 'Assignee', 'text': 'Charlie'}},\n { 'decoratedText': { 'topLabel': 'Status', 'text': 'Open'}},\n { 'decoratedText': { 'topLabel': 'Subject', 'text': 'It won\\'t turn on...' }},\n { 'buttonList': { 'buttons': [{\n 'text': 'OPEN CASE',\n 'onClick': { 'openLink': {\n 'url': 'https://support.example.com/orders/case123'\n }},\n }, {\n 'text': 'RESOLVE CASE',\n 'onClick': { 'openLink': {\n 'url': 'https://support.example.com/orders/case123?resolved=y',\n }},\n }, {\n 'text': 'ASSIGN TO ME',\n 'onClick': { 'action': { 'function': FUNCTION_URL }}\n }]}}\n ]}]\n }\n }]}}}}\n```\n\nExample:\n```text\n// Attach a card to the message for URLs of the subdomain \"support\"\nif (chatMessage.at(\"/matchedUrl/url\").asText().contains(\"support.example.com\")) {\n // A hard-coded card is used in this example. In a real-life scenario,\n // the case information would be fetched and used to build the card.\n CardWithId cardV2 = new CardWithId()\n .setCardId(\"attachCard\")\n .setCard(new GoogleAppsCardV1Card()\n .setHeader(new GoogleAppsCardV1CardHeader()\n .setTitle(\"Example Customer Service Case\")\n .setSubtitle(\"Case basics\"))\n .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Case ID\")\n .setText(\"case123\")),\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Assignee\")\n .setText(\"Charlie\")),\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Status\")\n .setText(\"Open\")),\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Subject\")\n .setText(\"It won't turn on...\")),\n new GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList()\n .setButtons(List.of(\n new GoogleAppsCardV1Button()\n .setText(\"OPEN CASE\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setOpenLink(new GoogleAppsCardV1OpenLink()\n .setUrl(\"https://support.example.com/orders/case123\"))),\n new GoogleAppsCardV1Button()\n .setText(\"RESOLVE CASE\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setOpenLink(new GoogleAppsCardV1OpenLink()\n .setUrl(\"https://support.example.com/orders/case123?resolved=y\"))),\n new GoogleAppsCardV1Button()\n .setText(\"ASSIGN TO ME\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setAction(new GoogleAppsCardV1Action().setFunction(FUNCTION_URL)))\n ))\n )\n ))))\n );\n\n return new GenericJson() {{\n put(\"hostAppDataAction\", new GenericJson() {{\n put(\"chatDataAction\", new GenericJson() {{\n put(\"updateInlinePreviewAction\", new GenericJson() {{\n put(\"cardsV2\", List.of(cardV2));\n }});\n }});\n }});\n }};\n}\n```\n\nExample:\n```text\n// Attach a card to the message for URLs of the subdomain \"support\".\nif (chatMessage.matchedUrl.url.includes(\"support.example.com\")) {\n // A hard-coded card is used in this example. In a real-life scenario,\n // the case information would be fetched and used to build the card.\n return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: { cardsV2: [{\n cardId: 'attachCard',\n card: {\n header: {\n title: 'Example Customer Service Case',\n subtitle: 'Case summary',\n },\n sections: [{ widgets: [\n { decoratedText: { topLabel: 'Case ID', text: 'case123'}},\n { decoratedText: { topLabel: 'Assignee', text: 'Charlie'}},\n { decoratedText: { topLabel: 'Status', text: 'Open'}},\n { decoratedText: { topLabel: 'Subject', text: 'It won\\'t turn on...' }},\n { buttonList: { buttons: [{\n text: 'OPEN CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123'\n }},\n }, {\n text: 'RESOLVE CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123?resolved=y',\n }},\n }, {\n text: 'ASSIGN TO ME',\n // Clicking this button triggers the execution of the function\n // \"assign\" from the Apps Script project.\n onClick: { action: { function: 'assign'}}\n }]}}\n ]}]\n }\n }]}}}};\n}\n```\n\nExample:\n```text\n/**\n * Respond to clicks by assigning and updating the card that's attached to a\n * message previewed link of the pattern \"support.example.com\".\n *\n * @param {Object} chatMessage The chat message object from Google Workspace Add On event.\n * @return {Object} Action response depending on the message author.\n */\nfunction handleCardClick(chatMessage) {\n // Creates the updated card that displays \"You\" for the assignee\n // and that disables the button.\n //\n // A hard-coded card is used in this example. In a real-life scenario,\n // an actual assign action would be performed before building the card.\n const message = { cardsV2: [{\n cardId: 'attachCard',\n card: {\n header: {\n title: 'Example Customer Service Case',\n subtitle: 'Case basics',\n },\n sections: [{ widgets: [\n { decoratedText: { topLabel: 'Case ID', text: 'case123'}},\n // The assignee is now \"You\"\n { decoratedText: { topLabel: 'Assignee', text: 'You'}},\n { decoratedText: { topLabel: 'Status', text: 'Open'}},\n { decoratedText: { topLabel: 'Subject', text: 'It won\\'t turn on...' }},\n { buttonList: { buttons: [{\n text: 'OPEN CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123'\n }},\n }, {\n text: 'RESOLVE CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123?resolved=y',\n }},\n }, {\n text: 'ASSIGN TO ME',\n // The button is now disabled\n disabled: true,\n onClick: { action: { function: FUNCTION_URL }}\n }]}}\n ]}]\n }\n }]};\n\n // Use the adequate action response type. It depends on whether the message\n // the preview link card is attached to was created by a human or a Chat app.\n if(chatMessage.sender.type === 'HUMAN') {\n return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: message }}};\n } else {\n return { hostAppDataAction: { chatDataAction: { updateMessageAction: message }}};\n }\n}\n```\n\nExample:\n```text\ndef handle_card_click(chatMessage: dict) -> dict:\n \"\"\"Respond to clicks by assigning and updating the card that's attached to a\n message previewed link of the pattern \"support.example.com\".\n\n - Reply with text messages that echo \"text.example.com\" link URLs in messages.\n - Attach cards to messages with \"support.example.com\" link URLs.\n\n Args:\n chatMessage (Mapping[str, Any]): The chat message object from Google Workspace Add On event.\n\n Returns:\n Mapping[str, Any]: Action response depending on the message author.\n \"\"\"\n # Creates the updated card that displays \"You\" for the assignee\n # and that disables the button.\n #\n # A hard-coded card is used in this example. In a real-life scenario,\n # an actual assign action would be performed before building the card.\n message = { 'cardsV2': [{\n 'cardId': 'attachCard',\n 'card': {\n 'header': {\n 'title': 'Example Customer Service Case',\n 'subtitle': 'Case basics',\n },\n 'sections': [{ 'widgets': [\n { 'decoratedText': { 'topLabel': 'Case ID', 'text': 'case123'}},\n # The assignee is now \"You\"\n { 'decoratedText': { 'topLabel': 'Assignee', 'text': 'You'}},\n { 'decoratedText': { 'topLabel': 'Status', 'text': 'Open'}},\n { 'decoratedText': { 'topLabel': 'Subject', 'text': 'It won\\'t turn on...' }},\n { 'buttonList': { 'buttons': [{\n 'text': 'OPEN CASE',\n 'onClick': { 'openLink': {\n 'url': 'https://support.example.com/orders/case123'\n }},\n }, {\n 'text': 'RESOLVE CASE',\n 'onClick': { 'openLink': {\n 'url': 'https://support.example.com/orders/case123?resolved=y',\n }},\n }, {\n 'text': 'ASSIGN TO ME',\n # The button is now disabled\n 'disabled': True,\n 'onClick': { 'action': { 'function': FUNCTION_URL }}\n }]}}\n ]}]\n }\n }]}\n\n # Use the adequate action response type. It depends on whether the message\n # the preview link card is attached to was created by a human or a Chat app.\n if chatMessage.get('sender').get('type') == 'HUMAN':\n return { 'hostAppDataAction': { 'chatDataAction': { 'updateInlinePreviewAction': message }}}\n else:\n return { 'hostAppDataAction': { 'chatDataAction': { 'updateMessageAction': message }}}\n```\n\nExample:\n```text\n/**\n * Respond to clicks by assigning and updating the card that's attached to a\n * message previewed link of the pattern \"support.example.com\".\n *\n * @param chatMessage The chat message object from Google Workspace Add On event.\n * @return Action response depending on the message author.\n */\nGenericJson handleCardClick(JsonNode chatMessage) {\n // Creates the updated card that displays \"You\" for the assignee\n // and that disables the button.\n //\n // A hard-coded card is used in this example. In a real-life scenario,\n // an actual assign action would be performed before building the card.\n Message message = new Message().setCardsV2(List.of(new CardWithId()\n .setCardId(\"attachCard\")\n .setCard(new GoogleAppsCardV1Card()\n .setHeader(new GoogleAppsCardV1CardHeader()\n .setTitle(\"Example Customer Service Case\")\n .setSubtitle(\"Case basics\"))\n .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Case ID\")\n .setText(\"case123\")),\n // The assignee is now \"You\"\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Assignee\")\n .setText(\"You\")),\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Status\")\n .setText(\"Open\")),\n new GoogleAppsCardV1Widget().setDecoratedText(new GoogleAppsCardV1DecoratedText()\n .setTopLabel(\"Subject\")\n .setText(\"It won't turn on...\")),\n new GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList()\n .setButtons(List.of(\n new GoogleAppsCardV1Button()\n .setText(\"OPEN CASE\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setOpenLink(new GoogleAppsCardV1OpenLink()\n .setUrl(\"https://support.example.com/orders/case123\"))),\n new GoogleAppsCardV1Button()\n .setText(\"RESOLVE CASE\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setOpenLink(new GoogleAppsCardV1OpenLink()\n .setUrl(\"https://support.example.com/orders/case123?resolved=y\"))),\n new GoogleAppsCardV1Button()\n .setText(\"ASSIGN TO ME\")\n // The button is now disabled\n .setDisabled(true)\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setAction(new GoogleAppsCardV1Action().setFunction(FUNCTION_URL)))\n ))\n )\n ))))\n )\n ));\n\n // Use the adequate action response type. It depends on whether the message\n // the preview link card is attached to was created by a human or a Chat app.\n if(\"HUMAN\".equals(chatMessage.at(\"/sender/type\").asText())) {\n return new GenericJson() {{\n put(\"hostAppDataAction\", new GenericJson() {{\n put(\"chatDataAction\", new GenericJson() {{\n put(\"updateInlinePreviewAction\", message);\n }});\n }});\n }};\n } else {\n return new GenericJson() {{\n put(\"hostAppDataAction\", new GenericJson() {{\n put(\"chatDataAction\", new GenericJson() {{\n put(\"updateMessageAction\", message);\n }});\n }});\n }};\n }\n}\n```\n\nExample:\n```text\n/**\n * Assigns and updates the card that's attached to a message with a\n * previewed link of the pattern \"support.example.com\".\n *\n * @param {Object} event The event object from the Google Workspace add-on.\n * @return {Object} Action response depending on the message author.\n */\nfunction assign(event) {\n // Creates the updated card that displays \"You\" for the assignee\n // and that disables the button.\n //\n // A hard-coded card is used in this example. In a real-life scenario,\n // an actual assign action would be performed before building the card.\n const message = { cardsV2: [{\n cardId: 'attachCard',\n card: {\n header: {\n title: 'Example Customer Service Case',\n subtitle: 'Case summary',\n },\n sections: [{ widgets: [\n { decoratedText: { topLabel: 'Case ID', text: 'case123'}},\n // The assignee is now \"You\"\n { decoratedText: { topLabel: 'Assignee', text: 'You'}},\n { decoratedText: { topLabel: 'Status', text: 'Open'}},\n { decoratedText: { topLabel: 'Subject', text: 'It won\\'t turn on...' }},\n { buttonList: { buttons: [{\n text: 'OPEN CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123'\n }},\n }, {\n text: 'RESOLVE CASE',\n onClick: { openLink: {\n url: 'https://support.example.com/orders/case123?resolved=y',\n }},\n }, {\n text: 'ASSIGN TO ME',\n // The button is now disabled\n disabled: true,\n onClick: { action: { function: 'assign'}}\n }]}}\n ]}]\n }\n }]};\n\n // Use the adequate action response type. It depends on whether the message\n // the preview link card is attached to was created by a human or a Chat app.\n if(event.chat.buttonClickedPayload.message.sender.type === 'HUMAN') {\n return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: message }}};\n } else {\n return { hostAppDataAction: { chatDataAction: { updateMessageAction: message }}};\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.549Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":488,"estimatedTokens":4513}}715{"id":"doc-build_google_chat_interfaces_google_workspace_ad-0416eb5e","source":"documentation","title":"Build Google Chat interfaces | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/build","text":"Example:\n```text\nasync function onEvent(req, res) {\n // Trigger asynchronous job that will respond using the Google Chat API.\n ...\n\n // Respond with an empty response to the Google Chat platform.\n return res.send({});\n};\n```\n\nExample:\n```text\ndef on_event(event) -> dict:\n # Trigger asynchronous job that will respond using the Google Chat API.\n ...\n\n # Respond with an empty response to the Google Chat platform.\n return {}\n```\n\nExample:\n```text\npublic String onEvent(JsonNode event) {\n // Trigger asynchronous job that will respond using the Google Chat API.\n ...\n\n // Respond with an empty response to the Google Chat platform.\n return \"{}\";\n}\n```\n\nExample:\n```text\nfunction onEvent(event) {\n // Trigger asynchronous job that will respond using the Google Chat API.\n ...\n\n // Respond with an empty response to the Google Chat platform.\n return null;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.550Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":44,"estimatedTokens":222}}716{"id":"doc-pass_data_between_steps_with_an_output_variable_-e1dd9a84","source":"documentation","title":"Pass data between steps with an output variable | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/output-variables","text":"Example:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Calculator\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"calculatorDemo\",\n \"state\": \"ACTIVE\",\n \"name\": \"Calculate\",\n \"description\": \"Asks the user for two values and a math operation, then performs the math operation on the values and outputs the result.\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"value1\",\n \"description\": \"value1\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n },\n {\n \"id\": \"value2\",\n \"description\": \"value2\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n },\n {\n \"id\": \"operation\",\n \"description\": \"operation\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"outputs\": [\n {\n \"id\": \"result\",\n \"description\": \"Calculated result\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfigCalculate\",\n \"onExecuteFunction\": \"onExecuteCalculate\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Executes the calculation step based on the inputs from a flow event.\n *\n * This function retrieves input values and the operation from the flow event,\n * performs the calculation, and returns the result as an output variable.\n * The function logs the event for debugging purposes.\n */\nfunction onExecuteCalculate(event) {\n console.log(\"output: \" + JSON.stringify(event));\n var calculatedValue = 0;\n var value1 = event.workflow.actionInvocation.inputs[\"value1\"];\n var value2 = event.workflow.actionInvocation.inputs[\"value2\"];\n var operation = event.workflow.actionInvocation.inputs[\"operation\"].stringValues[0];\n\n if (operation == \"+\") {\n calculatedValue = value1 + value2;\n } else if (operation == \"-\") {\n calculatedValue = value1 - value2;\n } else if (operation == \"x\") {\n calculatedValue = value1 * value2;\n } else if (operation == \"/\") {\n calculatedValue = value1 / value2;\n }\n const variableData = AddOnsResponseService.newVariableData()\n .addIntegerValue(calculatedValue);\n \n const workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()\n .addVariableData(\"result\", variableData);\n\n const hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.551Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":107,"estimatedTokens":818}}717{"id":"doc-validate_an_input_variable_google_workspace_add_-7f97855c","source":"documentation","title":"Validate an input variable | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/validate-inputs","text":"Example:\n```text\nconst validation = CardService.newValidation().setCharacterLimit('10').setInputType(\n CardService.InputType.TEXT);\n```\n\nExample:\n```text\nfunction onConfig() {\n // Create a Card\n let cardBuilder = CardService.newCardBuilder();\n\n const textInput_1 = CardService.newTextInput()\n .setTitle(\"Input field 1\")\n .setFieldName(\"value1\"); // FieldName's value must match a corresponding ID defined in the inputs[] array in the manifest file.\n const textInput_2 = CardService.newTextInput()\n .setTitle(\"Input field 2\")\n .setFieldName(\"value2\"); // FieldName's value must match a corresponding ID defined in the inputs[] array in the manifest file.\n let sections = CardService.newCardSection()\n .setHeader(\"Enter same values for the two input fields\")\n .addWidget(textInput_1)\n .addWidget(textInput_2);\n\n // CEL Validation\n\n // Define Conditions\n const condition_success = CardService.newCondition()\n .setActionRuleId(\"CEL_TEXTINPUT_SUCCESS_RULE_ID\")\n .setExpressionDataCondition(\n CardService.newExpressionDataCondition()\n .setConditionType(\n CardService.ExpressionDataConditionType.EXPRESSION_EVALUATION_SUCCESS));\n const condition_fail = CardService.newCondition()\n .setActionRuleId(\"CEL_TEXTINPUT_FAILURE_RULE_ID\")\n .setExpressionDataCondition(\n CardService.newExpressionDataCondition()\n .setConditionType(\n CardService.ExpressionDataConditionType.EXPRESSION_EVALUATION_FAILURE));\n\n // Define Card-side EventAction\n const expressionDataAction = CardService.newExpressionDataAction()\n .setActionType(\n CardService.ExpressionDataActionType.START_EXPRESSION_EVALUATION);\n // Define Triggers for each Condition respectively\n const trigger_success = CardService.newTrigger()\n .setActionRuleId(\"CEL_TEXTINPUT_SUCCESS_RULE_ID\");\n const trigger_failure = CardService.newTrigger()\n .setActionRuleId(\"CEL_TEXTINPUT_FAILURE_RULE_ID\");\n\n const eventAction = CardService.newEventAction()\n .setActionRuleId(\"CEL_TEXTINPUT_EVALUATION_RULE_ID\")\n .setExpressionDataAction(expressionDataAction)\n .addPostEventTrigger(trigger_success)\n .addPostEventTrigger(trigger_failure);\n\n // Define ExpressionData for the current Card\n const expressionData = CardService.newExpressionData()\n .setId(\"expData_id\")\n .setExpression(\"value1 == value2\") // CEL expression\n .addCondition(condition_success)\n .addCondition(condition_fail)\n .addEventAction(eventAction);\n card = card.addExpressionData(expressionData);\n\n // Create Widget-side EventActions and a widget to display error message\n const widgetEventActionFail = CardService.newEventAction()\n .setActionRuleId(\"CEL_TEXTINPUT_FAILURE_RULE_ID\")\n .setCommonWidgetAction(\n CardService.newCommonWidgetAction()\n .setUpdateVisibilityAction(\n CardService.newUpdateVisibilityAction()\n .setVisibility(\n CardService.Visibility.VISIBLE)));\n const widgetEventActionSuccess = CardService.newEventAction()\n .setActionRuleId(\"CEL_TEXTINPUT_SUCCESS_RULE_ID\")\n .setCommonWidgetAction(\n CardService.newCommonWidgetAction()\n .setUpdateVisibilityAction(\n CardService.newUpdateVisibilityAction()\n .setVisibility(\n CardService.Visibility.HIDDEN)));\n const errorWidget = CardService.newTextParagraph()\n .setText(\"<font color=\\\"#FF0000\\\"><b>Error:</b> Please enter the same values for both input fields.</font>\")\n .setVisibility(CardService.Visibility.HIDDEN) // Initially hidden\n .addEventAction(widgetEventActionFail)\n .addEventAction(widgetEventActionSuccess);\n sections = sections.addWidget(errorWidget);\n\n card = card.addSection(sections);\n // Build and return the Card\n return card.build();\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"CEL validation example\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"cel_validation_demo\",\n \"state\": \"ACTIVE\",\n \"name\": \"CEL Demo\",\n \"description\": \"Demonstrates CEL Validation\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"value1\",\n \"description\": \"The first number\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n },\n {\n \"id\": \"value2\",\n \"description\": \"The second number\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfig\",\n \"onExecuteFunction\": \"onExecute\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Server-side validation example\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"server_validation_demo\",\n \"state\": \"ACTIVE\",\n \"name\": \"Email address validation\",\n \"description\": \"Asks the user for an email address\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"email\",\n \"description\": \"email address\",\n \"cardinality\": \"SINGLE\",\n \"required\": true,\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfig\",\n \"onExecuteFunction\": \"onExecute\",\n \"onSaveFunction\": \"onSave\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n// A helper method to push a card interface\nfunction pushCard(card) {\n const navigation = AddOnsResponseService.newNavigation()\n .pushCard(card);\n\n const action = AddOnsResponseService.newAction()\n .addNavigation(navigation);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setAction(action)\n .build();\n}\n\nfunction onConfig() {\n const emailInput = CardService.newTextInput()\n .setFieldName(\"email\")\n .setTitle(\"User e-mail\")\n .setId(\"email\");\n\n const saveButton = CardService.newTextButton()\n .setText(\"Save!\")\n .setOnClickAction(\n CardService.newAction()\n .setFunctionName('onSave')\n )\n\n const sections = CardService.newCardSection()\n .setHeader(\"Server-side validation\")\n .setId(\"section_1\")\n .addWidget(emailInput)\n .addWidget(saveButton);\n\n let card = CardService.newCardBuilder()\n .addSection(sections)\n .build();\n\n return pushCard(card);\n}\n\nfunction onExecute(event) {\n}\n\n/**\n* Validates user input asynchronously when the user\n* navigates away from a step's configuration card.\n*/\nfunction onSave(event) {\n console.log(JSON.stringify(event, null, 2));\n\n // \"email\" matches the input ID specified in the manifest file.\n var email = event.formInputs[\"email\"][0];\n\n console.log(JSON.stringify(email, null, 2));\n\n // Validate that the email address contains an \"@\" sign:\n if (email.includes(\"@\")) {\n // If successfully validated, save and proceed.\n const hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(\n AddOnsResponseService.newSaveWorkflowAction()\n );\n\n const textDeletion = AddOnsResponseService.newRemoveWidget()\n .setWidgetId(\"errorMessage\");\n\n const modifyAction = AddOnsResponseService.newAction()\n .addModifyCard(\n AddOnsResponseService.newModifyCard()\n .setRemoveWidget(textDeletion)\n );\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .setAction(modifyAction)\n .build();\n\n } else {\n // If the input is invalid, return a card with an error message\n\n const textParagraph = CardService.newTextParagraph()\n .setId(\"errorMessage\")\n .setMaxLines(1)\n .setText(\"<font color=\\\"#FF0000\\\"><b>Error:</b> Email addresses must include the '@' sign.</font>\");\n\n const emailInput = CardService.newTextInput()\n .setFieldName(\"email\")\n .setTitle(\"User e-mail\")\n .setId(\"email\");\n\n const saveButton = CardService.newTextButton()\n .setText(\"Save!\")\n .setOnClickAction(\n CardService.newAction().setFunctionName('onSave')\n )\n\n const sections = CardService.newCardSection()\n .setHeader(\"Server-side validation\")\n .setId(\"section_1\")\n .addWidget(emailInput)\n .addWidget(textParagraph) //Insert the error message\n .addWidget(saveButton);\n\n let card = CardService.newCardBuilder()\n .addSection(sections)\n .build();\n\n const navigation = AddOnsResponseService.newNavigation()\n .pushCard(card);\n\n const action = AddOnsResponseService.newAction()\n .addNavigation(navigation);\n\n const hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(\n AddOnsResponseService.newWorkflowValidationErrorAction()\n .setSeverity(AddOnsResponseService.ValidationErrorSeverity.CRITICAL)\n );\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .setAction(action)\n .build();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.552Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":313,"estimatedTokens":2423}}718{"id":"doc-collect_and_process_information_from_google_chat-f29e5c0b","source":"documentation","title":"Collect and process information from Google Chat users | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/collect-information","text":"Example:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Section Header\",\n \"widgets\": [\n {\n \"selectionInput\": {\n \"name\": \"contacts\",\n \"type\": \"DROPDOWN\",\n \"label\": \"Select contact from organization\",\n \"data_source_configs\": [\n {\n \"platformDataSource\": {\n \"commonDataSource\": \"USER\"\n },\n \"min_characters_trigger\": 1\n }\n ]\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Section Header\",\n \"widgets\": [\n {\n \"selectionInput\": {\n \"name\": \"crm_leads\",\n \"type\": \"DROPDOWN\",\n \"label\": \"Select CRM Lead\",\n \"data_source_configs\": [\n {\n \"remoteDataSource\": {\n \"function\": \"getCrmLeads\"\n },\n \"min_characters_trigger\": 2\n }\n ],\n \"items\": [\n {\n \"text\": \"Suggested Lead 1\",\n \"value\": \"lead-1\"\n }\n ]\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"selectionInput\": {\n \"name\": \"contacts\",\n \"type\": \"MULTI_SELECT\",\n \"label\": \"Selected contacts\",\n \"multiSelectMaxSelectedItems\": 5,\n \"multiSelectMinQueryLength\": 1,\n \"platformDataSource\": {\n \"commonDataSource\": \"USER\"\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"selectionInput\": {\n \"name\": \"spaces\",\n \"type\": \"MULTI_SELECT\",\n \"label\": \"Selected contacts\",\n \"multiSelectMaxSelectedItems\": 3,\n \"multiSelectMinQueryLength\": 1,\n \"platformDataSource\": {\n \"hostAppDataSource\": {\n \"chatDataSource\": {\n \"spaceDataSource\": {\n \"defaultToCurrentSpace\": true\n }\n }\n }\n }\n }\n}\n```\n\nExample:\n```text\nselectionInput: {\n name: \"contacts\",\n type: \"MULTI_SELECT\",\n label: \"Selected contacts\",\n multiSelectMaxSelectedItems: 3,\n multiSelectMinQueryLength: 1,\n externalDataSource: { function: FUNCTION_URL },\n // Suggested items loaded by default.\n // The list is static here but it could be dynamic.\n items: [getSuggestedContact(\"3\")]\n}\n```\n\nExample:\n```text\n'selectionInput': {\n 'name': \"contacts\",\n 'type': \"MULTI_SELECT\",\n 'label': \"Selected contacts\",\n 'multiSelectMaxSelectedItems': 3,\n 'multiSelectMinQueryLength': 1,\n 'externalDataSource': { 'function': FUNCTION_URL },\n # Suggested items loaded by default.\n # The list is static here but it could be dynamic.\n 'items': [get_suggested_contact(\"3\")]\n}\n```\n\nExample:\n```text\n.setSelectionInput(new GoogleAppsCardV1SelectionInput()\n .setName(\"contacts\")\n .setType(\"MULTI_SELECT\")\n .setLabel(\"Selected contacts\")\n .setMultiSelectMaxSelectedItems(3)\n .setMultiSelectMinQueryLength(1)\n .setExternalDataSource(new GoogleAppsCardV1Action().setFunction(FUNCTION_URL))\n // Suggested items loaded by default.\n // The list is static here but it could be dynamic.\n .setItems(List.of(getSuggestedContact(\"3\")))))))))));\n```\n\nExample:\n```text\nselectionInput: {\n name: \"contacts\",\n type: \"MULTI_SELECT\",\n label: \"Selected contacts\",\n multiSelectMaxSelectedItems: 3,\n multiSelectMinQueryLength: 1,\n externalDataSource: { function: \"queryContacts\" },\n // Suggested items loaded by default.\n // The list is static here but it could be dynamic.\n items: [getSuggestedContact(\"3\")]\n}\n```\n\nExample:\n```text\n{\n \"commonEventObject\": { \"formInputs\": {\n \"contactName\": { \"stringInputs\": {\n \"value\": [\"Kai 0\"]\n }},\n \"contactBirthdate\": { \"dateInput\": {\n \"msSinceEpoch\": 1000425600000\n }},\n \"contactType\": { \"stringInputs\": {\n \"value\": [\"Personal\"]\n }}\n }}\n}\n```\n\nExample:\n```text\n/**\n * Web app that responds to events sent from a Google Chat space.\n *\n * @param {Object} req Request sent from Google Chat space\n * @param {Object} res Response to send back\n */\napp.post('/', async (req, res) => {\n // Stores the Google Chat event\n const chatEvent = req.body.chat;\n\n // Handle user interaction with multiselect.\n if(chatEvent.widgetUpdatedPayload) {\n return res.json(queryContacts(req.body));\n }\n\n // Replies with a card that contains the multiselect menu.\n return res.json({ hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n cardsV2: [{\n cardId: \"contactSelector\",\n card: { sections:[{ widgets: [{\n selectionInput: {\n name: \"contacts\",\n type: \"MULTI_SELECT\",\n label: \"Selected contacts\",\n multiSelectMaxSelectedItems: 3,\n multiSelectMinQueryLength: 1,\n externalDataSource: { function: FUNCTION_URL },\n // Suggested items loaded by default.\n // The list is static here but it could be dynamic.\n items: [getSuggestedContact(\"3\")]\n }\n }]}]}\n }]\n }}}}});\n});\n\n/**\n * Get contact suggestions based on text typed by users.\n *\n * @param {Object} event the event object that contains the user's query\n * @return {Object} suggestions\n */\nfunction queryContacts(event) {\n const query = event.commonEventObject.parameters[\"autocomplete_widget_query\"];\n return { action: { modifyOperations: [{ updateWidget: { selectionInputWidgetSuggestions: { suggestions: [\n // The list is static here but it could be dynamic.\n getSuggestedContact(\"1\"), getSuggestedContact(\"2\"), getSuggestedContact(\"3\"), getSuggestedContact(\"4\"), getSuggestedContact(\"5\")\n // Only return items based on the query from the user.\n ].filter(e => !query || e.text.includes(query)) }}}]}};\n}\n\n/**\n * Generate a suggested contact given an ID.\n *\n * @param {String} id The ID of the contact to return.\n * @return {Object} The contact formatted as a selection item in the menu.\n */\nfunction getSuggestedContact(id) {\n return {\n value: id,\n startIconUri: \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n text: \"Contact \" + id\n };\n}\n```\n\nExample:\n```text\n@app.route('/', methods=['POST'])\ndef post() -> Mapping[str, Any]:\n \"\"\"Handle requests from Google Chat\n\n Returns:\n Mapping[str, Any]: The response\n \"\"\"\n # Stores the Google Chat event\n chatEvent = request.get_json().get('chat')\n\n # Handle user interaction with multiselect.\n if chatEvent.get('widgetUpdatedPayload') is not None:\n return json.jsonify(query_contacts(request.get_json()))\n\n # Replies with a card that contains the multiselect menu.\n return json.jsonify({ 'hostAppDataAction': { 'chatDataAction': { 'createMessageAction': {\n 'message': { 'cardsV2': [{\n 'cardId': \"contactSelector\",\n 'card': { 'sections':[{ 'widgets': [{\n 'selectionInput': {\n 'name': \"contacts\",\n 'type': \"MULTI_SELECT\",\n 'label': \"Selected contacts\",\n 'multiSelectMaxSelectedItems': 3,\n 'multiSelectMinQueryLength': 1,\n 'externalDataSource': { 'function': FUNCTION_URL },\n # Suggested items loaded by default.\n # The list is static here but it could be dynamic.\n 'items': [get_suggested_contact(\"3\")]\n }\n }]}]}\n }]}\n }}}})\n\n\ndef query_contacts(event: dict) -> dict:\n \"\"\"Get contact suggestions based on text typed by users.\n\n Args:\n event (Mapping[str, Any]): The event object that contains the user's query\n\n Returns:\n Mapping[str, Any]: The response with contact suggestions.\n \"\"\"\n query = event.get(\"commonEventObject\").get(\"parameters\").get(\"autocomplete_widget_query\")\n return { 'action': { 'modifyOperations': [{ 'updateWidget': { 'selectionInputWidgetSuggestions': { 'suggestions': list(\n filter(lambda e: query is None or query in e[\"text\"], [\n # The list is static here but it could be dynamic.\n get_suggested_contact(\"1\"), get_suggested_contact(\"2\"), get_suggested_contact(\"3\"), get_suggested_contact(\"4\"), get_suggested_contact(\"5\")\n # Only return items based on the query from the user\n ])\n )}}}]}}\n\n\ndef get_suggested_contact(id: str) -> dict:\n \"\"\"Generate a suggested contact given an ID.\n\n Args:\n id (str): The ID of the contact to return.\n\n Returns:\n Mapping[str, Any]: The contact formatted as a selection item in the menu.\n \"\"\"\n return {\n 'value': id,\n 'startIconUri': \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n 'text': \"Contact \" + id\n }\n```\n\nExample:\n```text\n@SpringBootApplication\n@RestController\n// Web app that responds to events sent from a Google Chat space.\npublic class App {\n private static final String FUNCTION_URL = \"your-function-url\";\n\n public static void main(String[] args) {\n SpringApplication.run(App.class, args);\n }\n\n /**\n * Handle requests from Google Chat\n * \n * @param event the event object sent by Google Chat\n * @return The response to be sent back to Google Chat\n */\n @PostMapping(\"/\")\n @ResponseBody\n public GenericJson onEvent(@RequestBody JsonNode event) throws Exception {\n // Stores the Google Chat event\n JsonNode chatEvent = event.at(\"/chat\");\n\n // Handle user interaction with multiselect.\n if (!chatEvent.at(\"/widgetUpdatedPayload\").isEmpty()) {\n return queryContacts(event);\n }\n\n // Replies with a card that contains the multiselect menu.\n Message message = new Message().setCardsV2(List.of(new CardWithId()\n .setCardId(\"contactSelector\")\n .setCard(new GoogleAppsCardV1Card()\n .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(new GoogleAppsCardV1Widget()\n .setSelectionInput(new GoogleAppsCardV1SelectionInput()\n .setName(\"contacts\")\n .setType(\"MULTI_SELECT\")\n .setLabel(\"Selected contacts\")\n .setMultiSelectMaxSelectedItems(3)\n .setMultiSelectMinQueryLength(1)\n .setExternalDataSource(new GoogleAppsCardV1Action().setFunction(FUNCTION_URL))\n // Suggested items loaded by default.\n // The list is static here but it could be dynamic.\n .setItems(List.of(getSuggestedContact(\"3\")))))))))));\n\n return new GenericJson() {{\n put(\"hostAppDataAction\", new GenericJson() {{\n put(\"chatDataAction\", new GenericJson() {{\n put(\"createMessageAction\", new GenericJson() {{\n put(\"message\", message);\n }});\n }});\n }});\n }};\n }\n\n /**\n * Get contact suggestions based on text typed by users.\n *\n * @param event the event object that contains the user's query.\n * @return The response with contact suggestions.\n */\n GenericJson queryContacts(JsonNode event) throws Exception {\n String query = event.at(\"/commonEventObject/parameters/autocomplete_widget_query\").asText();\n List<GoogleAppsCardV1SelectionItem> suggestions = List.of(\n // The list is static here but it could be dynamic.\n getSuggestedContact(\"1\"), getSuggestedContact(\"2\"), getSuggestedContact(\"3\"), getSuggestedContact(\"4\"), getSuggestedContact(\"5\")\n // Only return items based on the query from the user\n ).stream().filter(e -> query == null || e.getText().indexOf(query) > -1).toList();\n\n return new GenericJson() {{\n put(\"action\", new GenericJson() {{\n put(\"modifyOperations\", List.of(new GenericJson() {{\n put(\"updateWidget\", new GenericJson() {{\n put(\"selectionInputWidgetSuggestions\", new GenericJson() {{\n put(\"suggestions\", suggestions);\n }});\n }});\n }}));\n }});\n }};\n }\n\n /**\n * Generate a suggested contact given an ID.\n * \n * @param id The ID of the contact to return.\n * @return The contact formatted as a selection item in the menu.\n */\n GoogleAppsCardV1SelectionItem getSuggestedContact(String id) {\n return new GoogleAppsCardV1SelectionItem()\n .setValue(id)\n .setStartIconUri(\"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\")\n .setText(\"Contact \" + id);\n }\n}\n```\n\nExample:\n```text\n/**\n* Responds to a Message trigger in Google Chat.\n*\n* @param {Object} event the event object from Google Chat\n* @return {Object} Response from the Chat app.\n*/\nfunction onMessage(event) {\n // Replies with a card that contains the multiselect menu.\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n cardsV2: [{\n cardId: \"contactSelector\",\n card: { sections:[{ widgets: [{\n selectionInput: {\n name: \"contacts\",\n type: \"MULTI_SELECT\",\n label: \"Selected contacts\",\n multiSelectMaxSelectedItems: 3,\n multiSelectMinQueryLength: 1,\n externalDataSource: { function: \"queryContacts\" },\n // Suggested items loaded by default.\n // The list is static here but it could be dynamic.\n items: [getSuggestedContact(\"3\")]\n }\n }]}]}\n }]\n }}}}};\n}\n\n/**\n* Get contact suggestions based on text typed by users.\n*\n* @param {Object} event the event object that contains the user's query\n* @return {Object} suggestions\n*/\nfunction queryContacts(event) {\n const query = event.commonEventObject.parameters[\"autocomplete_widget_query\"];\n return { action: { modifyOperations: [{ updateWidget: { selectionInputWidgetSuggestions: { suggestions: [\n // The list is static here but it could be dynamic.\n getSuggestedContact(\"1\"), getSuggestedContact(\"2\"), getSuggestedContact(\"3\"), getSuggestedContact(\"4\"), getSuggestedContact(\"5\")\n // Only return items based on the query from the user.\n ].filter(e => !query || e.text.includes(query)) }}}]}};\n}\n\n/**\n* Generate a suggested contact given an ID.\n*\n* @param {String} id The ID of the contact to return.\n* @return {Object} The contact formatted as a selection item in the menu.\n*/\nfunction getSuggestedContact(id) {\n return {\n value: id,\n startIconUri: \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n text: \"Contact \" + id\n };\n}\n```\n\nExample:\n```text\n{ buttonList: { buttons: [{\n text: \"SUBMIT\",\n onClick: { action: {\n function: FUNCTION_URL,\n parameters: [\n { key: \"actionName\", value: \"submitDialog\" },\n // Pass input values as parameters for last dialog step (submission)\n { key: \"contactName\", value: name },\n { key: \"contactBirthdate\", value: birthdate },\n { key: \"contactType\", value: type }\n ]\n }}\n}]}}\n```\n\nExample:\n```text\n{ 'buttonList': { 'buttons': [{\n 'text': \"SUBMIT\",\n 'onClick': { 'action': {\n 'function': FUNCTION_URL,\n 'parameters': [\n { 'key': \"actionName\", 'value': \"submitDialog\" },\n # Pass input values as parameters for last dialog step (submission)\n { 'key': \"contactName\", 'value': name },\n { 'key': \"contactBirthdate\", 'value': birthdate },\n { 'key': \"contactType\", 'value': type }\n ]\n }}\n}]}}\n```\n\nExample:\n```text\nnew GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(\n new GoogleAppsCardV1Button()\n .setText(\"SUBMIT\")\n .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()\n .setFunction(FUNCTION_URL)\n .setParameters(List.of(\n new GoogleAppsCardV1ActionParameter().setKey(\"actionName\").setValue(\"submitDialog\"),\n // Pass input values as parameters for last dialog step (submission)\n new GoogleAppsCardV1ActionParameter().setKey(\"contactName\").setValue(name),\n new GoogleAppsCardV1ActionParameter().setKey(\"contactBirthdate\").setValue(birthdate),\n new GoogleAppsCardV1ActionParameter().setKey(\"contactType\").setValue(type))))))))))));\n```\n\nExample:\n```text\n{ buttonList: { buttons: [{\n text: \"SUBMIT\",\n onClick: { action: {\n function: \"submitDialog\",\n // Pass input values as parameters for last dialog step (submission)\n parameters: [\n { key: \"contactName\", value: name },\n { key: \"contactBirthdate\", value: birthdate },\n { key: \"contactType\", value: type }\n ]\n }}\n}]}}\n```\n\nExample:\n```text\nreturn { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: \"✅ \" + event.commonEventObject.parameters[\"contactName\"] + \" has been added to your contacts.\"\n}}}}};\n```\n\nExample:\n```text\nreturn { 'hostAppDataAction': { 'chatDataAction': { 'createMessageAction': { 'message': {\n 'text': \"✅ \" + event.get('commonEventObject').get('parameters')[\"contactName\"] + \" has been added to your contacts.\"\n}}}}}\n```\n\nExample:\n```text\nreturn new GenericJson() {{\n put(\"hostAppDataAction\", new GenericJson() {{\n put(\"chatDataAction\", new GenericJson() {{\n put(\"createMessageAction\", new GenericJson() {{\n put(\"message\", new Message()\n .setText( \"✅ \" + event.at(\"/commonEventObject/parameters/contactName\").asText() +\n \" has been added to your contacts.\"));\n }});\n }});\n }});\n}};\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.554Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":20,"totalLines":569,"estimatedTokens":4186}}719{"id":"doc-represent_complex_data_with_a_custom_resource_go-b4c387ce","source":"documentation","title":"Represent complex data with a custom resource | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/custom-resources","text":"Example:\n```text\n{\n \"workflowResourceDefinitions\": [\n {\n \"id\": \"resource_id\",\n \"name\": \"Custom Resource\",\n \"fields\": [\n {\n \"selector\": \"field_1\",\n \"name\": \"Field 1\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n },\n {\n \"selector\": \"field_2\",\n \"name\": \"Field 2\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"providerFunction\": \"onMessageResourceFunction\"\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"outputs\": [\n {\n \"id\": \"resource_data\",\n \"description\": \"Resource Data\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"resourceType\": {\n \"workflowResourceDefinitionId\": \"resource_id\"\n }\n }\n }\n ],\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Custom Resource (as reference)\",\n \"logoUrl\": \"https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"getResourceDataReference\",\n \"state\": \"ACTIVE\",\n \"name\": \"Custom Resource (as reference)\",\n \"description\": \"Output a custom resource as a reference\",\n \"workflowAction\": {\n \"outputs\": [\n {\n \"id\": \"resource_data\",\n \"description\": \"Resource Data\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"resourceType\": {\n \"workflowResourceDefinitionId\": \"resource_id\"\n }\n }\n }\n ],\n \"onConfigFunction\": \"onConfigResourceFunction\",\n \"onExecuteFunction\": \"onExecuteResourceFunction\"\n }\n }\n ],\n \"workflowResourceDefinitions\": [\n {\n \"id\": \"resource_id\",\n \"name\": \"Custom Resource\",\n \"fields\": [\n {\n \"selector\": \"field_1\",\n \"name\": \"Field 1\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n },\n {\n \"selector\": \"field_2\",\n \"name\": \"Field 2\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"providerFunction\": \"onMessageResourceFunction\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\nfunction onMessageResourceFunction(e) {\n console.log(\"Payload in onMessageResourceFunction: \" + JSON.stringify(e));\n\n var resource_id = e.workflow.resourceRetrieval.resourceReference.resourceId;\n let fieldValue_1;\n let fieldValue_2;\n\n // Using an if condition to mock a database call.\n if (resource_id == \"sample_resource_reference_id\") {\n fieldValue_1 = AddOnsResponseService.newVariableData()\n .addStringValue(\"value1\");\n fieldValue_2 = AddOnsResponseService.newVariableData()\n .addStringValue(\"value2\");\n } else {\n fieldValue_1 = AddOnsResponseService.newVariableData()\n .addStringValue(\"field_1 value not found\");\n fieldValue_2 = AddOnsResponseService.newVariableData()\n .addStringValue(\"field_2 value not found\");\n }\n\n let resourceData = AddOnsResponseService.newResourceData()\n .addVariableData(\"field_1\", fieldValue_1)\n .addVariableData(\"field_2\", fieldValue_2)\n\n let workflowAction = AddOnsResponseService.newResourceRetrievedAction()\n .setResourceData(resourceData)\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n}\n```\n\nExample:\n```text\nfunction onExecuteResourceFunction(e) {\n console.log(\"Payload in onExecuteResourceFunction: \" + JSON.stringify(e));\n\n let outputVariables = AddOnsResponseService.newVariableData()\n .addResourceReference(\"sample_resource_reference_id\");\n\n let workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()\n .addVariableData(\"resource_data\", outputVariables);\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n}\n```\n\nExample:\n```text\nfunction onConfigResourceFunction() {\n let section = CardService.newCardSection()\n .addWidget(\n CardService.newTextParagraph()\n .setText(\"This is the Custom Resource Demo card\")\n );\n\n const card = CardService.newCardBuilder()\n .addSection(section)\n .build();\n\n return card;\n}\n\nfunction onMessageResourceFunction(e) {\n console.log(\"Payload in onMessageResourceFunction: \" + JSON.stringify(e));\n\n var resource_id = e.workflow.resourceRetrieval.resourceReference.resourceId;\n let fieldValue_1;\n let fieldValue_2;\n\n // Using an if condition to mock a database call.\n if (resource_id == \"sample_resource_reference_id\") {\n fieldValue_1 = AddOnsResponseService.newVariableData()\n .addStringValue(\"value1\");\n fieldValue_2 = AddOnsResponseService.newVariableData()\n .addStringValue(\"value2\");\n } else {\n fieldValue_1 = AddOnsResponseService.newVariableData()\n .addStringValue(\"field_1 value not found\");\n fieldValue_2 = AddOnsResponseService.newVariableData()\n .addStringValue(\"field_2 value not found\");\n }\n\n let resourceData = AddOnsResponseService.newResourceData()\n .addVariableData(\"field_1\", fieldValue_1)\n .addVariableData(\"field_2\", fieldValue_2)\n\n let workflowAction = AddOnsResponseService.newResourceRetrievedAction()\n .setResourceData(resourceData)\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n}\n\nfunction onExecuteResourceFunction(e) {\n console.log(\"Payload in onExecuteResourceFunction: \" + JSON.stringify(e));\n\n let outputVariables = AddOnsResponseService.newVariableData()\n .addResourceReference(\"sample_resource_reference_id\");\n\n let workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()\n .addVariableData(\"resource_data\", outputVariables);\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.555Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":240,"estimatedTokens":1645}}720{"id":"doc-build_interactive_dialogs_google_workspace_add_o-338d414a","source":"documentation","title":"Build interactive dialogs | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/dialogs","text":"Example:\n```text\n{ buttonList: { buttons: [{\n text: \"ADD CONTACT\",\n onClick: { action: {\n function: FUNCTION_URL,\n interaction: \"OPEN_DIALOG\",\n parameters: [\n { key: \"actionName\", value: \"openInitialDialog\" }\n ]\n }}\n}]}}\n```\n\nExample:\n```text\n{ 'buttonList': { 'buttons': [{\n 'text': \"ADD CONTACT\",\n 'onClick': { 'action': {\n 'function': FUNCTION_URL,\n 'interaction': \"OPEN_DIALOG\",\n 'parameters': [\n { 'key': \"actionName\", 'value': \"openInitialDialog\" }\n ]\n }}\n}]}}\n```\n\nExample:\n```text\n.setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()\n .setText(\"ADD CONTACT\")\n .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()\n .setFunction(FUNCTION_URL)\n .setInteraction(\"OPEN_DIALOG\")\n .setParameters(List.of(\n new GoogleAppsCardV1ActionParameter().setKey(\"actionName\").setValue(\"openInitialDialog\"))))))))));\n```\n\nExample:\n```text\n{ buttonList: { buttons: [{\n text: \"ADD CONTACT\",\n onClick: { action: {\n function: \"openInitialDialog\",\n interaction: \"OPEN_DIALOG\"\n }}\n}]}}\n```\n\nExample:\n```text\n/**\n * Opens the initial step of the dialog that lets users add contact details.\n *\n * @return {Object} open the dialog.\n */\nfunction openInitialDialog() {\n return { action: { navigations: [{ pushCard: { sections: [{ widgets: [\n { textInput: {\n name: \"contactName\",\n label: \"First and last name\",\n type: \"SINGLE_LINE\"\n }},\n { dateTimePicker: {\n name: \"contactBirthdate\",\n label: \"Birthdate\",\n type: \"DATE_ONLY\"\n }},\n { selectionInput: {\n name: \"contactType\",\n label: \"Contact type\",\n type: \"RADIO_BUTTON\",\n items: [\n { text: \"Work\", value: \"Work\", selected: false },\n { text: \"Personal\", value: \"Personal\", selected: false }\n ]\n }},\n { buttonList: { buttons: [{\n text: \"NEXT\",\n onClick: { action: {\n function: FUNCTION_URL,\n parameters: [\n { key: \"actionName\", value: \"openConfirmationDialog\" }\n ]\n }}\n }]}}\n ]}]}}]}};\n}\n```\n\nExample:\n```text\ndef open_initial_dialog() -> Mapping[str, Any]:\n \"\"\"Opens the initial step of the dialog that lets users add contact details.\n\n Returns:\n Mapping[str, Any]: open the dialog.\n \"\"\"\n return { 'action': { 'navigations': [{ 'pushCard': { 'sections': [{ 'widgets': [\n { 'textInput': {\n 'name': \"contactName\",\n 'label': \"First and last name\",\n 'type': \"SINGLE_LINE\"\n }},\n { 'dateTimePicker': {\n 'name': \"contactBirthdate\",\n 'label': \"Birthdate\",\n 'type': \"DATE_ONLY\"\n }},\n { 'selectionInput': {\n 'name': \"contactType\",\n 'label': \"Contact type\",\n 'type': \"RADIO_BUTTON\",\n 'items': [\n { 'text': \"Work\", 'value': \"Work\", 'selected': False },\n { 'text': \"Personal\", 'value': \"Personal\", 'selected': False }\n ]\n }},\n { 'buttonList': { 'buttons': [{\n 'text': \"NEXT\",\n 'onClick': { 'action': {\n 'function': FUNCTION_URL,\n 'parameters': [\n { 'key': \"actionName\", 'value': \"openConfirmationDialog\" }\n ]\n }}\n }]}}\n ]}]}}]}}\n```\n\nExample:\n```text\n/**\n * Opens the initial step of the dialog that lets users add contact details.\n *\n * @return {Object} open the dialog.\n */\nGenericJson openInitialDialog() {\n GoogleAppsCardV1Card cardV2 = new GoogleAppsCardV1Card()\n .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(\n new GoogleAppsCardV1Widget().setTextInput(new GoogleAppsCardV1TextInput()\n .setName(\"contactName\")\n .setLabel(\"First and last name\")\n .setType(\"SINGLE_LINE\")),\n new GoogleAppsCardV1Widget().setDateTimePicker(new GoogleAppsCardV1DateTimePicker()\n .setName(\"contactBirthdate\")\n .setLabel(\"Birthdate\")\n .setType(\"DATE_ONLY\")),\n new GoogleAppsCardV1Widget().setSelectionInput(new GoogleAppsCardV1SelectionInput()\n .setName(\"contactType\")\n .setLabel(\"Contact type\")\n .setType(\"RADIO_BUTTON\")\n .setItems(List.of(\n new GoogleAppsCardV1SelectionItem()\n .setText(\"Work\")\n .setValue(\"Work\")\n .setSelected(false),\n new GoogleAppsCardV1SelectionItem()\n .setText(\"Personal\")\n .setValue(\"Personal\")\n .setSelected(false)))),\n new GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(\n new GoogleAppsCardV1Button()\n .setText(\"NEXT\")\n .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()\n .setFunction(FUNCTION_URL)\n .setParameters(List.of(\n new GoogleAppsCardV1ActionParameter().setKey(\"actionName\").setValue(\"openConfirmationDialog\"))))))))))));\n return new GenericJson() {{\n put(\"action\", new GenericJson() {{\n put(\"navigations\", List.of(new GenericJson() {{\n put(\"pushCard\", cardV2);\n }}));\n }});\n }};\n}\n```\n\nExample:\n```text\n/**\n * Opens the initial step of the dialog that lets users add contact details.\n *\n * @param {Object} event The event object from the Google Workspace add-on.\n * @return {Object} open the dialog.\n */\nfunction openInitialDialog(event) {\n return { action: { navigations: [{ pushCard: { sections: [{ widgets: [\n { textInput: {\n name: \"contactName\",\n label: \"First and last name\",\n type: \"SINGLE_LINE\"\n }},\n { dateTimePicker: {\n name: \"contactBirthdate\",\n label: \"Birthdate\",\n type: \"DATE_ONLY\"\n }},\n { selectionInput: {\n name: \"contactType\",\n label: \"Contact type\",\n type: \"RADIO_BUTTON\",\n items: [\n { text: \"Work\", value: \"Work\", selected: false },\n { text: \"Personal\", value: \"Personal\", selected: false }\n ]\n }},\n { buttonList: { buttons: [{\n text: \"NEXT\",\n onClick: { action: { function : \"openConfirmationDialog\" }}\n }]}}\n ]}]}}]}};\n}\n```\n\nExample:\n```text\n/**\n * Responds to a message in Google Chat.\n *\n * @return {Object} response that handles dialogs.\n */\nfunction handleMessage() {\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: \"To add a contact, use the `ADD CONTACT` button below.\",\n accessoryWidgets: [\n { buttonList: { buttons: [{\n text: \"ADD CONTACT\",\n onClick: { action: {\n function: FUNCTION_URL,\n interaction: \"OPEN_DIALOG\",\n parameters: [\n { key: \"actionName\", value: \"openInitialDialog\" }\n ]\n }}\n }]}}\n ]\n }}}}};\n}\n\n/**\n * Responds to a button clicked in Google Chat.\n *\n * @param {Object} event The event object from the Google Workspace add-on.\n * @return {Object} response depending on the button clicked.\n */\nfunction handleButtonClicked(event) {\n // Initial dialog form page\n if (event.commonEventObject.parameters.actionName === \"openInitialDialog\") {\n return openInitialDialog();\n // Confirmation dialog form page\n } else if (event.commonEventObject.parameters.actionName === \"openConfirmationDialog\") {\n return openConfirmationDialog(event);\n // Submission dialog form page\n } else if (event.commonEventObject.parameters.actionName === \"submitDialog\") {\n return submitDialog(event);\n }\n}\n\n/**\n * Opens the initial step of the dialog that lets users add contact details.\n *\n * @return {Object} open the dialog.\n */\nfunction openInitialDialog() {\n return { action: { navigations: [{ pushCard: { sections: [{ widgets: [\n { textInput: {\n name: \"contactName\",\n label: \"First and last name\",\n type: \"SINGLE_LINE\"\n }},\n { dateTimePicker: {\n name: \"contactBirthdate\",\n label: \"Birthdate\",\n type: \"DATE_ONLY\"\n }},\n { selectionInput: {\n name: \"contactType\",\n label: \"Contact type\",\n type: \"RADIO_BUTTON\",\n items: [\n { text: \"Work\", value: \"Work\", selected: false },\n { text: \"Personal\", value: \"Personal\", selected: false }\n ]\n }},\n { buttonList: { buttons: [{\n text: \"NEXT\",\n onClick: { action: {\n function: FUNCTION_URL,\n parameters: [\n { key: \"actionName\", value: \"openConfirmationDialog\" }\n ]\n }}\n }]}}\n ]}]}}]}};\n}\n\n/**\n * Opens the second step of the dialog that lets users confirm details.\n *\n * @param {Object} event The event object from the Google Workspace add-on.\n * @return {Object} update the dialog.\n */\nfunction openConfirmationDialog(event) {\n // Retrieve the form input values\n const name = event.commonEventObject.formInputs[\"contactName\"].stringInputs.value[0];\n const birthdate = event.commonEventObject.formInputs[\"contactBirthdate\"].dateInput.msSinceEpoch;\n const type = event.commonEventObject.formInputs[\"contactType\"].stringInputs.value[0];\n // Display the input values for confirmation\n return { action: { navigations: [{ pushCard: { sections: [{ widgets: [\n { textParagraph: { text: \"Confirm contact information and submit:\" }},\n { textParagraph: { text: \"<b>Name:</b> \" + name }},\n { textParagraph: { text: \"<b>Birthday:</b> \" + new Date(birthdate) }},\n { textParagraph: { text: \"<b>Type:</b> \" + type }},\n { buttonList: { buttons: [{\n text: \"SUBMIT\",\n onClick: { action: {\n function: FUNCTION_URL,\n parameters: [\n { key: \"actionName\", value: \"submitDialog\" },\n // Pass input values as parameters for last dialog step (submission)\n { key: \"contactName\", value: name },\n { key: \"contactBirthdate\", value: birthdate },\n { key: \"contactType\", value: type }\n ]\n }}\n }]}}\n ]}]}}]}};\n}\n```\n\nExample:\n```text\ndef handle_message() -> Mapping[str, Any]:\n \"\"\"Responds to a message in Google Chat.\n\n Returns:\n Mapping[str, Any]: the response that handles dialogs.\n \"\"\"\n return { 'hostAppDataAction': { 'chatDataAction': { 'createMessageAction': { 'message': {\n 'text': \"To add a contact, use the `ADD CONTACT` button below.\",\n 'accessoryWidgets': [\n { 'buttonList': { 'buttons': [{\n 'text': \"ADD CONTACT\",\n 'onClick': { 'action': {\n 'function': FUNCTION_URL,\n 'interaction': \"OPEN_DIALOG\",\n 'parameters': [\n { 'key': \"actionName\", 'value': \"openInitialDialog\" }\n ]\n }}\n }]}}\n ]\n }}}}}\n\n\ndef handle_button_clicked(event: Mapping[str, Any]) -> Mapping[str, Any]:\n \"\"\"Responds to a button clicked in Google Chat.\n\n Args:\n Mapping[str, Any] event: the event object from the Google Workspace add-on\n\n Returns:\n Mapping[str, Any]: the response depending on the button clicked.\n \"\"\"\n # Initial dialog form page\n if \"openInitialDialog\" == event['commonEventObject']['parameters']['actionName']:\n return open_initial_dialog()\n # Confirmation dialog form page\n elif \"openConfirmationDialog\" == event['commonEventObject']['parameters']['actionName'] :\n return open_confirmation_dialog(event)\n # Submission dialog form page\n elif \"submitDialog\" == event['commonEventObject']['parameters']['actionName']:\n return submit_dialog(event)\n\n\ndef open_initial_dialog() -> Mapping[str, Any]:\n \"\"\"Opens the initial step of the dialog that lets users add contact details.\n\n Returns:\n Mapping[str, Any]: open the dialog.\n \"\"\"\n return { 'action': { 'navigations': [{ 'pushCard': { 'sections': [{ 'widgets': [\n { 'textInput': {\n 'name': \"contactName\",\n 'label': \"First and last name\",\n 'type': \"SINGLE_LINE\"\n }},\n { 'dateTimePicker': {\n 'name': \"contactBirthdate\",\n 'label': \"Birthdate\",\n 'type': \"DATE_ONLY\"\n }},\n { 'selectionInput': {\n 'name': \"contactType\",\n 'label': \"Contact type\",\n 'type': \"RADIO_BUTTON\",\n 'items': [\n { 'text': \"Work\", 'value': \"Work\", 'selected': False },\n { 'text': \"Personal\", 'value': \"Personal\", 'selected': False }\n ]\n }},\n { 'buttonList': { 'buttons': [{\n 'text': \"NEXT\",\n 'onClick': { 'action': {\n 'function': FUNCTION_URL,\n 'parameters': [\n { 'key': \"actionName\", 'value': \"openConfirmationDialog\" }\n ]\n }}\n }]}}\n ]}]}}]}}\n\n\ndef open_confirmation_dialog(event: Mapping[str, Any]) -> Mapping[str, Any]:\n \"\"\"Opens the second step of the dialog that lets users confirm details.\n\n Args:\n Mapping[str, Any] event: the event object from the Google Workspace add-on\n\n Returns:\n Mapping[str, Any]: update the dialog.\n \"\"\"\n name = event.get('commonEventObject').get('formInputs')[\"contactName\"].get('stringInputs').get('value')[0]\n birthdateEpoch = event.get('commonEventObject').get('formInputs')[\"contactBirthdate\"].get('dateInput').get('msSinceEpoch')\n birthdate = datetime.fromtimestamp(int(birthdateEpoch) / 1000.0).strftime(\"%Y-%m-%d\")\n type = event.get('commonEventObject').get('formInputs')[\"contactType\"].get('stringInputs').get('value')[0]\n # Display the input values for confirmation\n return { 'action': { 'navigations': [{ 'pushCard': { 'sections': [{ 'widgets': [\n { 'textParagraph': { 'text': \"Confirm contact information and submit:\" }},\n { 'textParagraph': { 'text': \"<b>Name:</b> \" + name }},\n { 'textParagraph': { 'text': \"<b>Birthday:</b> \" + birthdate }},\n { 'textParagraph': { 'text': \"<b>Type:</b> \" + type }},\n { 'buttonList': { 'buttons': [{\n 'text': \"SUBMIT\",\n 'onClick': { 'action': {\n 'function': FUNCTION_URL,\n 'parameters': [\n { 'key': \"actionName\", 'value': \"submitDialog\" },\n # Pass input values as parameters for last dialog step (submission)\n { 'key': \"contactName\", 'value': name },\n { 'key': \"contactBirthdate\", 'value': birthdate },\n { 'key': \"contactType\", 'value': type }\n ]\n }}\n }]}}\n ]}]}}]}}\n```\n\nExample:\n```text\n/**\n * Responds to a message in Google Chat.\n *\n * @return response that handles dialogs.\n */\nGenericJson handleMessage() {\n Message message = new Message()\n .setText(\"To add a contact, use the `ADD CONTACT` button below.\")\n .setAccessoryWidgets(List.of(new AccessoryWidget()\n .setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()\n .setText(\"ADD CONTACT\")\n .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()\n .setFunction(FUNCTION_URL)\n .setInteraction(\"OPEN_DIALOG\")\n .setParameters(List.of(\n new GoogleAppsCardV1ActionParameter().setKey(\"actionName\").setValue(\"openInitialDialog\"))))))))));\n return new GenericJson() {{\n put(\"hostAppDataAction\", new GenericJson() {{\n put(\"chatDataAction\", new GenericJson() {{\n put(\"createMessageAction\", new GenericJson() {{\n put(\"message\", message);\n }});\n }});\n }});\n }};\n}\n\n/**\n * Responds to a button clicked in Google Chat.\n *\n * @param event The event object from the Google Workspace add-on.\n * @return response depending on the button clicked.\n */\nGenericJson handleButtonClicked(JsonNode event) {\n String actionName = event.at(\"/commonEventObject/parameters/actionName\").asText();\n // Initial dialog form page\n if (\"openInitialDialog\".equals(actionName)) {\n return openInitialDialog();\n // Confirmation dialog form page\n } else if (\"openConfirmationDialog\".equals(actionName)) {\n return openConfirmationDialog(event);\n // Submission dialog form page\n } else if (\"submitDialog\".equals(actionName)) {\n return submitDialog(event);\n }\n return null; \n}\n\n/**\n * Opens the initial step of the dialog that lets users add contact details.\n *\n * @return {Object} open the dialog.\n */\nGenericJson openInitialDialog() {\n GoogleAppsCardV1Card cardV2 = new GoogleAppsCardV1Card()\n .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(\n new GoogleAppsCardV1Widget().setTextInput(new GoogleAppsCardV1TextInput()\n .setName(\"contactName\")\n .setLabel(\"First and last name\")\n .setType(\"SINGLE_LINE\")),\n new GoogleAppsCardV1Widget().setDateTimePicker(new GoogleAppsCardV1DateTimePicker()\n .setName(\"contactBirthdate\")\n .setLabel(\"Birthdate\")\n .setType(\"DATE_ONLY\")),\n new GoogleAppsCardV1Widget().setSelectionInput(new GoogleAppsCardV1SelectionInput()\n .setName(\"contactType\")\n .setLabel(\"Contact type\")\n .setType(\"RADIO_BUTTON\")\n .setItems(List.of(\n new GoogleAppsCardV1SelectionItem()\n .setText(\"Work\")\n .setValue(\"Work\")\n .setSelected(false),\n new GoogleAppsCardV1SelectionItem()\n .setText(\"Personal\")\n .setValue(\"Personal\")\n .setSelected(false)))),\n new GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(\n new GoogleAppsCardV1Button()\n .setText(\"NEXT\")\n .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()\n .setFunction(FUNCTION_URL)\n .setParameters(List.of(\n new GoogleAppsCardV1ActionParameter().setKey(\"actionName\").setValue(\"openConfirmationDialog\"))))))))))));\n return new GenericJson() {{\n put(\"action\", new GenericJson() {{\n put(\"navigations\", List.of(new GenericJson() {{\n put(\"pushCard\", cardV2);\n }}));\n }});\n }};\n}\n\n/**\n * Opens the second step of the dialog that lets users confirm details.\n *\n * @param event The event object from the Google Workspace add-on.\n * @return update the dialog.\n */\nGenericJson openConfirmationDialog(JsonNode event) {\n // Retrieve the form input values\n String name = event.at(\"/commonEventObject/formInputs/contactName/stringInputs/value\").get(0).asText();\n String birthdateEpoch = event.at(\"/commonEventObject/formInputs/contactBirthdate/dateInput/msSinceEpoch\").asText();\n String birthdate = new SimpleDateFormat(\"MM/dd/yyyy\").format(new Date((long)Double.parseDouble(birthdateEpoch)));\n String type = event.at(\"/commonEventObject/formInputs/contactType/stringInputs/value\").get(0).asText();\n // Display the input values for confirmation\n GoogleAppsCardV1Card cardV2 = new GoogleAppsCardV1Card()\n .setSections(List.of(new GoogleAppsCardV1Section().setWidgets(List.of(\n new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()\n .setText(\"Confirm contact information and submit:\")),\n new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()\n .setText(\"<b>Name:</b> \" + name)),\n new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()\n .setText(\"<b>Birthday:</b> \" + birthdate)),\n new GoogleAppsCardV1Widget().setTextParagraph(new GoogleAppsCardV1TextParagraph()\n .setText(\"<b>Type:</b> \" + type)),\n new GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(\n new GoogleAppsCardV1Button()\n .setText(\"SUBMIT\")\n .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()\n .setFunction(FUNCTION_URL)\n .setParameters(List.of(\n new GoogleAppsCardV1ActionParameter().setKey(\"actionName\").setValue(\"submitDialog\"),\n // Pass input values as parameters for last dialog step (submission)\n new GoogleAppsCardV1ActionParameter().setKey(\"contactName\").setValue(name),\n new GoogleAppsCardV1ActionParameter().setKey(\"contactBirthdate\").setValue(birthdate),\n new GoogleAppsCardV1ActionParameter().setKey(\"contactType\").setValue(type))))))))))));\n return new GenericJson() {{\n put(\"action\", new GenericJson() {{\n put(\"navigations\", List.of(new GenericJson() {{\n put(\"pushCard\", cardV2);\n }}));\n }});\n }};\n}\n```\n\nExample:\n```text\n/**\n * Responds to a message in Google Chat.\n *\n * @param {Object} event The event object from the Google Workspace add-on.\n * @return {Object} response that handles dialogs.\n */\nfunction onMessage(event) {\n // Reply with a message that contains a button to open the initial dialog\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: \"To add a contact, use the `ADD CONTACT` button below.\",\n accessoryWidgets: [\n { buttonList: { buttons: [{\n text: \"ADD CONTACT\",\n onClick: { action: {\n function: \"openInitialDialog\",\n interaction: \"OPEN_DIALOG\"\n }}\n }]}}\n ]\n }}}}};\n}\n\n/**\n * Opens the initial step of the dialog that lets users add contact details.\n *\n * @param {Object} event The event object from the Google Workspace add-on.\n * @return {Object} open the dialog.\n */\nfunction openInitialDialog(event) {\n return { action: { navigations: [{ pushCard: { sections: [{ widgets: [\n { textInput: {\n name: \"contactName\",\n label: \"First and last name\",\n type: \"SINGLE_LINE\"\n }},\n { dateTimePicker: {\n name: \"contactBirthdate\",\n label: \"Birthdate\",\n type: \"DATE_ONLY\"\n }},\n { selectionInput: {\n name: \"contactType\",\n label: \"Contact type\",\n type: \"RADIO_BUTTON\",\n items: [\n { text: \"Work\", value: \"Work\", selected: false },\n { text: \"Personal\", value: \"Personal\", selected: false }\n ]\n }},\n { buttonList: { buttons: [{\n text: \"NEXT\",\n onClick: { action: { function : \"openConfirmationDialog\" }}\n }]}}\n ]}]}}]}};\n}\n\n/**\n * Opens the second step of the dialog that lets users confirm details.\n *\n * @param {Object} event The event object from the Google Workspace add-on.\n * @return {Object} update the dialog.\n */\nfunction openConfirmationDialog(event) {\n // Retrieve the form input values\n const name = event.commonEventObject.formInputs[\"contactName\"].stringInputs.value[0];\n const birthdate = event.commonEventObject.formInputs[\"contactBirthdate\"].dateInput.msSinceEpoch;\n const type = event.commonEventObject.formInputs[\"contactType\"].stringInputs.value[0];\n // Display the input values for confirmation\n return { action: { navigations: [{ pushCard: { sections: [{ widgets: [\n { textParagraph: { text: \"Confirm contact information and submit:\" }},\n { textParagraph: { text: \"<b>Name:</b> \" + name }},\n { textParagraph: { text: \"<b>Birthday:</b> \" + new Date(birthdate) }},\n { textParagraph: { text: \"<b>Type:</b> \" + type }},\n { buttonList: { buttons: [{\n text: \"SUBMIT\",\n onClick: { action: {\n function: \"submitDialog\",\n // Pass input values as parameters for last dialog step (submission)\n parameters: [\n { key: \"contactName\", value: name },\n { key: \"contactBirthdate\", value: birthdate },\n { key: \"contactType\", value: type }\n ]\n }}\n }]}}\n ]}]}}]}};\n}\n```\n\nExample:\n```text\n// Validate the parameters.\nif (!event.commonEventObject.parameters[\"contactName\"]) {\n return { action: {\n navigations: [{ endNavigation: { action: \"CLOSE_DIALOG\"}}],\n notification: { text: \"Failure, the contact name was missing!\" }\n }};\n}\n```\n\nExample:\n```text\n# Validate the parameters.\nif event.get('commonEventObject').get('parameters')[\"contactName\"] == \"\":\n return { 'action': {\n 'navigations': [{ 'endNavigation': { 'action': \"CLOSE_DIALOG\"}}],\n 'notification': { 'text': \"Failure, the contact name was missing!\" }\n }}\n```\n\nExample:\n```text\n// Validate the parameters.\n if (event.at(\"/commonEventObject/parameters/contactName\").asText().isEmpty()) {\n return new GenericJson() {{\n put(\"action\", new GenericJson() {{\n put(\"navigations\", List.of(new GenericJson() {{\n put(\"endNavigation\", new GenericJson() {{\n put(\"action\", \"CLOSE_DIALOG\");\n }});\n }}));\n put(\"notification\", new GenericJson() {{\n put(\"text\", \"Failure, the contact name was missing!\");\n }});\n }});\n }};\n }\n\n return new GenericJson() {{\n put(\"hostAppDataAction\", new GenericJson() {{\n put(\"chatDataAction\", new GenericJson() {{\n put(\"createMessageAction\", new GenericJson() {{\n put(\"message\", new Message()\n .setText( \"✅ \" + event.at(\"/commonEventObject/parameters/contactName\").asText() +\n \" has been added to your contacts.\"));\n }});\n }});\n }});\n }};\n }\n}\n```\n\nExample:\n```text\nreturn { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: \"✅ \" + event.commonEventObject.parameters[\"contactName\"] + \" has been added to your contacts.\"\n}}}}};\n```\n\nExample:\n```text\nreturn { 'hostAppDataAction': { 'chatDataAction': { 'createMessageAction': { 'message': {\n 'text': \"✅ \" + event.get('commonEventObject').get('parameters')[\"contactName\"] + \" has been added to your contacts.\"\n}}}}}\n```\n\nExample:\n```text\nreturn new GenericJson() {{\n put(\"hostAppDataAction\", new GenericJson() {{\n put(\"chatDataAction\", new GenericJson() {{\n put(\"createMessageAction\", new GenericJson() {{\n put(\"message\", new Message()\n .setText( \"✅ \" + event.at(\"/commonEventObject/parameters/contactName\").asText() +\n \" has been added to your contacts.\"));\n }});\n }});\n }});\n}};\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.558Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":764,"estimatedTokens":6290}}721{"id":"doc-best_practices_google_workspace_add_ons_google_f-69265128","source":"documentation","title":"Best practices | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/guides/workspace-best-practices","text":"Example:\n```text\nLogger.log(response.printJson());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.560Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":17}}722{"id":"doc-collect_data_with_an_input_variable_google_works-d4c69844","source":"documentation","title":"Collect data with an input variable | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/input-variables","text":"Example:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Calculator\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/productlogos/calculator_search/v1/web-24dp/logo_calculator_search_color_1x_web_24dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"calculatorDemo\",\n \"state\": \"ACTIVE\",\n \"name\": \"Calculate\",\n \"description\": \"Asks the user for two values and a math operation, then performs the math operation on the values and outputs the result.\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"value1\",\n \"description\": \"value1\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n },\n {\n \"id\": \"value2\",\n \"description\": \"value2\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n },\n {\n \"id\": \"operation\",\n \"description\": \"operation\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"outputs\": [\n {\n \"id\": \"result\",\n \"description\": \"Calculated result\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfigCalculate\",\n \"onExecuteFunction\": \"onExecuteCalculate\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n* Generates and displays a configuration card for the sample calculation step.\n*\n* This function creates a card with input fields for two values and a drop-down\n* for selecting an arithmetic operation.\n*\n* The input fields are configured to let the user select outputs from previous\n* workflow steps as input values using the `hostAppDataSource` property.\n*/\nfunction onConfigCalculate() {\n const firstInput = CardService.newTextInput()\n .setFieldName(\"value1\") // \"FieldName\" must match an \"id\" in the manifest file's inputs[] array.\n .setTitle(\"First Value\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n )\n );\n\n const secondInput = CardService.newTextInput()\n .setFieldName(\"value2\") // \"FieldName\" must match an \"id\" in the manifest file's inputs[] array.\n .setTitle(\"Second Value\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n )\n );\n\n const selectionInput = CardService.newSelectionInput()\n .setTitle(\"operation\")\n .setFieldName(\"operation\") // \"FieldName\" must match an \"id\" in the manifest file's inputs[] array.\n .setType(CardService.SelectionInputType.DROPDOWN)\n .addItem(\"+\", \"+\", false)\n .addItem(\"-\", \"-\", true)\n .addItem(\"x\", \"x\", false)\n .addItem(\"/\", \"/\", false);\n\n const sections = CardService.newCardSection()\n .setHeader(\"Action_sample: Calculate\")\n .setId(\"section_1\")\n .addWidget(firstInput)\n .addWidget(selectionInput)\n .addWidget(secondInput)\n\n let card = CardService.newCardBuilder()\n .addSection(sections)\n .build();\n\n return card;\n}\n```\n\nExample:\n```text\nconst selectionInput = CardService.newSelectionInput()\n .setFieldName(\"variable_picker_1\")\n .setTitle(\"Variable Picker\")\n .setType(\n CardService.SelectionInputType.OVERFLOW_MENU\n );\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/Toronto\",\n \"dependencies\": {},\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Text and output variable demo\",\n \"logoUrl\": \"https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"richTextDemo\",\n \"state\": \"ACTIVE\",\n \"name\": \"Rich Text Demo\",\n \"description\": \"Show the difference between rich text and plain text TextInput widgets\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"value1\",\n \"description\": \"First user input\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n },\n {\n \"id\": \"value2\",\n \"description\": \"Second user input\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfiguration\",\n \"onExecuteFunction\": \"onExecution\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\nfunction onConfiguration() {\n const input1 = CardService.newTextInput()\n .setFieldName(\"value1\")\n .setId(\"value1\")\n .setTitle(\"Rich Text\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n )\n )\n // Set input mode to RICH_TEXT to allow mixed text and variables.\n .setInputMode(CardService.TextInputMode.RICH_TEXT);\n\n const input2 = CardService.newTextInput()\n .setFieldName(\"value2\")\n .setId(\"value2\")\n .setTitle(\"Plain text\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n )\n )\n // Set input mode to PLAIN_TEXT to enforce single variable selection.\n .setInputMode(CardService.TextInputMode.PLAIN_TEXT);\n\n const section = CardService.newCardSection()\n .addWidget(input1)\n .addWidget(input2);\n\n const card = CardService.newCardBuilder()\n .addSection(section)\n .build();\n\n return card;\n}\n\nfunction onExecution(e) {\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"dependencies\": {},\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/script.locale\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Variable button customization\",\n \"logoUrl\": \"https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"variable_picker_customization\",\n \"state\": \"ACTIVE\",\n \"name\": \"Variable Picker demo\",\n \"description\": \"List all possible variable picker customization options\",\n \"workflowAction\": {\n \"onConfigFunction\": \"onUpdateCardConfigFunction\",\n \"onExecuteFunction\": \"onUpdateCardExecuteFunction\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\nfunction onUpdateCardConfigFunction(event) {\n const textInput1 = CardService.newTextInput()\n .setFieldName(\"value1\")\n .setTitle(\"Regular variable picker button\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource().setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n .setVariableButtonSize(CardService.VariableButtonSize.UNSPECIFIED)\n )\n );\n\n const textInput2 = CardService.newTextInput()\n .setFieldName(\"value2\")\n .setTitle(\"Size: Unspecified\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource().setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n .setVariableButtonSize(CardService.VariableButtonSize.UNSPECIFIED)\n )\n );\n\n const textInput3 = CardService.newTextInput()\n .setFieldName(\"value3\")\n .setTitle(\"Size: Full size\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource().setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n .setVariableButtonSize(CardService.VariableButtonSize.FULL_SIZE)\n )\n );\n\n const textInput4 = CardService.newTextInput()\n .setFieldName(\"value4\")\n .setTitle(\"Size: Compact\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource().setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n .setVariableButtonSize(CardService.VariableButtonSize.COMPACT)\n )\n );\n\n const textInput5 = CardService.newTextInput()\n .setFieldName(\"value5\")\n .setTitle(\"Custom button label\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource().setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n .setVariableButtonLabel(\"New button label!\")\n )\n );\n\n var cardSection = CardService.newCardSection()\n .addWidget(textInput1)\n .addWidget(textInput2)\n .addWidget(textInput3)\n .addWidget(textInput4)\n .addWidget(textInput5)\n .setId(\"section_1\");\n\n var card = CardService.newCardBuilder().addSection(cardSection).build();\n\n return card;\n}\n\nfunction onUpdateCardExecuteFunction(event) {\n}\n```\n\nExample:\n```text\n// User Autocomplete\nvar multiSelect2 =\n CardService.newSelectionInput()\n .setFieldName(\"value2\")\n .setTitle(\"User Autocomplete\")\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setMultiSelectMaxSelectedItems(3)\n .setPlatformDataSource(\n CardService.newPlatformDataSource()\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n .setType(CardService.WorkflowDataSourceType.USER)\n ))\n );\n\n// Chat Space Autocomplete\nvar multiSelect3 =\n CardService.newSelectionInput()\n .setFieldName(\"value3\")\n .setTitle(\"Chat Space Autocomplete\")\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setMultiSelectMaxSelectedItems(3)\n .setPlatformDataSource(\n CardService.newPlatformDataSource()\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n .setType(CardService.WorkflowDataSourceType.SPACE)\n ))\n );\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Autocomplete Demo\",\n \"logoUrl\": \"https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"autocomplete_demo\",\n \"state\": \"ACTIVE\",\n \"name\": \"Autocomplete Demo\",\n \"description\": \"Provide autocompletion in input fields\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"value1\",\n \"description\": \"A multi-select field with autocompletion\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfigAutocomplete\",\n \"onExecuteFunction\": \"onExecuteAutocomplete\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\nfunction onConfigAutocompleteTest(event) {\n // Handle autocomplete request\n if (event.workflow && event.workflow.elementUiAutocomplete) {\n return handleAutocompleteRequest(event);\n }\n\n // Server-side autocomplete widget\n var multiSelect1 =\n CardService.newSelectionInput()\n .setFieldName(\"value1\")\n .setTitle(\"Server Autocomplete\")\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setMultiSelectMaxSelectedItems(3)\n .addDataSourceConfig(\n CardService.newDataSourceConfig()\n .setRemoteDataSource(\n CardService.newAction().setFunctionName('getAutocompleteResults')\n )\n )\n .addDataSourceConfig(\n CardService.newDataSourceConfig()\n .setPlatformDataSource(\n CardService.newPlatformDataSource()\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n ))\n )\n );\n\n // User autocomplete widget\n var multiSelect2 =\n CardService.newSelectionInput()\n .setFieldName(\"value2\")\n .setTitle(\"User Autocomplete\")\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setMultiSelectMaxSelectedItems(3)\n .setPlatformDataSource(\n CardService.newPlatformDataSource()\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n .setType(CardService.WorkflowDataSourceType.USER)\n ))\n );\n\n // Space autocomplete widget\n var multiSelect3 =\n CardService.newSelectionInput()\n .setFieldName(\"value3\")\n .setTitle(\"Chat Space Autocomplete\")\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setMultiSelectMaxSelectedItems(3)\n .setPlatformDataSource(\n CardService.newPlatformDataSource()\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n .setType(CardService.WorkflowDataSourceType.SPACE)\n ))\n );\n\n var sectionBuilder =\n CardService.newCardSection()\n .addWidget(multiSelect1)\n .addWidget(multiSelect2)\n .addWidget(multiSelect3);\n\n var card =\n CardService.newCardBuilder()\n .addSection(sectionBuilder)\n .build();\n return card;\n}\n\nfunction handleAutocompleteRequest(event) {\n var invokedFunction = event.workflow.elementUiAutocomplete.invokedFunction;\n var query = event.workflow.elementUiAutocomplete.query;\n\n if (invokedFunction != \"getAutocompleteResults\" || query == undefined || query == \"\") {\n return {};\n }\n\n // Query your data source to get results\n let autocompleteResponse = AddOnsResponseService.newUpdateWidget()\n .addSuggestion(\n query + \" option 1\",\n query + \"_option1\",\n false,\n \"https://developers.google.com/workspace/add-ons/images/person-icon.png\",\n \"option 1 bottom text\"\n )\n .addSuggestion(\n query + \" option 2\",\n query + \"_option2\",\n false,\n \"https://developers.google.com/workspace/add-ons/images/person-icon.png\",\n \"option 2 bottom text\"\n ).addSuggestion(\n query + \" option 3\",\n query + \"_option3\",\n false,\n \"https://developers.google.com/workspace/add-ons/images/person-icon.png\",\n \"option 3 bottom text\"\n );\n\n const modifyAction = AddOnsResponseService.newAction()\n .addModifyCard(\n AddOnsResponseService.newModifyCard()\n .setUpdateWidget(autocompleteResponse)\n );\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setAction(modifyAction)\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.561Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":544,"estimatedTokens":3951}}723{"id":"doc-log_activity_and_errors_google_workspace_add_ons-08a6ce9a","source":"documentation","title":"Log activity and errors | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/activity-logs","text":"Example:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Log and Error Demo\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/productlogos/gsuite_addons/v6/web-24dp/logo_gsuite_addons_color_1x_web_24dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"log_and_error_demo\",\n \"state\": \"ACTIVE\",\n \"name\": \"Log and Error Demo\",\n \"description\": \"Display a log message when executed successfully, display an error message and retry execution instead.\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"value1\",\n \"description\": \"value1\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n }\n ],\n \"outputs\": [\n {\n \"id\": \"result\",\n \"description\": \"execution result\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfigFunctionCreateDocument\",\n \"onExecuteFunction\": \"onExecuteFunctionCreateDocument\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\nfunction onConfigFunctionCreateDocument() {\n const firstInput = CardService.newTextInput()\n .setFieldName(\"value1\")\n .setTitle(\"First Value\") //\"FieldName\" must match an \"id\" in the manifest file's inputs[] array.\n .setHint(\"Enter 1 to successfully execute the step, 0 to fail the step and return an error.\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n )\n );\n\n let cardSection = CardService.newCardSection()\n .addWidget(firstInput);\n\n return CardService.newCardBuilder()\n .addSection(cardSection)\n .build();\n}\n\nfunction onExecuteFunctionCreateDocument(event) {\n\n // true if the document is successfully created, false if something goes wrong.\n var successfulRun = event.workflow.actionInvocation.inputs[\"value1\"].integerValues[0];\n console.log(\"The user input is: \", successfulRun);\n\n // If successful, return an activity log linking to the created document.\n if (successfulRun == 1) {\n let logChip = AddOnsResponseService.newTextFormatChip()\n .setTextFormatIcon(\n AddOnsResponseService.newTextFormatIcon()\n .setMaterialIconName(\"edit_document\")\n )\n .setUrl(\"https://docs.google.com/document/d/{DOCUMENT}\")\n .setLabel(\"Mock Document\");\n\n let output = AddOnsResponseService.newVariableData()\n .addStringValue(\"Created Google Doc\");\n\n const workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()\n .addVariableData(\"result\", output)\n // Set the user-facing error log\n .setLog(\n AddOnsResponseService.newWorkflowTextFormat()\n .addTextFormatElement(\n AddOnsResponseService.newTextFormatElement()\n .setText(\"Created Google Doc\")\n )\n .addTextFormatElement(\n AddOnsResponseService.newTextFormatElement()\n .setTextFormatChip(logChip)\n )\n .addTextFormatElement(\n AddOnsResponseService.newTextFormatElement()\n .setText(\"Created doc detailing how to improve product.\")\n )\n );\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n }\n // Otherwise, return an activity log containing an error explaining what happened and how to resolve the issue.\n else {\n let errorChip = AddOnsResponseService.newTextFormatChip()\n .setTextFormatIcon(\n AddOnsResponseService.newTextFormatIcon()\n .setMaterialIconName(\"file_open\")\n )\n .setLabel(\"Mock Document\");\n\n const workflowAction = AddOnsResponseService.newReturnElementErrorAction()\n .setErrorActionability(AddOnsResponseService.ErrorActionability.ACTIONABLE)\n .setErrorRetryability(AddOnsResponseService.ErrorRetryability.NOT_RETRYABLE)\n // Set the user-facing error log\n .setErrorLog(\n AddOnsResponseService.newWorkflowTextFormat()\n .addTextFormatElement(\n AddOnsResponseService.newTextFormatElement()\n .setText(\"Failed to create Google Doc.\")\n )\n .addTextFormatElement(\n AddOnsResponseService.newTextFormatElement()\n .setTextFormatChip(errorChip)\n )\n .addTextFormatElement(\n AddOnsResponseService.newTextFormatElement()\n .setText(\"Unable to create Google Document because OAuth verification failed. Grant one of these authorization scopes and try again: https://www.googleapis.com/auth/documents, \\nhttps://www.googleapis.com/auth/drive, \\nhttps://www.googleapis.com/auth/drive.file\")\n )\n );\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.562Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":158,"estimatedTokens":1368}}724{"id":"doc-test_and_debug_http_google_workspace_add_ons_goo-04664d40","source":"documentation","title":"Test and debug HTTP Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/guides/debug","text":"Example:\n```text\nnpm install -g nodemon\n```\n\nExample:\n```text\n{\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\",\n \"https://www.googleapis.com/auth/workspace.linkcreate\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Manage support cases\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"NGROK_STATIC_DOMAIN\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n },\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ],\n \"createActionTriggers\": [\n {\n \"id\": \"createCase\",\n \"labelText\": \"Create support case\",\n \"localizedLabelText\": {\n \"es\": \"Crear caso de soporte\"\n },\n \"runFunction\": \"$URL2\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ]\n },\n \"httpOptions\": {\n \"granularOauthPermissionSupport\": \"OPT_IN\"\n }\n }\n}\n```\n\nExample:\n```text\ngcloud config set project PROJECT_ID\n```\n\nExample:\n```text\ngcloud auth application-default login\n```\n\nExample:\n```text\ngcloud workspace-add-ons deployments create manageSupportCases \\\n --deployment-file=DEPLOYMENT_FILE_PATH\n```\n\nExample:\n```text\ngcloud workspace-add-ons deployments install manageSupportCases\n```\n\nExample:\n```text\n{\n ...\n \"dependencies\": {\n ...\n \"@google-cloud/functions-framework\": \"^3.3.0\"\n },\n \"scripts\": {\n ...\n \"start\": \"npx functions-framework --target=createLinkPreview --port=9000\",\n \"debug-watch\": \"nodemon --watch ./ --exec npm start\"\n }\n ...\n}\n```\n\nExample:\n```text\nnpm install\n```\n\nExample:\n```text\n{\n \"version\": \"0.2.0\",\n \"configurations\": [{\n \"type\": \"node\",\n \"request\": \"launch\",\n \"name\": \"Debug Watch\",\n \"cwd\": \"${workspaceRoot}\",\n \"runtimeExecutable\": \"npm\",\n \"runtimeArgs\": [\"run-script\", \"debug-watch\"]\n }]\n}\n```\n\nExample:\n```text\nngrok http --domain=NGROK_STATIC_DOMAIN 9000\n```\n\nExample:\n```text\nhttps://example.com/support/case/?name=Name1&description=Description1&priority=P1\n```\n\nExample:\n```text\nvirtualenv envsource env/bin/activate\n```\n\nExample:\n```text\npip install -r requirements.txt\n```\n\nExample:\n```text\n{\n \"version\": \"0.2.0\",\n \"configurations\": [{\n \"type\": \"python\",\n \"request\": \"launch\",\n \"name\": \"Debug Watch\",\n \"python\": \"${workspaceFolder}/env/bin/python3\",\n \"module\": \"functions_framework\",\n \"args\": [\n \"--target\", \"create_link_preview\",\n \"--port\", \"9000\",\n \"--debug\"\n ]\n }]\n}\n```\n\nExample:\n```text\n...\n<plugin>\n <groupId>com.google.cloud.functions</groupId>\n <artifactId>function-maven-plugin</artifactId>\n <version>0.11.0</version>\n <configuration>\n <functionTarget>CreateLinkPreview</functionTarget>\n <port>9000</port>\n </configuration>\n</plugin>\n...\n```\n\nExample:\n```text\nmvnDebug function:run\nPreparing to execute Maven in debug mode\nListening for transport dt_socket at address: 8000\n```\n\nExample:\n```text\n{\n \"version\": \"0.2.0\",\n \"configurations\": [{\n \"type\": \"java\",\n \"request\": \"attach\",\n \"name\": \"Remote Debug Watch\",\n \"projectName\": \"http-function\",\n \"hostName\": \"localhost\",\n \"port\": 8000\n }]\n}\n```\n\nExample:\n```text\nssh -L LOCAL_DEBUG_PORT:localhost:REMOTE_DEBUG_PORT REMOTE_USERNAME@REMOTE_ADDRESS\n```\n\nExample:\n```text\n{\n \"version\": \"0.2.0\",\n \"configurations\": [{\n \"type\": \"node\",\n \"request\": \"attach\",\n \"name\": \"Debug Remote\",\n \"address\": \"127.0.0.1\",\n \"port\": LOCAL_DEBUG_PORT\n }]\n}\n```\n\nExample:\n```text\n{\n \"version\": \"0.2.0\",\n \"configurations\": [{\n \"type\": \"python\",\n \"request\": \"attach\",\n \"name\": \"Debug Remote\",\n \"connect\": {\n \"host\": \"127.0.0.1\",\n \"port\": LOCAL_DEBUG_PORT\n }\n }]\n}\n```\n\nExample:\n```text\n{\n \"version\": \"0.2.0\",\n \"configurations\": [{\n \"type\": \"java\",\n \"request\": \"attach\",\n \"name\": \"Debug Remote\",\n \"hostName\": \"127.0.0.1\",\n \"port\": LOCAL_DEBUG_PORT\n }]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.564Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":21,"totalLines":247,"estimatedTokens":1196}}725{"id":"doc-query_error_logs_for_google_workspace_add_ons_go-55d2f410","source":"documentation","title":"Query error logs for Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/guides/query-logs","text":"Example:\n```text\nseverity>=ERROR\nprotoPayload.serviceName=\"gsuiteaddons.googleapis.com\"\n```\n\nExample:\n```text\n\"exceptionLogging\": \"STACKDRIVER\",\n```\n\nExample:\n```text\n// Disable error logging\n\"exceptionLogging\": \"NONE\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.564Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":18,"estimatedTokens":59}}726{"id":"doc-build_a_configuration_card_for_a_step_google_wor-47e3fa48","source":"documentation","title":"Build a configuration card for a step | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/configuration-cards","text":"Example:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Chat space selector\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/productlogos/gsuite_addons/v6/web-24dp/logo_gsuite_addons_color_1x_web_24dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"actionElement\",\n \"state\": \"ACTIVE\",\n \"name\": \"Chat space selector\",\n \"description\": \"Lets the user select a space from Google Chat\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"chooseSpace\",\n \"description\": \"Choose a Chat space\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfigSpacePicker\",\n \"onExecuteFunction\": \"onExecuteSpacePicker\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Generates and displays a configuration card to choose a Chat space\n */\nfunction onConfigSpacePicker() {\n\n const selectionInput = CardService.newSelectionInput()\n .setTitle(\"First Value\")\n .setFieldName(\"chooseSpace\")\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setPlatformDataSource(\n CardService.newPlatformDataSource()\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n .setType(CardService.WorkflowDataSourceType.SPACE)\n )\n )\n );\n\n const cardSection = CardService.newCardSection()\n .setHeader(\"Select Chat Space\")\n .setId(\"section_1\")\n .addWidget(selectionInput)\n\n var card = CardService.newCardBuilder()\n .addSection(cardSection)\n .build();\n\n return card;\n}\n\nfunction onExecuteSpacePicker(e) {\n}\n```\n\nExample:\n```text\n// In your onConfig function:\nvar multiSelect1 =\n CardService.newSelectionInput()\n .setFieldName(\"value1\")\n .setTitle(\"Server Autocomplete\")\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setMultiSelectMaxSelectedItems(3)\n .addDataSourceConfig(\n CardService.newDataSourceConfig()\n .setRemoteDataSource(\n CardService.newAction().setFunctionName('getAutocompleteResults')\n )\n )\n .addDataSourceConfig(\n CardService.newDataSourceConfig()\n .setPlatformDataSource(\n CardService.newPlatformDataSource()\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n ))\n )\n );\n\n// ... add widget to card ...\n```\n\nExample:\n```text\nfunction handleAutocompleteRequest(event) {\n var invokedFunction = event.workflow.elementUiAutocomplete.invokedFunction;\n var query = event.workflow.elementUiAutocomplete.query;\n\n if (invokedFunction != \"getAutocompleteResults\" || query == undefined || query == \"\") {\n return {};\n }\n\n // Query your data source to get results based on the query\n let autocompleteResponse = AddOnsResponseService.newUpdateWidget()\n .addSuggestion(\n query + \" option 1\",\n query + \"_option1\",\n false,\n \"https://developers.google.com/workspace/add-ons/images/person-icon.png\",\n \"option 1 bottom text\"\n )\n .addSuggestion(\n query + \" option 2\",\n query + \"_option2\",\n false,\n \"https://developers.google.com/workspace/add-ons/images/person-icon.png\",\n \"option 2 bottom text\"\n ).addSuggestion(\n query + \" option 3\",\n query + \"_option3\",\n false,\n \"https://developers.google.com/workspace/add-ons/images/person-icon.png\",\n \"option 3 bottom text\"\n );\n\n const modifyAction = AddOnsResponseService.newAction()\n .addModifyCard(\n AddOnsResponseService.newModifyCard()\n .setUpdateWidget(autocompleteResponse)\n );\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setAction(modifyAction)\n .build();\n}\n\n// In your onConfig function, handle the autocomplete event\nfunction onConfigAutocompleteTest(event) {\n // Handle autocomplete request\n if (event.workflow && event.workflow.elementUiAutocomplete) {\n return handleAutocompleteRequest(event);\n }\n\n // ... rest of your card building logic ...\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.565Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":166,"estimatedTokens":1142}}727{"id":"doc-build_a_google_workspace_add_on_using_http_endpo-9de427b5","source":"documentation","title":"Build a Google Workspace add-on using HTTP endpoints | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/guides/alternate-runtimes","text":"Example:\n```text\n{\n \"authorizationEventObject\": {\n \"userOAuthToken\": \"USER_OAUTH_TOKEN\"\n \"authorizedScopes\": [\n \"https://www.googleapis.com/auth/gmail.addons.execute\",\n \"https://www.googleapis.com/auth/script.locale\"\n ]\n }\n}\n```\n\nExample:\n```text\n// A NodeJS HTTP handler that reads the latest email and creates a calendar event.\nexport async function createCalendarEventFromEmail(req, res) {\n // Ensure the required scopes are authorized.\n const authorizedScopes = req.body.authorizationEventObject.authorizedScopes || [];\n if (!authorizedScopes.includes('https://www.googleapis.com/auth/gmail.readonly') ||\n !authorizedScopes.includes('https://www.googleapis.com/auth/calendar.events.owned')) {\n res.send({\n 'requesting_google_scopes': { 'scopes': ['https://www.googleapis.com/auth/gmail.readonly', 'https://www.googleapis.com/auth/calendar.events.owned'] }\n });\n return;\n }\n\n // Use the token for API calls and return a card response.\n // ...\n}\n```\n\nExample:\n```text\n{\n \"requesting_google_scopes\": {\n \"scopes\": [\n \"https://www.googleapis.com/auth/books\",\n \"https://www.googleapis.com/auth/youtube\"\n ]\n }\n}\n```\n\nExample:\n```text\n{\n \"requesting_google_scopes\": {\n \"all_scopes\": true\n }\n}\n```\n\nExample:\n```text\n{\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/gmail.addons.execute\",\n \"https://www.googleapis.com/auth/gmail.addons.current.message.metadata\"\n ],\n \"addOns\": {\n \"common\": {...},\n \"gmail\": {\n \"contextualTriggers\": [\n {\n \"unconditional\": {},\n \"onTriggerFunction\": \"https://us-central1-test-http-runtime.cloudfunctions.net/GmailExample/simpleMessageInfo\"\n }\n ],\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"commonEventObject\": {\n \"hostApp\": \"GMAIL\",\n \"platform\": \"WEB\"\n },\n \"authorizationEventObject\": {\n \"userOAuthToken\": \"ya29...\",\n \"systemIdToken\": \"eyJhbGc...\",\n \"userIdToken\": \"jaaa45...\",\n },\n \"gmail\": {\n \"messageId\": \"msg-f:1234567\",\n \"threadId\": \"thread-f:2345678\",\n \"accessToken\": \"xedf241...\",\n },\n}\n```\n\nExample:\n```text\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.google.api.client.auth.oauth2.BearerToken;\nimport com.google.api.client.auth.oauth2.Credential;\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.json.jackson2.JacksonFactory;\nimport com.google.api.client.http.HttpHeaders;\nimport com.google.api.services.gmail.Gmail;\nimport com.google.api.services.gmail.model.Message;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class GmailMessageDemoController {\n\n @PostMapping(\"/message\")\n public JsonNode onViewMessage(@RequestBody JsonNode event)\n throws Exception {\n String messageId = event.at(\"/gmail/messageId\").asText();\n String messageToken = event.at(\"//gmail/accessToken\").asText();\n String accessToken = event.at(\"//authorizationEventObject/userOauthToken\")\n .asText();\n Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod());\n credential.setAccessToken(accessToken);\n\n Gmail gmailClient = new Gmail.Builder(\n GoogleNetHttpTransport.newTrustedTransport(),\n JacksonFactory.getDefaultInstance(),\n credential)\n .setApplicationName(\"GSAO Demo\")\n .build();\n HttpHeaders headers = new HttpHeaders()\n .set(\"X-Goog-Gmail-Access-Token\", messageToken);\n Message message = gmailClient.users().messages().get(\"me\", messageId)\n .setFormat(\"metadata\")\n .setRequestHeaders(headers)\n .execute();\n // Build and return response...\n }\n}\n```\n\nExample:\n```text\nimport express from \"express\";\nimport { Request, Response } from \"express\";\nimport { google } from \"googleapis\";\nimport asyncHandler from \"express-async-handler\";\nimport { OAuth2Client } from \"google-auth-library\";\n\nconst gmail = google.gmail({version: \"v1\"});\n\nconst app = express();\n\napp.post(\"/\", asyncHandler(async (req, res) => {\n const currentMessageId = req.body.gmail.messageId;\n const event = req.body;\n const accessToken = event.authorizationEventObject.userOAuthToken;\n const messageToken = event.gmail.accessToken;\n const auth = new OAuth2Client();\n auth.setCredentials({access_token: accessToken});\n\n const gmailResponse = await gmail.users.messages.get({\n id: currentMessageId,\n userId: \"me\",\n format: \"metadata\",\n auth,\n headers: { \"X-Goog-Gmail-Access-Token\": messageToken }\n });\n\n const message = gmailResponse.data;\n const response = ...; // Build and return response\n res.json(response);\n}));\n```\n\nExample:\n```text\n{\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/userinfo.email\",\n ],\n \"addOns\": {\n \"common\": {...},\n }\n}\n```\n\nExample:\n```text\ngcloud workspace-add-ons get-authorization\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.jackson2.JacksonFactory;\n\nfinal static String CLIENT_ID = \"CLIENT_ID_FOR_ADDON\";\n\n//...\n\nGoogleIdToken.Payload decodeIdToken(JsonNode event) throws Exception {\n String idToken = event.at(\"/authorizationEventObject/userIdToken\").asText();\n GoogleIdTokenVerifier.Builder builder = new GoogleIdTokenVerifier.Builder(\n GoogleNetHttpTransport.newTrustedTransport(),\n JacksonFactory.getDefaultInstance());\n builder.setAudience(Collections.singletonList(CLIENT_ID);\n GoogleIdTokenVerifier verifier = builder.build();\n GoogleIdToken decodedToken = verifier.verify(idToken);\n GoogleIdToken.Payload payload = decodedToken.getPayload();\n return payload;\n // E.g. payload.getEmail() for email, payload.getSubject() for user ID\n}\n```\n\nExample:\n```text\nconst { OAuth2Client } = require('google-auth-library');\nconst CLIENT_ID = 'CLIENT_ID_FOR_ADDON';\n\n// ...\n\nasync decodeIdToken(event) {\n const oAuth2Client = new OAuth2Client();\n const decodedToken = await oAuth2Client.verifyIdToken({\n idToken: event.authorizationEventObject.userIdToken,\n audience: CLIENT_ID\n });\n const payload = decodedToken.getPayload();\n // E.g. payload.email for email, payload.sub for user ID\n return payload;\n}\n```\n\nExample:\n```text\n/**\n * Determine whether a Google Workspace add-on request is legitimate.\n * \n * @param {Object} req Request sent from Google Workspace add-on\n * @return {boolean} Whether the request is legitimate\n */\nasync function verifyAddOnRequest(req) {\n try {\n const authorization = req.headers.authorization;\n const idToken = authorization.substring('Bearer '.length, authorization.length);\n const ticket = await new OAuth2Client().verifyIdToken({idToken, audience: HTTP_ENDPOINT});\n return ticket.getPayload().email_verified\n && ticket.getPayload().email === SERVICE_ACCOUNT_EMAIL;\n } catch (unused) {\n return false;\n }\n}\n```\n\nExample:\n```text\ndef verifyAddOnRequest() -> bool:\n \"\"\"Determine whether a Google Workspace add-on request is legitimate.\n\n Args:\n request: Request sent from Google Workspace add-on\n\n Returns:\n Whether the request is legitimate\n \"\"\"\n try:\n bearer = request.headers.get('Authorization')[len(\"Bearer \"):]\n token = id_token.verify_oauth2_token(bearer, requests.Request(), HTTP_ENDPOINT)\n return token['email'] == SERVICE_ACCOUNT_EMAIL\n\n except:\n return False\n```\n\nExample:\n```text\n/**\n * Determine whether a Google Workspace add-on request is legitimate.\n * \n * @param event Event sent from Google Workspace add-on\n * @param authorization Authorization header from the request\n * @return {boolean} Whether the request is legitimate\n */\nprivate boolean verifyAddOnRequest(JsonNode event, String authorization) throws Exception {\n JsonFactory factory = JacksonFactory.getDefaultInstance();\n\n GoogleIdTokenVerifier verifier =\n new GoogleIdTokenVerifier.Builder(new ApacheHttpTransport(), factory)\n .setAudience(Collections.singletonList(HTTP_ENDPOINT))\n .build();\n\n String bearer = authorization.substring(\"Bearer \".length(), authorization.length());\n GoogleIdToken idToken = GoogleIdToken.parse(factory, bearer);\n return idToken != null\n && verifier.verify(idToken)\n && idToken.getPayload().getEmailVerified()\n && idToken.getPayload().getEmail().equals(SERVICE_ACCOUNT_EMAIL);\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"description\": \"The card object used to build UIs for Google Workspace add-ons.\",\n \"definitions\": {\n \"textParagraph\": {\n \"$id\": \"/properties/textParagraph\",\n \"type\": \"object\",\n \"description\": \"Text paragraph widget.\",\n \"required\": [\n \"text\"\n ],\n \"properties\": {\n \"text\": {\n \"type\": \"string\",\n \"description\": \"The text of the paragraph. Can contain formatted text.\"\n }\n }\n },\n \"image\": {\n \"$id\": \"/properties/image\",\n \"type\": \"object\",\n \"description\": \"Image widget.\",\n \"required\": [\n \"imageUrl\"\n ],\n \"properties\": {\n \"imageUrl\": {\n \"type\": \"string\",\n \"description\": \"Sets the image to use by providing its URL or data string.\"\n },\n \"altText\": {\n \"type\": \"string\",\n \"description\": \"Sets the alternative text of the image for accessibility.\"\n },\n \"onClick\": {\n \"type\": \"object\",\n \"description\": \"Sets an action that executes when the object is clicked.\",\n \"$ref\": \"#/definitions/onClick\"\n }\n }\n },\n \"icon\": {\n \"$id\": \"/properties/icon\",\n \"type\": \"object\",\n \"description\": \"The icon, can be specified by KnownIcon string or a URL.\",\n \"oneOf\": [\n {\n \"properties\": {\n \"knownIcon\": {\n \"type\": \"string\",\n \"description\": \"The icon specified by the string name of a list of known icons\"\n },\n \"iconUrl\": {\n \"type\": \"string\",\n \"description\": \"The icon specified by a URL.\"\n }\n }\n }\n ],\n \"properties\": {\n \"altText\": {\n \"type\": \"string\",\n \"description\": \"The description of icon which is used for accessibility.\"\n }\n }\n },\n \"divider\": {\n \"$id\": \"/properties/divider\",\n \"type\": \"object\",\n \"description\": \"A horizontal divider.\"\n },\n \"button\": {\n \"$id\": \"/properties/button\",\n \"type\": \"object\",\n \"description\": \"A button. Can be a text button or an image button.\",\n \"required\": [\"onClick\", \"text\"],\n \"properties\": {\n \"text\": {\n \"type\": \"string\",\n \"description\": \"The text of the button.\"\n },\n \"icon\": {\n \"type\": \"object\",\n \"description\": \"The icon image\",\n \"$ref\": \"#/definitions/icon\"\n },\n \"color\": {\n \"type\": \"object\",\n \"description\": \"If set, the button is filled with solid background.\",\n \"$ref\": \"#/definitions/color\"\n },\n \"onClick\": {\n \"type\": \"object\",\n \"description\": \"The onClick action of the button.\",\n \"$ref\": \"#/definitions/onClick\"\n },\n \"disabled\": {\n \"type\": \"boolean\",\n \"description\": \"If true, the button is displayed in a disabled state and doesn't respond to user actions\"\n }\n }\n },\n \"buttonList\": {\n \"$id\": \"/properties/buttonList\",\n \"type\": \"object\",\n \"properties\": {\n \"buttons\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/button\"\n },\n \"description\": \"A list of buttons laid out horizontally\"\n }\n }\n },\n \"decoratedText\": {\n \"$id\": \"/properties/decoratedText\",\n \"type\": \"object\",\n \"required\": [\n \"text\"\n ],\n \"properties\": {\n \"button\" : {\n \"type\": \"object\",\n \"description\": \"A button that can be clicked to trigger an action\",\n \"$ref\": \"#/definitions/button\"\n },\n \"switchControl\": {\n \"type\": \"object\",\n \"description\": \"A switch widget can be clicked to change its state or trigger an action.\",\n \"$ref\": \"#/definitions/switchControl\"\n },\n \"icon\": {\n \"type\": \"object\",\n \"description\": \"The icon displayed in front of the text.\",\n \"$ref\": \"#/definitions/icon\"\n },\n \"imageType\": {\n \"type\": \"string\",\n \"enum\": [\n \"SQUARE\",\n \"CIRCLE\"\n ],\n \"description\": \"Define the cropping of the image.\"\n },\n \"topLabel\": {\n \"type\": \"string\",\n \"description\": \"The formatted text label that shows above the main text.\"\n },\n \"text\": {\n \"type\": \"string\",\n \"description\": \"The main widget formatted text.\"\n },\n \"wrapText\": {\n \"type\": \"boolean\",\n \"description\": \"The wrap text setting. If true, the text is wrapped and displayed in multiline.\\nOtherwise the text is truncated.\"\n },\n \"bottomLabel\": {\n \"type\": \"string\",\n \"description\": \"The formatted text label that shows below the main text.\"\n },\n \"onClick\": {\n \"type\": \"object\",\n \"description\": \"Only the top/bottom label + content region is clickable.\",\n \"$ref\": \"#/definitions/onClick\"\n }\n }\n },\n \"switchControl\": {\n \"$id\": \"/properties/switchControl\",\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"description\": \"The name of the switch widget which is used in formInput.\"\n },\n \"value\": {\n \"type\": \"string\",\n \"description\": \"The value is what is passed back in the Apps Script callback.\"\n },\n \"selected\": {\n \"type\": \"boolean\",\n \"description\": \"If the switch is selected.\"\n },\n \"onChangeAction\": {\n \"type\": \"object\",\n \"description\": \"The action when the switch state is changed.\",\n \"$ref\": \"#/definitions/action\"\n },\n \"controlType\": {\n \"type\": \"string\",\n \"description\": \"The control type, it could be either Switch or Checkbox.\",\n \"enum\": [\n \"SWITCH\",\n \"CHECKBOX\"\n ]\n }\n }\n },\n \"onClick\": {\n \"$id\": \"/properties/onClick\",\n \"type\": \"object\",\n \"properties\": {\n \"action\": {\n \"type\": \"object\",\n \"$ref\": \"#/definitions/action\",\n \"description\": \"An action is triggered by this onClick, if specified.\"\n },\n \"openLink\": {\n \"type\": \"object\",\n \"$ref\": \"#/definitions/openLink\",\n \"description\": \"This onClick triggers an open link action if specified.\"\n },\n \"openDynamicLinkAction\": {\n \"type\": \"object\",\n \"$ref\": \"#/definitions/action\",\n \"description\": \"An add-on triggers this action when the action needs to open a link.\\nThis differs from the openLink above in that this needs to talk to server to get the link.\\nThus some preparation work is required for web client to do before the open link action response comes back.\"\n },\n \"card\": {\n \"type\": \"object\",\n \"$ref\": \"#/definitions/card\",\n \"description\": \"A new card is pushed to the card stack after clicking if specified.\"\n }\n }\n },\n \"openLink\": {\n \"$id\": \"/properties/openLink\",\n \"description\": \"Opens a URL\",\n \"required\": [\"url\"],\n \"properties\": {\n \"url\": {\n \"type\": \"string\",\n \"description\": \"The URL to open.\"\n },\n \"openAs\": {\n \"type\": \"string\",\n \"description\": \"When an onClick opens a link, then the client can either open it as a \\n full size (window if that is the frame used by the client), or an \\n overlay (such as a pop-up). The implementation depends on the client\\nplatform capabilities, and the value selected may be ignored if the\\nclient does not support it. FULL_SIZE is supported by all clients.\",\n \"enum\": [\n \"FULL_SIZE\",\n \"OVERLAY\"\n ]\n },\n \"onClose\": {\n \"type\": \"string\",\n \"enum\": [\n \"NOTHING\",\n \"RELOAD\"\n ]\n }\n }\n },\n \"textInput\": {\n \"$id\": \"/properties/textInput\",\n \"type\": \"object\",\n \"description\": \"A text input is a UI item where the users can input text.\",\n \"required\": [\"name\"],\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"description\": \"The name of the text input which is used in formInput.\"\n },\n \"label\": {\n \"type\": \"string\",\n \"description\": \"At least one of label and hintText is required to be specified.\"\n },\n \"hintText\": {\n \"type\": \"string\",\n \"description\": \"The hint text.\"\n },\n \"value\": {\n \"type\": \"string\",\n \"description\": \"The default value when no input from user.\"\n },\n \"type\": {\n \"type\": \"string\",\n \"enum\": [\n \"SINGLE_LINE\",\n \"MULTIPLE_LINE\"\n ],\n \"description\": \"The style of the text (for example, single line or multiple line).\"\n },\n \"onChangeAction\": {\n \"type\": \"object\",\n \"description\": \"The onChange action (for example, invoke an Apps Script)\",\n \"$ref\": \"#/definitions/action\"\n },\n \"initialSuggestions\": {\n \"type\": \"object\",\n \"description\": \"The initial suggestions made before any user input\",\n \"$ref\": \"#/definitions/suggestions\"\n },\n \"autoCompleteAction\": {\n \"type\": \"object\",\n \"$ref\": \"#/definitions/action\",\n \"description\": \"The refresh function which returns suggestions based on the user's input text.\"\n },\n \"multipleSuggestions\": {\n \"type\": \"boolean\",\n \"description\": \"When set to true, a user can input multiple suggestions items.\"\n }\n }\n },\n \"suggestions\": {\n \"$id\": \"/properties/suggestions\",\n \"description\": \"A container wrapping elements necessary for showing suggestion items used in text input autocomplete.\",\n \"properties\": {\n \"items\": {\n \"type\": \"array\",\n \"description\": \"A list of suggestion items which will be used in are used in autocomplete.\",\n \"items\": {\n \"$ref\": \"#/definitions/suggestionItem\"\n }\n }\n }\n },\n \"suggestionItem\": {\n \"$id\": \"/properties/suggestionItem\",\n \"type\": \"object\",\n \"description\": \"A Suggestion Item. Only supports text for now.\",\n \"properties\": {\n \"text\": {\n \"type\": \"string\",\n \"description\": \"Text.\"\n }\n }\n },\n \"selectionInput\": {\n \"$id\": \"/properties/selectionInput\",\n \"description\": \"A widget which creates a UI item (for example, a drop-down list) with options for users to select.\",\n \"required\": [\"name\"],\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"description\": \"\"\n },\n \"label\": {\n \"type\": \"string\",\n \"description\": \"The label displayed ahead of the switch control.\"\n },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"The type of the selection.\",\n \"enum\": [\n \"CHECK_BOX\",\n \"RADIO_BUTTON\",\n \"SWITCH\",\n \"DROPDOWN\"\n ]\n },\n \"items\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/selectionItem\"\n },\n \"description\": \"The item/items in the switch control.\"\n },\n \"onChangeAction\": {\n \"type\": \"object\",\n \"description\": \"If specified, form is submitted when selection changes\",\n \"$ref\": \"#/definitions/action\"\n }\n }\n },\n \"selectionItem\": {\n \"type\":\"object\",\n \"$id\": \"/properties/selectionItem\",\n \"description\": \"The item in the switch control.\",\n \"properties\": {\n \"text\": {\n \"type\": \"string\",\n \"description\": \"The text to be displayed\"\n },\n \"value\": {\n \"type\": \"string\",\n \"description\": \"The value associated with this item which is sent back to Apps Script.\\nThe client should use this as a form input value.\"\n },\n \"selected\": {\n \"type\": \"boolean\",\n \"description\": \"If more than one items are selected for RADIO_BUTTON or DROPDOWN,\\nthe first selected item is treated as selected and the after ones are all ignored.\"\n }\n }\n },\n \"dateTimePicker\": {\n \"$id\": \"/properties/dateTimePicker\",\n \"description\": \"The widget to allow users to specify date and time\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"description\": \"The name of the text input which is used in formInput, and uniquely identifies this input.\"\n },\n \"label\": {\n \"type\": \"string\",\n \"description\": \"The label for the field, which is displayed to the user.\"\n },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"The type of the date time picker.\",\n \"enum\": [\n \"DATE_AND_TIME\",\n \"DATE_ONLY\",\n \"TIME_ONLY\"\n ]\n },\n \"valueMsEpoch\": {\n \"type\": \"number\",\n \"description\": \"The value to display which can be the default value before user input or previous user input.\\nIt is represented in milliseconds (Epoch time)\"\n },\n \"timezoneOffsetDate\": {\n \"type\": \"number\",\n \"description\": \"The number representing the time-zone offset from UTC, in minutes.\"\n },\n \"onChangeAction\": {\n \"type\": \"object\",\n \"description\": \"Triggered when the user clicks on the Save, or Clear button from the date time picker dialog.\",\n \"$ref\": \"#/definitions/action\"\n }\n }\n },\n \"borderStyle\": {\n \"$id\": \"/properties/borderStyle\",\n \"type\": \"object\",\n \"description\": \"A border style.\",\n \"required\": [\"type\"],\n \"properties\": {\n \"type\": {\n \"type\": \"string\",\n \"description\": \"The border type.\",\n \"enum\": [\n \"NO_BORDER\",\n \"STROKE\"\n ]\n },\n \"strokeColor\": {\n \"description\": \"The border color.\",\n \"$ref\": \"#/definitions/color\"\n },\n \"cornerRadius\": {\n \"type\": \"number\",\n \"description\": \"The border corner radius.\"\n }\n }\n },\n \"imageCropStyle\": {\n \"$id\": \"/properties/imageCropStyle\",\n \"type\": \"object\",\n \"description\": \"A crop style that can be applied to images.\",\n \"required\": [\"type\"],\n \"properties\": {\n \"type\": {\n \"type\": \"string\",\n \"description\": \"The crop type.\",\n \"enum\": [\n \"SQUARE\",\n \"CIRCLE\",\n \"RECTANGLE_CUSTOM\",\n \"RECTANGLE_4_3\"\n ]\n },\n \"aspectRatio\": {\n \"type\": \"number\",\n \"description\": \"The aspect ratio for a custom rectangular crop.\"\n }\n }\n },\n \"imageComponent\": {\n \"$id\": \"/properties/imageComponent\",\n \"type\": \"object\",\n \"description\": \"An image and its properties.\",\n \"required\": [\"imageUri\"],\n \"properties\": {\n \"imageUri\": {\n \"type\": \"string\",\n \"description\": \"The URL for the image resource.\"\n },\n \"altText\": {\n \"type\": \"string\",\n \"description\": \"The accessibility label for the image.\"\n },\n \"cropStyle\": {\n \"$ref\": \"#/definitions/imageCropStyle\",\n \"description\": \"The crop style to apply to the image.\"\n },\n \"borderStyle\": {\n \"$ref\": \"#/definitions/borderStyle\",\n \"description\": \"The border style to apply to the image.\"\n }\n }\n },\n \"grid\": {\n \"$id\": \"/properties/grid\",\n \"type\": \"object\",\n \"description\": \"A grid that displays a collection of grid items.\",\n \"required\": [],\n \"properties\": {\n \"title\": {\n \"type\": \"string\",\n \"description\": \"The title of the grid.\"\n },\n \"items\": {\n \"description\": \"List of grid items.\",\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/griditem\"\n }\n },\n \"borderStyle\": {\n \"description\": \"The border style for the grid items.\",\n \"$ref\": \"#/definitions/borderStyle\"\n },\n \"columnCount\": {\n \"type\": \"number\",\n \"description\": \"The number of columns in the grid.\"\n },\n \"onClick\": {\n \"description\": \"The action that executes when a grid item is clicked.\",\n \"$ref\": \"#/definitions/onClick\"\n }\n }\n },\n \"griditem\": {\n \"$id\": \"/properties/griditem\",\n \"type\": \"object\",\n \"description\": \"An item that can be displayed in a grid widget.\",\n \"required\": [],\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"description\": \"An identifier for the grid item.\"\n },\n \"image\": {\n \"description\": \"The image to display in the grid item.\",\n \"$ref\": \"#/definitions/imageComponent\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"The title of the grid item.\"\n },\n \"subtitle\": {\n \"type\": \"string\",\n \"description\": \"The subtitle of the grid item.\"\n },\n \"textAlignment\": {\n \"description\": \"The text alignment for the grid item's text.\",\n \"$ref\": \"#/definitions/horizontalAlignment\"\n },\n \"layout\": {\n \"type\": \"string\",\n \"description\": \"The grid item layout.\",\n \"enum\": [\n \"TEXT_BELOW\",\n \"TEXT_ABOVE\"\n ]\n }\n }\n },\n \"horizontalAlignment\": {\n \"$id\": \"/properties/horizontalAlignment\",\n \"type\": \"string\",\n \"description\": \"Horizontal alignment options.\",\n \"enum\": [\n \"START\",\n \"CENTER\",\n \"END\"\n ]\n },\n \"widget\": {\n \"$id\": \"/properties/widget\",\n \"type\": \"object\",\n \"properties\": {\n \"textParagraph\": {\n \"type\": \"object\",\n \"description\": \"Display a text paragraph in this widget\",\n \"$ref\": \"#/definitions/textParagraph\"\n },\n \"image\": {\n \"type\": \"object\",\n \"description\": \"Display an image in this widget\",\n \"$ref\": \"#/definitions/image\"\n },\n \"decoratedText\": {\n \"type\": \"object\",\n \"description\": \"Display a decorated text item in this widget\",\n \"$ref\": \"#/definitions/decoratedText\"\n },\n \"buttonList\": {\n \"type\": \"object\",\n \"description\": \"A List of buttons\",\n \"$ref\": \"#/definitions/buttonList\"\n },\n \"textInput\": {\n \"type\": \"object\",\n \"description\": \"Display a text input in this widget\",\n \"$ref\": \"#/definitions/textInput\"\n },\n \"selectionInput\": {\n \"type\": \"object\",\n \"description\": \"Display a switch control in this widget\",\n \"$ref\": \"#/definitions/selectionInput\"\n },\n \"dateTimePicker\": {\n \"type\": \"object\",\n \"description\": \"Display a date/time picker in this widget\",\n \"$ref\": \"#/definitions/dateTimePicker\"\n },\n \"horizontalAlignment\": {\n \"description\": \"The horizontal alignment of this widget.\",\n \"$ref\": \"#/definitions/horizontalAlignment\"\n },\n \"divider\": {\n \"description\": \"Inserts a divider.\",\n \"$ref\": \"#/definitions/divider\"\n },\n \"grid\": {\n \"description\": \"Display a grid control in this widget.\",\n \"$ref\": \"#/definitions/grid\"\n }\n }\n },\n \"section\": {\n \"$id\": \"/properties/section\",\n \"type\": \"object\",\n \"required\": [\n \"widgets\"\n ],\n \"properties\": {\n \"header\": {\n \"type\": \"string\",\n \"description\": \"The text header of a section\"\n },\n \"collapsible\": {\n \"type\": \"boolean\",\n \"description\": \"Whether section can be collapsed or not.\"\n },\n \"widgets\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/widget\"\n },\n \"description\": \"The widgets within a section. Example of a widget is TextParagraph or Image.\"\n },\n \"uncollapsibleWidgetsCount\": {\n \"type\": \"number\",\n \"description\": \"The number of uncollapsable widgets\"\n }\n }\n },\n \"color\": {\n \"$id\": \"/properties/color\",\n \"type\": \"object\",\n \"description\": \"Represents a color in the RGBA color space.\",\n \"required\": [\n \"red\",\n \"green\",\n \"blue\"\n ],\n \"properties\": {\n \"red\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"maximum\": 1,\n \"description\": \"The amount of red in the color as a value in the interval [0, 1]\"\n },\n \"green\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"maximum\": 1,\n \"description\": \"The amount of green in the color as a value in the interval [0, 1]\"\n },\n \"blue\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"maximum\": 1,\n \"description\": \"The amount of blue in the color as a value in the interval [0, 1]\"\n },\n \"alpha\": {\n \"type\": \"number\",\n \"minimum\": 0,\n \"maximum\": 1,\n \"description\": \"The alpha value of the color as a value in the interval [0, 1]. 1 is sloid color and 0 is transparent\"\n }\n }\n },\n \"cardHeader\": {\n \"$id\": \"/properties/cardHeader\",\n \"type\": \"object\",\n \"description\": \"Optional header in the card.\",\n \"required\": [\n \"title\"\n ],\n \"properties\": {\n \"title\": {\n \"type\": \"string\",\n \"description\": \"Required title in the header.\"\n },\n \"subtitle\": {\n \"type\": \"string\",\n \"description\": \"Optional - renders beneath the title. If not specified, title will take up both lines.\"\n },\n \"imageUrl\": {\n \"type\": \"string\",\n \"description\": \"Optional - renders an image on the right of the title.\"\n },\n \"imageType\": {\n \"type\": \"string\",\n \"enum\": [\n \"SQUARE\",\n \"CIRCLE\"\n ],\n \"description\": \"Define the cropping of the image in the header.\"\n },\n \"imageAltText\": {\n \"type\": \"string\",\n \"description\": \"The Alternative text of this image\"\n }\n }\n },\n \"cardAction\": {\n \"$id\": \"/properties/cardAction\",\n \"description\": \"A Card action is the action associated with the card.\",\n \"properties\": {\n \"actionLabel\": {\n \"type\": \"string\",\n \"description\": \"The label used to be displayed in the action menu item.\"\n },\n \"onClick\": {\n \"type\": \"object\",\n \"description\": \"The onClick action for this action item.\",\n \"$ref\": \"#/definitions/onClick\"\n }\n }\n },\n \"cardFixedFooter\": {\n \"$id\": \"/properties/cardFixedFooter\",\n \"description\": \"A persistent (sticky) footer that is added to the bottom of the card.\",\n \"properties\": {\n \"primaryButton\": {\n \"type\": \"object\",\n \"description\": \"The Primary button of the fixed footer.\",\n \"$ref\": \"#/definitions/button\"\n },\n \"secondaryButton\": {\n \"type\": \"object\",\n \"description\": \"The Secondary button of the fixed footer.\",\n \"$ref\": \"#/definitions/button\"\n }\n }\n },\n \"card\": {\n \"$id\": \"/properties/card\",\n \"type\": \"object\",\n \"required\": [\n \"sections\"\n ],\n \"properties\": {\n \"header\": {\n \"type\": \"object\",\n \"description\": \"The Header of the card.\",\n \"$ref\": \"#/definitions/cardHeader\"\n },\n \"sections\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/section\"\n },\n \"description\": \"A card consist of 1 or more sections. Widgets are defined within a section.\"\n },\n \"cardActions\": {\n \"type\": \"object\",\n \"description\": \"The actions of this card.\",\n \"$ref\": \"#/definitions/cardAction\"\n },\n \"name\": {\n \"type\": \"string\",\n \"description\": \"Name of the card which is used as an identifier for the card in the card navigation.\"\n },\n \"fixedFooter\": {\n \"type\": \"object\",\n \"description\": \"The fixed footer that is shown at the bottom of this card.\",\n \"$ref\": \"#/definitions/cardFixedFooter\"\n },\n \"displayStyle\": {\n \"type\": \"string\",\n \"description\": \"The Display Style for the peekCardHeader.\",\n \"enum\": [\n \"DISPLAY_STYLE_UNSPECIFIED\",\n \"PEEK\",\n \"REPLACE\"\n ]\n },\n \"peekCardHeader\": {\n \"type\": \"object\",\n \"description\": \"When displaying contextual content, the peek card header acts as a placeholder so that the user can\\nnavigate forward between the homepage cards and the contextual cards.\",\n \"$ref\": \"#/definitions/cardHeader\"\n }\n }\n },\n \"action\": {\n \"$id\": \"/properties/action\",\n \"type\": \"object\",\n \"description\": \"An action that describes the behavior when a form is submitted - triggered from an onclick event on an input widget (e.g. button).\",\n \"required\": [\n \"function\"\n ],\n \"properties\": {\n \"function\": {\n \"description\": \"The apps script callback function or the HTTPS endpoint if using HTTP deployments.\",\n \"type\": \"string\"\n }\n }\n }\n },\n \"type\": \"object\",\n \"$ref\": \"#/definitions/card\"\n}\n```\n\nExample:\n```text\n{\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"sections\": [\n {\n \"header\": \"Budget Performance\",\n \"widgets\": [\n {\n \"decoratedText\": {\n \"text\": \"Current QuarterBudget $18,000 (Ends in 5 days)Spent: $410.75\",\n \"wrapText\": true\n }\n },\n {\n \"decoratedText\": {\n \"text\": \"Top Expense Category\"\n }\n },\n {\n \"decoratedText\": {\n \"text\": \"Flights · 30%\",\n \"icon\": {\n \"iconUrl\" : \"http://ssl.gstatic.com/travel-trips-fe/icon_flight_grey_64.png\"\n }\n }\n },\n {\n \"decoratedText\": {\n \"text\": \"Hotels · 50%\",\n \"icon\": {\n \"iconUrl\" : \"http://ssl.gstatic.com/travel-trips-fe/icon_hotel_grey_64.png\"\n }\n }\n }\n ]\n },\n {\n \"header\": \"Expense Awaiting Your Approval\",\n \"widgets\": [\n {\n \"decoratedText\": {\n \"text\": \"Submitted by Pam Bee\",\n \"icon\": {\n \"iconUrl\" : \"https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcQDQGRxqHJ2XZenPL496tC1EkP2b8wtvENQ4QIClnde2Hq1C7u3\"\n }\n }\n },\n {\n \"decoratedText\": {\n \"text\": \"Team Lunch (Internal)Cost: $85.00 USDDate: 10/16/2019Expense no. #7453\"\n }\n },\n {\n \"buttonList\": {\n \"buttons\": [\n {\n \"text\": \"Approve\",\n \"color\": {\n \"red\": 0,\n \"green\": 0.4784,\n \"blue\": 0.353\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://www.domain.com/approve\"\n }\n }\n },\n {\n \"text\": \"Decline\",\n \"color\": {\n \"red\": 1,\n \"green\": 0.07843,\n \"blue\": 0.07843\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://www.domain.com/decline\"\n }\n }\n },\n {\n \"text\": \"View Details\",\n \"color\": {\n \"red\": 0.650,\n \"green\": 0.650,\n \"blue\": 0.650\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://www.domain.com/view\"\n }\n }\n }\n ]\n }\n }\n ]\n },\n {\n \"widgets\": [\n {\n \"decoratedText\": {\n \"text\": \"Submitted by Dwight Smith\",\n \"icon\": {\n \"iconUrl\" : \"https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcQDQGRxqHJ2XZenPL496tC1EkP2b8wtvENQ4QIClnde2Hq1C7u3\"\n }\n }\n },\n {\n \"decoratedText\": {\n \"text\": \"Flight to New YorkCost: 530.00 USDDate: 11/21/2019Expense no. #9866\"\n }\n },\n {\n \"buttonList\": {\n \"buttons\": [\n {\n \"text\": \"Approve\",\n \"color\": {\n \"red\": 0,\n \"green\": 0.4784,\n \"blue\": 0.353\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://www.domain.com/approve\"\n }\n }\n },\n {\n \"text\": \"Decline\",\n \"color\": {\n \"red\": 1,\n \"green\": 0.07843,\n \"blue\": 0.07843\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://www.domain.com/decline\"\n }\n }\n },\n {\n \"text\": \"View Details\",\n \"color\": {\n \"red\": 0.650,\n \"green\": 0.650,\n \"blue\": 0.650\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://www.domain.com/view\"\n }\n }\n }\n ]\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n}\n```\n\nExample:\n```text\n/**\n * A sample Google Workspace add-on with a form submit with a happy/sad button.\n */\nexports.homepage = (req, res) => {\n console.log(\"event: \", req.body);\n console.log(\"headers: \",JSON.stringify(req.headers));\n let parameters = req.body.commonEventObject.parameters;\n if (parameters) {\n let happy = parameters.happy;\n if (happy == '1') {\n res.status(200).send(createHappyCard());\n } else {\n res.status(200).send(createSadCard());\n }\n } else {\n res.status(200).send(createQuestionCard());\n }\n};\n\nfunction createSadCard() {\n return {\n \"renderActions\": {\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"sections\": [\n {\n \"widgets\": [\n {\n \"image\": {\n \"imageUrl\": \"https://cdn.pixabay.com/photo/2017/08/15/12/58/emoticon-2643814_960_720.jpg\"\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n }\n };\n}\n\nfunction createHappyCard() {\n return {\n \"renderActions\": {\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"sections\": [\n {\n \"widgets\": [\n {\n \"image\": {\n \"imageUrl\": \"https://dg.imgix.net/do-you-think-you-re-happy-jgdbfiey-en/landscape/do-you-think-you-re-happy-jgdbfiey-9bb0198eeccd0a3c3c13aed064e2e2b3.jpg\"\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n }\n };\n}\n\nfunction createQuestionCard() {\n return {\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"sections\": [\n {\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"Are you having a good day today?\"\n }\n },\n {\n \"buttonList\": {\n \"buttons\": [\n {\n \"text\": \"Happy\",\n \"color\": {\n \"red\": 0,\n \"green\": 0.4784,\n \"blue\": 0.353\n },\n \"onClick\": {\n \"action\": {\n \"function\": \"https://us-central1-elevated-surge-267316.cloudfunctions.net/HttpGSAO\",\n \"parameters\": [\n {\n \"key\": \"happy\",\n \"value\": \"1\"\n }\n ]\n }\n }\n },\n {\n \"text\": \"Sad\",\n \"color\": {\n \"red\": 1,\n \"green\": 0.07843,\n \"blue\": 0.07843\n },\n \"onClick\": {\n \"action\": {\n \"function\": \"https://us-central1-elevated-surge-267316.cloudfunctions.net/HttpGSAO\",\n \"parameters\": [\n {\n \"key\": \"happy\",\n \"value\": \"0\"\n }\n ]\n }\n }\n }\n ]\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n };\n}\n```\n\nExample:\n```text\n{\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"sections\": [\n {\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"You have a new request:\"\n }\n },\n {\n \"textParagraph\": {\n \"text\":\n \"John Dolittle - New Device Request\"\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"Type:\",\n \"text\": \"Computer (laptop)\"\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"When:\",\n \"text\": \"Submitted Aug 10\"\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"Reason:\",\n \"text\": \"Keyboard is not working\"\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"Specs:\",\n \"text\": \"Cheetah Pro 15\"\n }\n },\n {\n \"buttonList\": {\n \"buttons\": [\n {\n \"text\": \"Approve\",\n \"color\": {\n \"red\": 0,\n \"green\": 0.4784,\n \"blue\": 0.353\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://www.domain.com/approve\"\n }\n }\n },\n {\n \"text\": \"Deny\",\n \"color\": {\n \"red\": 1,\n \"green\": 0.07843,\n \"blue\": 0.07843\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://www.domain.com/deny\"\n }\n }\n }\n ]\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n}\n```\n\nExample:\n```text\n/**\n * A sample Google Workspace add-on that creates a top level card with a list of\n * contacts. Clicking on a contact will navigate (client side) to a full contact\n * card.\n */\nexports.homepage = (req, res) => {\n res.status(200).send(createListCard());\n};\n\nfunction createContactCard(name, title, email, phone, location) {\n return {\n \"header\": {\n \"imageType\": \"CIRCLE\",\n \"imageUrl\": \"https://ssl.gstatic.com/images/branding/product/1x/avatar_square_blue_512dp.png\",\n \"title\": name,\n \"subtitle\": title\n },\n \"sections\": [\n {\n \"widgets\": [\n {\n \"decoratedText\": {\n \"topLabel\": \"Email:\",\n \"text\": email\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"Phone Number:\",\n \"text\": phone\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"Location:\",\n \"text\": location\n }\n }\n ]\n }\n ]\n };\n}\n\nfunction createContactListEntry(name, title, email, phone, location) {\n return {\n \"decoratedText\": {\n \"topLabel\": title,\n \"text\": name,\n \"onClick\": {\n \"card\": createContactCard(name, title, email, phone, location)\n },\n \"icon\": {\n \"iconUrl\": \"https://ssl.gstatic.com/images/branding/product/1x/avatar_square_blue_512dp.png\"\n }\n }\n };\n}\n\nfunction createListCard() {\n var LIST1 = createContactListEntry(\"John Dolittle\", \"President\", \"john@gmail.com\", \"800-555-0100\", \"Markham, ON\");\n var LIST2 = createContactListEntry(\"Huckleberry Finn\", \"CFO\", \"huck@gmail.com\", \"800-555-0111\", \"Kingston, ON\");\n var LIST3 = createContactListEntry(\"Grace Harlowe\", \"Senior Director\", \"grace@gmail.com\", \"800-555-0122\", \"Toronto, ON\");\n return {\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"sections\": [\n {\n \"widgets\": [\n LIST1,\n LIST2,\n LIST3\n ]\n }\n ]\n }\n }\n ]\n }\n };\n}\n```\n\nExample:\n```text\n{\n \"custom_authorization_prompt\": {\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"sections\": [\n {\n \"widgets\": [\n {\n \"image\": {\n \"imageUrl\": \"https://www.example.com/images/logo\",\n \"altText\": \"Example organization logo\"\n }\n },\n {\n \"divider\": {}\n },\n {\n \"textParagraph\": {\n \"text\": \"Sign in to get started.\"\n }\n },\n {\n \"buttonList\": {\n \"buttons\": [\n {\n \"text\": \"Sign in\",\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://www.example.com/auth\",\n \"onClose\": \"RELOAD\",\n \"openAs\": \"OVERLAY\"\n }\n },\n \"color\": {\n \"red\": 0,\n \"green\": 0,\n \"blue\": 1,\n \"alpha\": 1,\n }\n }\n ]\n }\n },\n {\n \"textParagraph\": {\n \"text\": \"If you don't have an account, <a href=\\\"https://www.example.com/signup\\\">sign up</a> here.\"\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"/Users/dummy_user/Documents/JSON_schema/renderActionSchema.json\",\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"header\": {\n \"title\": \"Main Card\"\n },\n \"name\": \"Main Card\",\n \"peekCardHeader\": {\n \"title\": \"This is a peek card\",\n \"imageType\": \"SQUARE\",\n \"imageUrl\": \"http://ssl.gstatic.com/travel-trips-fe/icon_hotel_grey_64.png\",\n \"imageAltText\": \"Image of Cards\",\n \"subtitle\": \"No Subtitle\"\n },\n \"cardActions\": [\n {\n \"actionLabel\": \"This is Card action - 1\",\n \"onClick\": {\n \"openDynamicLinkAction\": {\n \"function\": \"https://dummy-function-from-resources.net/openLinkCallback\"\n }\n }\n },\n {\n \"actionLabel\": \"This is Card action - 2\",\n \"onClick\": {\n \"action\": {\n \"function\": \"https://dummy-function-from-resources.net/generic_submit_form_response\"\n }\n }\n },\n {\n \"actionLabel\": \"This is Card action - 3\",\n \"onClick\": {\n \"openLink\": {\n \"onClose\": \"RELOAD\",\n \"openAs\": \"OVERLAY\",\n \"url\": \"https://dummy-function-from-resources.net/open_link_sample\"\n }\n }\n },\n {\n \"actionLabel\": \"This is Card action - 4\",\n \"onClick\": {\n \"card\": {\n \"header\": {\n \"title\": \"This card is shown after card action 4 is clicked\"\n },\n \"sections\": [\n {\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"This is a sample text for the card that's shown after action 4 of the card is clicked\"\n }\n }\n ]\n }\n ]\n }\n }\n }\n ],\n \"fixedFooter\": {\n \"primaryButton\": {\n \"text\": \"Primary Button\",\n \"color\": {\n \"red\": 0,\n \"blue\": 0,\n \"green\": 0\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"www.google.ca\",\n \"onClose\": \"NOTHING\",\n \"openAs\": \"FULL_SIZE\"\n }\n }\n },\n \"secondaryButton\": {\n \"text\": \"Secondary Button - Disabled\",\n \"disabled\": true,\n \"color\": {\n \"red\": 0.32421,\n \"blue\": 0.23421,\n \"green\": 0.2353614\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"www.google.com\",\n \"onClose\": \"NOTHING\",\n \"openAs\": \"FULL_SIZE\"\n }\n }\n }\n },\n \"sections\": [\n {\n \"header\": \"Section 1 - Date Time\",\n \"collapsible\": true,\n \"widgets\": [\n {\n \"dateTimePicker\": {\n \"name\": \"Date Time Picker - EST\",\n \"label\": \"Date Time Picker - EST\",\n \"valueMsEpoch\": 1585166673000,\n \"onChangeAction\": {\n \"function\": \"https://dummy-function-from-resources.net/sample_notification\"\n },\n \"timezoneOffsetDate\": -240,\n \"type\": \"DATE_AND_TIME\"\n }\n },\n {\n \"dateTimePicker\": {\n \"name\": \"Date Picker - CST\",\n \"label\": \"Date Time Picker - CST\",\n \"valueMsEpoch\": 1585166673000,\n \"onChangeAction\": {\n \"function\": \"https://dummy-function-from-resources.net/sample_notification\"\n },\n \"timezoneOffsetDate\": -300,\n \"type\": \"DATE_AND_TIME\"\n }\n },\n {\n \"dateTimePicker\": {\n \"name\": \"Date Time Picker - PST\",\n \"label\": \"Date Time Picker - PST\",\n \"valueMsEpoch\": 1585166673000,\n \"onChangeAction\": {\n \"function\": \"https://dummy-function-from-resources.net/sample_notification\"\n },\n \"timezoneOffsetDate\": -420,\n \"type\": \"DATE_AND_TIME\"\n }\n }\n ]\n },\n {\n \"header\": \"Section 2 - Decorated Text\",\n \"collapsible\": true,\n \"uncollapsibleWidgetsCount\": 2,\n \"widgets\": [\n {\n \"decoratedText\": {\n \"topLabel\": \"Top Label - Decorated Text CHECKBOX\",\n \"switchControl\": {\n \"controlType\": \"CHECKBOX\",\n \"name\": \"Name - Check Box Sample\",\n \"value\": \"Value - Check Box Sample\"\n },\n \"text\": \"Text - Decorated Text\",\n \"bottomLabel\": \"Bottom Label - Decorated Text CHECKBOX\",\n \"wrapText\": false,\n \"onClick\": {\n \"card\": {\n \"header\": {\n \"title\": \"Decorated Text - On Click Action Card\"\n },\n \"sections\": [\n {\n \"widgets\": [\n {\n \"image\": {\n \"imageUrl\": \"https://cataas.com/cat/says/hello%20world!\",\n \"altText\": \"Hello World - Cat Image\"\n }\n }\n ]\n }\n ]\n }\n }\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"Top Label - Decorated Text SWITCH\",\n \"switchControl\": {\n \"controlType\": \"SWITCH\",\n \"name\": \"Name - SWITCH Sample\",\n \"value\": \"Value - SWITCH Sample\"\n },\n \"text\": \"Text - Decorated Text\",\n \"bottomLabel\": \"Bottom Label - Decorated Text SWITCH\",\n \"wrapText\": false,\n \"onClick\": {\n \"card\": {\n \"header\": {\n \"title\": \"Decorated Text - On Click Action Card\"\n },\n \"sections\": [\n {\n \"widgets\": [\n {\n \"image\": {\n \"imageUrl\": \"https://cataas.com/cat/says/hello%20world!\",\n \"altText\": \"Hello World - Cat Image\",\n \"onClick\": {\n \"action\": {\n \"function\": \"https://dummy-function-from-resources.net/pop_to_root\"\n }\n }\n }\n }\n ]\n }\n ]\n }\n }\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"Top Label - Decorated Text Button\",\n \"bottomLabel\": \"Bottom Label - Decorated Text Button\",\n \"text\": \"Text - Decorated Text Button\",\n \"button\": {\n \"icon\": {\n \"altText\": \"Assessment Blue\",\n \"icon_url\": \"http://ssl.gstatic.com/travel-trips-fe/icon_hotel_grey_64.png\"\n },\n \"text\": \"Assessment Blue\",\n \"onClick\": {\n \"openLink\": {\n \"url\": \"http://ssl.gstatic.com/travel-trips-fe/icon_hotel_grey_64.png\",\n \"openAs\": \"OVERLAY\",\n \"onClose\": \"RELOAD\"\n }\n }\n }\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"Top Label - Decorated Text CHECKBOX\",\n \"switchControl\": {\n \"controlType\": \"CHECKBOX\",\n \"name\": \"Name - Check Box Sample\",\n \"value\": \"Value - Check Box Sample\"\n },\n \"text\": \"Text - Decorated Text\",\n \"bottomLabel\": \"Bottom Label - Decorated Text CHECKBOX\",\n \"wrapText\": false,\n \"onClick\": {\n \"card\": {\n \"header\": {\n \"title\": \"Decorated Text - On Click Action Card\"\n },\n \"sections\": [\n {\n \"widgets\": [\n {\n \"image\": {\n \"imageUrl\": \"https://cataas.com/cat/says/hello%20world!\",\n \"altText\": \"Hello World - Cat Image\"\n }\n }\n ]\n }\n ]\n }\n }\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"Top Label - Decorated Text Icon\",\n \"bottomLabel\": \"Bottom Label - Decorated Text Icon\",\n \"text\": \"Text - Decorated Text Icon\",\n \"icon\": {\n \"iconUrl\": \"http://ssl.gstatic.com/travel-trips-fe/icon_hotel_grey_64.png\",\n \"altText\": \"Arrow Right Blue\"\n }\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"Top Label - Decorated Text Wrap\",\n \"bottomLabel\": \"Bottom Label - Decorated Text Wrap\",\n \"text\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam fringilla facilisis ne.\",\n \"wrapText\": true\n }\n },\n {\n \"decoratedText\": {\n \"topLabel\": \"Top Label - Decorated Text Non-Wrap\",\n \"bottomLabel\": \"Bottom Label - Decorated Text Non-Wrap\",\n \"text\": \"Nunc ultrices massa ut nisl porttitor, ut euismod nisl tincidunt. Vivamus pharetra, est sed sagittis consequat, arcu nisi.\",\n \"wrapText\": false\n }\n }\n ]\n },\n {\n \"header\": \"Section 3 - Button List\",\n \"collapsible\": true,\n \"widgets\": [\n {\n \"buttonList\": {\n \"buttons\": [\n {\n \"icon\": {\n \"iconUrl\": \"http://ssl.gstatic.com/travel-trips-fe/icon_hotel_grey_64.png\",\n \"altText\": \"G - Button\"\n },\n \"color\": {\n \"red\": 0,\n \"blue\": 0,\n \"green\": 1\n },\n \"disabled\": false,\n \"onClick\": {\n \"openLink\": {\n \"url\": \"www.google.ca/\"\n }\n },\n \"text\": \"Green - Google.ca\"\n },\n {\n \"color\": {\n \"red\": 1,\n \"blue\": 0,\n \"green\": 0\n },\n \"disabled\": false,\n \"onClick\": {\n \"action\": {\n \"function\": \"https://dummy-function-from-resources.net/pop_to_card_2\"\n }\n },\n \"text\": \"Pop to Card 2\"\n },\n {\n \"color\": {\n \"red\": 0,\n \"blue\": 1,\n \"green\": 0\n },\n \"disabled\": false,\n \"onClick\": {\n \"openLink\": {\n \"url\": \"www.google.ca/\"\n }\n },\n \"text\": \"Blue - Google\"\n },\n {\n \"color\": {\n \"red\": 1,\n \"blue\": 1,\n \"green\": 1\n },\n \"disabled\": true,\n \"onClick\": {\n \"openLink\": {\n \"url\": \"www.google.ca/\"\n }\n\n },\n \"text\": \"Disabled Button\"\n }\n ]\n }\n }\n ]\n },\n {\n \"header\": \"Section 4 - Images\",\n \"collapsible\": true,\n \"widgets\": [\n {\n \"image\": {\n \"imageUrl\": \"http://ssl.gstatic.com/travel-trips-fe/icon_hotel_grey_64.png\",\n \"onClick\": {\n \"openLink\": {\n \"url\": \"http://ssl.gstatic.com/travel-trips-fe/icon_hotel_grey_64.png\",\n \"openAs\": \"FULL_SIZE\",\n \"onClose\": \"NOTHING\"\n }\n }\n }\n },\n {\n \"image\": {\n \"imageUrl\": \"http://ssl.gstatic.com/travel-trips-fe/icon_hotel_grey_64.png\",\n \"altText\": \"Commute - Black\",\n \"onClick\": {\n \"openLink\": {\n \"url\": \"http://ssl.gstatic.com/travel-trips-fe/icon_hotel_grey_64.png\",\n \"openAs\": \"FULL_SIZE\",\n \"onClose\": \"RELOAD\"\n }\n }\n }\n }\n ]\n },\n {\n \"header\": \"Section 5 - Text Paragraph\",\n \"collapsible\": true,\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam fringilla facilisis neque, condimentum egestas dolor dapibus id.\"\n }\n }\n ]\n },\n {\n \"header\": \"Section 6 - Selection Input\",\n \"collapsible\": true,\n \"widgets\": [\n {\n \"selectionInput\": {\n \"name\": \"Selection Input Check box\",\n \"label\": \"Selection Input Check box\",\n \"type\": \"CHECK_BOX\",\n \"items\": [\n {\n \"text\": \"Selection Input item 1 Text\",\n \"value\": \"Selection Input item 1 Value\"\n },\n {\n \"text\": \"Selection Input item 2 Text\",\n \"value\": \"Selection Input item 2 Value\"\n }\n ]\n }\n },\n {\n \"selectionInput\": {\n \"name\": \"Selection Input Dropdown\",\n \"label\": \"Selection Input Dropdown\",\n \"type\": \"DROPDOWN\",\n \"items\": [\n {\n \"text\": \"Selection Input item 1 Text\",\n \"value\": \"Selection Input item 1 Value\"\n },\n {\n \"text\": \"Selection Input item 2 Text\",\n \"value\": \"Selection Input item 2 Value\"\n }\n ]\n }\n },\n {\n \"selectionInput\": {\n \"name\": \"Selection Input Radio\",\n \"label\": \"Selection Input Radio\",\n \"type\": \"RADIO_BUTTON\",\n \"items\": [\n {\n \"text\": \"Selection Input item 1 Text\",\n \"value\": \"Selection Input item 1 Value\"\n },\n {\n \"text\": \"Selection Input item 2 Text\",\n \"value\": \"Selection Input item 2 Value\"\n }\n ]\n }\n },\n {\n \"selectionInput\": {\n \"name\": \"Selection Input Switch\",\n \"label\": \"Selection Input Switch\",\n \"type\": \"SWITCH\",\n \"items\": [\n {\n \"text\": \"Selection Input item 1 Text\",\n \"value\": \"Selection Input item 1 Value\"\n },\n {\n \"text\": \"Selection Input item 2 Text\",\n \"value\": \"Selection Input item 2 Value\"\n }\n ]\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"/Users/dummy_user/Documents/JSON_schema/submit_form_response_schema.json\",\n \"renderActions\": {\n \"action\": {\n \"notification\": {\n \"text\": \"This is a sample notification\"\n }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"/Users/dummy_user/Documents/JSON_schema/submit_form_response_schema.json\",\n \"renderActions\": {\n \"action\": {\n \"navigations\": [\n {\n \"popToRoot\": true\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"/Users/dummy_user/Documents/JSON_schema/submit_form_response_schema.json\",\n \"render_actions\": {\n \"action\": {\n \"navigations\": [\n {\n \"popToCard\": \"card_2\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"/Users/dummy_user/Documents/JSON_schema/render_action_schema.json\",\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"header\": {\n \"title\": \"Open Link Test Case - HTTP\"\n },\n \"sections\": [\n {\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"Verify a link is opened in a new tab. When returned to Gmail, the bolded text should say AFTER.\\nText BEFORE opening the link.\"\n }\n },\n {\n \"buttonList\": {\n \"buttons\": [\n {\n \"text\": \"Open tab, close, refresh\",\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://www.google.ca/\",\n \"onClose\": \"RELOAD\",\n \"openAs\": \"OVERLAY\"\n }\n }\n }\n ]\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"/Users/dummy_user/Documents/JSON_schema/submit_form_response_schema.json\",\n \"stateChanged\": true,\n \"renderActions\": {\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"peekCardHeader\": {\n \"title\": \"Peek Card\"\n },\n \"header\": {\n \"title\": \"Generic Submit Form Response Card\"\n },\n \"sections\": [\n {\n \"widgets\": [\n {\n \"image\": {\n \"imageUrl\": \"https://cataas.com/cat\"\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"/Users/dummy_user/Documents/JSON_schema/render_action_schema.json\",\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"header\": {\n \"title\": \"Main Card\"\n },\n \"sections\": [\n {\n \"header\": \"Grid Widget\",\n \"widgets\": [\n {\n \"grid\": {\n \"title\": \"A fine collection of cats\",\n \"borderStyle\": {\n \"type\": \"STROKE\",\n \"cornerRadius\": 5.0\n },\n \"columnCount\": 2,\n \"items\": [\n {\n \"id\": \"itemA\",\n \"image\": {\n \"imageUri\": \"https://cataas.com/cat?0.001\",\n \"cropStyle\": {\n \"type\": \"CIRCLE\"\n }\n },\n \"title\": \"First cat\"\n },\n {\n \"id\": \"itemB\",\n \"image\": {\n \"imageUri\": \"https://cataas.com/cat?0.002\",\n \"cropStyle\": {\n \"type\": \"CIRCLE\"\n }\n },\n \"title\": \"Second cat\",\n \"subtitle\": \"Top rated\"\n },\n {\n \"id\": \"itemC\",\n \"image\": {\n \"imageUri\": \"https://cataas.com/cat?0.003\",\n \"cropStyle\": {\n \"type\": \"CIRCLE\"\n }\n },\n \"title\": \"Third cat\"\n },\n {\n \"id\": \"itemD\",\n \"image\": {\n \"imageUri\": \"https://cataas.com/cat?0.004\",\n \"cropStyle\": {\n \"type\": \"CIRCLE\"\n }\n },\n \"title\": \"Fourth cat\",\n },\n {\n \"id\": \"itemE\",\n \"image\": {\n \"imageUri\": \"https://cataas.com/cat?0.005\",\n \"cropStyle\": {\n \"type\": \"CIRCLE\"\n }\n },\n \"title\": \"Fifth cat\",\n \"subtitle\": \"Top rated\"\n },\n {\n \"id\": \"itemF\",\n \"image\": {\n \"imageUri\": \"https://cataas.com/cat?0.006\",\n \"cropStyle\": {\n \"type\": \"CIRCLE\"\n }\n },\n \"title\": \"Sixth cat\"\n }\n ],\n \"onClick\": {\n \"action\": {\n \"function\": \"https://dummy-function-from-resources.net/grid_item_clicked\",\n \"parameters\": [\n {\n \"key\": \"datasource\",\n \"value\": \"Favorite cat pics\"\n }\n ]\n }\n }\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\":\"object\",\n \"definitions\": {\n \"eventObject\": {\n \"$id\": \"/properties/eventObject\",\n \"type\":\"object\",\n \"description\": \"Event objects are JSON structures that are automatically constructed and passed as parameters to trigger or callback functions when a user interacts with an add-on (https://developers.google.com/workspace/add-ons/concepts/event-objects)\",\n \"properties\": {\n \"commonEventObject\": {\n \"$ref\": \"#/definitions/commonEventObject\"\n },\n \"authorizationEventObject\": {\n \"$ref\": \"#/definitions/authorizationEventObject\",\n \"description\": \"Set for requests to HTTP endpoints\"\n },\n \"gmail\": {\n \"$ref\": \"./gmail_event_object_schema.json#/definitions/gmailEventObject\",\n \"description\": \"An object containing Gmail information.\"\n },\n \"drive\": {\n \"$ref\": \"./drive_event_object_schema.json#/definitions/driveEventObject\",\n \"description\": \"An object containing Drive information.\"\n },\n \"docs\": {\n \"$ref\": \"./docs_event_object_schema.json#/definitions/docsEventObject\",\n \"description\": \"An object containing Docs information.\"\n },\n \"sheets\": {\n \"$ref\": \"./sheets_event_object_schema.json#/definitions/sheetsEventObject\",\n \"description\": \"An object containing Sheets information.\"\n },\n \"slides\": {\n \"$ref\": \"./slides_event_object_schema.json#/definitions/slidesEventObject\",\n \"description\": \"An object containing Slides information.\"\n },\n \"calendar\": {\n \"$ref\": \"./calendar_event_object_schema.json#/definitions/calendarEventObject\",\n \"description\": \"An object containing calendar and event information.\"\n }\n }\n },\n \"commonEventObject\": {\n \"$id\": \"/properties/commonEventObject\",\n \"type\":\"object\",\n \"description\": \"An object containing information common to all event objects, regardless of the host application.\",\n \"properties\": {\n \"userLocale\": {\n \"type\": \"string\",\n \"description\": \"The user's language and country/region identifier in the format of ISO 639 language code-ISO 3166 country/region code. For example, en-US.\"\n },\n \"hostApp\": {\n \"type\": \"string\",\n \"description\": \"Indicates the host app the add-on is active in when the event object is generated. Possible values include the following:\\nGMAIL\\nCALENDAR\\nDRIVE\",\n \"enum\": [\"GMAIL\", \"DRIVE\", \"CALENDAR\"]\n },\n \"platform\": {\n \"type\": \"string\",\n \"description\": \"Indicates where the event originates (`WEB`, `IOS`, or `ANDROID`)\",\n \"enum\": [\"WEB\", \"ANDRIOD\", \"IOS\"]\n },\n \"timeZone\": {\n \"type\": \"object\",\n \"$ref\": \"#/definitions/timeZone\",\n \"description\": \"The timezone ID and offset. To enable this field, you must set `addOns.common.useLocaleFromApp` to `true` in your add-on's manifest. Your add-on's scope list must also include `https://www.googleapis.com/auth/script.locale`\\n See https://developers.google.com/workspace/add-ons/how-tos/access-user-locale for more details\"\n },\n \"formInputs\": {\n \"type\": \"object\",\n \"description\": \"A map containing the current values of the widgets in the displayed card. The map keys are the string IDs assigned with each widget, and each value is another wrapper object with a single \\\"\\\" key.\",\n \"additionalProperties\": {\n \"type\":\"object\",\n \"properties\": {\n \"stringInputs\": {\n \"$ref\": \"#/definitions/stringInputs\"\n },\n \"dateTimeInput\": {\n \"$ref\": \"#/definitions/dateTimeInput\"\n },\n \"dateInput\": {\n \"$ref\": \"#/definitions/dateInput\"\n },\n \"timeInput\": {\n \"$ref\": \"#/definitions/timeInput\"\n }\n }\n }\n },\n \"parameters\": {\n \"type\": \"object\",\n \"description\": \"Any additional parameters.\",\n \"additionalProperties\": {\n \"type\": \"string\"\n }\n }\n }\n },\n \"authorizationEventObject\": {\n \"type\": \"object\",\n \"properties\": {\n \"userOAuthToken\": {\n \"description\": \"The end user OAuth access token, authorized with the requested scopes\",\n \"type\": \"string\"\n },\n \"userIdToken\": {\n \"type\": \"string\",\n \"description\": \"An end-user ID token, if appropriate ID scopes are requested\"\n },\n \"systemIdToken\": {\n \"type\": \"string\",\n \"description\": \"An ID token for the Google Workspace add-ons service account for this deployment\"\n }\n }\n },\n \"timeZone\": {\n \"$id\": \"/properties/timeZone\",\n \"type\":\"object\",\n \"properties\": {\n \"id\": {\n \"type\":\"string\",\n \"description\": \"The timezone identifier of the user's timezone. Examples include: America/New_York, Europe/Vienna, and Asia/Seoul. To enable this field, you must set `addOns.common.useLocaleFromApp` to `true` in your add-on's manifest. Your add-on's scope list must also include `https://www.googleapis.com/auth/script.locale`. See https://developers.google.com/workspace/add-ons/how-tos/access-user-locale for more details\"\n },\n \"offset\": {\n \"type\": \"integer\",\n \"description\": \"The time offset from Coordinated Universal Time (UTC) of the user's timezone, measured in milliseconds. See https://developers.google.com/workspace/add-ons/how-tos/access-user-locale for more details\",\n \"minimum\": -2147483648,\n \"maximum\": 2147483647\n }\n }\n },\n \"stringInputs\": {\n \"$id\": \"/properties/stringInputs\",\n \"type\":\"object\",\n \"description\": \"Input parameter for regular widgets.\\nFor single-valued widgets, it will be a single value list; for\\nmulti-valued widgets, such as checkbox, all the values are presented.\",\n \"properties\": {\n \"value\": {\n \"type\":\"array\",\n \"items\": {\n \"type\":\"string\"\n }\n }\n }\n },\n \"dateTimeInput\": {\n \"$id\": \"/properties/dateTimeInput\",\n \"type\": \"object\",\n \"description\": \"Input Parameter for Date and Time Picker widget.\",\n \"properties\": {\n \"msSinceEpoch\": {\n \"type\": \"integer\",\n \"description\": \"The time selected by the user, in milliseconds since epoch (00:00:00 UTC on 1 January 1970).\",\n \"minimum\": -9223372036854775808,\n \"maximum\": 9223372036854775807\n },\n \"hasDate\": {\n \"type\": \"boolean\",\n \"description\": \"true if the input date time includes a date; if false only a time is included.\"\n },\n \"hasTime\": {\n \"type\": \"boolean\",\n \"description\": \"true if the input date time includes a time; if false only a date is included.\"\n }\n }\n },\n \"dateInput\": {\n \"$id\": \"/properties/dateInput\",\n \"type\": \"object\",\n \"description\": \"Input Parameter for Date Picker widget.\",\n \"properties\": {\n \"msSinceEpoch\": {\n \"type\": \"integer\",\n \"description\": \"The time selected by the user, in milliseconds since epoch (00:00:00 UTC on 1 January 1970).\",\n \"minimum\": -9223372036854775808,\n \"maximum\": 9223372036854775807\n }\n }\n },\n \"timeInput\": {\n \"description\": \"Input Parameter for Time Picker widget.\",\n \"$id\": \"/properties/timeInput\",\n \"type\": \"object\",\n \"properties\": {\n \"hours\": {\n \"type\": \"integer\",\n \"description\": \"The hour number selected by the user.\",\n \"minimum\": 0,\n \"maximum\": 23\n },\n \"minutes\": {\n \"type\": \"integer\",\n \"description\": \"The minute number selected by the user.\",\n \"minimum\": 0,\n \"maximum\": 59\n }\n }\n }\n },\n \"$ref\": \"#/definitions/eventObject\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"definitions\": {\n \"gmailEventObject\": {\n \"$id\": \"/properties/gmailEventObject\",\n \"type\": \"object\",\n \"description\": \"The Gmail event object is the portion of the overall event object that carries information about a user's Gmail messages. It's only present in an event object if the host application is Gmail.\",\n \"properties\": {\n \"messageId\": {\n \"type\": \"string\",\n \"description\": \"The ID of the currently open Gmail message.\"\n },\n \"threadId\": {\n \"type\": \"string\",\n \"description\": \"The currently open Gmail thread ID.\"\n },\n \"accessToken\": {\n \"description\": \"The Gmail-specific access token. You can use this token with the \\\"X-Goog-Gmail-Access-Token\\\" HTTP header to grant your add-on temporary access to a user's currently open Gmail message or let your add-on compose new drafts.\",\n \"type\": \"string\"\n },\n \"toRecipients\": {\n \"type\":\"array\",\n \"description\": \"The list of \\\"To:\\\" recipient email addresses currently included in a draft the add-on is composing\",\n \"items\": {\n \"type\":\"string\"\n }\n },\n \"ccRecipients\": {\n \"type\":\"array\",\n \"description\": \"The list of \\\"CC:\\\" recipient email addresses currently included in a draft the add-on is composing\",\n \"items\": {\n \"type\":\"string\"\n }\n },\n \"bccRecipients\": {\n \"type\":\"array\",\n \"description\": \"The list of \\\"BCC:\\\" recipient email addresses currently included in a draft the add-on is composing\",\n \"items\": {\n \"type\":\"string\"\n }\n }\n }\n }\n },\n \"$ref\": \"#/definitions/gmailEventObject\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"definitions\": {\n \"calendarEventObject\": {\n \"$id\": \"/properties/calendarEventObject\",\n \"description\": \"The Calendar event object is the portion of the overall event object that carries information about a user's calendar and calendar events. It's only present in an event object if the host application is Google Calendar.\",\n \"type\":\"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"description\": \"The event ID.\"\n },\n \"recurringEventId\": {\n \"type\": \"string\",\n \"description\": \"The ID of a recurring event.\"\n },\n \"calendarId\": {\n \"type\": \"string\",\n \"description\": \"The calendar ID.\"\n },\n \"organizer\": {\n \"type\": \"object\",\n \"description\": \"An object representing the organizer of the event.\",\n \"properties\": {\n \"email\": {\n \"description\": \"The event organizer's email address.\",\n \"type\": \"string\"\n }\n }\n },\n \"attendees\": {\n \"type\": \"array\",\n \"description\": \"A list of the attendees of the calendar event.\",\n \"items\": {\n \"$ref\": \"#definitions/attendee\"\n }\n },\n \"conferenceData\": {\n \"type\": \"object\",\n \"description\": \"An object representing any conference data associated with this event, such as Google Meet conference details.\",\n \"$ref\": \"#definitions/conferenceData\"\n },\n \"capabilities\": {\n \"type\": \"object\",\n \"description\": \"An object describing the capabilities of the add-on to view or update event information.\",\n \"properties\": {\n \"canSeeAttendees\": {\n \"type\": \"boolean\",\n \"description\": \"true if the add-on can read the event attendee list; false otherwise.\"\n },\n \"canAddAttendees\": {\n \"type\": \"boolean\",\n \"description\": \"true if the add-on can add new attendees to the event attendee list; false otherwise.\"\n },\n \"canSeeConferenceData\": {\n \"type\": \"boolean\",\n \"description\": \"true if the add-on can read the event conference data; false otherwise.\"\n },\n \"canSetConferenceData\": {\n \"type\": \"boolean\",\n \"description\": \"true if the add-on can update the event conference data; false otherwise.\"\n }\n }\n }\n }\n },\n \"attendee\": {\n \"type\": \"object\",\n \"$id\": \"/properties/attendee\",\n \"properties\": {\n \"email\": {\n \"type\": \"string\",\n \"description\": \"The attendee email address.\"\n },\n \"optional\": {\n \"type\": \"boolean\",\n \"description\": \"true if the attendance for this attendee is marked as optional; false otherwise.\"\n },\n \"displayName\": {\n \"type\": \"string\",\n \"description\": \"The attendee displayed name.\"\n },\n \"organizer\": {\n \"type\": \"boolean\",\n \"description\": \"true if the attendee is an organizer for this event.\"\n },\n \"self\": {\n \"type\": \"boolean\",\n \"description\": \"true if this attendee represents the calendar in which this event appears; false otherwise.\"\n },\n \"resource\": {\n \"type\": \"boolean\",\n \"description\": \"true if the attendee represents a resource, such as room or piece of equipment; false otherwise.\"\n },\n \"responseStatus\": {\n \"type\": \"string\",\n \"description\": \"The attendee's response status. Possible values include the following:\\naccepted: The attendee has accepted the event invitation.\\ndeclined: The attendee has declined the event invitation.\\nneedsAction: The attendee has not responded to the event invitation.\\ntentative: The attendee has tentatively accepted the event invitation.\"\n },\n \"comment\": {\n \"type\": \"string\",\n \"description\": \"The attendee's response comment, if any.\"\n },\n \"additionalGuests\": {\n \"type\": \"integer\",\n \"description\": \"The number of additional guests the attendee had indicated they are bringing. Defaults to zero.\",\n \"default\": 0,\n \"minimum\": 0,\n \"maximum\": 2147483647\n }\n }\n },\n \"conferenceData\": {\n \"$id\": \"/properties/conferenceData\",\n \"type\": \"object\",\n \"properties\": {\n \"conferenceId\": {\n \"type\": \"string\",\n \"description\": \"The ID of the conference. This ID is meant to allow applications to keep track of conferences; you shouldn't display this ID to users.\"\n },\n \"conferenceSolution\": {\n \"type\": \"object\",\n \"description\": \"An object representing the conference solution, such as Hangouts or Google Meet.\",\n \"$ref\": \"#/definitions/conferenceSolution\"\n },\n \"entryPoints\": {\n \"type\": \"array\",\n \"description\": \"The list of conference entry points, such as URLs or phone numbers.\",\n \"items\": {\n \"$ref\": \"#/definitions/entryPoint\"\n }\n },\n \"notes\": {\n \"type\": \"string\",\n \"description\": \"Additional notes (such as instructions from the domain administrator or legal notices) about the conference to display to the user. Can contain HTML. The maximum length is 2048 characters.\"\n },\n \"parameters\": {\n \"type\": \"object\",\n \"$ref\": \"#/definitions/parameters\",\n \"description\": \"An object containing a map of defined parameter data for use by the add-on.\"\n }\n }\n },\n \"conferenceSolution\": {\n \"type\": \"object\",\n \"description\": \"An object representing the conference solution, such as Hangouts or Google Meet.\",\n \"$id\": \"/properties/conferenceSolution\",\n \"properties\": {\n \"iconUri\": {\n \"type\": \"string\",\n \"description\": \"The URI for the user-visible icon representing this conference solution.\"\n },\n \"key\": {\n \"type\": \"object\",\n \"description\": \"The key which uniquely identifies the conference solution for this event.\",\n \"properties\": {\n \"type\": {\n \"type\": \"string\",\n \"description\": \"The conference solution type. Possible values include the following:\\neventHangout for Hangouts for consumers (http://hangouts.google.com).\\neventNamedHangout for classic Hangouts for Google Workspace users (http://hangouts.google.com).\\nhangoutsMeet for Google Meet (http://meet.google.com).\"\n }\n }\n },\n \"name\": {\n \"type\":\"string\",\n \"description\": \"The user-visible name of this conference solution (not localized).\"\n }\n }\n },\n \"entryPoint\": {\n \"$id\": \"/properties/entryPoint\",\n \"description\": \"Entry point objects carry information about the established means of accessing a given conference, such as by phone or video. This information is present in the event object if and only if the data is present in the Calendar event and the add-on sets its addOns.calendar.currentEventAccess manifest (https://developers.google.com/workspace/add-ons/concepts/manifests#calendar_fields) field to READ or READ_WRITE.\",\n \"properties\": {\n \"accessCode\": {\n \"type\":\"string\",\n \"description\": \"The access code used to access the conference. The maximum length is 128 characters. Conference providers typically only use a subset of {accessCode, meetingCode, passcode, password, pin} to provide access to conferences. Match and only ever display the fields the conference provider uses.\"\n },\n \"entryPointFeatures\": {\n \"type\":\"array\",\n \"description\": \"Features of the entry point. Currently these features only apply to phone entry points:\\ntoll: The entry point is a toll phone call.\\ntoll_free: The entry point is a toll-free phone call.\",\n \"items\": {\n \"type\": \"string\"\n }\n },\n \"entryPointType\": {\n \"type\":\"string\",\n \"description\": \"The type of entry point. Possible values are the following:\\nmore: Additional conference joining instructions, such as alternate phone numbers. A conference can only have one more entry point; if present at least one other type of entry point is also required.\\nphone: Join the conference via a phone number. A conference can have zero or more phone entry points. Google Calendar only displays the first two phone entry points, after formatting and sorting alphabetically.\\nsip: Join the conference over SIP. A conference can have at most one sip entry point.\\nvideo: Join the conference over HTTP. A conference can have at most one video entry point.\"\n },\n \"label\": {\n \"type\":\"string\",\n \"description\": \"The user-visible label for the entry point URI (not localized).\"\n },\n \"meetingCode\": {\n \"type\":\"string\",\n \"description\": \"The meeting code used to access the conference. The maximum length is 128 characters. Conference providers typically only use a subset of {accessCode, meetingCode, passcode, password, pin} to provide access to conferences. Match and only ever display the fields the conference provider uses.\"\n },\n \"passcode\": {\n \"type\":\"string\",\n \"description\": \"The passcode used to access the conference. The maximum length is 128 characters. Conference providers typically only use a subset of {accessCode, meetingCode, passcode, password, pin} to provide access to conferences. Match and only ever display the fields the conference provider uses.\"\n },\n \"password\": {\n \"type\":\"string\",\n \"description\": \"The password used to access the conference. The maximum length is 128 characters. Conference providers typically only use a subset of {accessCode, meetingCode, passcode, password, pin} to provide access to conferences. Match and only ever display the fields the conference provider uses.\"\n },\n \"pin\": {\n \"type\":\"string\",\n \"description\": \"The PIN used to access the conference. The maximum length is 128 characters. Conference providers typically only use a subset of {accessCode, meetingCode, passcode, password, pin} to provide access to conferences. Match and only ever display the fields the conference provider uses.\"\n },\n \"regionCode\": {\n \"type\":\"string\",\n \"description\": \"Region code of the phone number. Needed by users if the URI doesn't include a country code. Values are based on the public CLDR list of region codes (http://cldr.unicode.org/translation/country-names).\"\n },\n \"uri\": {\n \"type\":\"string\",\n \"description\": \"The URI of the entry point. The maximum length is 1300 characters. The formatting depends on the entry point type:\\nmore: A http: or https: schema is required.\\nphone: A tel: schema is required. The URI should include the entire dial sequence (for example, \\\"tel:+12345678900,,,12345678;1234\\\").\\nsip: A sip: or sips: schema is required. For example \\\"sip:12345678@myprovider.com\\\".\\nvideo: A http: or https: schema is required.\"\n }\n }\n },\n \"parameters\": {\n \"$id\": \"/properties/parameters\",\n \"description\": \"An object containing a map of defined parameter data for use by the add-on.\",\n \"properties\": {\n \"addOnParameters\": {\n \"description\": \"A map of parameter string keys and values. These keys and values are defined by the add-on developer to attach information to a specific conference for the add-on's use.\",\n \"type\": \"object\",\n \"$ref\": \"#/definitions/addOnParameters\"\n }\n }\n },\n \"addOnParameters\": {\n \"$id\": \"/properties/addOnParameters\",\n \"description\": \"A map of parameter string keys and values. These keys and values are defined by the add-on developer to attach information to a specific conference for the add-on's use.\",\n \"type\": \"object\",\n \"properties\": {\n \"parameters\": {\n \"type\": \"object\",\n \"additionalProperties\": {\n \"type\": \"string\"\n }\n }\n }\n }\n },\n \"$ref\": \"#/definitions/calendarEventObject\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"definitions\": {\n \"driveEventObject\": {\n \"$id\": \"/properties/driveEventObject\",\n \"type\": \"object\",\n \"description\": \"The Drive event object is the portion of the overall event object that carries information about a user's Google Drive and its contents. It's only present in an event object if the host application is Google Drive.\",\n \"properties\": {\n \"activeCursorItem\": {\n \"type\": \"object\",\n \"description\": \"The Drive item currently active.\",\n \"$ref\": \"#definitions/driveItemMetaData\"\n },\n \"selectedItems\": {\n \"type\":\"array\",\n \"description\": \"A list of items (files or folders) selected in Drive.\",\n \"items\": {\n \"$ref\": \"#definitions/driveItemMetaData\"\n }\n }\n }\n },\n \"driveItemMetaData\": {\n \"$id\": \"/properties/driveItemMetaData\",\n \"type\":\"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"description\": \"The ID of the selected item.\"\n },\n \"iconUrl\": {\n \"type\": \"string\",\n \"description\": \"The URL of the icon that represents the selected item.\"\n },\n \"mimeType\": {\n \"type\": \"string\",\n \"description\": \"The MIME type of the selected item.\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"The title of the selected item.\"\n },\n \"addonHasFileScopePermission\": {\n \"type\": \"boolean\",\n \"description\": \"If true, the add-on has requested and received https://www.googleapis.com/auth/drive.file scope authorization for this item; otherwise this field is false.\"\n }\n }\n }\n },\n \"$ref\": \"#/definitions/driveEventObject\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"definitions\": {\n \"docsEventObject\": {\n \"$id\": \"/properties/docsEventObject\",\n \"type\": \"object\",\n \"description\": \"The Docs event object is the portion of the overall event object that carries information about a user's Google Docs document. It's only present in an event object if the host application is Google Docs.\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"description\": \"The ID of the document open in the Docs UI\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"The title of the document open in the Docs UI\"\n },\n \"matchedUrl\": {\n \"$id\": \"/properties/matchedUrlObject\",\n \"type\": \"object\",\n \"description\": \"Object that contains the URL that matches the pattern set in the link preview trigger.\"\n \"properties\": {\n \"url\": {\n \"type\": \"string\",\n \"description\": \"URL that matches the pattern set in the link preview trigger.\"\n },\n }\n },\n \"addonHasFileScopePermission\": {\n \"type\":\"boolean\",\n \"description\": \" Whether or not the add-on has drive.file scope permission for this document.\"\n }\n }\n },\n },\n \"$ref\": \"#/definitions/docsEventObject\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"definitions\": {\n \"docsEventObject\": {\n \"$id\": \"/properties/docsEventObject\",\n \"type\": \"object\",\n \"description\": \"The Docs event object is the portion of the overall event object that carries information about a user's Google Sheet and its contents. It's only present in an event object if the host application is Google Sheets.\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"description\": \"The ID of the document open in the Sheets UI\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"The title of the document open in the Sheets UI\"\n },\n \"addonHasFileScopePermission\": {\n \"type\":\"boolean\",\n \"description\": \" Whether or not the add-on has drive.file scope permission for this document.\"\n }\n }\n },\n },\n \"$ref\": \"#/definitions/docsEventObject\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"definitions\": {\n \"docsEventObject\": {\n \"$id\": \"/properties/docsEventObject\",\n \"type\": \"object\",\n \"description\": \"The Slides event object is the portion of the overall event object that carries information about a user's Google Slides document. It's only present in an event object if the host application is Google Slides.\",\n \"properties\": {\n \"id\": {\n \"type\": \"string\",\n \"description\": \"The ID of the document open in the Slides UI\"\n },\n \"title\": {\n \"type\": \"string\",\n \"description\": \"The title of the document open in the Slides UI\"\n },\n \"addonHasFileScopePermission\": {\n \"type\":\"boolean\",\n \"description\": \" Whether or not the add-on has drive.file scope permission for this document.\"\n }\n }\n },\n },\n \"$ref\": \"#/definitions/docsEventObject\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"description\": \"The response to a form submit from the add-on. The most common response will contain a navigation of cards - e.g. pushing a card to render content to reflected the modified content.\",\n \"definitions\": {\n \"submitFormResponse\": {\n \"$id\": \"/properties/submitFormResponse\",\n \"type\": \"object\",\n \"required\": [\"renderActions\"],\n \"properties\": {\n \"renderActions\": {\n \"type\" : \"object\",\n \"$ref\": \"./renderActionSchema.json#/definitions/renderAction\"\n },\n \"stateChanged\": {\n \"type\" : \"boolean\",\n \"description\": \"Whether the state of the cards has changed and data in existing cards is stale.\"\n }\n }\n }\n },\n \"type\": \"object\",\n \"$ref\": \"#/definitions/submitFormResponse\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"description\": \"The response to the invocation of the add-on. The most common response will contain a navigation for cards - e.g. pushing a card to render content.\",\n \"definitions\": {\n \"notification\": {\n \"$id\": \"/properties/notification\",\n \"type\": \"object\",\n \"description\": \"Card action which displays a notification in the host app.\",\n \"properties\":{\n \"text\": {\n \"type\": \"string\",\n \"description\": \"Plain text to display for the notification, without html tags.\"\n }\n }\n },\n \"navigation\": {\n \"$id\": \"/properties/navigation\",\n \"type\": \"object\",\n \"properties\": {\n \"popToRoot\": {\n \"type\" : \"boolean\",\n \"description\": \"Card stack pops all card off except the root card.\"\n },\n \"pop\": {\n \"type\" : \"boolean\",\n \"description\": \"Card stack pops one card off.\"\n },\n \"popToCard\": {\n \"type\" : \"string\",\n \"description\": \"Card stack pops all cards above the specified card with given card name.\"\n },\n \"pushCard\": {\n \"type\" : \"object\",\n \"description\": \"A card to push on top of the stack, which will be shown to end users.\",\n \"$ref\": \"./cardSchema.json#/definitions/card\"\n },\n \"updateCard\": {\n \"type\" : \"object\",\n \"description\": \"Card stack updates the top card with a new card, preserving filled form\\nfields values. For non-equivalent field, the value is dropped.\",\n \"$ref\": \"./cardSchema.json#/definitions/card\"\n }\n }\n },\n \"action\": {\n \"$id\": \"/properties/action\",\n \"type\": \"object\",\n \"description\": \"Google Workspace add-on response to interact with the end user. The most common interaction is to render a card with a navigation.\",\n \"properties\": {\n \"navigations\": {\n \"description\": \"Specify the navigation within the card stack.\",\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/navigation\"\n }\n },\n \"link\": {\n \"type\": \"object\",\n \"description\": \"Immediately open the target link in a new tab or a popup.\",\n \"$ref\": \"./cardSchema.json#/definitions/openLink\"\n },\n \"notification\": {\n \"type\": \"object\",\n \"description\": \"Display a notification to the end-user.\",\n \"$ref\": \"#/definitions/notification\"\n }\n }\n },\n \"renderAction\": {\n \"type\": \"object\",\n \"properties\": {\n \"action\": {\n \"type\": \"object\",\n \"$ref\": \"#/definitions/action\",\n \"description\": \"Google Workspace add-on response to interact with the end user. The most common interaction is to render a card with a navigation.\"\n },\n \"hostAppAction\": {\n \"type\":\"object\",\n \"description\": \"Actions handled by individual host apps.\",\n \"$ref\": \"./hostAppActionSchema.json#/definitions/hostAppActionMarkup\"\n },\n \"schema\": {\n \"type\": \"string\",\n \"description\": \"This is a no-op schema field that may be present in the markup for syntax\\n checking.\"\n }\n }\n }\n },\n \"type\": \"object\",\n \"$ref\": \"#/definitions/renderAction\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"definitions\": {\n \"hostAppActionMarkup\": {\n \"$id\": \"/properties/hostAppActionMarkup\",\n \"type\": \"object\",\n \"properties\": {\n \"gmailAction\": {\n \"$id\": \"/properties/gmailAction\",\n \"type\": \"object\",\n \"$ref\": \"./gmailSchema.json#/definitions/gmailClientActionMarkup\"\n },\n \"calendarAction\": {\n \"$id\": \"/properties/calendarAction\",\n \"type\": \"object\",\n \"$ref\": \"./calendarSchema.json#/definitions/calendarClientActionMarkup\"\n },\n \"driveAction\": {\n \"$id\": \"/properties/driveAction\",\n \"type\": \"object\",\n \"$ref\": \"./drive_schema.json#/definitions/drive_client_action_markup\"\n },\n \"editor_action\": {\n \"$id\": \"/properties/editor_action\",\n \"type\": \"object\",\n \"$ref\": \"./editor_schema.json#/definitions/editor_client_action_markup\"\n }\n },\n \"description\": \"\"\n }\n },\n \"type\": \"object\",\n \"$ref\": \"#/definitions/hostAppActionMarkup\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"definitions\": {\n \"gmailClientActionMarkup\": {\n \"$id\": \"/properties/gmailClientActionMarkup\",\n \"type\": \"object\",\n \"properties\": {\n \"updateDraftActionMarkup\": {\n \"type\": \"object\",\n \"description\": \"Update Draft Action Markup\",\n \"$ref\": \"#definitions/updateDraftActionMarkup\"\n },\n \"openCreatedDraftActionMarkup\": {\n \"type\": \"object\",\n \"description\": \"Open Created Draft Action Markup\",\n \"$ref\": \"#definitions/openCreatedDraftActionMarkup\"\n }\n }\n },\n \"updateDraftActionMarkup\": {\n \"$id\" : \"/properties/updateDraftActionMarkup\",\n \"type\": \"object\",\n \"properties\": {\n \"updateBody\": {\n \"type\": \"object\",\n \"$ref\": \"#/definitions/updateBody\"\n },\n \"updateToRecipients\": {\n \"type\": \"object\",\n \"description\": \"Update To Recipients.\",\n \"$ref\": \"#/definitions/updateToRecipients\"\n },\n \"updateCcRecipients\": {\n \"type\": \"object\",\n \"description\": \"Update CC Recipients.\",\n \"$ref\": \"#/definitions/updateCcRecipients\"\n },\n \"updateBccRecipients\": {\n \"type\": \"object\",\n \"description\": \"Update BCC Recipients.\",\n \"$ref\": \"#/definitions/updateBccRecipients\"\n },\n \"updateSubject\": {\n \"type\": \"object\",\n \"description\": \"Update Subject\",\n \"$ref\": \"#definitions/updateSubject\"\n }\n }\n },\n \"updateBody\": {\n \"$id\": \"/prioperties/updateBody\",\n \"type\": \"object\",\n \"description\": \"A field which contains a series of updates action to perform on the draft\\n body that user is currently editing.\",\n \"properties\": {\n \"insertContents\": {\n \"type\": \"array\",\n \"description\": \"A repeated field which contains a series of insert content to perform\\n on the draft that user is currently editing. The content currently\\n contains 1) HTML content or 2) plain text content.\",\n \"items\": {\n \"$ref\": \"#definitions/insertContent\"\n }\n },\n \"type\": {\n \"type\": \"string\",\n \"description\": \"\",\n \"enum\": [\n \"IN_PLACE_INSERT\"\n ]\n }\n }\n },\n \"insertContent\": {\n \"$id\": \"/prioperties/insertContent\",\n \"type\": \"object\",\n \"properties\": {\n \"content\": {\n \"type\": \"string\",\n \"description\": \"The content to be inserted\"\n },\n \"contentType\": {\n \"type\": \"string\",\n \"description\": \"The type of inserted content\",\n \"enum\": [\n \"TEXT\",\n \"MUTABLE_HTML\",\n \"IMMUTABLE_HTML\"\n ]\n }\n }\n },\n \"recipient\": {\n \"$id\": \"/properties/recipient\",\n \"type\": \"object\",\n \"description\": \"recipient\",\n \"properties\": {\n \"email\": {\n \"type\": \"string\"\n }\n }\n },\n \"updateToRecipients\": {\n \"$id\": \"/properties/updateToRecipients\",\n \"description\": \"If set, replaces the existing To recipients of the draft the user is currently editing.\",\n \"type\": \"object\",\n \"properties\": {\n \"toRecipients\": {\n \"type\": \"array\",\n \"description\": \"To Recipients\",\n \"items\": {\n \"$ref\": \"#/definitions/recipient\"\n }\n }\n }\n },\n \"updateCcRecipients\": {\n \"$id\": \"/properties/updateCcRecipients\",\n \"description\": \"If set, replaces the existing Cc recipients of the draft the user is currently editing.\",\n \"type\": \"object\",\n \"properties\": {\n \"CcRecipients\": {\n \"type\": \"array\",\n \"description\": \"CC Recipients\",\n \"items\": {\n \"$ref\": \"#/definitions/recipient\"\n }\n }\n }\n },\n \"updateBccRecipients\": {\n \"$id\": \"/properties/updateBccRecipients\",\n \"description\": \"If set, replaces the existing Bcc recipients of the draft the user is currently editing.\",\n \"type\": \"object\",\n \"properties\": {\n \"BccRecipients\": {\n \"type\": \"array\",\n \"description\": \"BCC Recipients\",\n \"items\": {\n \"$ref\": \"#/definitions/recipient\"\n }\n }\n }\n },\n \"updateSubject\": {\n \"$id\": \"/properties/updateSubject\",\n \"description\": \"If set, replaces the existing subject of the draft the user is currently editing.\",\n \"type\": \"object\",\n \"properties\": {\n \"subject\": {\n \"type\": \"string\"\n }\n }\n },\n \"openCreatedDraftActionMarkup\": {\n \"type\": \"object\",\n \"$id\": \"/properties/openCreatedDraftActionMarkup\",\n \"description\": \"\",\n \"properties\": {\n \"draftId\": {\n \"type\": \"string\",\n \"description\": \"The ID of the newly-created draft in the form \\\"r123\\\".\"\n },\n \"draftThreadId\": {\n \"type\": \"string\",\n \"description\": \"The ID of the thread containing the newly-created draft, e.g., \\\"15e9fa622ce1029d\\\".\"\n }\n }\n }\n },\n \"type\": \"object\",\n \"$ref\": \"#/definitions/gmailClientActionMarkup\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"definitions\": {\n \"editAttendeesActionMarkup\": {\n \"$id\": \"/properties/editAttendeesActionMarkup\",\n \"type\": \"object\",\n \"properties\": {\n \"addAttendeeEmails\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n }\n }\n },\n \"description\": \"\"\n },\n \"editConferenceDataActionMarkup\": {\n \"$id\": \"/properties/editConferenceDataActionMarkup\",\n \"type\": \"object\",\n \"properties\": {\n \"conferenceData\": {\n \"type\": \"object\",\n \"$ref\": \"./conferenceDataMarkupSchema.json#/definitions/conferenceDataMarkup\"\n }\n }\n },\n \"addAttachmentsActionMarkup\": {\n \"$id\": \"/properties/addAttachmentsActionMarkup\",\n \"type\": \"object\",\n \"properties\": {\n \"addonAttachments\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"resourceUrl\": {\n \"type\": \"string\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"mimeType\": {\n \"type\": \"string\"\n },\n \"iconUrl\": {\n \"type\": \"string\"\n }\n }\n }\n }\n }\n },\n \"createConferenceDataActionMarkup\": {\n \"$id\": \"/properties/createConferenceDataActionMarkup\",\n \"type\": \"object\",\n \"properties\": {\n \"createConferenceData\": {\n \"type\": \"object\",\n \"$ref\": \"./conferenceDataMarkupSchema.json#/definitions/conferenceDataMarkup\"\n }\n }\n },\n \"createConferenceSettingUrlActionMarkup\": {\n \"$id\": \"/properties/createConferenceSettingUrlActionMarkup\",\n \"type\": \"object\",\n \"properties\": {\n \"settingsUrl\": {\n \"type\": \"string\"\n }\n }\n },\n \"calendarSubscriptionActionMarkup\": {\n \"$id\": \"/properties/calendarSubscriptionActionMarkup\",\n \"type\": \"object\",\n \"properties\": {\n \"operation\": {\n \"type\": \"string\",\n \"enum\": [\n \"OPERATION_UNSPECIFIED\",\n \"CREATE\"\n ]\n },\n \"calendarId\": {\n \"type\": \"string\"\n }\n }\n },\n \"calendarClientActionMarkup\": {\n \"$id\": \"/properties/calendarClientActionMarkup\",\n \"type\": \"object\",\n \"properties\": {\n \"editAttendeesActionMarkup\": {\n \"type\": \"object\",\n \"description\": \"\",\n \"$ref\": \"#/definitions/editAttendeesActionMarkup\"\n },\n \"editConferenceDataActionMarkup\": {\n \"type\": \"object\",\n \"description\": \"\",\n \"$ref\": \"#/definitions/editConferenceDataActionMarkup\"\n },\n \"addAttachmentsActionMarkup\": {\n \"type\": \"object\",\n \"description\": \"\",\n \"$ref\": \"#/definitions/addAttachmentsActionMarkup\"\n },\n \"createConferenceDataActionMarkup\": {\n \"type\": \"object\",\n \"description\": \"\",\n \"$ref\": \"#/definitions/createConferenceDataActionMarkup\"\n },\n \"createConferenceSettingUrlActionMarkup\": {\n \"type\": \"object\",\n \"description\": \"\",\n \"$ref\": \"#/definitions/createConferenceSettingUrlActionMarkup\"\n },\n \"calendarSubscriptionActionMarkup\": {\n \"type\": \"object\",\n \"description\": \"\",\n \"$ref\": \"#/definitions/calendarSubscriptionActionMarkup\"\n }\n }\n }\n },\n \"type\": \"object\",\n \"$ref\": \"#/definitions/calendarClientActionMarkup\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"definitions\": {\n \"driveClientActionMarkup\": {\n \"$id\": \"/properties/driveClientActionMarkup\",\n \"type\": \"object\",\n \"properties\": {\n \"requestFileScope\": {\n \"type\": \"object\",\n \"properties\": {\n \"itemId\": {\n \"type\": \"string\"\n }\n }\n }\n }\n }\n },\n \"type\": \"object\",\n \"$ref\": \"#/definitions/driveClientActionMarkup\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"definitions\": {\n \"editor_client_action_markup\": {\n \"$id\": \"/properties/editor_client_action_markup\",\n \"type\": \"object\",\n \"properties\": {\n \"request_file_scope_for_active_document\": {\n \"type\": \"object\"\n }\n }\n }\n },\n \"type\": \"object\",\n \"$ref\": \"#/definitions/editor_client_action_markup\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"description\": \"A response for getting autocomplete container, which includes elements\\n necessary for showing auto complete items for text field.\",\n \"definitions\": {\n \"getAutocompletionResponse\": {\n \"$id\": \"/properties/getAutocompletionResponse\",\n \"type\": \"object\",\n \"required\": [\"autoComplete\"],\n \"properties\": {\n \"autoComplete\": {\n \"type\" : \"object\",\n \"$ref\": \"./cardSchema.json#/definitions/suggestions\"\n },\n \"schema\": {\n \"type\" : \"string\",\n \"description\": \"This is a no-op schema field that may be present in the markup for syntax checking.\"\n }\n }\n }\n },\n \"type\": \"object\",\n \"$ref\": \"#/definitions/getAutocompletionResponse\"\n}\n```\n\nExample:\n```text\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"definitions\": {\n \"requesting_google_scopes\": {\n \"$id\": \"/properties/requesting_google_scopes\",\n \"type\": \"object\",\n \"properties\": {\n \"scopes\": {\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\"\n },\n \"description\": \"The scopes that the add-on is requesting.\"\n },\n \"all_scopes\": {\n \"type\": \"boolean\",\n \"description\": \"If true, the add-on is requesting all scopes from the manifest. The scopes field should be empty in this case.\"\n }\n },\n \"description\": \"Represents the scopes an add-on is requesting from the end-user.\"\n }\n },\n \"type\": \"object\",\n \"$ref\": \"#/definitions/requesting_google_scopes\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.571Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":44,"totalLines":3628,"estimatedTokens":29772}}728{"id":"doc-update_a_configuration_card_google_workspace_add-c25b9911","source":"documentation","title":"Update a configuration card | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/update-cards","text":"Example:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Test Project\",\n \"logoUrl\": \"https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"modify_card\",\n \"state\": \"ACTIVE\",\n \"name\": \"Modify Card\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"value1\",\n \"description\": \"The first input\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n },\n {\n \"id\": \"value2\",\n \"description\": \"The second number\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n },\n {\n \"id\": \"value3\",\n \"description\": \"The third number\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"outputs\": [\n {\n \"id\": \"result\",\n \"description\": \"Modify Card result\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfig\",\n \"onExecuteFunction\": \"onExecute\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n// Return a configuration card for the step.\nfunction onConfig() {\n\n const textInput_1 = CardService.newTextInput()\n .setFieldName(\"value1\")\n .setTitle(\"First Value!\")\n .setId(\"text_input_1\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n )\n );\n\n const textInput_2 = CardService.newTextInput()\n .setFieldName(\"value2\")\n .setTitle(\"Second Value!\")\n .setId(\"text_input_2\")\n .setHostAppDataSource(\n CardService.newHostAppDataSource()\n .setWorkflowDataSource(\n CardService.newWorkflowDataSource()\n .setIncludeVariables(true)\n )\n );\n\n // Create buttons that call functions to modify the card.\n const buttonSet = CardService.newButtonSet()\n .setId(\"card_modification_buttons\")\n .addButton(\n CardService.newTextButton()\n .setAltText(\"Insert Card Section\")\n .setText(\"Insert Card Section\")\n .setOnClickAction(\n CardService.newAction()\n .setFunctionName('insertSection')\n )\n )\n .addButton(\n CardService.newTextButton()\n .setAltText(\"Insert Text Widget\")\n .setText(\"Insert Text Widget\")\n .setOnClickAction(\n CardService.newAction()\n .setFunctionName('insertWidget')\n )\n );\n\n var firstSection =\n CardService.newCardSection()\n .setId(\"card_section_1\")\n .addWidget(textInput_1)\n .addWidget(textInput_2)\n .addWidget(buttonSet);\n\n var card = CardService.newCardBuilder()\n .addSection(firstSection)\n .build();\n\n var navigation = AddOnsResponseService.newNavigation()\n .pushCard(card);\n\n var action = AddOnsResponseService.newAction()\n .addNavigation(navigation);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setAction(action)\n .build();\n}\n// If the step runs, return output variables.\nfunction onExecute(event) {\n var value1 = event.workflow.actionInvocation.inputs[\"value1\"].stringValues[0];\n var value2 = event.workflow.actionInvocation.inputs[\"value2\"].stringValues[0];\n var value3; // Declare value3\n var result;\n // Check if the \"value3\" key exists in the inputs, which only exists if\n // the third text input is inserted in the card.\n if (event.workflow.actionInvocation.inputs[\"value3\"]) {\n value3 = event.workflow.actionInvocation.inputs[\"value3\"].stringValues[0];\n\n result = value1 + \"\\n\" + value2 + \"\\n\" + value3;\n } else {\n result = value1 + \"\\n\" + value2;\n }\n // Output the file ID through a variableData, which can be used by\n // later steps in a workflow.\n const variableData = AddOnsResponseService.newVariableData()\n .addStringValue(result);\n\n let textFormatElement = AddOnsResponseService.newTextFormatElement()\n .setText(\"Output: \" + JSON.stringify(variableData));\n\n let workflowTextFormat = AddOnsResponseService.newWorkflowTextFormat()\n .addTextFormatElement(textFormatElement);\n\n // The string key for each variableData must match with the IDs of the\n // outputs defined in the manifest.\n let returnAction = AddOnsResponseService.newReturnOutputVariablesAction()\n .setVariables({ \"result\": variableData })\n .setLog(workflowTextFormat);\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(returnAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction).build();\n}\n\n/**\n * Inserts a new CardSection with several widgets.\n */\nfunction insertSection(event) {\n console.log(\"event: \" + JSON.stringify(event, null, 3));\n\n const optionalSection = CardService.newCardSection().setId(\"card_section_2\")\n .addWidget(\n CardService.newTextParagraph().setText(\"This is a text paragraph inside the new card section\")\n )\n .addWidget(\n CardService.newButtonSet().addButton(\n CardService.newTextButton().setText(\"Remove Card Section\").setOnClickAction(CardService.newAction().setFunctionName(\"removeSection\"))))\n .addWidget(\n CardService.newButtonSet().addButton(\n CardService.newTextButton().setText(\"Replace Card Section\").setOnClickAction(CardService.newAction().setFunctionName(\"replaceSection\"))));\n\n // Insert the new section beneath section \"card_section_1\".\n // You can also insert at the top of a Card.\n const sectionInsertion = AddOnsResponseService.newInsertSection().insertBelowSection(\"card_section_1\").setSection(optionalSection);\n\n const modifyAction = AddOnsResponseService.newAction()\n .addModifyCard(AddOnsResponseService.newModifyCard().setInsertSection(sectionInsertion));\n\n return AddOnsResponseService.newRenderActionBuilder().setAction(modifyAction).build();\n}\n/**\n * Replaces an existing CardSection with a new CardSection with the same ID.\n */\nfunction replaceSection(event) {\n console.log(\"event: \" + JSON.stringify(event, null, 3));\n\n const replacementSection = CardService.newCardSection().setId(\"card_section_2\")\n .addWidget(\n CardService.newTextParagraph().setText(\"Card Section replaced!\")\n )\n .addWidget(\n CardService.newButtonSet().addButton(\n CardService.newTextButton().setText(\"Remove Card Section\").setOnClickAction(CardService.newAction().setFunctionName(\"removeSection\"))));\n\n const modifyAction = AddOnsResponseService.newAction()\n .addModifyCard(AddOnsResponseService.newModifyCard().setReplaceSection(replacementSection));\n\n return AddOnsResponseService.newRenderActionBuilder().setAction(modifyAction).build();\n}\n\n/**\n* Replaces an existing CardWidget with a new CardWidget with the same ID.\n*/\nfunction replaceWidget(event) {\nconsole.log(\"event: \" + JSON.stringify(event, null, 3));\n\nconst replacementWidget = CardService.newTextParagraph().setText(\"This is a replacement widget!\").setId(\"text_input_3\");\n\nconst modifyAction = AddOnsResponseService.newAction()\n .addModifyCard(AddOnsResponseService.newModifyCard().setReplaceWidget(replacementWidget));\n\nreturn AddOnsResponseService.newRenderActionBuilder().setAction(modifyAction).build();\n}\n\n/**\n * Inserts an additional text input widget and a button that can remove it.\n */\nfunction insertWidget(event) {\n console.log(\"event: \" + JSON.stringify(event, null, 3));\n\n const buttonSet = CardService.newButtonSet().setId(\"widget_1\")\n .addButton(\n CardService.newTextButton()\n .setAltText(\"Remove Widget\")\n .setText(\"Remove Widget\")\n .setOnClickAction(\n CardService.newAction().setFunctionName('removeWidget')\n ))\n .addButton(\n CardService.newTextButton()\n .setAltText(\"Replace Widget\")\n .setText(\"Replace Widget\")\n .setOnClickAction(\n CardService.newAction().setFunctionName('replaceWidget')\n ));\n\n const textInput_3 = CardService.newTextInput().setFieldName(\"value3\").setTitle(\"Third Value\").setId(\"text_input_3\");\n\n // Widgets can be inserted either before or after another widget.\n // This example inserts a button below a text input, then inserts\n // another text input between the existing text input and the new button.\n const buttonSetInsertion = AddOnsResponseService.newInsertWidget().insertBelowWidget(\"text_input_2\").setWidget(buttonSet);\n const textInputInsertion = AddOnsResponseService.newInsertWidget().insertAboveWidget(\"widget_1\").setWidget(textInput_3);\n\n const modifyAction = AddOnsResponseService.newAction()\n .addModifyCard(AddOnsResponseService.newModifyCard().setInsertWidget(buttonSetInsertion))\n .addModifyCard(AddOnsResponseService.newModifyCard().setInsertWidget(textInputInsertion));\n\n return AddOnsResponseService.newRenderActionBuilder().setAction(modifyAction).build();\n}\n\n/**\n * Removes one or more existing widgets by ID.\n */\nfunction removeWidget(event) {\n console.log(\"event: \" + JSON.stringify(event, null, 3));\n\n const textInputDeletion = AddOnsResponseService.newRemoveWidget().setWidgetId(\"text_input_3\");\n const buttonSetDeletion = AddOnsResponseService.newRemoveWidget().setWidgetId(\"widget_1\");\n\n const modifyAction = AddOnsResponseService.newAction()\n .addModifyCard(\n AddOnsResponseService.newModifyCard().setRemoveWidget(textInputDeletion)\n )\n .addModifyCard(\n AddOnsResponseService.newModifyCard().setRemoveWidget(buttonSetDeletion)\n );\n\n return AddOnsResponseService.newRenderActionBuilder().setAction(modifyAction).build();\n}\n\n/**\n * Removes an existing card section by ID.\n */\nfunction removeSection(event) {\n console.log(\"event: \" + JSON.stringify(event, null, 3));\n\n const sectionDeletion = AddOnsResponseService.newRemoveSection().setSectionId('card_section_2');\n\n const modifyAction = AddOnsResponseService.newAction()\n .addModifyCard(\n AddOnsResponseService.newModifyCard().setRemoveSection(sectionDeletion)\n );\n\n return AddOnsResponseService.newRenderActionBuilder().setAction(modifyAction).build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.573Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":311,"estimatedTokens":2651}}729{"id":"doc-define_a_dynamic_variable_google_workspace_add_o-d75cd0c1","source":"documentation","title":"Define a dynamic variable | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/dynamic-variables","text":"Example:\n```text\n\"flows\": {\n \"workflowElements\" : [{\n \"id\": \"getDynamicVariable\",\n \"state\": \"ACTIVE\",\n \"name\": \"Get Dynamic Variable\",\n \"description\": \"Get Dynamic Variable\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"dynamic_resource_input\",\n \"description\": \"Dynamic Resource Input\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"INTEGER\"\n }\n }\n ],\n \"outputs\": [\n {\n \"id\": \"dynamic_resource_output\",\n \"description\": \"Dynamic Data\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"resourceType\": {\n \"workflowResourceDefinitionId\": \"resource_definition_1\"\n }\n }\n }\n ],\n \"onConfigFunction\": \"onDynamicVariableConfigFunction\",\n \"onExecuteFunction\": \"onDynamicVariableExecuteFunction\"\n }\n }],\n \"workflowResourceDefinitions\": [{\n \"id\": \"resource_definition_1\",\n \"name\": \"Dynamic Resource\",\n \"providerFunction\": \"onDynamicProviderFunction\",\n \"resourceType\" : \"DYNAMIC\"\n }],\n \"dynamicResourceDefinitionProvider\" : \"onDynamicDefinitionFunction\",\n}\n```\n\nExample:\n```text\nfunction onDynamicVariableConfigFunction() {\n let section = CardService.newCardSection()\n .addWidget(\n CardService.newTextInput()\n .setFieldName(\"dynamic_resource_input\")\n .setTitle(\"Dynamic Resource Input\")\n .setHint(\"Enter an integer value between 1 and 3 (inclusive) for the corresponding number of output variables\")\n );\n\n const card = CardService.newCardBuilder()\n .addSection(section)\n .build();\n\n return card;\n}\n\nfunction onDynamicDefinitionFunction(e) {\n console.log(\"Payload in onDynamicDefinitionFunction: \", JSON.stringify(e));\n var input_value = e.workflow.resourceFieldsDefinitionRetrieval.inputs.dynamic_resource_input.integerValues[0];\n\n let resourceDefinitions = AddOnsResponseService.newDynamicResourceDefinition()\n .setResourceId(\"resource_definition_1\")\n .addResourceField(\n AddOnsResponseService.newResourceField()\n .setSelector(\"question_1\")\n .setDisplayText(\"Question 1\")\n );\n\n if (input_value == 2 || input_value == 3) {\n resourceDefinitions = resourceDefinitions\n .addResourceField(\n AddOnsResponseService.newResourceField()\n .setSelector(\"question_2\")\n .setDisplayText(\"Question 2\")\n );\n }\n if (input_value == 3) {\n resourceDefinitions = resourceDefinitions\n .addResourceField(\n AddOnsResponseService.newResourceField()\n .setSelector(\"question_3\")\n .setDisplayText(\"Question 3\")\n );\n }\n\n let workflowAction = AddOnsResponseService.newResourceFieldsDefinitionRetrievedAction()\n .addDynamicResourceDefinition(resourceDefinitions);\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n let renderAction = AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n\n return renderAction;\n}\n\nfunction onDynamicVariableExecuteFunction(e) {\n console.log(\"Payload in onDynamicVariableExecuteFunction: \", JSON.stringify(e));\n\n let workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()\n .setVariableDataMap({\n \"dynamic_resource_output\": AddOnsResponseService.newVariableData()\n .addResourceReference(\"my_dynamic_resource_id\")\n });\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n let renderAction = AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n\n return renderAction;\n}\n\nfunction onDynamicProviderFunction(e) {\n console.log(\"Payload in onDynamicProviderFunction: \", JSON.stringify(e));\n\n // resourceId == \"my_dynamic_resource_id\"\n var resourceId = e.workflow.resourceRetrieval.resourceReference.resourceId;\n // workflowResourceDefinitionId == \"resource_definition_1\"\n var workflowResourceDefinitionId = e.workflow.resourceRetrieval.resourceReference.resourceType.workflowResourceDefinitionId;\n\n const workflowAction = AddOnsResponseService.newResourceRetrievedAction()\n .setResourceData(\n AddOnsResponseService.newResourceData()\n .addVariableData(\"question_1\", AddOnsResponseService.newVariableData().addStringValue(\"Answer 1\"))\n .addVariableData(\"question_2\", AddOnsResponseService.newVariableData().addStringValue(\"Answer 2\"))\n .addVariableData(\"question_3\", AddOnsResponseService.newVariableData().addStringValue(\"Answer 3\"))\n );\n\n const hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n const renderAction = AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n\n return renderAction;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.573Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":152,"estimatedTokens":1212}}730{"id":"doc-handle_errors_google_workspace_add_ons_google_fo-77668c9b","source":"documentation","title":"Handle errors | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/handle-errors","text":"Example:\n```text\n{\n \"timeZone\": \"America/Toronto\",\n \"dependencies\": {},\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Retry Errors Example\",\n \"logoUrl\": \"https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"handle_error_action\",\n \"state\": \"ACTIVE\",\n \"name\": \"Handle Error Action\",\n \"description\": \"To notify the user that some error has occurred\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"value1\",\n \"description\": \"The input from the user\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"outputs\": [\n {\n \"id\": \"output_1\",\n \"description\": \"The output\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfiguration\",\n \"onExecuteFunction\": \"onExecution\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Returns a configuration card for the step.\n * This card contains a text input field for the user.\n */\nfunction onConfiguration() {\n let section = CardService.newCardSection()\n .addWidget(CardService.newTextInput()\n .setFieldName(\"value1\")\n .setId(\"value1\")\n .setTitle(\"Please input negative numbers!\"));\n const card = CardService.newCardBuilder().addSection(section).build();\n return card;\n}\n\n/**\n * Gets an integer value from variable data, handling both string and integer formats.\n * @param {Object} variableData The variable data object from the event.\n * @return {number} The extracted integer value.\n */\nfunction getIntValue(variableData) {\n if (variableData.stringValues) {\n return parseInt(variableData.stringValues[0]);\n }\n return variableData.integerValues[0];\n}\n\n/**\n * Executes the step.\n * If the user input is a positive number, it throws an error and returns an\n * actionable error message. Otherwise, it returns the input as an output variable.\n * @param {Object} e The event object.\n */\nfunction onExecution(e) {\n try {\n var input_value = getIntValue(e.workflow.actionInvocation.inputs[\"value1\"]);\n if (input_value > 0) {\n throw new Error('Found invalid positive input value!');\n }\n\n // If execution is successful, return the output variable and a log.\n const styledText_1 = AddOnsResponseService.newStyledText()\n .setText(\"Execution completed, the number you entered was: \")\n .addStyle(AddOnsResponseService.TextStyle.ITALIC)\n .addStyle(AddOnsResponseService.TextStyle.UNDERLINE)\n\n const styledText_2 = AddOnsResponseService.newStyledText()\n .setText(input_value)\n .setFontWeight(AddOnsResponseService.FontWeight.BOLD)\n\n const workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()\n .setVariableDataMap(\n {\n \"output_1\": AddOnsResponseService.newVariableData()\n .addStringValue(input_value)\n }\n )\n .setLog(AddOnsResponseService.newWorkflowTextFormat()\n .addTextFormatElement(\n AddOnsResponseService.newTextFormatElement()\n .setStyledText(styledText_1)\n ).addTextFormatElement(\n AddOnsResponseService.newTextFormatElement()\n .setStyledText(styledText_2)\n ));\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n\n } catch (err) {\n Logger.log('An error occurred: ' + err.message);\n\n // If an error occurs, return an actionable error action.\n const workflowAction = AddOnsResponseService.newReturnElementErrorAction()\n // Sets the user-facing error message.\n .setErrorLog(\n AddOnsResponseService.newWorkflowTextFormat()\n .addTextFormatElement(\n AddOnsResponseService.newTextFormatElement()\n .setText(\"Failed due to invalid input values!\"))\n )\n // Makes the error actionable, letting the user correct the input.\n .setErrorActionability(AddOnsResponseService.ErrorActionability.ACTIONABLE)\n // Specifies that the error is not automatically retried.\n .setErrorRetryability(AddOnsResponseService.ErrorRetryability.NOT_RETRYABLE)\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction).build();\n\n } finally {\n console.log(\"Execution completed\")\n }\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/Toronto\",\n \"dependencies\": {},\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Retry Errors Example\",\n \"logoUrl\": \"https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"retryError\",\n \"state\": \"ACTIVE\",\n \"name\": \"Retry an error\",\n \"description\": \"Simulates a temporary failure and retries the step.\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"value1\",\n \"description\": \"Any input value\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"outputs\": [\n {\n \"id\": \"output_1\",\n \"description\": \"The output\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"onConfigFunction\": \"onRetryConfiguration\",\n \"onExecuteFunction\": \"onRetryExecution\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Returns a configuration card for the step.\n * This card contains a text input field for the user.\n */\nfunction onRetryConfiguration() {\n let section = CardService.newCardSection()\n .addWidget(CardService.newTextInput()\n .setFieldName(\"value1\")\n .setId(\"value1\")\n .setTitle(\"Enter any value\"));\n const card = CardService.newCardBuilder().addSection(section).build();\n return card;\n}\n\n/**\n * Executes the step and simulates a transient error.\n * This function fails 80% of the time. When it fails, it returns an\n * error that can be retried.\n * @param {Object} e The event object.\n */\nfunction onRetryExecution(e) {\n try {\n // Simulate a transient error that fails 80% of the time.\n if (Math.random() < 0.8) {\n throw new Error('Simulated transient failure!');\n }\n\n // If execution is successful, return the output variable and a log.\n var input_value = e.workflow.actionInvocation.inputs[\"value1\"].stringValues[0];\n\n const styledText = AddOnsResponseService.newStyledText()\n .setText(`Execution succeeded for input: ${input_value}`);\n\n const workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()\n .setVariables({\n \"output_1\": AddOnsResponseService.newVariableData()\n .addStringValue(input_value)\n })\n .setLog(AddOnsResponseService.newWorkflowTextFormat()\n .addTextFormatElement(\n AddOnsResponseService.newTextFormatElement()\n .setStyledText(styledText)\n ));\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n\n } catch (err) {\n // If a transient error occurs, return an error message saying the step tries to run again.\n Logger.log('An error occurred, trying to run the step again: ' + err.message);\n\n const workflowAction = AddOnsResponseService.newReturnElementErrorAction()\n // Sets the user-facing error message.\n .setErrorLog(\n AddOnsResponseService.newWorkflowTextFormat()\n .addTextFormatElement(\n AddOnsResponseService.newTextFormatElement()\n .setText(\"A temporary error occurred. The step will be retried.\"))\n )\n // Makes the error not actionable by the user.\n .setErrorActionability(AddOnsResponseService.ErrorActionability.NOT_ACTIONABLE)\n // Specifies that the error is automatically retried.\n .setErrorRetryability(AddOnsResponseService.ErrorRetryability.RETRYABLE);\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n } finally {\n console.log(\"Execution completed\")\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.574Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":288,"estimatedTokens":2283}}731{"id":"doc-workspace_studio_event_objects_google_workspace_-b5f03457","source":"documentation","title":"Workspace Studio event objects | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/event-objects","text":"Example:\n```text\n{\n \"workflow\": {\n \"triggerEventSource\": \"TRIGGER_EVENT_SOURCE_AUTOMATED\",\n \"actionInvocation\": {\n \"inputs\": {\n \"operation\": {\n \"stringValues\": [\n \"+\"\n ]\n },\n \"value2\": {\n \"integerValues\": [\n 2\n ]\n },\n \"value1\": {\n \"integerValues\": [\n 2\n ]\n }\n }\n }\n },\n \"userLocale\": \"en\",\n \"hostApp\": \"flows\",\n \"clientPlatform\": \"web\",\n \"commonEventObject\": {\n \"timeZone\": {\n \"offset\": -14400000,\n \"id\": \"America/New_York\"\n },\n \"userLocale\": \"en-US\",\n \"hostApp\": \"WORKFLOW\",\n \"platform\": \"WEB\"\n },\n \"userCountry\": \"US\",\n \"userTimezone\": {\n \"id\": \"America/New_York\",\n \"offSet\": \"-14400000\"\n }\n}\n```\n\nExample:\n```text\n{\n \"workflow\": {\n \"resourceRetrieval\": {\n \"resourceReference\": {\n \"resourceType\": {\n \"workflowBundleId\": \"workflow_bundle_id\",\n \"workflowResourceDefinitionId\": \"workflow_resource_definition_id\"\n },\n \"resourceId\": \"resource_id\"\n }\n }\n },\n \"userLocale\": \"en\",\n \"hostApp\": \"flows\",\n \"clientPlatform\": \"web\",\n \"commonEventObject\": {\n \"timeZone\": {\n \"offset\": -14400000,\n \"id\": \"America/New_York\"\n },\n \"userLocale\": \"en-US\",\n \"hostApp\": \"WORKFLOW\",\n \"platform\": \"WEB\"\n },\n \"userCountry\": \"US\",\n \"userTimezone\": {\n \"id\": \"America/New_York\",\n \"offSet\": \"-14400000\"\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.576Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":80,"estimatedTokens":459}}732{"id":"doc-connect_your_google_workspace_add_on_to_a_third_-949e5fdf","source":"documentation","title":"Connect your Google Workspace add-on to a third-party service | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/guides/connect-third-party-service","text":"Example:\n```text\nCardService.newAuthorizationException()\n .setAuthorizationUrl('AUTHORIZATION_URL')\n .setResourceDisplayName('RESOURCE_DISPLAY_NAME')\n .throwException();\n```\n\nExample:\n```text\n{\n \"basic_authorization_prompt\": {\n \"authorization_url\": \"AUTHORIZATION_URL\",\n \"resource\": \"RESOURCE_DISPLAY_NAME\"\n }\n}\n```\n\nExample:\n```text\nfunction customAuthorizationCard() {\n let cardSection1Image1 = CardService.newImage()\n .setImageUrl('LOGO_URL')\n .setAltText('LOGO_ALT_TEXT');\n\n let cardSection1Divider1 = CardService.newDivider();\n\n let cardSection1TextParagraph1 = CardService.newTextParagraph()\n .setText('DESCRIPTION');\n\n let cardSection1ButtonList1Button1 = CardService.newTextButton()\n .setText('Sign in')\n .setBackgroundColor('#0055ff')\n .setTextButtonStyle(CardService.TextButtonStyle.FILLED)\n .setAuthorizationAction(CardService.newAuthorizationAction()\n .setAuthorizationUrl('AUTHORIZATION_URL'));\n\n let cardSection1ButtonList1 = CardService.newButtonSet()\n .addButton(cardSection1ButtonList1Button1);\n\n let cardSection1TextParagraph2 = CardService.newTextParagraph()\n .setText('TEXT_SIGN_UP');\n\n let cardSection1 = CardService.newCardSection()\n .addWidget(cardSection1Image1)\n .addWidget(cardSection1Divider1)\n .addWidget(cardSection1TextParagraph1)\n .addWidget(cardSection1ButtonList1)\n .addWidget(cardSection1TextParagraph2);\n\n let card = CardService.newCardBuilder()\n .addSection(cardSection1)\n .build();\n return [card];\n}\n\nfunction startNonGoogleAuth() {\n CardService.newAuthorizationException()\n .setAuthorizationUrl('AUTHORIZATION_URL')\n .setResourceDisplayName('RESOURCE_DISPLAY_NAME')\n .setCustomUiCallback('customAuthorizationCard')\n .throwException();\n }\n```\n\nExample:\n```text\n{\n \"custom_authorization_prompt\": {\n \"action\": {\n \"navigations\": [\n {\n \"pushCard\": {\n \"sections\": [\n {\n \"widgets\": [\n {\n \"image\": {\n \"imageUrl\": \"LOGO_URL\",\n \"altText\": \"LOGO_ALT_TEXT\"\n }\n },\n {\n \"divider\": {}\n },\n {\n \"textParagraph\": {\n \"text\": \"DESCRIPTION\"\n }\n },\n {\n \"buttonList\": {\n \"buttons\": [\n {\n \"text\": \"Sign in\",\n \"onClick\": {\n \"openLink\": {\n \"url\": \"AUTHORIZATION_URL\",\n \"onClose\": \"RELOAD\",\n \"openAs\": \"OVERLAY\"\n }\n },\n \"color\": {\n \"red\": 0,\n \"green\": 0,\n \"blue\": 1,\n \"alpha\": 1,\n }\n }\n ]\n }\n },\n {\n \"textParagraph\": {\n \"text\": \"TEXT_SIGN_UP\"\n }\n }\n ]\n }\n ]\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n* Attempts to access a non-Google API using a constructed service\n* object.\n*\n* If your add-on needs access to non-Google APIs that require OAuth,\n* you need to implement this method. You can use the OAuth1 and\n* OAuth2 Apps Script libraries to help implement it.\n*\n* @param {String} url The URL to access.\n* @param {String} method_opt The HTTP method. Defaults to GET.\n* @param {Object} headers_opt The HTTP headers. Defaults to an empty\n* object. The Authorization field is added\n* to the headers in this method.\n* @return {HttpResponse} the result from the UrlFetchApp.fetch() call.\n*/\nfunction accessProtectedResource(url, method_opt, headers_opt) {\n var service = getOAuthService();\n var maybeAuthorized = service.hasAccess();\n if (maybeAuthorized) {\n // A token is present, but it may be expired or invalid. Make a\n // request and check the response code to be sure.\n\n // Make the UrlFetch request and return the result.\n var accessToken = service.getAccessToken();\n var method = method_opt || 'get';\n var headers = headers_opt || {};\n headers['Authorization'] =\n Utilities.formatString('Bearer %s', accessToken);\n var resp = UrlFetchApp.fetch(url, {\n 'headers': headers,\n 'method' : method,\n 'muteHttpExceptions': true, // Prevents thrown HTTP exceptions.\n });\n\n var code = resp.getResponseCode();\n if (code >= 200 && code < 300) {\n return resp.getContentText(\"utf-8\"); // Success\n } else if (code == 401 || code == 403) {\n // Not fully authorized for this action.\n maybeAuthorized = false;\n } else {\n // Handle other response codes by logging them and throwing an\n // exception.\n console.error(\"Backend server error (%s): %s\", code.toString(),\n resp.getContentText(\"utf-8\"));\n throw (\"Backend server error: \" + code);\n }\n }\n\n if (!maybeAuthorized) {\n // Invoke the authorization flow using the default authorization\n // prompt card.\n CardService.newAuthorizationException()\n .setAuthorizationUrl(service.getAuthorizationUrl())\n .setResourceDisplayName(\"Display name to show to the user\")\n .throwException();\n }\n}\n\n/**\n* Create a new OAuth service to facilitate accessing an API.\n* This example assumes there is a single service that the add-on needs to\n* access. Its name is used when persisting the authorized token, so ensure\n* it is unique within the scope of the property store. You must set the\n* client secret and client ID, which are obtained when registering your\n* add-on with the API.\n*\n* See the Apps Script OAuth2 Library documentation for more\n* information:\n* https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service\n*\n* @return A configured OAuth2 service object.\n*/\nfunction getOAuthService() {\n return OAuth2.createService('SERVICE_NAME')\n .setAuthorizationBaseUrl('SERVICE_AUTH_URL')\n .setTokenUrl('SERVICE_AUTH_TOKEN_URL')\n .setClientId('CLIENT_ID')\n .setClientSecret('CLIENT_SECRET')\n .setScope('SERVICE_SCOPE_REQUESTS')\n .setCallbackFunction('authCallback')\n .setCache(CacheService.getUserCache())\n .setPropertyStore(PropertiesService.getUserProperties());\n}\n\n/**\n* Boilerplate code to determine if a request is authorized and returns\n* a corresponding HTML message. When the user completes the OAuth2 flow\n* on the service provider's website, this function is invoked from the\n* service. In order for authorization to succeed you must make sure that\n* the service knows how to call this function by setting the correct\n* redirect URL.\n*\n* The redirect URL to enter is:\n* https://script.google.com/macros/d/<Apps Script ID>/usercallback\n*\n* See the Apps Script OAuth2 Library documentation for more\n* information:\n* https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service\n*\n* @param {Object} callbackRequest The request data received from the\n* callback function. Pass it to the service's\n* handleCallback() method to complete the\n* authorization process.\n* @return {HtmlOutput} a success or denied HTML message to display to\n* the user.\n*/\nfunction authCallback(callbackRequest) {\n var authorized = getOAuthService().handleCallback(callbackRequest);\n if (authorized) {\n return HtmlService.createHtmlOutput(\n 'Success!');\n } else {\n return HtmlService.createHtmlOutput('Denied');\n }\n}\n\n/**\n* Unauthorizes the non-Google service. This is useful for OAuth\n* development/testing. Run this method (Run > resetOAuth in the script\n* editor) to reset OAuth to re-prompt the user for OAuth.\n*/\nfunction resetOAuth() {\n getOAuthService().reset();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.577Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":258,"estimatedTokens":2076}}733{"id":"doc-select_google_drive_files_and_folders_with_googl-2f72fa0b","source":"documentation","title":"Select Google Drive files and folders with Google Picker | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/studio/drive-picker","text":"Example:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Drive Picker Demo\",\n \"logoUrl\": \"https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png\",\n \"useLocaleFromApp\": true\n },\n \"flows\": {\n \"workflowElements\": [\n {\n \"id\": \"file_selection\",\n \"state\": \"ACTIVE\",\n \"name\": \"File selection\",\n \"workflowAction\": {\n \"inputs\": [\n {\n \"id\": \"drive_picker_1\",\n \"description\": \"Choose a file from Google Drive\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"outputs\": [\n {\n \"id\": \"fileId\",\n \"description\": \"The ID of the selected file\",\n \"cardinality\": \"SINGLE\",\n \"dataType\": {\n \"basicType\": \"STRING\"\n }\n }\n ],\n \"onConfigFunction\": \"onConfig\",\n \"onExecuteFunction\": \"onExecute\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n* Returns a configuration card for the step.\n* This card includes a selection input widget configured as a Google Picker\n* that lets users select spreadsheets and PDFs.\n*/\nfunction onConfig() {\n // Lets users select either spreadsheets or PDFs\n const driveSpec = CardService.newDriveDataSourceSpec()\n .addItemType(\n CardService.DriveItemType.SPREADSHEETS\n )\n .addItemType(\n CardService.DriveItemType.PDFS\n );\n\n const platformSource = CardService.newPlatformDataSource()\n .setCommonDataSource(\n CardService.CommonDataSource.DRIVE\n )\n .setDriveDataSourceSpec(driveSpec);\n\n const selectionInput =\n CardService.newSelectionInput()\n .setFieldName(\"drive_picker_1\")\n .setPlatformDataSource(platformSource)\n .setTitle(\"Drive Picker\")\n .setType(\n CardService.SelectionInputType.MULTI_SELECT\n );\n\n var sectionBuilder =\n CardService.newCardSection()\n .addWidget(selectionInput)\n\n return CardService.newCardBuilder()\n .addSection(sectionBuilder)\n .build();\n}\n\n/**\n* Executes when the step runs.\n* This function retrieves the file ID of the item selected in the Google Picker\n* and returns it as an output variable.\n* @param {Object} event The event object passed by the Flows runtime.\n* @return {Object} The output variables object.\n*/\nfunction onExecute(event) {\n // Extract the selected file's ID during execution\n console.log(\"eventObject: \" + JSON.stringify(event));\n var fileId = event.workflow.actionInvocation.inputs[\"drive_picker_1\"].stringValues[0];\n\n const variableData = AddOnsResponseService.newVariableData()\n .addStringValue(fileId);\n\n let textFormatElement = AddOnsResponseService.newTextFormatElement()\n .setText(\"A file has been selected!\");\n\n let workflowTextFormat = AddOnsResponseService.newWorkflowTextFormat()\n .addTextFormatElement(textFormatElement);\n\n let workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()\n .addVariableData(\"fileId\", variableData)\n .setLog(workflowTextFormat);\n\n let hostAppAction = AddOnsResponseService.newHostAppAction()\n .setWorkflowAction(workflowAction);\n\n return AddOnsResponseService.newRenderActionBuilder()\n .setHostAppAction(hostAppAction)\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.578Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":124,"estimatedTokens":869}}734{"id":"doc-triggers_for_editor_add_ons_google_workspace_add-89e5fb77","source":"documentation","title":"Triggers for Editor add-ons | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/concepts/editor-triggers","text":"Example:\n```text\n/**\n * Responds to a form when submitted.\n * @param {event} e The Form submit event.\n */\nfunction respondToFormSubmit(e) {\n const addonTitle = \"My Add-on Title\";\n const props = PropertiesService.getDocumentProperties();\n const authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);\n\n // Check if the actions of the trigger requires authorization that has not\n // been granted yet; if so, warn the user via email. This check is required\n // when using triggers with add-ons to maintain functional triggers.\n if (\n authInfo.getAuthorizationStatus() === ScriptApp.AuthorizationStatus.REQUIRED\n ) {\n // Re-authorization is required. In this case, the user needs to be alerted\n // that they need to re-authorize; the normal trigger action is not\n // conducted, since it requires authorization first. Send at most one\n // \"Authorization Required\" email per day to avoid spamming users.\n const lastAuthEmailDate = props.getProperty(\"lastAuthEmailDate\");\n const today = new Date().toDateString();\n if (lastAuthEmailDate !== today) {\n if (MailApp.getRemainingDailyQuota() > 0) {\n const html = HtmlService.createTemplateFromFile(\"AuthorizationEmail\");\n html.url = authInfo.getAuthorizationUrl();\n html.addonTitle = addonTitle;\n const message = html.evaluate();\n MailApp.sendEmail(\n Session.getEffectiveUser().getEmail(),\n \"Authorization Required\",\n message.getContent(),\n {\n name: addonTitle,\n htmlBody: message.getContent(),\n },\n );\n }\n props.setProperty(\"lastAuthEmailDate\", today);\n }\n } else {\n // Authorization has been granted, so continue to respond to the trigger.\n // Main trigger logic here.\n }\n}\n```\n\nExample:\n```text\n<p>The Google Sheets add-on <i><?= addonTitle ?></i> is set to run automatically\n whenever a form is submitted. The add-on was recently updated and it needs you\n to re-authorize it to run on your behalf.</p>\n\n<p>The add-on's automatic functions are temporarily disabled until you\n re-authorize it. To do so, open Google Sheets and run the add-on from the\n Add-ons menu. Alternatively, you can click this link to authorize it:</p>\n\n<p><a href=\"<?= url ?>\">Re-authorize the add-on.</a></p>\n\n<p>This notification email will be sent to you at most once per day until the\n add-on is re-authorized.</p>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.580Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":65,"estimatedTokens":612}}735{"id":"doc-css_package_for_editor_add_ons_google_workspace_-13b29806","source":"documentation","title":"CSS package for Editor add-ons | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/guides/css","text":"Example:\n```text\n<link rel=\"stylesheet\" href=\"https://ssl.gstatic.com/docs/script/css/add-ons1.css\">\n```\n\nExample:\n```text\n<h1>Titles and headers</h1>\n<b>Bold text</b>\nNormal text\n<a href=\"\">Links</a>\n<span class=\"current\">Current navigation selection</span>\n<span class=\"error\">Form input errors</span>\n<span class=\"gray\">Gray text</span>\n<span class=\"secondary\">Secondary text</span>\n```\n\nExample:\n```text\n<button class=\"action\">Translate</button>\n```\n\nExample:\n```text\n<button class=\"create\">Create</button>\n```\n\nExample:\n```text\n<button class=\"share\">Share</button>\n```\n\nExample:\n```text\n<div class=\"block form-group\">\n <label for=\"select\">Select</label>\n <select id=\"select\">\n <option selected>Google Docs</option>\n <option>Google Forms</option>\n <option>Google Sheets</option>\n </select>\n</div>\n<div class=\"block form-group\">\n <label for=\"disabled-select\">Disabled select</label>\n <select id=\"disabled-select\" disabled>\n <option selected>Google Docs</option>\n <option>Google Forms</option>\n <option>Google Sheets</option>\n </select>\n</div>\n```\n\nExample:\n```text\n<div class=\"form-group\">\n <label for=\"sampleTextArea\">Label</label>\n <textarea id=\"sampleTextArea\" rows=\"3\"></textarea>\n</div>\n```\n\nExample:\n```text\n<div class=\"inline form-group\">\n <label for=\"city\">City</label>\n <input type=\"text\" id=\"city\" style=\"width: 150px;\">\n</div>\n<div class=\"inline form-group\">\n <label for=\"state\">State</label>\n <input type=\"text\" id=\"state\" style=\"width: 40px;\">\n</div>\n<div class=\"inline form-group\">\n <label for=\"zip-code\">Zip code</label>\n <input type=\"text\" id=\"zip-code\" style=\"width: 65px;\">\n</div>\n```\n\nExample:\n```text\n<style>\n.branding-below {\n bottom: 56px;\n top: 0;\n}\n</style>\n\n<div class=\"sidebar branding-below\">\n <div class=\"block form-group\">\n <label for=\"translated-text\">\n <b>Translation</b></label>\n <textarea id=\"translated-text\" rows=\"15\">\n </textarea>\n </div>\n\n <div class=\"block\">\n <input type=\"checkbox\" id=\"save-prefs\">\n <label for=\"save-prefs\">\n Use these languages by default</label>\n </div>\n\n <div class=\"block\">\n <button class=\"blue\">Translate</button>\n <button>Insert</button>\n </div>\n</div>\n\n<div class=\"sidebar bottom\">\n <span class=\"gray\">\n Translate sample by Google</span>\n</div>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.584Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":112,"estimatedTokens":576}}736{"id":"doc-create_third_party_resources_from_the_menu_googl-8362841d","source":"documentation","title":"Create third-party resources from the @ menu | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/guides/create-insert-resource-smart-chip","text":"Example:\n```text\n{\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\",\n \"https://www.googleapis.com/auth/workspace.linkcreate\"\n ],\n \"addOns\": {\n \"docs\": {\n \"linkPreviewTriggers\": [\n ...\n ],\n \"createActionTriggers\": [\n {\n \"id\": \"createCase\",\n \"labelText\": \"Create support case\",\n \"localizedLabelText\": {\n \"es\": \"Crear caso de soporte\"\n },\n \"runFunction\": \"createCaseInputCard\",\n \"logoUrl\": \"https://www.example.com/images/case.png\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Produces a support case creation form card.\n * \n * @param {!Object} event The event object.\n * @param {!Object=} errors An optional map of per-field error messages.\n * @param {boolean} isUpdate Whether to return the form as an update card navigation.\n * @return {!Card|!ActionResponse} The resulting card or action response.\n */\nfunction createCaseInputCard(event, errors, isUpdate) {\n\n const cardHeader = CardService.newCardHeader()\n .setTitle('Create a support case')\n\n const cardSectionTextInput1 = CardService.newTextInput()\n .setFieldName('name')\n .setTitle('Name')\n .setMultiline(false);\n\n const cardSectionTextInput2 = CardService.newTextInput()\n .setFieldName('description')\n .setTitle('Description')\n .setMultiline(true);\n\n const cardSectionSelectionInput1 = CardService.newSelectionInput()\n .setFieldName('priority')\n .setTitle('Priority')\n .setType(CardService.SelectionInputType.DROPDOWN)\n .addItem('P0', 'P0', false)\n .addItem('P1', 'P1', false)\n .addItem('P2', 'P2', false)\n .addItem('P3', 'P3', false);\n\n const cardSectionSelectionInput2 = CardService.newSelectionInput()\n .setFieldName('impact')\n .setTitle('Impact')\n .setType(CardService.SelectionInputType.CHECK_BOX)\n .addItem('Blocks a critical customer operation', 'Blocks a critical customer operation', false);\n\n const cardSectionButtonListButtonAction = CardService.newAction()\n .setPersistValues(true)\n .setFunctionName('submitCaseCreationForm')\n .setParameters({});\n\n const cardSectionButtonListButton = CardService.newTextButton()\n .setText('Create')\n .setTextButtonStyle(CardService.TextButtonStyle.TEXT)\n .setOnClickAction(cardSectionButtonListButtonAction);\n\n const cardSectionButtonList = CardService.newButtonSet()\n .addButton(cardSectionButtonListButton);\n\n // Builds the form inputs with error texts for invalid values.\n const cardSection = CardService.newCardSection();\n if (errors?.name) {\n cardSection.addWidget(createErrorTextParagraph(errors.name));\n }\n cardSection.addWidget(cardSectionTextInput1);\n if (errors?.description) {\n cardSection.addWidget(createErrorTextParagraph(errors.description));\n }\n cardSection.addWidget(cardSectionTextInput2);\n if (errors?.priority) {\n cardSection.addWidget(createErrorTextParagraph(errors.priority));\n }\n cardSection.addWidget(cardSectionSelectionInput1);\n if (errors?.impact) {\n cardSection.addWidget(createErrorTextParagraph(errors.impact));\n }\n\n cardSection.addWidget(cardSectionSelectionInput2);\n cardSection.addWidget(cardSectionButtonList);\n\n const card = CardService.newCardBuilder()\n .setHeader(cardHeader)\n .addSection(cardSection)\n .build();\n\n if (isUpdate) {\n return CardService.newActionResponseBuilder()\n .setNavigation(CardService.newNavigation().updateCard(card))\n .build();\n } else {\n return card;\n }\n}\n```\n\nExample:\n```text\n/**\n * Produces a support case creation form card.\n * \n * @param {!Object} event The event object.\n * @param {!Object=} errors An optional map of per-field error messages.\n * @param {boolean} isUpdate Whether to return the form as an update card navigation.\n * @return {!Card|!ActionResponse} The resulting card or action response.\n */\nfunction createCaseInputCard(event, errors, isUpdate) {\n\n const cardHeader1 = {\n title: \"Create a support case\"\n };\n\n const cardSection1TextInput1 = {\n textInput: {\n name: \"name\",\n label: \"Name\"\n }\n };\n\n const cardSection1TextInput2 = {\n textInput: {\n name: \"description\",\n label: \"Description\",\n type: \"MULTIPLE_LINE\"\n }\n };\n\n const cardSection1SelectionInput1 = {\n selectionInput: {\n name: \"priority\",\n label: \"Priority\",\n type: \"DROPDOWN\",\n items: [{\n text: \"P0\",\n value: \"P0\"\n }, {\n text: \"P1\",\n value: \"P1\"\n }, {\n text: \"P2\",\n value: \"P2\"\n }, {\n text: \"P3\",\n value: \"P3\"\n }]\n }\n };\n\n const cardSection1SelectionInput2 = {\n selectionInput: {\n name: \"impact\",\n label: \"Impact\",\n items: [{\n text: \"Blocks a critical customer operation\",\n value: \"Blocks a critical customer operation\"\n }]\n }\n };\n\n const cardSection1ButtonList1Button1Action1 = {\n function: process.env.URL,\n parameters: [\n {\n key: \"submitCaseCreationForm\",\n value: true\n }\n ],\n persistValues: true\n };\n\n const cardSection1ButtonList1Button1 = {\n text: \"Create\",\n onClick: {\n action: cardSection1ButtonList1Button1Action1\n }\n };\n\n const cardSection1ButtonList1 = {\n buttonList: {\n buttons: [cardSection1ButtonList1Button1]\n }\n };\n\n // Builds the creation form and adds error text for invalid inputs.\n const cardSection1 = [];\n if (errors?.name) {\n cardSection1.push(createErrorTextParagraph(errors.name));\n }\n cardSection1.push(cardSection1TextInput1);\n if (errors?.description) {\n cardSection1.push(createErrorTextParagraph(errors.description));\n }\n cardSection1.push(cardSection1TextInput2);\n if (errors?.priority) {\n cardSection1.push(createErrorTextParagraph(errors.priority));\n }\n cardSection1.push(cardSection1SelectionInput1);\n if (errors?.impact) {\n cardSection1.push(createErrorTextParagraph(errors.impact));\n }\n\n cardSection1.push(cardSection1SelectionInput2);\n cardSection1.push(cardSection1ButtonList1);\n\n const card = {\n header: cardHeader1,\n sections: [{\n widgets: cardSection1\n }]\n };\n\n if (isUpdate) {\n return {\n renderActions: {\n action: {\n navigations: [{\n updateCard: card\n }]\n }\n }\n };\n } else {\n return {\n action: {\n navigations: [{\n pushCard: card\n }]\n }\n };\n }\n}\n```\n\nExample:\n```text\ndef create_case_input_card(event, errors = {}, isUpdate = False):\n \"\"\"Produces a support case creation form card.\n Args:\n event: The event object.\n errors: An optional dict of per-field error messages.\n isUpdate: Whether to return the form as an update card navigation.\n Returns:\n The resulting card or action response.\n \"\"\"\n card_header1 = {\n \"title\": \"Create a support case\"\n }\n\n card_section1_text_input1 = {\n \"textInput\": {\n \"name\": \"name\",\n \"label\": \"Name\"\n }\n }\n\n card_section1_text_input2 = {\n \"textInput\": {\n \"name\": \"description\",\n \"label\": \"Description\",\n \"type\": \"MULTIPLE_LINE\"\n }\n }\n\n card_section1_selection_input1 = {\n \"selectionInput\": {\n \"name\": \"priority\",\n \"label\": \"Priority\",\n \"type\": \"DROPDOWN\",\n \"items\": [{\n \"text\": \"P0\",\n \"value\": \"P0\"\n }, {\n \"text\": \"P1\",\n \"value\": \"P1\"\n }, {\n \"text\": \"P2\",\n \"value\": \"P2\"\n }, {\n \"text\": \"P3\",\n \"value\": \"P3\"\n }]\n }\n }\n\n card_section1_selection_input2 = {\n \"selectionInput\": {\n \"name\": \"impact\",\n \"label\": \"Impact\",\n \"items\": [{\n \"text\": \"Blocks a critical customer operation\",\n \"value\": \"Blocks a critical customer operation\"\n }]\n }\n }\n\n card_section1_button_list1_button1_action1 = {\n \"function\": os.environ[\"URL\"],\n \"parameters\": [\n {\n \"key\": \"submitCaseCreationForm\",\n \"value\": True\n }\n ],\n \"persistValues\": True\n }\n\n card_section1_button_list1_button1 = {\n \"text\": \"Create\",\n \"onClick\": {\n \"action\": card_section1_button_list1_button1_action1\n }\n }\n\n card_section1_button_list1 = {\n \"buttonList\": {\n \"buttons\": [card_section1_button_list1_button1]\n }\n }\n\n # Builds the creation form and adds error text for invalid inputs.\n card_section1 = []\n if \"name\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"name\"]))\n card_section1.append(card_section1_text_input1)\n if \"description\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"description\"]))\n card_section1.append(card_section1_text_input2)\n if \"priority\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"priority\"]))\n card_section1.append(card_section1_selection_input1)\n if \"impact\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"impact\"]))\n\n card_section1.append(card_section1_selection_input2)\n card_section1.append(card_section1_button_list1)\n\n card = {\n \"header\": card_header1,\n \"sections\": [{\n \"widgets\": card_section1\n }]\n }\n\n if isUpdate:\n return {\n \"renderActions\": {\n \"action\": {\n \"navigations\": [{\n \"updateCard\": card\n }]\n }\n }\n }\n else:\n return {\n \"action\": {\n \"navigations\": [{\n \"pushCard\": card\n }]\n }\n }\n```\n\nExample:\n```text\n/**\n * Produces a support case creation form.\n * \n * @param event The event object.\n * @param errors A map of per-field error messages.\n * @param isUpdate Whether to return the form as an update card navigation.\n * @return The resulting card or action response.\n */\nJsonObject createCaseInputCard(JsonObject event, Map<String, String> errors, boolean isUpdate) {\n JsonObject cardHeader = new JsonObject();\n cardHeader.add(\"title\", new JsonPrimitive(\"Create a support case\"));\n\n JsonObject cardSectionTextInput1 = new JsonObject();\n cardSectionTextInput1.add(\"name\", new JsonPrimitive(\"name\"));\n cardSectionTextInput1.add(\"label\", new JsonPrimitive(\"Name\"));\n\n JsonObject cardSectionTextInput1Widget = new JsonObject();\n cardSectionTextInput1Widget.add(\"textInput\", cardSectionTextInput1);\n\n JsonObject cardSectionTextInput2 = new JsonObject();\n cardSectionTextInput2.add(\"name\", new JsonPrimitive(\"description\"));\n cardSectionTextInput2.add(\"label\", new JsonPrimitive(\"Description\"));\n cardSectionTextInput2.add(\"type\", new JsonPrimitive(\"MULTIPLE_LINE\"));\n\n JsonObject cardSectionTextInput2Widget = new JsonObject();\n cardSectionTextInput2Widget.add(\"textInput\", cardSectionTextInput2);\n\n JsonObject cardSectionSelectionInput1ItemsItem1 = new JsonObject();\n cardSectionSelectionInput1ItemsItem1.add(\"text\", new JsonPrimitive(\"P0\"));\n cardSectionSelectionInput1ItemsItem1.add(\"value\", new JsonPrimitive(\"P0\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem2 = new JsonObject();\n cardSectionSelectionInput1ItemsItem2.add(\"text\", new JsonPrimitive(\"P1\"));\n cardSectionSelectionInput1ItemsItem2.add(\"value\", new JsonPrimitive(\"P1\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem3 = new JsonObject();\n cardSectionSelectionInput1ItemsItem3.add(\"text\", new JsonPrimitive(\"P2\"));\n cardSectionSelectionInput1ItemsItem3.add(\"value\", new JsonPrimitive(\"P2\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem4 = new JsonObject();\n cardSectionSelectionInput1ItemsItem4.add(\"text\", new JsonPrimitive(\"P3\"));\n cardSectionSelectionInput1ItemsItem4.add(\"value\", new JsonPrimitive(\"P3\"));\n\n JsonArray cardSectionSelectionInput1Items = new JsonArray();\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem1);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem2);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem3);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem4);\n\n JsonObject cardSectionSelectionInput1 = new JsonObject();\n cardSectionSelectionInput1.add(\"name\", new JsonPrimitive(\"priority\"));\n cardSectionSelectionInput1.add(\"label\", new JsonPrimitive(\"Priority\"));\n cardSectionSelectionInput1.add(\"type\", new JsonPrimitive(\"DROPDOWN\"));\n cardSectionSelectionInput1.add(\"items\", cardSectionSelectionInput1Items);\n\n JsonObject cardSectionSelectionInput1Widget = new JsonObject();\n cardSectionSelectionInput1Widget.add(\"selectionInput\", cardSectionSelectionInput1);\n\n JsonObject cardSectionSelectionInput2ItemsItem = new JsonObject();\n cardSectionSelectionInput2ItemsItem.add(\"text\", new JsonPrimitive(\"Blocks a critical customer operation\"));\n cardSectionSelectionInput2ItemsItem.add(\"value\", new JsonPrimitive(\"Blocks a critical customer operation\"));\n\n JsonArray cardSectionSelectionInput2Items = new JsonArray();\n cardSectionSelectionInput2Items.add(cardSectionSelectionInput2ItemsItem);\n\n JsonObject cardSectionSelectionInput2 = new JsonObject();\n cardSectionSelectionInput2.add(\"name\", new JsonPrimitive(\"impact\"));\n cardSectionSelectionInput2.add(\"label\", new JsonPrimitive(\"Impact\"));\n cardSectionSelectionInput2.add(\"items\", cardSectionSelectionInput2Items);\n\n JsonObject cardSectionSelectionInput2Widget = new JsonObject();\n cardSectionSelectionInput2Widget.add(\"selectionInput\", cardSectionSelectionInput2);\n\n JsonObject cardSectionButtonListButtonActionParametersParameter = new JsonObject();\n cardSectionButtonListButtonActionParametersParameter.add(\"key\", new JsonPrimitive(\"submitCaseCreationForm\"));\n cardSectionButtonListButtonActionParametersParameter.add(\"value\", new JsonPrimitive(true));\n\n JsonArray cardSectionButtonListButtonActionParameters = new JsonArray();\n cardSectionButtonListButtonActionParameters.add(cardSectionButtonListButtonActionParametersParameter);\n\n JsonObject cardSectionButtonListButtonAction = new JsonObject();\n cardSectionButtonListButtonAction.add(\"function\", new JsonPrimitive(System.getenv().get(\"URL\")));\n cardSectionButtonListButtonAction.add(\"parameters\", cardSectionButtonListButtonActionParameters);\n cardSectionButtonListButtonAction.add(\"persistValues\", new JsonPrimitive(true));\n\n JsonObject cardSectionButtonListButtonOnCLick = new JsonObject();\n cardSectionButtonListButtonOnCLick.add(\"action\", cardSectionButtonListButtonAction);\n\n JsonObject cardSectionButtonListButton = new JsonObject();\n cardSectionButtonListButton.add(\"text\", new JsonPrimitive(\"Create\"));\n cardSectionButtonListButton.add(\"onClick\", cardSectionButtonListButtonOnCLick);\n\n JsonArray cardSectionButtonListButtons = new JsonArray();\n cardSectionButtonListButtons.add(cardSectionButtonListButton);\n\n JsonObject cardSectionButtonList = new JsonObject();\n cardSectionButtonList.add(\"buttons\", cardSectionButtonListButtons);\n\n JsonObject cardSectionButtonListWidget = new JsonObject();\n cardSectionButtonListWidget.add(\"buttonList\", cardSectionButtonList);\n\n // Builds the form inputs with error texts for invalid values.\n JsonArray cardSection = new JsonArray();\n if (errors.containsKey(\"name\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"name\").toString()));\n }\n cardSection.add(cardSectionTextInput1Widget);\n if (errors.containsKey(\"description\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"description\").toString()));\n }\n cardSection.add(cardSectionTextInput2Widget);\n if (errors.containsKey(\"priority\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"priority\").toString()));\n }\n cardSection.add(cardSectionSelectionInput1Widget);\n if (errors.containsKey(\"impact\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"impact\").toString()));\n }\n\n cardSection.add(cardSectionSelectionInput2Widget);\n cardSection.add(cardSectionButtonListWidget);\n\n JsonObject cardSectionWidgets = new JsonObject();\n cardSectionWidgets.add(\"widgets\", cardSection);\n\n JsonArray sections = new JsonArray();\n sections.add(cardSectionWidgets);\n\n JsonObject card = new JsonObject();\n card.add(\"header\", cardHeader);\n card.add(\"sections\", sections);\n\n JsonObject navigation = new JsonObject();\n if (isUpdate) {\n navigation.add(\"updateCard\", card);\n } else {\n navigation.add(\"pushCard\", card);\n }\n\n JsonArray navigations = new JsonArray();\n navigations.add(navigation);\n\n JsonObject action = new JsonObject();\n action.add(\"navigations\", navigations);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n if (!isUpdate) {\n return renderActions;\n }\n\n JsonObject update = new JsonObject();\n update.add(\"renderActions\", renderActions);\n\n return update;\n}\n```\n\nExample:\n```text\n/**\n * Returns a submit form response that inserts a link into the document.\n * \n * @param {string} title The title of the link to insert.\n * @param {string} url The URL of the link to insert.\n * @return {!SubmitFormResponse} The resulting submit form response.\n */\nfunction createLinkRenderAction(title, url) {\n return {\n renderActions: {\n action: {\n links: [{\n title: title,\n url: url\n }]\n }\n }\n };\n}\n```\n\nExample:\n```text\ndef create_link_render_action(title, url):\n \"\"\"Returns a submit form response that inserts a link into the document.\n Args:\n title: The title of the link to insert.\n url: The URL of the link to insert.\n Returns:\n The resulting submit form response.\n \"\"\"\n return {\n \"renderActions\": {\n \"action\": {\n \"links\": [{\n \"title\": title,\n \"url\": url\n }]\n }\n }\n }\n```\n\nExample:\n```text\n/**\n * Returns a submit form response that inserts a link into the document.\n * \n * @param title The title of the link to insert.\n * @param url The URL of the link to insert.\n * @return The resulting submit form response.\n */\nJsonObject createLinkRenderAction(String title, String url) {\n JsonObject link = new JsonObject();\n link.add(\"title\", new JsonPrimitive(title));\n link.add(\"url\", new JsonPrimitive(url));\n\n JsonArray links = new JsonArray();\n links.add(link);\n\n JsonObject action = new JsonObject();\n action.add(\"links\", links);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n JsonObject linkRenderAction = new JsonObject();\n linkRenderAction.add(\"renderActions\", renderActions);\n\n return linkRenderAction;\n}\n```\n\nExample:\n```text\n/**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param {!Object} event The event object with form input values.\n * @return {!ActionResponse|!SubmitFormResponse} The resulting response.\n */\nfunction submitCaseCreationForm(event) {\n const caseDetails = {\n name: event.formInput.name,\n description: event.formInput.description,\n priority: event.formInput.priority,\n impact: !!event.formInput.impact,\n };\n\n const errors = validateFormInputs(caseDetails);\n if (Object.keys(errors).length > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n const title = `Case ${caseDetails.name}`;\n // Adds the case details as parameters to the generated link URL.\n const url = 'https://example.com/support/cases/?' + generateQuery(caseDetails);\n return createLinkRenderAction(title, url);\n }\n}\n\n/**\n* Build a query path with URL parameters.\n*\n* @param {!Map} parameters A map with the URL parameters.\n* @return {!string} The resulting query path.\n*/\nfunction generateQuery(parameters) {\n return Object.entries(parameters).flatMap(([k, v]) =>\n Array.isArray(v) ? v.map(e => `${k}=${encodeURIComponent(e)}`) : `${k}=${encodeURIComponent(v)}`\n ).join(\"&\");\n}\n```\n\nExample:\n```text\n/**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param {!Object} event The event object with form input values.\n * @return {!ActionResponse|!SubmitFormResponse} The resulting response.\n */\nfunction submitCaseCreationForm(event) {\n const caseDetails = {\n name: event.commonEventObject.formInputs?.name?.stringInputs?.value[0],\n description: event.commonEventObject.formInputs?.description?.stringInputs?.value[0],\n priority: event.commonEventObject.formInputs?.priority?.stringInputs?.value[0],\n impact: !!event.commonEventObject.formInputs?.impact?.stringInputs?.value[0],\n };\n\n const errors = validateFormInputs(caseDetails);\n if (Object.keys(errors).length > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n const title = `Case ${caseDetails.name}`;\n // Adds the case details as parameters to the generated link URL.\n const url = new URL('https://example.com/support/cases/');\n for (const [key, value] of Object.entries(caseDetails)) {\n url.searchParams.append(key, value);\n }\n return createLinkRenderAction(title, url.href);\n }\n}\n```\n\nExample:\n```text\ndef submit_case_creation_form(event):\n \"\"\"Submits the creation form.\n\n If valid, returns a render action that inserts a new link\n into the document. If invalid, returns an update card navigation that\n re-renders the creation form with error messages.\n Args:\n event: The event object with form input values.\n Returns:\n The resulting response.\n \"\"\"\n formInputs = event[\"commonEventObject\"][\"formInputs\"] if \"formInputs\" in event[\"commonEventObject\"] else None\n case_details = {\n \"name\": None,\n \"description\": None,\n \"priority\": None,\n \"impact\": None,\n }\n if formInputs is not None:\n case_details[\"name\"] = formInputs[\"name\"][\"stringInputs\"][\"value\"][0] if \"name\" in formInputs else None\n case_details[\"description\"] = formInputs[\"description\"][\"stringInputs\"][\"value\"][0] if \"description\" in formInputs else None\n case_details[\"priority\"] = formInputs[\"priority\"][\"stringInputs\"][\"value\"][0] if \"priority\" in formInputs else None\n case_details[\"impact\"] = formInputs[\"impact\"][\"stringInputs\"][\"value\"][0] if \"impact\" in formInputs else False\n\n errors = validate_form_inputs(case_details)\n if len(errors) > 0:\n return create_case_input_card(event, errors, True) # Update mode\n else:\n title = f'Case {case_details[\"name\"]}'\n # Adds the case details as parameters to the generated link URL.\n url = \"https://example.com/support/cases/?\" + urlencode(case_details)\n return create_link_render_action(title, url)\n```\n\nExample:\n```text\n/**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param event The event object with form input values.\n * @return The resulting response.\n */\nJsonObject submitCaseCreationForm(JsonObject event) throws Exception {\n JsonObject formInputs = event.getAsJsonObject(\"commonEventObject\").getAsJsonObject(\"formInputs\");\n Map<String, String> caseDetails = new HashMap<String, String>();\n if (formInputs != null) {\n if (formInputs.has(\"name\")) {\n caseDetails.put(\"name\", formInputs.getAsJsonObject(\"name\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"description\")) {\n caseDetails.put(\"description\", formInputs.getAsJsonObject(\"description\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"priority\")) {\n caseDetails.put(\"priority\", formInputs.getAsJsonObject(\"priority\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"impact\")) {\n caseDetails.put(\"impact\", formInputs.getAsJsonObject(\"impact\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n }\n\n Map<String, String> errors = validateFormInputs(caseDetails);\n if (errors.size() > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n String title = String.format(\"Case %s\", caseDetails.get(\"name\"));\n // Adds the case details as parameters to the generated link URL.\n URIBuilder uriBuilder = new URIBuilder(\"https://example.com/support/cases/\");\n for (String caseDetailKey : caseDetails.keySet()) {\n uriBuilder.addParameter(caseDetailKey, caseDetails.get(caseDetailKey));\n }\n return createLinkRenderAction(title, uriBuilder.build().toURL().toString());\n }\n}\n```\n\nExample:\n```text\n/**\n * Validates case creation form input values.\n * \n * @param {!Object} caseDetails The values of each form input submitted by the user.\n * @return {!Object} A map from field name to error message. An empty object\n * represents a valid form submission.\n */\nfunction validateFormInputs(caseDetails) {\n const errors = {};\n if (!caseDetails.name) {\n errors.name = 'You must provide a name';\n }\n if (!caseDetails.description) {\n errors.description = 'You must provide a description';\n }\n if (!caseDetails.priority) {\n errors.priority = 'You must provide a priority';\n }\n if (caseDetails.impact && caseDetails.priority !== 'P0' && caseDetails.priority !== 'P1') {\n errors.impact = 'If an issue blocks a critical customer operation, priority must be P0 or P1';\n }\n\n return errors;\n}\n\n/**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param {string} errorMessage A description of input value error.\n * @return {!TextParagraph} The resulting text paragraph.\n */\nfunction createErrorTextParagraph(errorMessage) {\n return CardService.newTextParagraph()\n .setText('<font color=\\\"#BA0300\\\"><b>Error:</b> ' + errorMessage + '</font>');\n}\n```\n\nExample:\n```text\n/**\n * Validates case creation form input values.\n * \n * @param {!Object} caseDetails The values of each form input submitted by the user.\n * @return {!Object} A map from field name to error message. An empty object\n * represents a valid form submission.\n */\nfunction validateFormInputs(caseDetails) {\n const errors = {};\n if (caseDetails.name === undefined) {\n errors.name = 'You must provide a name';\n }\n if (caseDetails.description === undefined) {\n errors.description = 'You must provide a description';\n }\n if (caseDetails.priority === undefined) {\n errors.priority = 'You must provide a priority';\n }\n if (caseDetails.impact && !(['P0', 'P1']).includes(caseDetails.priority)) {\n errors.impact = 'If an issue blocks a critical customer operation, priority must be P0 or P1';\n }\n\n return errors;\n}\n\n/**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param {string} errorMessage A description of input value error.\n * @return {!TextParagraph} The resulting text paragraph.\n */\nfunction createErrorTextParagraph(errorMessage) {\n return {\n textParagraph: {\n text: '<font color=\\\"#BA0300\\\"><b>Error:</b> ' + errorMessage + '</font>'\n }\n }\n}\n```\n\nExample:\n```text\ndef validate_form_inputs(case_details):\n \"\"\"Validates case creation form input values.\n Args:\n case_details: The values of each form input submitted by the user.\n Returns:\n A dict from field name to error message. An empty object represents a valid form submission.\n \"\"\"\n errors = {}\n if case_details[\"name\"] is None:\n errors[\"name\"] = \"You must provide a name\"\n if case_details[\"description\"] is None:\n errors[\"description\"] = \"You must provide a description\"\n if case_details[\"priority\"] is None:\n errors[\"priority\"] = \"You must provide a priority\"\n if case_details[\"impact\"] is not None and case_details[\"priority\"] not in ['P0', 'P1']:\n errors[\"impact\"] = \"If an issue blocks a critical customer operation, priority must be P0 or P1\"\n return errors\n\n\ndef create_error_text_paragraph(error_message):\n \"\"\"Returns a text paragraph with red text indicating a form field validation error.\n Args:\n error_essage: A description of input value error.\n Returns:\n The resulting text paragraph.\n \"\"\"\n return {\n \"textParagraph\": {\n \"text\": '<font color=\\\"#BA0300\\\"><b>Error:</b> ' + error_message + '</font>'\n }\n }\n```\n\nExample:\n```text\n/**\n * Validates case creation form input values.\n * \n * @param caseDetails The values of each form input submitted by the user.\n * @return A map from field name to error message. An empty object\n * represents a valid form submission.\n */\nMap<String, String> validateFormInputs(Map<String, String> caseDetails) {\n Map<String, String> errors = new HashMap<String, String>();\n if (!caseDetails.containsKey(\"name\")) {\n errors.put(\"name\", \"You must provide a name\");\n }\n if (!caseDetails.containsKey(\"description\")) {\n errors.put(\"description\", \"You must provide a description\");\n }\n if (!caseDetails.containsKey(\"priority\")) {\n errors.put(\"priority\", \"You must provide a priority\");\n }\n if (caseDetails.containsKey(\"impact\") && !Arrays.asList(new String[]{\"P0\", \"P1\"}).contains(caseDetails.get(\"priority\"))) {\n errors.put(\"impact\", \"If an issue blocks a critical customer operation, priority must be P0 or P1\");\n }\n\n return errors;\n}\n\n/**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param errorMessage A description of input value error.\n * @return The resulting text paragraph.\n */\nJsonObject createErrorTextParagraph(String errorMessage) {\n JsonObject textParagraph = new JsonObject();\n textParagraph.add(\"text\", new JsonPrimitive(\"<font color=\\\"#BA0300\\\"><b>Error:</b> \" + errorMessage + \"</font>\"));\n\n JsonObject textParagraphWidget = new JsonObject();\n textParagraphWidget.add(\"textParagraph\", textParagraph);\n\n return textParagraphWidget;\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/New_York\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\",\n \"https://www.googleapis.com/auth/workspace.linkcreate\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Manage support cases\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"caseLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n },\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ],\n \"createActionTriggers\": [\n {\n \"id\": \"createCase\",\n \"labelText\": \"Create support case\",\n \"localizedLabelText\": {\n \"es\": \"Crear caso de soporte\"\n },\n \"runFunction\": \"createCaseInputCard\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\",\n \"https://www.googleapis.com/auth/workspace.linkcreate\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Manage support cases\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"$URL1\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n },\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ],\n \"createActionTriggers\": [\n {\n \"id\": \"createCase\",\n \"labelText\": \"Create support case\",\n \"localizedLabelText\": {\n \"es\": \"Crear caso de soporte\"\n },\n \"runFunction\": \"$URL2\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2024 Google LLC\n * \n * 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.\n */\n\n/**\n* Entry point for a support case link preview.\n*\n* @param {!Object} event The event object.\n* @return {!Card} The resulting preview link card.\n*/\nfunction caseLinkPreview(event) {\n\n // If the event object URL matches a specified pattern for support case links.\n if (event.docs.matchedUrl.url) {\n\n // Uses the event object to parse the URL and identify the case details.\n const caseDetails = parseQuery(event.docs.matchedUrl.url);\n\n // Builds a preview card with the case name, and description\n const caseHeader = CardService.newCardHeader()\n .setTitle(`Case ${caseDetails[\"name\"][0]}`);\n const caseDescription = CardService.newTextParagraph()\n .setText(caseDetails[\"description\"][0]);\n\n // Returns the card.\n // Uses the text from the card's header for the title of the smart chip.\n return CardService.newCardBuilder()\n .setHeader(caseHeader)\n .addSection(CardService.newCardSection().addWidget(caseDescription))\n .build();\n }\n}\n\n/**\n* Extracts the URL parameters from the given URL.\n*\n* @param {!string} url The URL to parse.\n* @return {!Map} A map with the extracted URL parameters.\n*/\nfunction parseQuery(url) {\n const query = url.split(\"?\")[1];\n if (query) {\n return query.split(\"&\")\n .reduce(function(o, e) {\n var temp = e.split(\"=\");\n var key = temp[0].trim();\n var value = temp[1].trim();\n value = isNaN(value) ? value : Number(value);\n if (o[key]) {\n o[key].push(value);\n } else {\n o[key] = [value];\n }\n return o;\n }, {});\n }\n return null;\n}\n\n\n\n/**\n * Produces a support case creation form card.\n * \n * @param {!Object} event The event object.\n * @param {!Object=} errors An optional map of per-field error messages.\n * @param {boolean} isUpdate Whether to return the form as an update card navigation.\n * @return {!Card|!ActionResponse} The resulting card or action response.\n */\nfunction createCaseInputCard(event, errors, isUpdate) {\n\n const cardHeader = CardService.newCardHeader()\n .setTitle('Create a support case')\n\n const cardSectionTextInput1 = CardService.newTextInput()\n .setFieldName('name')\n .setTitle('Name')\n .setMultiline(false);\n\n const cardSectionTextInput2 = CardService.newTextInput()\n .setFieldName('description')\n .setTitle('Description')\n .setMultiline(true);\n\n const cardSectionSelectionInput1 = CardService.newSelectionInput()\n .setFieldName('priority')\n .setTitle('Priority')\n .setType(CardService.SelectionInputType.DROPDOWN)\n .addItem('P0', 'P0', false)\n .addItem('P1', 'P1', false)\n .addItem('P2', 'P2', false)\n .addItem('P3', 'P3', false);\n\n const cardSectionSelectionInput2 = CardService.newSelectionInput()\n .setFieldName('impact')\n .setTitle('Impact')\n .setType(CardService.SelectionInputType.CHECK_BOX)\n .addItem('Blocks a critical customer operation', 'Blocks a critical customer operation', false);\n\n const cardSectionButtonListButtonAction = CardService.newAction()\n .setPersistValues(true)\n .setFunctionName('submitCaseCreationForm')\n .setParameters({});\n\n const cardSectionButtonListButton = CardService.newTextButton()\n .setText('Create')\n .setTextButtonStyle(CardService.TextButtonStyle.TEXT)\n .setOnClickAction(cardSectionButtonListButtonAction);\n\n const cardSectionButtonList = CardService.newButtonSet()\n .addButton(cardSectionButtonListButton);\n\n // Builds the form inputs with error texts for invalid values.\n const cardSection = CardService.newCardSection();\n if (errors?.name) {\n cardSection.addWidget(createErrorTextParagraph(errors.name));\n }\n cardSection.addWidget(cardSectionTextInput1);\n if (errors?.description) {\n cardSection.addWidget(createErrorTextParagraph(errors.description));\n }\n cardSection.addWidget(cardSectionTextInput2);\n if (errors?.priority) {\n cardSection.addWidget(createErrorTextParagraph(errors.priority));\n }\n cardSection.addWidget(cardSectionSelectionInput1);\n if (errors?.impact) {\n cardSection.addWidget(createErrorTextParagraph(errors.impact));\n }\n\n cardSection.addWidget(cardSectionSelectionInput2);\n cardSection.addWidget(cardSectionButtonList);\n\n const card = CardService.newCardBuilder()\n .setHeader(cardHeader)\n .addSection(cardSection)\n .build();\n\n if (isUpdate) {\n return CardService.newActionResponseBuilder()\n .setNavigation(CardService.newNavigation().updateCard(card))\n .build();\n } else {\n return card;\n }\n}\n\n\n/**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param {!Object} event The event object with form input values.\n * @return {!ActionResponse|!SubmitFormResponse} The resulting response.\n */\nfunction submitCaseCreationForm(event) {\n const caseDetails = {\n name: event.formInput.name,\n description: event.formInput.description,\n priority: event.formInput.priority,\n impact: !!event.formInput.impact,\n };\n\n const errors = validateFormInputs(caseDetails);\n if (Object.keys(errors).length > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n const title = `Case ${caseDetails.name}`;\n // Adds the case details as parameters to the generated link URL.\n const url = 'https://example.com/support/cases/?' + generateQuery(caseDetails);\n return createLinkRenderAction(title, url);\n }\n}\n\n/**\n* Build a query path with URL parameters.\n*\n* @param {!Map} parameters A map with the URL parameters.\n* @return {!string} The resulting query path.\n*/\nfunction generateQuery(parameters) {\n return Object.entries(parameters).flatMap(([k, v]) =>\n Array.isArray(v) ? v.map(e => `${k}=${encodeURIComponent(e)}`) : `${k}=${encodeURIComponent(v)}`\n ).join(\"&\");\n}\n\n\n/**\n * Validates case creation form input values.\n * \n * @param {!Object} caseDetails The values of each form input submitted by the user.\n * @return {!Object} A map from field name to error message. An empty object\n * represents a valid form submission.\n */\nfunction validateFormInputs(caseDetails) {\n const errors = {};\n if (!caseDetails.name) {\n errors.name = 'You must provide a name';\n }\n if (!caseDetails.description) {\n errors.description = 'You must provide a description';\n }\n if (!caseDetails.priority) {\n errors.priority = 'You must provide a priority';\n }\n if (caseDetails.impact && caseDetails.priority !== 'P0' && caseDetails.priority !== 'P1') {\n errors.impact = 'If an issue blocks a critical customer operation, priority must be P0 or P1';\n }\n\n return errors;\n}\n\n/**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param {string} errorMessage A description of input value error.\n * @return {!TextParagraph} The resulting text paragraph.\n */\nfunction createErrorTextParagraph(errorMessage) {\n return CardService.newTextParagraph()\n .setText('<font color=\\\"#BA0300\\\"><b>Error:</b> ' + errorMessage + '</font>');\n}\n\n\n/**\n * Returns a submit form response that inserts a link into the document.\n * \n * @param {string} title The title of the link to insert.\n * @param {string} url The URL of the link to insert.\n * @return {!SubmitFormResponse} The resulting submit form response.\n */\nfunction createLinkRenderAction(title, url) {\n return {\n renderActions: {\n action: {\n links: [{\n title: title,\n url: url\n }]\n }\n }\n };\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2024 Google LLC\n *\n * 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.\n */\n\n/**\n * Responds to any HTTP request related to link previews.\n *\n * @param {Object} req An HTTP request context.\n * @param {Object} res An HTTP response context.\n */\nexports.createLinkPreview = (req, res) => {\n const event = req.body;\n if (event.docs.matchedUrl.url) {\n const url = event.docs.matchedUrl.url;\n const parsedUrl = new URL(url);\n // If the event object URL matches a specified pattern for preview links.\n if (parsedUrl.hostname === 'example.com') {\n if (parsedUrl.pathname.startsWith('/support/cases/')) {\n return res.json(caseLinkPreview(parsedUrl));\n }\n }\n }\n};\n\n\n/**\n * \n * A support case link preview.\n *\n * @param {!URL} url The event object.\n * @return {!Card} The resulting preview link card.\n */\nfunction caseLinkPreview(url) {\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n // Parses the URL and identify the case details.\n const name = `Case ${url.searchParams.get(\"name\")}`;\n return {\n action: {\n linkPreview: {\n title: name,\n previewCard: {\n header: {\n title: name\n },\n sections: [{\n widgets: [{\n textParagraph: {\n text: url.searchParams.get(\"description\")\n }\n }]\n }]\n }\n }\n }\n };\n}\n\n\n\n/**\n * Responds to any HTTP request related to 3P resource creations.\n *\n * @param {Object} req An HTTP request context.\n * @param {Object} res An HTTP response context.\n */\nexports.create3pResources = (req, res) => {\n const event = req.body;\n if (event.commonEventObject.parameters?.submitCaseCreationForm) {\n res.json(submitCaseCreationForm(event));\n } else {\n res.json(createCaseInputCard(event));\n }\n};\n\n\n/**\n * Produces a support case creation form card.\n * \n * @param {!Object} event The event object.\n * @param {!Object=} errors An optional map of per-field error messages.\n * @param {boolean} isUpdate Whether to return the form as an update card navigation.\n * @return {!Card|!ActionResponse} The resulting card or action response.\n */\nfunction createCaseInputCard(event, errors, isUpdate) {\n\n const cardHeader1 = {\n title: \"Create a support case\"\n };\n\n const cardSection1TextInput1 = {\n textInput: {\n name: \"name\",\n label: \"Name\"\n }\n };\n\n const cardSection1TextInput2 = {\n textInput: {\n name: \"description\",\n label: \"Description\",\n type: \"MULTIPLE_LINE\"\n }\n };\n\n const cardSection1SelectionInput1 = {\n selectionInput: {\n name: \"priority\",\n label: \"Priority\",\n type: \"DROPDOWN\",\n items: [{\n text: \"P0\",\n value: \"P0\"\n }, {\n text: \"P1\",\n value: \"P1\"\n }, {\n text: \"P2\",\n value: \"P2\"\n }, {\n text: \"P3\",\n value: \"P3\"\n }]\n }\n };\n\n const cardSection1SelectionInput2 = {\n selectionInput: {\n name: \"impact\",\n label: \"Impact\",\n items: [{\n text: \"Blocks a critical customer operation\",\n value: \"Blocks a critical customer operation\"\n }]\n }\n };\n\n const cardSection1ButtonList1Button1Action1 = {\n function: process.env.URL,\n parameters: [\n {\n key: \"submitCaseCreationForm\",\n value: true\n }\n ],\n persistValues: true\n };\n\n const cardSection1ButtonList1Button1 = {\n text: \"Create\",\n onClick: {\n action: cardSection1ButtonList1Button1Action1\n }\n };\n\n const cardSection1ButtonList1 = {\n buttonList: {\n buttons: [cardSection1ButtonList1Button1]\n }\n };\n\n // Builds the creation form and adds error text for invalid inputs.\n const cardSection1 = [];\n if (errors?.name) {\n cardSection1.push(createErrorTextParagraph(errors.name));\n }\n cardSection1.push(cardSection1TextInput1);\n if (errors?.description) {\n cardSection1.push(createErrorTextParagraph(errors.description));\n }\n cardSection1.push(cardSection1TextInput2);\n if (errors?.priority) {\n cardSection1.push(createErrorTextParagraph(errors.priority));\n }\n cardSection1.push(cardSection1SelectionInput1);\n if (errors?.impact) {\n cardSection1.push(createErrorTextParagraph(errors.impact));\n }\n\n cardSection1.push(cardSection1SelectionInput2);\n cardSection1.push(cardSection1ButtonList1);\n\n const card = {\n header: cardHeader1,\n sections: [{\n widgets: cardSection1\n }]\n };\n\n if (isUpdate) {\n return {\n renderActions: {\n action: {\n navigations: [{\n updateCard: card\n }]\n }\n }\n };\n } else {\n return {\n action: {\n navigations: [{\n pushCard: card\n }]\n }\n };\n }\n}\n\n\n/**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param {!Object} event The event object with form input values.\n * @return {!ActionResponse|!SubmitFormResponse} The resulting response.\n */\nfunction submitCaseCreationForm(event) {\n const caseDetails = {\n name: event.commonEventObject.formInputs?.name?.stringInputs?.value[0],\n description: event.commonEventObject.formInputs?.description?.stringInputs?.value[0],\n priority: event.commonEventObject.formInputs?.priority?.stringInputs?.value[0],\n impact: !!event.commonEventObject.formInputs?.impact?.stringInputs?.value[0],\n };\n\n const errors = validateFormInputs(caseDetails);\n if (Object.keys(errors).length > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n const title = `Case ${caseDetails.name}`;\n // Adds the case details as parameters to the generated link URL.\n const url = new URL('https://example.com/support/cases/');\n for (const [key, value] of Object.entries(caseDetails)) {\n url.searchParams.append(key, value);\n }\n return createLinkRenderAction(title, url.href);\n }\n}\n\n\n/**\n * Validates case creation form input values.\n * \n * @param {!Object} caseDetails The values of each form input submitted by the user.\n * @return {!Object} A map from field name to error message. An empty object\n * represents a valid form submission.\n */\nfunction validateFormInputs(caseDetails) {\n const errors = {};\n if (caseDetails.name === undefined) {\n errors.name = 'You must provide a name';\n }\n if (caseDetails.description === undefined) {\n errors.description = 'You must provide a description';\n }\n if (caseDetails.priority === undefined) {\n errors.priority = 'You must provide a priority';\n }\n if (caseDetails.impact && !(['P0', 'P1']).includes(caseDetails.priority)) {\n errors.impact = 'If an issue blocks a critical customer operation, priority must be P0 or P1';\n }\n\n return errors;\n}\n\n/**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param {string} errorMessage A description of input value error.\n * @return {!TextParagraph} The resulting text paragraph.\n */\nfunction createErrorTextParagraph(errorMessage) {\n return {\n textParagraph: {\n text: '<font color=\\\"#BA0300\\\"><b>Error:</b> ' + errorMessage + '</font>'\n }\n }\n}\n\n\n/**\n * Returns a submit form response that inserts a link into the document.\n * \n * @param {string} title The title of the link to insert.\n * @param {string} url The URL of the link to insert.\n * @return {!SubmitFormResponse} The resulting submit form response.\n */\nfunction createLinkRenderAction(title, url) {\n return {\n renderActions: {\n action: {\n links: [{\n title: title,\n url: url\n }]\n }\n }\n };\n}\n```\n\nExample:\n```text\n# Copyright 2024 Google LLC\n#\n# 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.\n\nfrom typing import Any, Mapping\nfrom urllib.parse import urlencode\n\nimport os\nimport flask\nimport functions_framework\n\n\n@functions_framework.http\ndef create_3p_resources(req: flask.Request):\n \"\"\"Responds to any HTTP request related to 3P resource creations.\n Args:\n req: An HTTP request context.\n Returns:\n An HTTP response context.\n \"\"\"\n event = req.get_json(silent=True)\n parameters = event[\"commonEventObject\"][\"parameters\"] if \"parameters\" in event[\"commonEventObject\"] else None\n if parameters is not None and parameters[\"submitCaseCreationForm\"]:\n return submit_case_creation_form(event)\n else:\n return create_case_input_card(event)\n\n\n\n\ndef create_case_input_card(event, errors = {}, isUpdate = False):\n \"\"\"Produces a support case creation form card.\n Args:\n event: The event object.\n errors: An optional dict of per-field error messages.\n isUpdate: Whether to return the form as an update card navigation.\n Returns:\n The resulting card or action response.\n \"\"\"\n card_header1 = {\n \"title\": \"Create a support case\"\n }\n\n card_section1_text_input1 = {\n \"textInput\": {\n \"name\": \"name\",\n \"label\": \"Name\"\n }\n }\n\n card_section1_text_input2 = {\n \"textInput\": {\n \"name\": \"description\",\n \"label\": \"Description\",\n \"type\": \"MULTIPLE_LINE\"\n }\n }\n\n card_section1_selection_input1 = {\n \"selectionInput\": {\n \"name\": \"priority\",\n \"label\": \"Priority\",\n \"type\": \"DROPDOWN\",\n \"items\": [{\n \"text\": \"P0\",\n \"value\": \"P0\"\n }, {\n \"text\": \"P1\",\n \"value\": \"P1\"\n }, {\n \"text\": \"P2\",\n \"value\": \"P2\"\n }, {\n \"text\": \"P3\",\n \"value\": \"P3\"\n }]\n }\n }\n\n card_section1_selection_input2 = {\n \"selectionInput\": {\n \"name\": \"impact\",\n \"label\": \"Impact\",\n \"items\": [{\n \"text\": \"Blocks a critical customer operation\",\n \"value\": \"Blocks a critical customer operation\"\n }]\n }\n }\n\n card_section1_button_list1_button1_action1 = {\n \"function\": os.environ[\"URL\"],\n \"parameters\": [\n {\n \"key\": \"submitCaseCreationForm\",\n \"value\": True\n }\n ],\n \"persistValues\": True\n }\n\n card_section1_button_list1_button1 = {\n \"text\": \"Create\",\n \"onClick\": {\n \"action\": card_section1_button_list1_button1_action1\n }\n }\n\n card_section1_button_list1 = {\n \"buttonList\": {\n \"buttons\": [card_section1_button_list1_button1]\n }\n }\n\n # Builds the creation form and adds error text for invalid inputs.\n card_section1 = []\n if \"name\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"name\"]))\n card_section1.append(card_section1_text_input1)\n if \"description\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"description\"]))\n card_section1.append(card_section1_text_input2)\n if \"priority\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"priority\"]))\n card_section1.append(card_section1_selection_input1)\n if \"impact\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"impact\"]))\n\n card_section1.append(card_section1_selection_input2)\n card_section1.append(card_section1_button_list1)\n\n card = {\n \"header\": card_header1,\n \"sections\": [{\n \"widgets\": card_section1\n }]\n }\n\n if isUpdate:\n return {\n \"renderActions\": {\n \"action\": {\n \"navigations\": [{\n \"updateCard\": card\n }]\n }\n }\n }\n else:\n return {\n \"action\": {\n \"navigations\": [{\n \"pushCard\": card\n }]\n }\n }\n\n\n\n\ndef submit_case_creation_form(event):\n \"\"\"Submits the creation form.\n\n If valid, returns a render action that inserts a new link\n into the document. If invalid, returns an update card navigation that\n re-renders the creation form with error messages.\n Args:\n event: The event object with form input values.\n Returns:\n The resulting response.\n \"\"\"\n formInputs = event[\"commonEventObject\"][\"formInputs\"] if \"formInputs\" in event[\"commonEventObject\"] else None\n case_details = {\n \"name\": None,\n \"description\": None,\n \"priority\": None,\n \"impact\": None,\n }\n if formInputs is not None:\n case_details[\"name\"] = formInputs[\"name\"][\"stringInputs\"][\"value\"][0] if \"name\" in formInputs else None\n case_details[\"description\"] = formInputs[\"description\"][\"stringInputs\"][\"value\"][0] if \"description\" in formInputs else None\n case_details[\"priority\"] = formInputs[\"priority\"][\"stringInputs\"][\"value\"][0] if \"priority\" in formInputs else None\n case_details[\"impact\"] = formInputs[\"impact\"][\"stringInputs\"][\"value\"][0] if \"impact\" in formInputs else False\n\n errors = validate_form_inputs(case_details)\n if len(errors) > 0:\n return create_case_input_card(event, errors, True) # Update mode\n else:\n title = f'Case {case_details[\"name\"]}'\n # Adds the case details as parameters to the generated link URL.\n url = \"https://example.com/support/cases/?\" + urlencode(case_details)\n return create_link_render_action(title, url)\n\n\n\n\ndef validate_form_inputs(case_details):\n \"\"\"Validates case creation form input values.\n Args:\n case_details: The values of each form input submitted by the user.\n Returns:\n A dict from field name to error message. An empty object represents a valid form submission.\n \"\"\"\n errors = {}\n if case_details[\"name\"] is None:\n errors[\"name\"] = \"You must provide a name\"\n if case_details[\"description\"] is None:\n errors[\"description\"] = \"You must provide a description\"\n if case_details[\"priority\"] is None:\n errors[\"priority\"] = \"You must provide a priority\"\n if case_details[\"impact\"] is not None and case_details[\"priority\"] not in ['P0', 'P1']:\n errors[\"impact\"] = \"If an issue blocks a critical customer operation, priority must be P0 or P1\"\n return errors\n\n\ndef create_error_text_paragraph(error_message):\n \"\"\"Returns a text paragraph with red text indicating a form field validation error.\n Args:\n error_essage: A description of input value error.\n Returns:\n The resulting text paragraph.\n \"\"\"\n return {\n \"textParagraph\": {\n \"text\": '<font color=\\\"#BA0300\\\"><b>Error:</b> ' + error_message + '</font>'\n }\n }\n\n\n\n\ndef create_link_render_action(title, url):\n \"\"\"Returns a submit form response that inserts a link into the document.\n Args:\n title: The title of the link to insert.\n url: The URL of the link to insert.\n Returns:\n The resulting submit form response.\n \"\"\"\n return {\n \"renderActions\": {\n \"action\": {\n \"links\": [{\n \"title\": title,\n \"url\": url\n }]\n }\n }\n }\n```\n\nExample:\n```text\n# Copyright 2023 Google LLC\n#\n# 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.\n\nfrom typing import Any, Mapping\nfrom urllib.parse import urlparse, parse_qs\n\nimport flask\nimport functions_framework\n\n\n@functions_framework.http\ndef create_link_preview(req: flask.Request):\n \"\"\"Responds to any HTTP request related to link previews.\n Args:\n req: An HTTP request context.\n Returns:\n An HTTP response context.\n \"\"\"\n event = req.get_json(silent=True)\n if event[\"docs\"][\"matchedUrl\"][\"url\"]:\n url = event[\"docs\"][\"matchedUrl\"][\"url\"]\n parsed_url = urlparse(url)\n # If the event object URL matches a specified pattern for preview links.\n if parsed_url.hostname == \"example.com\":\n if parsed_url.path.startswith(\"/support/cases/\"):\n return case_link_preview(parsed_url)\n\n return {}\n\n\n\n\ndef case_link_preview(url):\n \"\"\"A support case link preview.\n Args:\n url: A matching URL.\n Returns:\n The resulting preview link card.\n \"\"\"\n\n # Parses the URL and identify the case details.\n query_string = parse_qs(url.query)\n name = f'Case {query_string[\"name\"][0]}'\n # Uses the text from the card's header for the title of the smart chip.\n return {\n \"action\": {\n \"linkPreview\": {\n \"title\": name,\n \"previewCard\": {\n \"header\": {\n \"title\": name\n },\n \"sections\": [{\n \"widgets\": [{\n \"textParagraph\": {\n \"text\": query_string[\"description\"][0]\n }\n }]\n }],\n }\n }\n }\n }\n```\n\nExample:\n```text\n/**\n * Copyright 2024 Google LLC\n *\n * 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.\n */\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.http.client.utils.URIBuilder;\n\nimport com.google.cloud.functions.HttpFunction;\nimport com.google.cloud.functions.HttpRequest;\nimport com.google.cloud.functions.HttpResponse;\nimport com.google.gson.Gson;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\npublic class Create3pResources implements HttpFunction {\n private static final Gson gson = new Gson();\n\n /**\n * Responds to any HTTP request related to 3p resource creations.\n *\n * @param request An HTTP request context.\n * @param response An HTTP response context.\n */\n @Override\n public void service(HttpRequest request, HttpResponse response) throws Exception {\n JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);\n JsonObject parameters = event.getAsJsonObject(\"commonEventObject\").getAsJsonObject(\"parameters\");\n if (parameters != null && parameters.has(\"submitCaseCreationForm\") && parameters.get(\"submitCaseCreationForm\").getAsBoolean()) {\n response.getWriter().write(gson.toJson(submitCaseCreationForm(event)));\n } else {\n response.getWriter().write(gson.toJson(createCaseInputCard(event, new HashMap<String, String>(), false)));\n }\n }\n\n\n /**\n * Produces a support case creation form.\n * \n * @param event The event object.\n * @param errors A map of per-field error messages.\n * @param isUpdate Whether to return the form as an update card navigation.\n * @return The resulting card or action response.\n */\n JsonObject createCaseInputCard(JsonObject event, Map<String, String> errors, boolean isUpdate) {\n JsonObject cardHeader = new JsonObject();\n cardHeader.add(\"title\", new JsonPrimitive(\"Create a support case\"));\n\n JsonObject cardSectionTextInput1 = new JsonObject();\n cardSectionTextInput1.add(\"name\", new JsonPrimitive(\"name\"));\n cardSectionTextInput1.add(\"label\", new JsonPrimitive(\"Name\"));\n\n JsonObject cardSectionTextInput1Widget = new JsonObject();\n cardSectionTextInput1Widget.add(\"textInput\", cardSectionTextInput1);\n\n JsonObject cardSectionTextInput2 = new JsonObject();\n cardSectionTextInput2.add(\"name\", new JsonPrimitive(\"description\"));\n cardSectionTextInput2.add(\"label\", new JsonPrimitive(\"Description\"));\n cardSectionTextInput2.add(\"type\", new JsonPrimitive(\"MULTIPLE_LINE\"));\n\n JsonObject cardSectionTextInput2Widget = new JsonObject();\n cardSectionTextInput2Widget.add(\"textInput\", cardSectionTextInput2);\n\n JsonObject cardSectionSelectionInput1ItemsItem1 = new JsonObject();\n cardSectionSelectionInput1ItemsItem1.add(\"text\", new JsonPrimitive(\"P0\"));\n cardSectionSelectionInput1ItemsItem1.add(\"value\", new JsonPrimitive(\"P0\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem2 = new JsonObject();\n cardSectionSelectionInput1ItemsItem2.add(\"text\", new JsonPrimitive(\"P1\"));\n cardSectionSelectionInput1ItemsItem2.add(\"value\", new JsonPrimitive(\"P1\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem3 = new JsonObject();\n cardSectionSelectionInput1ItemsItem3.add(\"text\", new JsonPrimitive(\"P2\"));\n cardSectionSelectionInput1ItemsItem3.add(\"value\", new JsonPrimitive(\"P2\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem4 = new JsonObject();\n cardSectionSelectionInput1ItemsItem4.add(\"text\", new JsonPrimitive(\"P3\"));\n cardSectionSelectionInput1ItemsItem4.add(\"value\", new JsonPrimitive(\"P3\"));\n\n JsonArray cardSectionSelectionInput1Items = new JsonArray();\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem1);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem2);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem3);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem4);\n\n JsonObject cardSectionSelectionInput1 = new JsonObject();\n cardSectionSelectionInput1.add(\"name\", new JsonPrimitive(\"priority\"));\n cardSectionSelectionInput1.add(\"label\", new JsonPrimitive(\"Priority\"));\n cardSectionSelectionInput1.add(\"type\", new JsonPrimitive(\"DROPDOWN\"));\n cardSectionSelectionInput1.add(\"items\", cardSectionSelectionInput1Items);\n\n JsonObject cardSectionSelectionInput1Widget = new JsonObject();\n cardSectionSelectionInput1Widget.add(\"selectionInput\", cardSectionSelectionInput1);\n\n JsonObject cardSectionSelectionInput2ItemsItem = new JsonObject();\n cardSectionSelectionInput2ItemsItem.add(\"text\", new JsonPrimitive(\"Blocks a critical customer operation\"));\n cardSectionSelectionInput2ItemsItem.add(\"value\", new JsonPrimitive(\"Blocks a critical customer operation\"));\n\n JsonArray cardSectionSelectionInput2Items = new JsonArray();\n cardSectionSelectionInput2Items.add(cardSectionSelectionInput2ItemsItem);\n\n JsonObject cardSectionSelectionInput2 = new JsonObject();\n cardSectionSelectionInput2.add(\"name\", new JsonPrimitive(\"impact\"));\n cardSectionSelectionInput2.add(\"label\", new JsonPrimitive(\"Impact\"));\n cardSectionSelectionInput2.add(\"items\", cardSectionSelectionInput2Items);\n\n JsonObject cardSectionSelectionInput2Widget = new JsonObject();\n cardSectionSelectionInput2Widget.add(\"selectionInput\", cardSectionSelectionInput2);\n\n JsonObject cardSectionButtonListButtonActionParametersParameter = new JsonObject();\n cardSectionButtonListButtonActionParametersParameter.add(\"key\", new JsonPrimitive(\"submitCaseCreationForm\"));\n cardSectionButtonListButtonActionParametersParameter.add(\"value\", new JsonPrimitive(true));\n\n JsonArray cardSectionButtonListButtonActionParameters = new JsonArray();\n cardSectionButtonListButtonActionParameters.add(cardSectionButtonListButtonActionParametersParameter);\n\n JsonObject cardSectionButtonListButtonAction = new JsonObject();\n cardSectionButtonListButtonAction.add(\"function\", new JsonPrimitive(System.getenv().get(\"URL\")));\n cardSectionButtonListButtonAction.add(\"parameters\", cardSectionButtonListButtonActionParameters);\n cardSectionButtonListButtonAction.add(\"persistValues\", new JsonPrimitive(true));\n\n JsonObject cardSectionButtonListButtonOnCLick = new JsonObject();\n cardSectionButtonListButtonOnCLick.add(\"action\", cardSectionButtonListButtonAction);\n\n JsonObject cardSectionButtonListButton = new JsonObject();\n cardSectionButtonListButton.add(\"text\", new JsonPrimitive(\"Create\"));\n cardSectionButtonListButton.add(\"onClick\", cardSectionButtonListButtonOnCLick);\n\n JsonArray cardSectionButtonListButtons = new JsonArray();\n cardSectionButtonListButtons.add(cardSectionButtonListButton);\n\n JsonObject cardSectionButtonList = new JsonObject();\n cardSectionButtonList.add(\"buttons\", cardSectionButtonListButtons);\n\n JsonObject cardSectionButtonListWidget = new JsonObject();\n cardSectionButtonListWidget.add(\"buttonList\", cardSectionButtonList);\n\n // Builds the form inputs with error texts for invalid values.\n JsonArray cardSection = new JsonArray();\n if (errors.containsKey(\"name\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"name\").toString()));\n }\n cardSection.add(cardSectionTextInput1Widget);\n if (errors.containsKey(\"description\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"description\").toString()));\n }\n cardSection.add(cardSectionTextInput2Widget);\n if (errors.containsKey(\"priority\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"priority\").toString()));\n }\n cardSection.add(cardSectionSelectionInput1Widget);\n if (errors.containsKey(\"impact\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"impact\").toString()));\n }\n\n cardSection.add(cardSectionSelectionInput2Widget);\n cardSection.add(cardSectionButtonListWidget);\n\n JsonObject cardSectionWidgets = new JsonObject();\n cardSectionWidgets.add(\"widgets\", cardSection);\n\n JsonArray sections = new JsonArray();\n sections.add(cardSectionWidgets);\n\n JsonObject card = new JsonObject();\n card.add(\"header\", cardHeader);\n card.add(\"sections\", sections);\n\n JsonObject navigation = new JsonObject();\n if (isUpdate) {\n navigation.add(\"updateCard\", card);\n } else {\n navigation.add(\"pushCard\", card);\n }\n\n JsonArray navigations = new JsonArray();\n navigations.add(navigation);\n\n JsonObject action = new JsonObject();\n action.add(\"navigations\", navigations);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n if (!isUpdate) {\n return renderActions;\n }\n\n JsonObject update = new JsonObject();\n update.add(\"renderActions\", renderActions);\n\n return update;\n }\n\n\n /**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param event The event object with form input values.\n * @return The resulting response.\n */\n JsonObject submitCaseCreationForm(JsonObject event) throws Exception {\n JsonObject formInputs = event.getAsJsonObject(\"commonEventObject\").getAsJsonObject(\"formInputs\");\n Map<String, String> caseDetails = new HashMap<String, String>();\n if (formInputs != null) {\n if (formInputs.has(\"name\")) {\n caseDetails.put(\"name\", formInputs.getAsJsonObject(\"name\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"description\")) {\n caseDetails.put(\"description\", formInputs.getAsJsonObject(\"description\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"priority\")) {\n caseDetails.put(\"priority\", formInputs.getAsJsonObject(\"priority\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"impact\")) {\n caseDetails.put(\"impact\", formInputs.getAsJsonObject(\"impact\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n }\n\n Map<String, String> errors = validateFormInputs(caseDetails);\n if (errors.size() > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n String title = String.format(\"Case %s\", caseDetails.get(\"name\"));\n // Adds the case details as parameters to the generated link URL.\n URIBuilder uriBuilder = new URIBuilder(\"https://example.com/support/cases/\");\n for (String caseDetailKey : caseDetails.keySet()) {\n uriBuilder.addParameter(caseDetailKey, caseDetails.get(caseDetailKey));\n }\n return createLinkRenderAction(title, uriBuilder.build().toURL().toString());\n }\n }\n\n\n /**\n * Validates case creation form input values.\n * \n * @param caseDetails The values of each form input submitted by the user.\n * @return A map from field name to error message. An empty object\n * represents a valid form submission.\n */\n Map<String, String> validateFormInputs(Map<String, String> caseDetails) {\n Map<String, String> errors = new HashMap<String, String>();\n if (!caseDetails.containsKey(\"name\")) {\n errors.put(\"name\", \"You must provide a name\");\n }\n if (!caseDetails.containsKey(\"description\")) {\n errors.put(\"description\", \"You must provide a description\");\n }\n if (!caseDetails.containsKey(\"priority\")) {\n errors.put(\"priority\", \"You must provide a priority\");\n }\n if (caseDetails.containsKey(\"impact\") && !Arrays.asList(new String[]{\"P0\", \"P1\"}).contains(caseDetails.get(\"priority\"))) {\n errors.put(\"impact\", \"If an issue blocks a critical customer operation, priority must be P0 or P1\");\n }\n\n return errors;\n }\n\n /**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param errorMessage A description of input value error.\n * @return The resulting text paragraph.\n */\n JsonObject createErrorTextParagraph(String errorMessage) {\n JsonObject textParagraph = new JsonObject();\n textParagraph.add(\"text\", new JsonPrimitive(\"<font color=\\\"#BA0300\\\"><b>Error:</b> \" + errorMessage + \"</font>\"));\n\n JsonObject textParagraphWidget = new JsonObject();\n textParagraphWidget.add(\"textParagraph\", textParagraph);\n\n return textParagraphWidget;\n }\n\n\n /**\n * Returns a submit form response that inserts a link into the document.\n * \n * @param title The title of the link to insert.\n * @param url The URL of the link to insert.\n * @return The resulting submit form response.\n */\n JsonObject createLinkRenderAction(String title, String url) {\n JsonObject link = new JsonObject();\n link.add(\"title\", new JsonPrimitive(title));\n link.add(\"url\", new JsonPrimitive(url));\n\n JsonArray links = new JsonArray();\n links.add(link);\n\n JsonObject action = new JsonObject();\n action.add(\"links\", links);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n JsonObject linkRenderAction = new JsonObject();\n linkRenderAction.add(\"renderActions\", renderActions);\n\n return linkRenderAction;\n }\n\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2024 Google LLC\n *\n * 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.\n */\n\nimport com.google.cloud.functions.HttpFunction;\nimport com.google.cloud.functions.HttpRequest;\nimport com.google.cloud.functions.HttpResponse;\nimport com.google.gson.Gson;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URL;\nimport java.net.URLDecoder;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class CreateLinkPreview implements HttpFunction {\n private static final Gson gson = new Gson();\n\n /**\n * Responds to any HTTP request related to link previews.\n *\n * @param request An HTTP request context.\n * @param response An HTTP response context.\n */\n @Override\n public void service(HttpRequest request, HttpResponse response) throws Exception {\n JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);\n String url = event.getAsJsonObject(\"docs\")\n .getAsJsonObject(\"matchedUrl\")\n .get(\"url\")\n .getAsString();\n URL parsedURL = new URL(url);\n // If the event object URL matches a specified pattern for preview links.\n if (\"example.com\".equals(parsedURL.getHost())) {\n if (parsedURL.getPath().startsWith(\"/support/cases/\")) {\n response.getWriter().write(gson.toJson(caseLinkPreview(parsedURL)));\n return;\n }\n }\n\n response.getWriter().write(\"{}\");\n }\n\n\n /**\n * A support case link preview.\n *\n * @param url A matching URL.\n * @return The resulting preview link card.\n */\n JsonObject caseLinkPreview(URL url) throws UnsupportedEncodingException {\n // Parses the URL and identify the case details.\n Map<String, String> caseDetails = new HashMap<String, String>();\n for (String pair : url.getQuery().split(\"&\")) {\n caseDetails.put(URLDecoder.decode(pair.split(\"=\")[0], \"UTF-8\"), URLDecoder.decode(pair.split(\"=\")[1], \"UTF-8\"));\n }\n\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n JsonObject cardHeader = new JsonObject();\n String caseName = String.format(\"Case %s\", caseDetails.get(\"name\"));\n cardHeader.add(\"title\", new JsonPrimitive(caseName));\n\n JsonObject textParagraph = new JsonObject();\n textParagraph.add(\"text\", new JsonPrimitive(caseDetails.get(\"description\")));\n\n JsonObject widget = new JsonObject();\n widget.add(\"textParagraph\", textParagraph);\n\n JsonArray widgets = new JsonArray();\n widgets.add(widget);\n\n JsonObject section = new JsonObject();\n section.add(\"widgets\", widgets);\n\n JsonArray sections = new JsonArray();\n sections.add(section);\n\n JsonObject previewCard = new JsonObject();\n previewCard.add(\"header\", cardHeader);\n previewCard.add(\"sections\", sections);\n\n JsonObject linkPreview = new JsonObject();\n linkPreview.add(\"title\", new JsonPrimitive(caseName));\n linkPreview.add(\"previewCard\", previewCard);\n\n JsonObject action = new JsonObject();\n action.add(\"linkPreview\", linkPreview);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n return renderActions;\n }\n\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.588Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":24,"totalLines":2406,"estimatedTokens":19415}}737{"id":"doc-translate_text_from_google_slides_google_workspa-86ff41a5","source":"documentation","title":"Translate text from Google Slides | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/editors/slides/quickstart/translate","text":"Example:\n```text\n/**\n * @OnlyCurrentDoc Limits the script to only accessing the current presentation.\n */\n\n/**\n * Create a open translate menu item.\n * @param {Event} event The open event.\n */\nfunction onOpen(event) {\n SlidesApp.getUi()\n .createAddonMenu()\n .addItem(\"Open Translate\", \"showSidebar\")\n .addToUi();\n}\n\n/**\n * Open the Add-on upon install.\n * @param {Event} event The install event.\n */\nfunction onInstall(event) {\n onOpen(event);\n}\n\n/**\n * Opens a sidebar in the document containing the add-on's user interface.\n */\nfunction showSidebar() {\n const ui =\n HtmlService.createHtmlOutputFromFile(\"sidebar\").setTitle(\"Translate\");\n SlidesApp.getUi().showSidebar(ui);\n}\n\n/**\n * Recursively gets child text elements a list of elements.\n * @param {PageElement[]} elements The elements to get text from.\n * @return {Text[]} An array of text elements.\n */\nfunction getElementTexts(elements) {\n let texts = [];\n for (const element of elements) {\n switch (element.getPageElementType()) {\n case SlidesApp.PageElementType.GROUP:\n for (const child of element.asGroup().getChildren()) {\n texts = texts.concat(getElementTexts(child));\n }\n break;\n case SlidesApp.PageElementType.TABLE: {\n const table = element.asTable();\n for (let r = 0; r < table.getNumRows(); ++r) {\n for (let c = 0; c < table.getNumColumns(); ++c) {\n texts.push(table.getCell(r, c).getText());\n }\n }\n break;\n }\n case SlidesApp.PageElementType.SHAPE:\n texts.push(element.asShape().getText());\n break;\n }\n }\n return texts;\n}\n\n/**\n * Translates selected slide elements to the target language using Apps Script's Language service.\n *\n * @param {string} targetLanguage The two-letter short form for the target language. (ISO 639-1)\n * @return {number} The number of elements translated.\n */\nfunction translateSelectedElements(targetLanguage) {\n // Get selected elements.\n const selection = SlidesApp.getActivePresentation().getSelection();\n const selectionType = selection.getSelectionType();\n let texts = [];\n switch (selectionType) {\n case SlidesApp.SelectionType.PAGE:\n for (const page of selection.getPageRange().getPages()) {\n texts = texts.concat(getElementTexts(page.getPageElements()));\n }\n break;\n case SlidesApp.SelectionType.PAGE_ELEMENT: {\n const pageElements = selection.getPageElementRange().getPageElements();\n texts = texts.concat(getElementTexts(pageElements));\n break;\n }\n case SlidesApp.SelectionType.TABLE_CELL:\n for (const cell of selection.getTableCellRange().getTableCells()) {\n texts.push(cell.getText());\n }\n break;\n case SlidesApp.SelectionType.TEXT:\n for (const element of selection.getPageElementRange().getPageElements()) {\n texts.push(element.asShape().getText());\n }\n break;\n }\n\n // Translate all elements in-place.\n for (const text of texts) {\n text.setText(\n LanguageApp.translate(text.asRenderedString(), \"\", targetLanguage),\n );\n }\n\n return texts.length;\n}\n```\n\nExample:\n```text\n<html>\n<head>\n <link rel=\"stylesheet\" href=\"https://ssl.gstatic.com/docs/script/css/add-ons1.css\">\n <style>\n .logo { vertical-align: middle; }\n ul { list-style-type: none; padding: 0; }\n h4 { margin: 0; }\n </style>\n</head>\n<body>\n<form class=\"sidebar branding-below\">\n <h4>Translate selected slides into:</h4>\n <ul id=\"languages\"></ul>\n <div class=\"block\" id=\"button-bar\">\n <button class=\"blue\" id=\"run-translation\">Translate</button>\n </div>\n <h5 class=\"error\" id=\"error\"></h5>\n</form>\n<div class=\"sidebar bottom\">\n <img alt=\"Add-on logo\" class=\"logo\"\n src=\"https://www.gstatic.com/images/branding/product/1x/translate_48dp.png\" width=\"27\" height=\"27\">\n <span class=\"gray branding-text\">Translate sample by Google</span>\n</div>\n\n<script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js\"></script>\n<script>\n $(function() {\n // Add an input radio button for every language.\n const languages = {\n ar: 'Arabic',\n zh: 'Chinese',\n en: 'English',\n fr: 'French',\n de: 'German',\n hi: 'Hindi',\n ja: 'Japanese',\n pt: 'Portuguese',\n es: 'Spanish'\n };\n const languageList = Object.keys(languages).map((id)=> {\n return $('<li>').html([\n $('<input>')\n .attr('type', 'radio')\n .attr('name', 'dest')\n .attr('id', 'radio-dest-' + id)\n .attr('value', id),\n $('<label>')\n .attr('for', 'radio-dest-' + id)\n .html(languages[id])\n ]);\n });\n\n $('#run-translation').click(runTranslation);\n $('#languages').html(languageList);\n });\n\n /**\n * Runs a server-side function to translate the text on all slides.\n */\n function runTranslation() {\n this.disabled = true;\n $('#error').text('');\n google.script.run\n .withSuccessHandler((numTranslatedElements, element) =>{\n element.disabled = false;\n if (numTranslatedElements === 0) {\n $('#error').empty()\n .append('Did you select elements to translate?')\n .append('<br/>')\n .append('Please select slides or individual elements.');\n }\n return false;\n })\n .withFailureHandler((msg, element)=> {\n element.disabled = false;\n $('#error').text('Something went wrong. Please check the add-on logs.');\n return false;\n })\n .withUserObject(this)\n .translateSelectedElements($('input[name=dest]:checked').val());\n }\n</script>\n</body>\n</html>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.590Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":199,"estimatedTokens":1447}}738{"id":"doc-custom_menus_for_editor_add_ons_google_workspace-7653fa1c","source":"documentation","title":"Custom menus for Editor add-ons | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/concepts/menus","text":"Example:\n```text\nfunction onOpen(e) {\n // Or DocumentApp, SlidesApp, or FormApp.\n var menu = SpreadsheetApp.getUi().createAddonMenu();\n if (e && e.authMode == ScriptApp.AuthMode.NONE) {\n // Add a normal menu item (works in all authorization modes).\n menu.addItem('Start workflow', 'startWorkflow');\n } else {\n // Add a menu item based on properties (doesn't work in AuthMode.NONE).\n var properties = PropertiesService.getDocumentProperties();\n var workflowStarted = properties.getProperty('workflowStarted');\n if (workflowStarted) {\n menu.addItem('Check workflow status', 'checkWorkflow');\n } else {\n menu.addItem('Start workflow', 'startWorkflow');\n }\n // Record analytics.\n UrlFetchApp.fetch('http://www.example.com/analytics?event=open');\n }\n menu.addToUi();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.591Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":25,"estimatedTokens":207}}739{"id":"doc-editor_add_on_authorization_google_workspace_add-9255860f","source":"documentation","title":"Editor add-on authorization | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/concepts/editor-auth-lifecycle","text":"Example:\n```text\nfunction onInstall(e) {\n onOpen(e);\n // Perform additional setup as needed.\n}\n```\n\nExample:\n```text\nfunction onOpen(e) {\n SpreadsheetApp.getUi().createAddonMenu() // Or DocumentApp.\n .addItem('Insert chart', 'insertChart')\n .addItem('Update charts', 'updateCharts')\n .addToUi();\n}\n```\n\nExample:\n```text\nfunction onOpen(e) {\n var menu = SpreadsheetApp.getUi().createAddonMenu(); // Or DocumentApp.\n if (e && e.authMode == ScriptApp.AuthMode.NONE) {\n // Add a normal menu item (works in all authorization modes).\n menu.addItem('Start workflow', 'startWorkflow');\n } else {\n // Add a menu item based on properties (doesn't work in AuthMode.NONE).\n var properties = PropertiesService.getDocumentProperties();\n var workflowStarted = properties.getProperty('workflowStarted');\n if (workflowStarted) {\n menu.addItem('Check workflow status', 'checkWorkflow');\n } else {\n menu.addItem('Start workflow', 'startWorkflow');\n }\n }\n menu.addToUi();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.592Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":40,"estimatedTokens":257}}740{"id":"doc-send_emails_about_new_google_forms_submissions_g-f5668cdd","source":"documentation","title":"Send emails about new Google Forms submissions | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/editors/forms/quickstart/forms-notifications","text":"Example:\n```text\n/**\n * @OnlyCurrentDoc\n *\n * The above comment directs Apps Script to limit the scope of file\n * access for this add-on. It specifies that this add-on will only\n * attempt to read or modify the files in which the add-on is used,\n * and not all of the user's files. The authorization request message\n * presented to users will reflect this limited scope.\n */\n\n/**\n * A global constant String holding the title of the add-on. This is\n * used to identify the add-on in the notification emails.\n */\nconst ADDON_TITLE = \"Form Notifications\";\n\n/**\n * A global constant 'notice' text to include with each email\n * notification.\n */\nconst NOTICE =\n \"Form Notifications was created as an sample add-on, and is\" +\n \" meant for\" +\n \"demonstration purposes only. It should not be used for complex or important\" +\n \"workflows. The number of notifications this add-on produces are limited by the\" +\n \"owner's available email quota; it will not send email notifications if the\" +\n \"owner's daily email quota has been exceeded. Collaborators using this add-on on\" +\n \"the same form will be able to adjust the notification settings, but will not be\" +\n \"able to disable the notification triggers set by other collaborators.\";\n\n/**\n * Adds a custom menu to the active form to show the add-on sidebar.\n *\n * @param {object} e The event parameter for a simple onOpen trigger. To\n * determine which authorization mode (ScriptApp.AuthMode) the trigger is\n * running in, inspect e.authMode.\n */\nfunction onOpen(e) {\n try {\n FormApp.getUi()\n .createAddonMenu()\n .addItem(\"Configure notifications\", \"showSidebar\")\n .addItem(\"About\", \"showAbout\")\n .addToUi();\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Runs when the add-on is installed.\n *\n * @param {object} e The event parameter for a simple onInstall trigger. To\n * determine which authorization mode (ScriptApp.AuthMode) the trigger is\n * running in, inspect e.authMode. (In practice, onInstall triggers always\n * run in AuthMode.FULL, but onOpen triggers may be AuthMode.LIMITED or\n * AuthMode.NONE).\n */\nfunction onInstall(e) {\n onOpen(e);\n}\n\n/**\n * Opens a sidebar in the form containing the add-on's user interface for\n * configuring the notifications this add-on will produce.\n */\nfunction showSidebar() {\n try {\n const ui =\n HtmlService.createHtmlOutputFromFile(\"sidebar\").setTitle(\n \"Form Notifications\",\n );\n FormApp.getUi().showSidebar(ui);\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Opens a purely-informational dialog in the form explaining details about\n * this add-on.\n */\nfunction showAbout() {\n try {\n const ui = HtmlService.createHtmlOutputFromFile(\"about\")\n .setWidth(420)\n .setHeight(270);\n FormApp.getUi().showModalDialog(ui, \"About Form Notifications\");\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Save sidebar settings to this form's Properties, and update the onFormSubmit\n * trigger as needed.\n *\n * @param {Object} settings An Object containing key-value\n * pairs to store.\n */\nfunction saveSettings(settings) {\n try {\n PropertiesService.getDocumentProperties().setProperties(settings);\n adjustFormSubmitTrigger();\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Queries the User Properties and adds additional data required to populate\n * the sidebar UI elements.\n *\n * @return {Object} A collection of Property values and\n * related data used to fill the configuration sidebar.\n */\nfunction getSettings() {\n try {\n const settings = PropertiesService.getDocumentProperties().getProperties();\n\n // Use a default email if the creator email hasn't been provided yet.\n if (!settings.creatorEmail) {\n settings.creatorEmail = Session.getEffectiveUser().getEmail();\n }\n\n // Get text field items in the form and compile a list\n // of their titles and IDs.\n const form = FormApp.getActiveForm();\n const textItems = form.getItems(FormApp.ItemType.TEXT);\n\n settings.textItems = [];\n for (let i = 0; i < textItems.length; i++) {\n settings.textItems.push({\n title: textItems[i].getTitle(),\n id: textItems[i].getId(),\n });\n }\n return settings;\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Adjust the onFormSubmit trigger based on user's requests.\n */\nfunction adjustFormSubmitTrigger() {\n try {\n const form = FormApp.getActiveForm();\n const triggers = ScriptApp.getUserTriggers(form);\n const settings = PropertiesService.getDocumentProperties();\n const triggerNeeded =\n settings.getProperty(\"creatorNotify\") === \"true\" ||\n settings.getProperty(\"respondentNotify\") === \"true\";\n\n // Create a new trigger if required; delete existing trigger\n // if it is not needed.\n let existingTrigger = null;\n for (let i = 0; i < triggers.length; i++) {\n if (triggers[i].getEventType() === ScriptApp.EventType.ON_FORM_SUBMIT) {\n existingTrigger = triggers[i];\n break;\n }\n }\n if (triggerNeeded && !existingTrigger) {\n const trigger = ScriptApp.newTrigger(\"respondToFormSubmit\")\n .forForm(form)\n .onFormSubmit()\n .create();\n } else if (!triggerNeeded && existingTrigger) {\n ScriptApp.deleteTrigger(existingTrigger);\n }\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Responds to a form submission event if an onFormSubmit trigger has been\n * enabled.\n *\n * @param {Object} e The event parameter created by a form\n * submission; see\n * https://developers.google.com/apps-script/understanding_events\n */\nfunction respondToFormSubmit(e) {\n try {\n const settings = PropertiesService.getDocumentProperties();\n const authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);\n\n // Check if the actions of the trigger require authorizations that have not\n // been supplied yet -- if so, warn the active user via email (if possible).\n // This check is required when using triggers with add-ons to maintain\n // functional triggers.\n if (\n authInfo.getAuthorizationStatus() ===\n ScriptApp.AuthorizationStatus.REQUIRED\n ) {\n // Re-authorization is required. In this case, the user needs to be alerted\n // that they need to reauthorize; the normal trigger action is not\n // conducted, since authorization needs to be provided first. Send at\n // most one 'Authorization Required' email a day, to avoid spamming users\n // of the add-on.\n sendReauthorizationRequest();\n } else {\n // All required authorizations have been granted, so continue to respond to\n // the trigger event.\n\n // Check if the form creator needs to be notified; if so, construct and\n // send the notification.\n if (settings.getProperty(\"creatorNotify\") === \"true\") {\n sendCreatorNotification();\n }\n\n // Check if the form respondent needs to be notified; if so, construct and\n // send the notification. Be sure to respect the remaining email quota.\n if (\n settings.getProperty(\"respondentNotify\") === \"true\" &&\n MailApp.getRemainingDailyQuota() > 0\n ) {\n sendRespondentNotification(e.response);\n }\n }\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Called when the user needs to reauthorize. Sends the user of the\n * add-on an email explaining the need to reauthorize and provides\n * a link for the user to do so. Capped to send at most one email\n * a day to prevent spamming the users of the add-on.\n */\nfunction sendReauthorizationRequest() {\n try {\n const settings = PropertiesService.getDocumentProperties();\n const authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);\n const lastAuthEmailDate = settings.getProperty(\"lastAuthEmailDate\");\n const today = new Date().toDateString();\n if (lastAuthEmailDate !== today) {\n if (MailApp.getRemainingDailyQuota() > 0) {\n const template =\n HtmlService.createTemplateFromFile(\"authorizationEmail\");\n template.url = authInfo.getAuthorizationUrl();\n template.notice = NOTICE;\n const message = template.evaluate();\n MailApp.sendEmail(\n Session.getEffectiveUser().getEmail(),\n \"Authorization Required\",\n message.getContent(),\n {\n name: ADDON_TITLE,\n htmlBody: message.getContent(),\n },\n );\n }\n settings.setProperty(\"lastAuthEmailDate\", today);\n }\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Sends out creator notification email(s) if the current number\n * of form responses is an even multiple of the response step\n * setting.\n */\nfunction sendCreatorNotification() {\n try {\n const form = FormApp.getActiveForm();\n const settings = PropertiesService.getDocumentProperties();\n let responseStep = settings.getProperty(\"responseStep\");\n responseStep = responseStep ? Number.parseInt(responseStep) : 10;\n\n // If the total number of form responses is an even multiple of the\n // response step setting, send a notification email(s) to the form\n // creator(s). For example, if the response step is 10, notifications\n // will be sent when there are 10, 20, 30, etc. total form responses\n // received.\n if (form.getResponses().length % responseStep === 0) {\n const addresses = settings.getProperty(\"creatorEmail\").split(\",\");\n if (MailApp.getRemainingDailyQuota() > addresses.length) {\n const template = HtmlService.createTemplateFromFile(\n \"creatorNotification\",\n );\n template.summary = form.getSummaryUrl();\n template.responses = form.getResponses().length;\n template.title = form.getTitle();\n template.responseStep = responseStep;\n template.formUrl = form.getEditUrl();\n template.notice = NOTICE;\n const message = template.evaluate();\n MailApp.sendEmail(\n settings.getProperty(\"creatorEmail\"),\n `${form.getTitle()}: Form submissions detected`,\n message.getContent(),\n {\n name: ADDON_TITLE,\n htmlBody: message.getContent(),\n },\n );\n }\n }\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Sends out respondent notification emails.\n *\n * @param {FormResponse} response FormResponse object of the event\n * that triggered this notification\n */\nfunction sendRespondentNotification(response) {\n try {\n const form = FormApp.getActiveForm();\n const settings = PropertiesService.getDocumentProperties();\n const emailId = settings.getProperty(\"respondentEmailItemId\");\n const emailItem = form.getItemById(Number.parseInt(emailId));\n const respondentEmail = response\n .getResponseForItem(emailItem)\n .getResponse();\n if (respondentEmail) {\n const template = HtmlService.createTemplateFromFile(\n \"respondentNotification\",\n );\n template.paragraphs = settings.getProperty(\"responseText\").split(\"\\n\");\n template.notice = NOTICE;\n const message = template.evaluate();\n MailApp.sendEmail(\n respondentEmail,\n settings.getProperty(\"responseSubject\"),\n message.getContent(),\n {\n name: form.getTitle(),\n htmlBody: message.getContent(),\n },\n );\n }\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <link rel=\"stylesheet\" href=\"https://ssl.gstatic.com/docs/script/css/add-ons1.css\">\n <!-- The CSS package above applies Google styling to buttons and other elements. -->\n <style>\n .branding-below {\n bottom: 54px;\n top: 0;\n }\n .branding-text {\n left: 7px;\n position: relative;\n top: 3px;\n }\n .logo {\n vertical-align: middle;\n }\n .width-100 {\n width: 100%;\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n }\n label {\n font-weight: bold;\n }\n #creator-options,\n #respondent-options {\n background-color: #eee;\n border-color: #eee;\n border-width: 5px;\n border-style: solid;\n display: none;\n }\n #creator-email,\n #respondent-email,\n #button-bar,\n #submit-subject {\n margin-bottom: 10px;\n }\n\n #response-step {\n display: inline;\n }\n </style>\n </head>\n <body>\n <div class=\"sidebar branding-below\">\n <form>\n <div class=\"block\">\n <input type=\"checkbox\" id=\"creator-notify\">\n <label for=\"creator-notify\">Notify me</label>\n </div>\n <div class=\"block form-group\" id=\"creator-options\">\n <label for=\"creator-email\">\n My email addresses (comma-separated)\n </label>\n <input type=\"text\" class=\"width-100\" id=\"creator-email\">\n <label for=\"response-step\">Send notifications after every</label>\n <input type=\"number\" id=\"response-step\" value=\"10\"\n min=\"1\" max=\"99999\"> responses (default 10)\n </div>\n\n <div class=\"block\">\n <input type=\"checkbox\" id=\"respondent-notify\">\n <label for=\"respondent-notify\">Notify respondents</label>\n </div>\n <div class=\"block form-group\" id=\"respondent-options\">\n <label for=\"respondent-email\">\n Which question asks for their email?\n </label>\n <select class=\"width-100\" id=\"respondent-email\"></select>\n <label for=\"submit-subject\">\n Notification email subject:\n </label>\n <input type=\"text\" class=\"width-100\" id=\"submit-subject\">\n <label for=\"submit-notice\">Notification email body:</label>\n <textarea rows=\"8\" cols=\"40\" id=\"submit-notice\"\n class=\"width-100\"></textarea>\n </div>\n\n <div class=\"block\" id=\"button-bar\">\n <button class=\"action\" id=\"save-settings\">Save</button>\n </div>\n </form>\n </div>\n\n <div class=\"sidebar bottom\">\n <img alt=\"Add-on logo\" class=\"logo\" width=\"25\"\n src=\"https://g-suite-documentation-images.firebaseapp.com/images/newFormNotificationsicon.png\">\n <span class=\"gray branding-text\">Form Notifications by Google</span>\n </div>\n\n <script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js\">\n </script>\n <script>\n /**\n * On document load, assign required handlers to each element,\n * and attempt to load any saved settings.\n */\n $(function() {\n $('#save-settings').click(saveSettingsToServer);\n $('#creator-notify').click(toggleCreatorNotify);\n $('#respondent-notify').click(toggleRespondentNotify);\n $('#response-step').change(validateNumber);\n google.script.run\n .withSuccessHandler(loadSettings)\n .withFailureHandler(showStatus)\n .withUserObject($('#button-bar').get())\n .getSettings();\n });\n\n /**\n * Callback function that populates the notification options using\n * previously saved values.\n *\n * @param {Object} settings The saved settings from the client.\n */\n function loadSettings(settings) {\n $('#creator-email').val(settings.creatorEmail);\n $('#response-step').val(!settings.responseStep ?\n 10 : settings.responseStep);\n $('#submit-subject').val(!settings.responseSubject ?\n 'Thank you for filling out our form!' :\n settings.responseSubject);\n $('#submit-notice').val(!settings.responseText ?\n 'Thank you for responding to our form!' :\n settings.responseText);\n\n if (settings.creatorNotify === 'true') {\n $('#creator-notify').prop('checked', true);\n $('#creator-options').show();\n }\n\n if (settings.respondentNotify === 'true') {\n $('#respondent-notify').prop('checked', true);\n $('#respondent-options').show();\n }\n\n // Fill the respondent email select box with the\n // titles given to the form's text Items. Also include\n // the form Item IDs as values so that they can be\n // easily recovered during the Save operation.\n for (var i = 0; i < settings.textItems.length; i++) {\n var option = $('<option>').attr('value', settings.textItems[i]['id'])\n .text(settings.textItems[i]['title']);\n $('#respondent-email').append(option);\n }\n $('#respondent-email').val(settings.respondentEmailItemId);\n }\n\n /**\n * Toggles the visibility of the form creator notification options.\n */\n function toggleCreatorNotify() {\n $('#status').remove();\n if ($('#creator-notify').is(':checked')) {\n $('#creator-options').show();\n } else {\n $('#creator-options').hide();\n }\n }\n\n /**\n * Toggles the visibility of the form sumbitter notification options.\n */\n function toggleRespondentNotify() {\n $('#status').remove();\n if($('#respondent-notify').is(':checked')) {\n $('#respondent-options').show();\n } else {\n $('#respondent-options').hide();\n }\n }\n\n /**\n * Ensures that the entered step is a number between 1\n * and 99999, inclusive.\n */\n function validateNumber() {\n var value = $('#response-step').val();\n if (!value) {\n $('#response-step').val(10);\n } else if (value < 1) {\n $('#response-step').val(1);\n } else if (value > 99999) {\n $('#response-step').val(99999);\n }\n }\n\n /**\n * Collects the options specified in the add-on sidebar and sends them to\n * be saved as Properties on the server.\n */\n function saveSettingsToServer() {\n this.disabled = true;\n $('#status').remove();\n var creatorNotify = $('#creator-notify').is(':checked');\n var respondentNotify = $('#respondent-notify').is(':checked');\n var settings = {\n 'creatorNotify': creatorNotify,\n 'respondentNotify': respondentNotify\n };\n\n // Only save creator options if notify is turned on\n if (creatorNotify) {\n settings.responseStep = $('#response-step').val();\n settings.creatorEmail = $('#creator-email').val().trim();\n\n // Abort save if entered email is blank\n if (!settings.creatorEmail) {\n showStatus('Enter an owner email', $('#button-bar'));\n this.disabled = false;\n return;\n }\n }\n\n // Only save respondent options if notify is turned on\n if (respondentNotify) {\n settings.respondentEmailItemId = $('#respondent-email').val();\n settings.responseSubject = $('#submit-subject').val();\n settings.responseText = $('#submit-notice').val();\n }\n\n // Save the settings on the server\n google.script.run\n .withSuccessHandler(\n function(msg, element) {\n showStatus('Saved settings', $('#button-bar'));\n element.disabled = false;\n })\n .withFailureHandler(\n function(msg, element) {\n showStatus(msg, $('#button-bar'));\n element.disabled = false;\n })\n .withUserObject(this)\n .saveSettings(settings);\n }\n\n /**\n * Inserts a div that contains an status message after a given element.\n *\n * @param {String} msg The status message to display.\n * @param {Object} element The element after which to display the Status.\n */\n function showStatus(msg, element) {\n var div = $('<div>')\n .attr('id', 'status')\n .attr('class','error')\n .text(msg);\n $(element).after(div);\n }\n </script>\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <link rel=\"stylesheet\" href=\"https://ssl.gstatic.com/docs/script/css/add-ons1.css\">\n <!-- The CSS package above applies Google styling to buttons and other elements. -->\n </head>\n <body>\n <div>\n <p>\n <i>Form Notifications</i> was created as an sample add-on, and is meant\n for demonstration purposes only. It should not be used for complex or\n important workflows.\n </p>\n <p>\n The number of notifications this add-on produces are limited by the owner's\n available email quota; it will not send email notifications if the owner's\n daily email quota has been exceeded. Collaborators using this add-on on the\n same form will be able to adjust the notification settings, but will not be\n able to disable the notification triggers set by other collaborators.\n </p>\n </div>\n </body>\n</html>\n```\n\nExample:\n```text\n<p>The Google Forms add-on <i>Form Notifications</i> is set to run automatically\nwhenever a form is submitted. The add-on was recently updated and it needs you\nto re-authorize it to run on your behalf.</p>\n\n<p>The add-on's automatic functions are temporarily disabled until you\nre-authorize the add-on. You can accomplish this by opening one of the forms\nusing the add-on and running the add-on through the menu. Alternatively, you can\nclick this link to approve authorization directly:</p>\n\n<p><a href=\"<?= url ?>\">Click here</a> to re-authorize the add-on.</p>\n\n<p>This notification email will be sent to you at most once per day until the\nadd-on is re-authorized.</p>\n\n<hr>\n\n<p style=\"font-size:80%\">This automatic message was sent to you via the <i>Form\nNotifications</i> add-on for Google Forms.\n<?= notice ?></p>\n```\n\nExample:\n```text\n<p><i>Form Notifications</i> (a Google Forms add-on) has detected that the form\ntitled <a href=\"<?= formUrl?>\"><b><?= title ?></b></a> has received\n<?= responses ?> responses so far.</p>\n\n<p><a href=\"<?= summary ?>\">Summary of form responses</a></p>\n\n<p>You are receiving this email because an editor of this form configured\n<i>Form Notifications</i> to alert you every time this form receives\n<b><?= responseStep ?></b> responses.</p>\n\n<p>To change this setting, or to stop receiving these notifications, have the\nform owner or editors open the form and adjust the <i>Form Notifications</i>\nadd-on configuration via the \"Configure notifications\" menu item.</p>\n\n<hr>\n\n<p style=\"font-size:80%\">This automatic message was sent to you via the <i>Form\nNotifications</i> add-on for Google Forms.\n<?= notice ?></p>\n```\n\nExample:\n```text\n<? for (var i = 0; i < paragraphs.length; i++) { ?>\n <p><?= paragraphs[i] ?></p>\n<? } ?>\n\n<hr>\n\n<p style=\"font-size:80%\">This automatic message was sent to you via the <i>Form\nNotifications</i> add-on for Google Forms.\n<?= notice ?></p>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.593Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":710,"estimatedTokens":5861}}741{"id":"doc-go_quickstart_google_drive_google_for_developers-afe18141","source":"documentation","title":"Go quickstart | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/quickstart/go","text":"Example:\n```text\nmkdir quickstart\n```\n\nExample:\n```text\ncd quickstart\n```\n\nExample:\n```text\ngo mod init quickstart\n```\n\nExample:\n```text\ngo get google.golang.org/api/drive/v3\ngo get golang.org/x/oauth2/google\n```\n\nExample:\n```text\npackage main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"golang.org/x/oauth2\"\n\t\"golang.org/x/oauth2/google\"\n\t\"google.golang.org/api/drive/v3\"\n\t\"google.golang.org/api/option\"\n)\n\n// Retrieve a token, saves the token, then returns the generated client.\nfunc getClient(config *oauth2.Config) *http.Client {\n\t// The file token.json stores the user's access and refresh tokens, and is\n\t// created automatically when the authorization flow completes for the first\n\t// time.\n\ttokFile := \"token.json\"\n\ttok, err := tokenFromFile(tokFile)\n\tif err != nil {\n\t\ttok = getTokenFromWeb(config)\n\t\tsaveToken(tokFile, tok)\n\t}\n\treturn config.Client(context.Background(), tok)\n}\n\n// Request a token from the web, then returns the retrieved token.\nfunc getTokenFromWeb(config *oauth2.Config) *oauth2.Token {\n\tauthURL := config.AuthCodeURL(\"state-token\", oauth2.AccessTypeOffline)\n\tfmt.Printf(\"Go to the following link in your browser then type the \"+\n\t\t\"authorization code: \\n%v\\n\", authURL)\n\n\tvar authCode string\n\tif _, err := fmt.Scan(&authCode); err != nil {\n\t\tlog.Fatalf(\"Unable to read authorization code %v\", err)\n\t}\n\n\ttok, err := config.Exchange(context.TODO(), authCode)\n\tif err != nil {\n\t\tlog.Fatalf(\"Unable to retrieve token from web %v\", err)\n\t}\n\treturn tok\n}\n\n// Retrieves a token from a local file.\nfunc tokenFromFile(file string) (*oauth2.Token, error) {\n\tf, err := os.Open(file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\ttok := &oauth2.Token{}\n\terr = json.NewDecoder(f).Decode(tok)\n\treturn tok, err\n}\n\n// Saves a token to a file path.\nfunc saveToken(path string, token *oauth2.Token) {\n\tfmt.Printf(\"Saving credential file to: %s\\n\", path)\n\tf, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)\n\tif err != nil {\n\t\tlog.Fatalf(\"Unable to cache oauth token: %v\", err)\n\t}\n\tdefer f.Close()\n\tjson.NewEncoder(f).Encode(token)\n}\n\nfunc main() {\n\tctx := context.Background()\n\tb, err := os.ReadFile(\"credentials.json\")\n\tif err != nil {\n\t\tlog.Fatalf(\"Unable to read client secret file: %v\", err)\n\t}\n\n\t// If modifying these scopes, delete your previously saved token.json.\n\tconfig, err := google.ConfigFromJSON(b, drive.DriveMetadataReadonlyScope)\n\tif err != nil {\n\t\tlog.Fatalf(\"Unable to parse client secret file to config: %v\", err)\n\t}\n\tclient := getClient(config)\n\n\tsrv, err := drive.NewService(ctx, option.WithHTTPClient(client))\n\tif err != nil {\n\t\tlog.Fatalf(\"Unable to retrieve Drive client: %v\", err)\n\t}\n\n\tr, err := srv.Files.List().PageSize(10).\n\t\tFields(\"nextPageToken, files(id, name)\").Do()\n\tif err != nil {\n\t\tlog.Fatalf(\"Unable to retrieve files: %v\", err)\n\t}\n\tfmt.Println(\"Files:\")\n\tif len(r.Files) == 0 {\n\t\tfmt.Println(\"No files found.\")\n\t} else {\n\t\tfor _, i := range r.Files {\n\t\t\tfmt.Printf(\"%s (%s)\\n\", i.Name, i.Id)\n\t\t}\n\t}\n}\n```\n\nExample:\n```text\ngo run quickstart.go\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.594Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":135,"estimatedTokens":767}}742{"id":"doc-python_quickstart_google_drive_google_for_develo-090d65b8","source":"documentation","title":"Python quickstart | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/quickstart/python","text":"Example:\n```text\npython3 -m pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib\n```\n\nExample:\n```text\nimport os.path\n\nfrom google.auth.transport.requests import Request\nfrom google.oauth2.credentials import Credentials\nfrom google_auth_oauthlib.flow import InstalledAppFlow\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n# If modifying these scopes, delete the file token.json.\nSCOPES = [\"https://www.googleapis.com/auth/drive.metadata.readonly\"]\n\n\ndef main():\n \"\"\"Shows basic usage of the Drive v3 API.\n Prints the names and ids of the first 10 files the user has access to.\n \"\"\"\n creds = None\n # The file token.json stores the user's access and refresh tokens, and is\n # created automatically when the authorization flow completes for the first\n # time.\n if os.path.exists(\"token.json\"):\n creds = Credentials.from_authorized_user_file(\"token.json\", SCOPES)\n # If there are no (valid) credentials available, let the user log in.\n if not creds or not creds.valid:\n if creds and creds.expired and creds.refresh_token:\n creds.refresh(Request())\n else:\n flow = InstalledAppFlow.from_client_secrets_file(\n \"credentials.json\", SCOPES\n )\n creds = flow.run_local_server(port=0)\n # Save the credentials for the next run\n with open(\"token.json\", \"w\") as token:\n token.write(creds.to_json())\n\n try:\n service = build(\"drive\", \"v3\", credentials=creds)\n\n # Call the Drive v3 API\n results = (\n service.files()\n .list(pageSize=10, fields=\"nextPageToken, files(id, name)\")\n .execute()\n )\n items = results.get(\"files\", [])\n\n if not items:\n print(\"No files found.\")\n return\n print(\"Files:\")\n for item in items:\n print(f\"{item['name']} ({item['id']})\")\n except HttpError as error:\n # TODO(developer) - Handle errors from drive API.\n print(f\"An error occurred: {error}\")\n\n\nif __name__ == \"__main__\":\n main()\n```\n\nExample:\n```text\npython3 quickstart.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.595Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":74,"estimatedTokens":515}}743{"id":"doc-create_a_shortcut_to_a_drive_file_google_drive_g-9090ab91","source":"documentation","title":"Create a shortcut to a Drive file | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/shortcuts","text":"Example:\n```text\nfile_metadata = {\n 'name': 'FILE_NAME',\n 'mimeType': 'text/plain'\n}\nfile = drive_service.files().create(body=file_metadata, fields='id').execute()\nprint('File ID: %s' % file.get('id'))\nshortcut_metadata = {\n 'Name': 'SHORTCUT_NAME',\n 'mimeType': 'application/vnd.google-apps.shortcut',\n 'shortcutDetails': {\n 'targetId': file.get('id')\n }\n}\nshortcut = drive_service.files().create(body=shortcut_metadata,\n fields='id,shortcutDetails').execute()\nprint('File ID: %s, Shortcut Target ID: %s, Shortcut Target MIME type: %s' % (\n shortcut.get('id'),\n shortcut.get('shortcutDetails').get('targetId'),\n shortcut.get('shortcutDetails').get('targetMimeType')))\n```\n\nExample:\n```text\nvar fileMetadata = {\n 'name': 'FILE_NAME',\n 'mimeType': 'text/plain'\n};\ndrive.files.create({\n 'resource': fileMetadata,\n 'fields': 'id'\n}, function (err, file) {\n if (err) {\n // Handle error\n console.error(err);\n } else {\n console.log('File Id: ' + file.id);\n shortcutMetadata = {\n 'name': 'SHORTCUT_NAME',\n 'mimeType': 'application/vnd.google-apps.shortcut'\n 'shortcutDetails': {\n 'targetId': file.id\n }\n };\n drive.files.create({\n 'resource': shortcutMetadata,\n 'fields': 'id,name,mimeType,shortcutDetails'\n }, function(err, shortcut) {\n if (err) {\n // Handle error\n console.error(err);\n } else {\n console.log('Shortcut Id: ' + shortcut.id +\n ', Name: ' + shortcut.name +\n ', target Id: ' + shortcut.shortcutDetails.targetId +\n ', target MIME type: ' + shortcut.shortcutDetails.targetMimeType);\n }\n }\n }\n});\n```\n\nExample:\n```text\nq: mimeType='application/vnd.google-apps.shortcut' AND shortcutDetails.targetMimeType='application/vnd.google-apps.spreadsheet'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.597Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":69,"estimatedTokens":476}}744{"id":"doc-java_quickstart_google_drive_google_for_develope-28de488f","source":"documentation","title":"Java quickstart | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/quickstart/java","text":"Example:\n```text\ngradle init --type basic\nmkdir -p src/main/java src/main/resources\n```\n\nExample:\n```text\napply plugin: 'java'\napply plugin: 'application'\n\nmainClassName = 'DriveQuickstart'\nsourceCompatibility = 11\ntargetCompatibility = 11\nversion = '1.0'\n\nrepositories {\n mavenCentral()\n}\n\ndependencies {\n implementation 'com.google.api-client:google-api-client:2.0.0'\n implementation 'com.google.oauth-client:google-oauth-client-jetty:1.34.1'\n implementation 'com.google.apis:google-api-services-drive:v3-rev20220815-2.0.0'\n}\n```\n\nExample:\n```text\nimport com.google.api.client.auth.oauth2.Credential;\nimport com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;\nimport com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;\nimport com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.JsonFactory;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.client.util.store.FileDataStoreFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.api.services.drive.model.FileList;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.security.GeneralSecurityException;\nimport java.util.Collections;\nimport java.util.List;\n\n/* class to demonstrate use of Drive files list API */\npublic class DriveQuickstart {\n /**\n * Application name.\n */\n private static final String APPLICATION_NAME = \"Google Drive API Java Quickstart\";\n /**\n * Global instance of the JSON factory.\n */\n private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();\n /**\n * Directory to store authorization tokens for this application.\n */\n private static final String TOKENS_DIRECTORY_PATH = \"tokens\";\n\n /**\n * Global instance of the scopes required by this quickstart.\n * If modifying these scopes, delete your previously saved tokens/ folder.\n */\n private static final List<String> SCOPES =\n Collections.singletonList(DriveScopes.DRIVE_METADATA_READONLY);\n private static final String CREDENTIALS_FILE_PATH = \"/credentials.json\";\n\n /**\n * Creates an authorized Credential object.\n *\n * @param HTTP_TRANSPORT The network HTTP Transport.\n * @return An authorized Credential object.\n * @throws IOException If the credentials.json file cannot be found.\n */\n private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT)\n throws IOException {\n // Load client secrets.\n InputStream in = DriveQuickstart.class.getResourceAsStream(CREDENTIALS_FILE_PATH);\n if (in == null) {\n throw new FileNotFoundException(\"Resource not found: \" + CREDENTIALS_FILE_PATH);\n }\n GoogleClientSecrets clientSecrets =\n GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));\n\n // Build flow and trigger user authorization request.\n GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(\n HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)\n .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))\n .setAccessType(\"offline\")\n .build();\n LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();\n Credential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize(\"user\");\n //returns an authorized Credential object.\n return credential;\n }\n\n public static void main(String... args) throws IOException, GeneralSecurityException {\n // Build a new authorized API client service.\n final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();\n Drive service = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))\n .setApplicationName(APPLICATION_NAME)\n .build();\n\n // Print the names and IDs for up to 10 files.\n FileList result = service.files().list()\n .setPageSize(10)\n .setFields(\"nextPageToken, files(id, name)\")\n .execute();\n List<File> files = result.getFiles();\n if (files == null || files.isEmpty()) {\n System.out.println(\"No files found.\");\n } else {\n System.out.println(\"Files:\");\n for (File file : files) {\n System.out.printf(\"%s (%s)\\n\", file.getName(), file.getId());\n }\n }\n }\n}\n```\n\nExample:\n```text\ngradle run\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.598Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":134,"estimatedTokens":1182}}745{"id":"doc-manage_long_running_operations_google_drive_goog-bbff4de3","source":"documentation","title":"Manage long-running operations | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/long-running-operations","text":"Example:\n```text\nFILE_ID\n```\n\nExample:\n```text\n{\n \"done\": true,\n \"metadata\": {\n \"@type\": \"type.googleapis.com/google.apps.drive.v3.DownloadFileMetadata\",\n \"resourceKey\": \"RESOURCE_KEY\"\n },\n \"name\": \"NAME\",\n \"response\": {\n \"@type\": \"type.googleapis.com/google.apps.drive.v3.DownloadFileResponse\",\n \"downloadUri\": \"DOWNLOAD_URI\",\n \"partialDownloadAllowed\": false\n }\n}\n```\n\nExample:\n```text\noperations.get(name='NAME');\n```\n\nExample:\n```text\ncurl -i -H \\\n 'Authorization: Bearer $(gcloud auth print-access-token)\" \\\n 'https://googleapis.com/drive/v3/operations/NAME?alt=json'\n```\n\nExample:\n```text\nFILE_IDREVISION_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.600Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":40,"estimatedTokens":166}}746{"id":"doc-add_custom_file_properties_google_drive_google_f-079cfc90","source":"documentation","title":"Add custom file properties | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/properties","text":"Example:\n```text\n\"appProperties\": {\n \"additionalID\": \"ID\",\n}\n```\n\nExample:\n```text\n{\n 'key': 'additionalID',\n 'value': 'ID',\n 'visibility': 'PRIVATE'\n}\n```\n\nExample:\n```text\nFILE_ID\n```\n\nExample:\n```text\n{\n \"properties\": {\n \"name\": \"wrench\",\n \"mass\": \"1.3kg\",\n \"count\": \"3\"\n }\n}\n```\n\nExample:\n```text\n{\n \"name\": null\n}\n```\n\nExample:\n```text\n{\n \"properties\": {\n \"mass\": \"1.3kg\",\n \"count\": \"3\"\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.600Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":50,"estimatedTokens":113}}747{"id":"doc-manage_comments_and_replies_google_drive_google_-3d940cf7","source":"documentation","title":"Manage comments and replies | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/manage-comments","text":"Example:\n```text\nfrom google.oauth2.credentials import Credentials\nfrom googleapiclient.errors import HttpError\n\n# --- Configuration ---\n# The ID of the file to comment on.\n# Example: '1_aBcDeFgHiJkLmNoPqRsTuVwXyZ'\nFILE_ID = 'FILE_ID'\n\n# The text content of the comment.\nCOMMENT_TEXT = 'This is an example of an anchored comment.'\n\n# The line number to anchor the comment to.\n# Note: Line numbers are based on the revision.\nANCHOR_LINE = 10\n# --- End of user-configuration section ---\n\nSCOPES = [\"https://www.googleapis.com/auth/drive\"]\n\ncreds = Credentials.from_authorized_user_file(\"token.json\", SCOPES)\n\ndef create_anchored_comment():\n \"\"\"\n Create an anchored comment on a specific line in a Google Doc.\n\n Returns:\n The created comment object or None if an error occurred.\n \"\"\"\n try:\n # Build the Drive API service\n service = build(\"drive\", \"v3\", credentials=creds)\n\n # Define the anchor region for the comment.\n # For Google Docs, the region is typically defined by 'line' and 'revision'.\n # Other file types might use different region classifiers.\n anchor = {\n 'region': {\n 'kind': 'drive#commentRegion',\n 'line': ANCHOR_LINE,\n 'rev': 'head'\n }\n }\n\n # The comment body.\n comment_body = {\n 'content': COMMENT_TEXT,\n 'anchor': anchor\n }\n\n # Create the comment request.\n comment = (\n service.comments()\n .create(fileId=FILE_ID, fields=\"*\", body=comment_body)\n .execute()\n )\n\n print(f\"Comment ID: {comment.get('id')}\")\n return comment\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return None\n\ncreate_anchored_comment()\n```\n\nExample:\n```text\nfrom google.oauth2.credentials import Credentials\nfrom googleapiclient.errors import HttpError\n\n# --- Configuration ---\n# The ID of the file to comment on.\n# Example: '1_aBcDeFgHiJkLmNoPqRsTuVwXyZ'\nFILE_ID = 'FILE_ID'\n\n# The text content of the comment.\nCOMMENT_TEXT = 'This is an example of an unanchored comment.'\n# --- End of user-configuration section ---\n\nSCOPES = [\"https://www.googleapis.com/auth/drive\"]\n\ncreds = Credentials.from_authorized_user_file(\"token.json\", SCOPES)\n\ndef create_unanchored_comment():\n \"\"\"\n Create an unanchored comment on a specific line in a Google Doc.\n\n Returns:\n The created comment object or None if an error occurred.\n \"\"\"\n try:\n # Build the Drive API service\n service = build(\"drive\", \"v3\", credentials=creds)\n\n # The comment body. For an unanchored comment,\n # omit the 'anchor' property.\n comment_body = {\n 'content': COMMENT_TEXT\n }\n\n # Create the comment request.\n comment = (\n service.comments()\n .create(fileId=FILE_ID, fields=\"*\", body=comment_body)\n .execute()\n )\n\n print(f\"Comment ID: {comment.get('id')}\")\n return comment\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return None\n\ncreate_unanchored_comment()\n```\n\nExample:\n```text\nPOST https://www.googleapis.com/drive/v3/files/FILE_ID/comments/COMMENT_ID/replies?fields=id,comment\n```\n\nExample:\n```text\n{\n \"content\": \"This is a reply to a comment.\"\n}\n```\n\nExample:\n```text\n{\n \"action\": \"resolve\",\n \"content\": \"This comment has been resolved.\"\n}\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v3/files/FILE_ID/comments/COMMENT_ID?fields=id,comment,modifiedTime,resolved\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v3/files/FILE_ID/comments?includeDeleted=true&fields=(id,comment,kind,modifiedTime,resolved)\n```\n\nExample:\n```text\nPATCH https://www.googleapis.com/drive/v3/files/FILE_ID/comments/COMMENT_ID?fields=id,comment\n```\n\nExample:\n```text\n{\n \"content\": \"This comment is now updated.\"\n}\n```\n\nExample:\n```text\nDELETE https://www.googleapis.com/drive/v3/files/FILE_ID/comments/COMMENT_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.601Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":167,"estimatedTokens":1013}}748{"id":"doc-access_link_shared_drive_files_using_resource_ke-d3eeb8f8","source":"documentation","title":"Access link-shared Drive files using resource keys | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/resource-keys","text":"Example:\n```text\nX-Goog-Drive-Resource-Keys: fileId1/resourceKey1,fileId2/resourceKey2,fileId3/resourceKey3\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.603Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":32}}749{"id":"doc-manage_file_metadata_google_drive_google_for_dev-1f2f04de","source":"documentation","title":"Manage file metadata | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/file-metadata","text":"Example:\n```text\nGET https://www.googleapis.com/drive/v3/files/FILE_ID?fields=id,name,mimeType,thumbnailLink\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v3/files/?fields=files(id,name,mimeType,thumbnailLink)\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v3/files/q=mimeType='application/vnd.google-apps.spreadsheet'&fields=files(id,name,mimeType,thumbnailLink)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.605Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":16,"estimatedTokens":102}}750{"id":"doc-store_application_specific_data_google_drive_goo-ac4b60b9","source":"documentation","title":"Store application-specific data | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/appdata","text":"Example:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.FileContent;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\n\n/**\n * Class to demonstrate use-case of create file in the application data folder.\n */\npublic class UploadAppData {\n\n /**\n * Creates a file in the application data folder.\n *\n * @return Created file's Id.\n */\n public static String uploadAppData() throws IOException {\n /*Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application.*/\n GoogleCredentials credentials = null;\n try {\n credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_APPDATA));\n } catch (IOException e) {\n e.printStackTrace();\n }\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n try {\n // File's metadata.\n File fileMetadata = new File();\n fileMetadata.setName(\"config.json\");\n fileMetadata.setParents(Collections.singletonList(\"appDataFolder\"));\n java.io.File filePath = new java.io.File(\"files/config.json\");\n FileContent mediaContent = new FileContent(\"application/json\", filePath);\n File file = service.files().create(fileMetadata, mediaContent)\n .setFields(\"id\")\n .execute();\n System.out.println(\"File ID: \" + file.getId());\n return file.getId();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to create file: \" + e.getDetails());\n throw e;\n }\n }\n\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\nfrom googleapiclient.http import MediaFileUpload\n\n\ndef upload_appdata():\n \"\"\"Insert a file in the application data folder and prints file Id.\n Returns : ID's of the inserted files\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # call drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n # pylint: disable=maybe-no-member\n file_metadata = {\"name\": \"abc.txt\", \"parents\": [\"appDataFolder\"]}\n media = MediaFileUpload(\"abc.txt\", mimetype=\"text/txt\", resumable=True)\n file = (\n service.files()\n .create(body=file_metadata, media_body=media, fields=\"id\")\n .execute()\n )\n print(f'File ID: {file.get(\"id\")}')\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n file = None\n\n return file.get(\"id\")\n\n\nif __name__ == \"__main__\":\n upload_appdata()\n```\n\nExample:\n```text\nimport fs from 'node:fs';\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Uploads a file to the application data folder.\n * @return {Promise<string>} The ID of the uploaded file.\n */\nasync function uploadAppdata() {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive.appdata',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // The metadata for the file to be uploaded.\n const fileMetadata = {\n name: 'config.json',\n parents: ['appDataFolder'],\n };\n\n // The media content to be uploaded.\n const media = {\n mimeType: 'application/json',\n body: fs.createReadStream('files/config.json'),\n };\n\n // Upload the file to the application data folder.\n const file = await service.files.create({\n requestBody: fileMetadata,\n media,\n fields: 'id',\n });\n\n // Print the ID of the uploaded file.\n console.log('File Id:', file.data.id);\n if (!file.data.id) {\n throw new Error('File ID not found.');\n }\n return file.data.id;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nfunction uploadAppData()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $client->addScope(Drive::DRIVE_APPDATA);\n $driveService = new Drive($client);\n $fileMetadata = new Drive\\DriveFile(array(\n 'name' => 'config.json',\n 'parents' => array('appDataFolder')\n ));\n $content = file_get_contents('../files/config.json');\n $file = $driveService->files->create($fileMetadata, array(\n 'data' => $content,\n 'mimeType' => 'application/json',\n 'uploadType' => 'multipart',\n 'fields' => 'id'));\n printf(\"File ID: %s\\n\", $file->id);\n return $file->id;\n\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n } \n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class of demonstrate the use of Drive upload app data. \n public class UploadAppData\n {\n /// <summary>\n /// Insert a file in the application data folder and prints file Id.\n /// </summary>\n /// <param name=\"filePath\">File path to upload.</param>\n /// <returns>ID's of the inserted files, null otherwise.</returns>\n public static string DriveUploadAppData(string filePath)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.DriveAppdata);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n var fileMetadata = new Google.Apis.Drive.v3.Data.File()\n {\n Name = \"config.json\",\n Parents = new List<string>()\n {\n \"appDataFolder\"\n }\n };\n FilesResource.CreateMediaUpload request;\n using (var stream = new FileStream(filePath,\n FileMode.Open))\n {\n request = service.Files.Create(\n fileMetadata, stream, \"application/json\");\n request.Fields = \"id\";\n request.Upload();\n }\n\n var file = request.ResponseBody;\n // Prints the file id.\n Console.WriteLine(\"File ID: \" + file.Id);\n return file.Id;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\ncurl --request POST \\\n'https://content.googleapis.com/drive/v3/files' \\\n -H 'authorization: Bearer ACCESS_TOKEN' \\\n -H 'content-type: application/json' \\\n -H 'x-origin: https://explorer.apis.google.com' \\\n --data-raw '{\"name\": \"config.json\", \"parents\":[\"appDataFolder\"]}'\n```\n\nExample:\n```text\n{\n \"kind\": \"drive#file\",\n \"id\": FILE_ID,\n \"name\": \"config.json\",\n \"mimeType\": \"application/json\"\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.api.services.drive.model.FileList;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\n\n/**\n * Class to demonstrate use-case of list 10 files in the application data folder.\n */\npublic class ListAppData {\n\n /**\n * list down files in the application data folder.\n *\n * @return list of 10 files.\n */\n public static FileList listAppData() throws IOException {\n /*Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application.*/\n GoogleCredentials credentials = null;\n try {\n credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_APPDATA));\n } catch (IOException e) {\n e.printStackTrace();\n }\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n try {\n FileList files = service.files().list()\n .setSpaces(\"appDataFolder\")\n .setFields(\"nextPageToken, files(id, name)\")\n .setPageSize(10)\n .execute();\n for (File file : files.getFiles()) {\n System.out.printf(\"Found file: %s (%s)\\n\",\n file.getName(), file.getId());\n }\n\n return files;\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to list files: \" + e.getDetails());\n throw e;\n }\n }\n\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef list_appdata():\n \"\"\"List all files inserted in the application data folder\n prints file titles with Ids.\n Returns : List of items\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # call drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n # pylint: disable=maybe-no-member\n response = (\n service.files()\n .list(\n spaces=\"appDataFolder\",\n fields=\"nextPageToken, files(id, name)\",\n pageSize=10,\n )\n .execute()\n )\n for file in response.get(\"files\", []):\n # Process change\n print(f'Found file: {file.get(\"name\")}, {file.get(\"id\")}')\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n response = None\n\n return response.get(\"files\")\n\n\nif __name__ == \"__main__\":\n list_appdata()\n```\n\nExample:\n```text\n/**\n * Copyright 2022 Google LLC\n *\n * 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.\n */\n\n\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Lists all files in the application data folder.\n * @return {Promise<object[]>} A list of files.\n */\nasync function listAppdata() {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive.appdata',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // List the files in the application data folder.\n const result = await service.files.list({\n spaces: 'appDataFolder',\n fields: 'nextPageToken, files(id, name)',\n pageSize: 100,\n });\n\n // Print the name and ID of each file.\n (result.data.files ?? []).forEach((file) => {\n console.log('Found file:', file.name, file.id);\n });\n\n return result.data.files ?? [];\n}\n\nexport {listAppdata};\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nfunction listAppData()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $response = $driveService->files->listFiles(array(\n 'spaces' => 'appDataFolder',\n 'fields' => 'nextPageToken, files(id, name)',\n 'pageSize' => 10\n ));\n foreach ($response->files as $file) {\n printf(\"Found file: %s (%s)\", $file->name, $file->id);\n }\n return $response->files;\n\n }catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Drive.v3.Data;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use-case of Drive's list files in the application data folder.\n public class ListAppData\n {\n /// <summary>\n /// List down files in the application data folder.\n /// </summary>\n /// <returns>list of 10 files, null otherwise.</returns>\n public static FileList DriveListAppData()\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.DriveAppdata);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n var request = service.Files.List();\n request.Spaces = \"appDataFolder\";\n request.Fields = \"nextPageToken, files(id, name)\";\n request.PageSize = 10;\n var result = request.Execute();\n foreach (var file in result.Files)\n {\n // Prints the list of 10 file names.\n Console.WriteLine(\"Found file: {0} ({1})\", file.Name, file.Id);\n }\n return result;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\ncurl \\\n -X GET \\\n -H \"Authorization: Bearer ACCESS_TOKEN\" \\\n \"https://www.googleapis.com/drive/v3/files?spaces=appDataFolder&fields=files(id,name,mimeType,size,modifiedTime)\"\n```\n\nExample:\n```text\n{\n \"files\": [\n {\n \"mimeType\": \"application/json\",\n \"size\": \"256\",\n \"id\": FILE_ID,\n \"name\": \"config.json\",\n \"modifiedTime\": \"2025-04-03T23:40:05.860Z\"\n },\n {\n \"mimeType\": \"text/plain\",\n \"size\": \"128\",\n \"id\": FILE_ID,\n \"name\": \"user_settings.txt\",\n \"modifiedTime\": \"2025-04-02T17:52:29.020Z\"\n }\n ]\n}\n```\n\nExample:\n```text\ncurl \\\n -X GET \\\n -H \"Authorization: Bearer ACCESS_TOKEN\" \\\n \"https://www.googleapis.com/drive/v3/files/FILE_ID?alt=media\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.606Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":583,"estimatedTokens":4317}}751{"id":"doc-return_user_info_google_drive_google_for_develop-514ecf54","source":"documentation","title":"Return user info | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/user-info","text":"Example:\n```text\nGET https://www.googleapis.com/drive/v3/about/?fields=kind,user,storageQuota\n```\n\nExample:\n```text\n{\n \"kind\": \"drive#about\",\n \"user\": {\n \"kind\": \"drive#user\",\n \"displayName\": \"DISPLAY_NAME\",\n \"photoLink\": \"PHOTO_LINK\",\n \"me\": true,\n \"permissionId\": \"PERMISSION_ID\",\n \"emailAddress\": \"EMAIL_ADDRESS\"\n },\n \"storageQuota\": {\n \"usage\": \"10845031958\",\n \"usageInDrive\": \"2222008387\",\n \"usageInDriveTrash\": \"91566\"\n }\n}\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v3/apps\n```\n\nExample:\n```text\n{\n \"kind\": \"drive#appList\",\n \"selfLink\": \"https://www.googleapis.com/drive/v3/apps\",\n \"items\": [\n {\n \"kind\": \"drive#app\",\n \"id\": \"ID\",\n \"name\": \"Google Sheets\",\n \"supportsCreate\": true,\n \"supportsImport\": true,\n \"supportsMultiOpen\": false,\n \"supportsOfflineCreate\": true,\n \"productUrl\": \"https://chrome.google.com/webstore/detail/felcaaldnbdncclmgdcncolpebgiejap\",\n \"productId\": \"PRODUCT_ID\"\n }\n ],\n \"defaultAppIds\": [\n \"ID\"\n ]\n}\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v3/apps/APP_ID\n```\n\nExample:\n```text\n{\n \"kind\": \"drive#app\",\n \"id\": \"ID\",\n \"name\": \"Google Sheets\",\n \"supportsCreate\": true,\n \"supportsImport\": true,\n \"supportsMultiOpen\": false,\n \"supportsOfflineCreate\": true,\n \"productUrl\": \"https://chrome.google.com/webstore/detail/felcaaldnbdncclmgdcncolpebgiejap\",\n \"productId\": \"PRODUCT_ID\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.607Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":75,"estimatedTokens":365}}752{"id":"doc-protect_file_content_google_drive_google_for_dev-1a1f5ffe","source":"documentation","title":"Protect file content | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/content-restrictions","text":"Example:\n```text\nFile updatedFile =\n new File()\n .setContentRestrictions(\n ImmutableList.of(new ContentRestriction().setReadOnly(true).setReason(\"Finalized contract.\"));\n\nFile response = driveService.files().update(\"FILE_ID\", updatedFile).setFields(\"contentRestrictions\").execute();\n```\n\nExample:\n```text\ncontent_restriction = {'readOnly': True, 'reason':'Finalized contract.'}\n\nresponse = drive_service.files().update(fileId=\"FILE_ID\", body = {'contentRestrictions' : [content_restriction]}, fields = \"contentRestrictions\").execute();\n```\n\nExample:\n```text\n/**\n* Set a content restriction on a file.\n* @return{obj} updated file\n**/\nasync function addContentRestriction() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});\n const service = google.drive({version: 'v3', auth});\n const contentRestriction = {\n 'readOnly': True,\n 'reason': 'Finalized contract.',\n };\n const updatedFile = {\n 'contentRestrictions': [contentRestriction],\n };\n try {\n const response = await service.files.update({\n fileId: 'FILE_ID',\n resource: updatedFile,\n fields: 'contentRestrictions',\n });\n return response;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\nExample:\n```text\nFile updatedFile =\nnew File()\n .setContentRestrictions(\n ImmutableList.of(new ContentRestriction().setReadOnly(false));\n\nFile response = driveService.files().update(\"FILE_ID\", updatedFile).setFields(\"contentRestrictions\").execute();\n```\n\nExample:\n```text\ncontent_restriction = {'readOnly': False}\n\nresponse = drive_service.files().update(fileId=\"FILE_ID\", body = {'contentRestrictions' : [content_restriction]}, fields = \"contentRestrictions\").execute();\n```\n\nExample:\n```text\n/**\n* Remove a content restriction on a file.\n* @return{obj} updated file\n**/\nasync function removeContentRestriction() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});\n const service = google.drive({version: 'v3', auth});\n const contentRestriction = {\n 'readOnly': False,\n };\n const updatedFile = {\n 'contentRestrictions': [contentRestriction],\n };\n try {\n const response = await service.files.update({\n fileId: 'FILE_ID',\n resource: updatedFile,\n fields: 'contentRestrictions',\n });\n return response;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\nExample:\n```text\nFile response = driveService.files().get(\"FILE_ID\").setFields(\"contentRestrictions\").execute();\n```\n\nExample:\n```text\nresponse = drive_service.files().get(fileId=\"FILE_ID\", fields = \"contentRestrictions\").execute();\n```\n\nExample:\n```text\n/**\n* Get content restrictions on a file.\n* @return{obj} updated file\n**/\nasync function fetchContentRestrictions() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});\n const service = google.drive({version: 'v3', auth});\n try {\n const response = await service.files.get({\n fileId: 'FILE_ID',\n fields: 'contentRestrictions',\n });\n return response;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\nExample:\n```text\nFile updatedFile =\n new File()\n .setContentRestrictions(\n ImmutableList.of(new ContentRestriction().setReadOnly(true).setOwnerRestricted(true).setReason(\"Finalized contract.\"));\n\nFile response = driveService.files().update(\"FILE_ID\", updatedFile).setFields(\"contentRestrictions\").execute();\n```\n\nExample:\n```text\ncontent_restriction = {'readOnly': True, 'ownerRestricted': True, 'reason':'Finalized contract.'}\n\nresponse = drive_service.files().update(fileId=\"FILE_ID\", body = {'contentRestrictions' : [content_restriction]}, fields = \"contentRestrictions\").execute();\n```\n\nExample:\n```text\n/**\n* Set an owner restricted content restriction on a file.\n* @return{obj} updated file\n**/\nasync function addOwnerRestrictedContentRestriction() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});\n const service = google.drive({version: 'v3', auth});\n const contentRestriction = {\n 'readOnly': True,\n 'ownerRestricted': True,\n 'reason': 'Finalized contract.',\n };\n const updatedFile = {\n 'contentRestrictions': [contentRestriction],\n };\n try {\n const response = await service.files.update({\n fileId: 'FILE_ID',\n resource: updatedFile,\n fields: 'contentRestrictions',\n });\n return response;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.608Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":198,"estimatedTokens":1342}}753{"id":"doc-manage_approvals_google_drive_google_for_develop-e383d259","source":"documentation","title":"Manage approvals | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/approvals","text":"Example:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files/FILE_ID/approvals:start' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"reviewerEmails\": [\n \"reviewer1@example.com\",\n \"reviewer2@example.com\"\n ],\n \"dueTime\": \"2026-04-01T15:01:23Z\",\n \"lockFile\": true,\n \"message\": \"Please review this file for approval.\",\n \"fileContentChangeBehavior\": \"RESET_APPROVAL\"\n }'\n```\n\nExample:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files/FILE_ID/approvals/APPROVAL_ID:comment' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"message\": \"The required comment on the approval.\"\n }'\n```\n\nExample:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files/FILE_ID/approvals/APPROVAL_ID:reassign' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"addReviewers\": [\n {\n \"addedReviewerEmail\": \"new_reviewer@example.com\"\n }\n ],\n \"replaceReviewers\": [\n {\n \"addedReviewerEmail\": \"replacement_reviewer@example.com\",\n \"removedReviewerEmail\": \"old_reviewer@example.com\"\n }\n ],\n \"message\": \"Reassigning reviewers for this approval request.\"\n }'\n```\n\nExample:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files/FILE_ID/approvals/APPROVAL_ID:cancel' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"message\": \"The optional reason for cancelling this approval request.\"\n }'\n```\n\nExample:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files/FILE_ID/approvals/APPROVAL_ID:decline' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"message\": \"The optional reason for declining this approval request.\"\n }'\n```\n\nExample:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files/FILE_ID/approvals/APPROVAL_ID:approve' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"message\": \"The optional reason for approving this approval request.\"\n }'\n```\n\nExample:\n```text\ncurl -X GET 'https://www.googleapis.com/drive/v3/files/FILE_ID/approvals/APPROVAL_ID' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Accept: application/json'\n```\n\nExample:\n```text\ncurl -X GET 'https://www.googleapis.com/drive/v3/files/FILE_ID/approvals?pageSize=10' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Accept: application/json'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.609Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":93,"estimatedTokens":627}}754{"id":"doc-create_a_shortcut_file_to_content_stored_by_your-4220257e","source":"documentation","title":"Create a shortcut file to content stored by your app | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/third-party-shortcuts","text":"Example:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\n\n/* Class to demonstrate Drive's create shortcut use-case */\npublic class CreateShortcut {\n\n /**\n * Creates shortcut for file.\n *\n * @throws IOException if service account credentials file not found.\n */\n public static String createShortcut() throws IOException {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application.*/\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n try {\n // Create Shortcut for file.\n File fileMetadata = new File();\n fileMetadata.setName(\"Project plan\");\n fileMetadata.setMimeType(\"application/vnd.google-apps.drive-sdk\");\n\n File file = service.files().create(fileMetadata)\n .setFields(\"id\")\n .execute();\n System.out.println(\"File ID: \" + file.getId());\n return file.getId();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to create shortcut: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef create_shortcut():\n \"\"\"Create a third party shortcut\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n file_metadata = {\n \"name\": \"Project plan\",\n \"mimeType\": \"application/vnd.google-apps.drive-sdk\",\n }\n\n # pylint: disable=maybe-no-member\n file = service.files().create(body=file_metadata, fields=\"id\").execute()\n print(f'File ID: {file.get(\"id\")}')\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return file.get(\"id\")\n\n\nif __name__ == \"__main__\":\n create_shortcut()\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nuse Google\\Service\\Drive\\DriveFile;\nfunction createShortcut()\n{\n try {\n\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $fileMetadata = new DriveFile(array(\n 'name' => 'Project plan',\n 'mimeType' => 'application/vnd.google-apps.drive-sdk'));\n $file = $driveService->files->create($fileMetadata, array(\n 'fields' => 'id'));\n printf(\"File ID: %s\\n\", $file->id);\n return $file->id;\n\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate Drive's create shortcut use-case\n public class CreateShortcut\n {\n /// <summary>\n /// Create a third party shortcut.\n /// </summary>\n /// <returns>newly created shortcut file id, null otherwise.</returns>\n public static string DriveCreateShortcut()\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential\n .GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n // Create Shortcut for file.\n var fileMetadata = new Google.Apis.Drive.v3.Data.File()\n {\n Name = \"Project plan\",\n MimeType = \"application/vnd.google-apps.drive-sdk\"\n };\n var request = service.Files.Create(fileMetadata);\n request.Fields = \"id\";\n var file = request.Execute();\n // Prints the shortcut file id.\n Console.WriteLine(\"File ID: \" + file.Id);\n return file.Id;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Creates a shortcut to a third-party resource.\n * @return {Promise<string|null|undefined>} The shortcut ID.\n */\nasync function createShortcut() {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // The metadata for the new shortcut.\n const fileMetadata = {\n name: 'Project plan',\n mimeType: 'application/vnd.google-apps.drive-sdk',\n };\n\n // Create the new shortcut.\n const file = await service.files.create({\n requestBody: fileMetadata,\n fields: 'id',\n });\n\n // Print the ID of the new shortcut.\n console.log('File Id:', file.data.id);\n return file.data.id;\n}\n```\n\nExample:\n```text\nPOST https://www.googleapis.com/drive/v3/files\nAuthorization: AUTHORIZATION_HEADER\n\n{\n \"title\": \"FILE_TITLE\",\n \"mimeType\": \"application/vnd.google-apps.drive-sdk\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.610Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":235,"estimatedTokens":1765}}755{"id":"doc-trash_or_delete_files_and_folders_google_drive_g-49ad3e01","source":"documentation","title":"Trash or delete files and folders | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/delete","text":"Example:\n```text\nbody_value = {'trashed': True}\n\nresponse = drive_service.files().update(fileId=\"FILE_ID\", body=body_value).execute()\n```\n\nExample:\n```text\nconst body_value = {\n 'trashed': true\n};\n\nconst response = await drive_service.files.update({\n fileId: 'FILE_ID',\n resource: body_value,\n });\n return response;\n```\n\nExample:\n```text\nbody_value = {'trashed': False}\n\nresponse = drive_service.files().update(fileId=\"FILE_ID\", body=body_value).execute()\n```\n\nExample:\n```text\nconst body_value = {\n 'trashed': false\n};\n\nconst response = await drive_service.files.update({\n fileId: 'FILE_ID',\n resource: body_value,\n });\n return response;\n```\n\nExample:\n```text\nresponse = drive_service.files().emptyTrash().execute()\n```\n\nExample:\n```text\nconst response = await drive_service.files.emptyTrash({\n });\n return response;\n```\n\nExample:\n```text\nresponse = drive_service.files().delete(fileId=\"FILE_ID\").execute()\n```\n\nExample:\n```text\nconst response = await drive_service.files.delete({\n fileId: 'FILE_ID'\n });\n return response;\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.611Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":66,"estimatedTokens":273}}756{"id":"doc-share_files_folders_and_drives_google_drive_goog-0a61a822","source":"documentation","title":"Share files, folders, and drives | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/manage-sharing","text":"Example:\n```text\nFILE_ID\n```\n\nExample:\n```text\n{\n \"capabilities\": {\n \"canAcceptOwnership\": false,\n \"canAddChildren\": false,\n \"canAddMyDriveParent\": false,\n \"canChangeCopyRequiresWriterPermission\": true,\n \"canChangeItemDownloadRestriction\": true,\n \"canChangeSecurityUpdateEnabled\": false,\n \"canChangeViewersCanCopyContent\": true,\n \"canComment\": true,\n \"canCopy\": true,\n \"canDelete\": true,\n \"canDisableInheritedPermissions\": false,\n \"canDownload\": true,\n \"canEdit\": true,\n \"canEnableInheritedPermissions\": true,\n \"canListChildren\": false,\n \"canModifyContent\": true,\n \"canModifyContentRestriction\": true,\n \"canModifyEditorContentRestriction\": true,\n \"canModifyOwnerContentRestriction\": true,\n \"canModifyLabels\": true,\n \"canMoveChildrenWithinDrive\": false,\n \"canMoveItemIntoTeamDrive\": true,\n \"canMoveItemOutOfDrive\": true,\n \"canMoveItemWithinDrive\": true,\n \"canReadLabels\": true,\n \"canReadRevisions\": true,\n \"canRemoveChildren\": false,\n \"canRemoveContentRestriction\": false,\n \"canRemoveMyDriveParent\": true,\n \"canRename\": true,\n \"canShare\": true,\n \"canTrash\": true,\n \"canUntrash\": true\n }\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"type\": \"user\",\n \"role\": \"commenter\",\n \"emailAddress\": \"alex@altostrat.com\"\n }\n ]\n}\n```\n\nExample:\n```text\nPERMISSION_ID\n```\n\nExample:\n```text\nFILE_IDPERMISSION_ID\n```\n\nExample:\n```text\n{\n \"role\": \"writer\"\n}\n```\n\nExample:\n```text\nINHERITED_FROM_ID\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.batch.BatchRequest;\nimport com.google.api.client.googleapis.batch.json.JsonBatchCallback;\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpHeaders;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.Permission;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/* Class to demonstrate use-case of modify permissions. */\npublic class ShareFile {\n\n /**\n * Batch permission modification.\n * realFileId file Id.\n * realUser User Id.\n * realDomain Domain of the user ID.\n *\n * @return list of modified permissions if successful, {@code null} otherwise.\n * @throws IOException if service account credentials file not found.\n */\n public static List<String> shareFile(String realFileId, String realUser, String realDomain)\n throws IOException {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application.application*/\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n\n final List<String> ids = new ArrayList<String>();\n\n\n JsonBatchCallback<Permission> callback = new JsonBatchCallback<Permission>() {\n @Override\n public void onFailure(GoogleJsonError e,\n HttpHeaders responseHeaders)\n throws IOException {\n // Handle error\n System.err.println(e.getMessage());\n }\n\n @Override\n public void onSuccess(Permission permission,\n HttpHeaders responseHeaders)\n throws IOException {\n System.out.println(\"Permission ID: \" + permission.getId());\n\n ids.add(permission.getId());\n\n }\n };\n BatchRequest batch = service.batch();\n Permission userPermission = new Permission()\n .setType(\"user\")\n .setRole(\"writer\");\n\n userPermission.setEmailAddress(realUser);\n try {\n service.permissions().create(realFileId, userPermission)\n .setFields(\"id\")\n .queue(batch, callback);\n\n Permission domainPermission = new Permission()\n .setType(\"domain\")\n .setRole(\"reader\");\n\n domainPermission.setDomain(realDomain);\n\n service.permissions().create(realFileId, domainPermission)\n .setFields(\"id\")\n .queue(batch, callback);\n\n batch.execute();\n\n return ids;\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to modify permission: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef share_file(real_file_id, real_user, real_domain):\n \"\"\"Batch permission modification.\n Args:\n real_file_id: file Id\n real_user: User ID\n real_domain: Domain of the user ID\n Prints modified permissions\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n ids = []\n file_id = real_file_id\n\n def callback(request_id, response, exception):\n if exception:\n # Handle error\n print(exception)\n else:\n print(f\"Request_Id: {request_id}\")\n print(f'Permission Id: {response.get(\"id\")}')\n ids.append(response.get(\"id\"))\n\n # pylint: disable=maybe-no-member\n batch = service.new_batch_http_request(callback=callback)\n user_permission = {\n \"type\": \"user\",\n \"role\": \"writer\",\n \"emailAddress\": \"user@example.com\",\n }\n batch.add(\n service.permissions().create(\n fileId=file_id,\n body=user_permission,\n fields=\"id\",\n )\n )\n domain_permission = {\n \"type\": \"domain\",\n \"role\": \"reader\",\n \"domain\": \"example.com\",\n }\n domain_permission[\"domain\"] = real_domain\n batch.add(\n service.permissions().create(\n fileId=file_id,\n body=domain_permission,\n fields=\"id\",\n )\n )\n batch.execute()\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n ids = None\n\n return ids\n\n\nif __name__ == \"__main__\":\n share_file(\n real_file_id=\"1dUiRSoAQKkM3a4nTPeNQWgiuau1KdQ_l\",\n real_user=\"gduser1@workspacesamples.dev\",\n real_domain=\"workspacesamples.dev\",\n )\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Shares a file with a user and a domain.\n * @param {string} fileId The ID of the file to share.\n * @param {string} targetUserEmail The email address of the user to share with.\n * @param {string} targetDomainName The domain to share with.\n * @return {Promise<Array<string>>} A promise that resolves to an array of permission IDs.\n */\nasync function shareFile(fileId, targetUserEmail, targetDomainName) {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n /** @type {Array<string>} */\n const permissionIds = [];\n\n // The permissions to create.\n const permissions = [\n {\n type: 'user',\n role: 'writer',\n emailAddress: targetUserEmail, // e.g., 'user@partner.com'\n },\n {\n type: 'domain',\n role: 'writer',\n domain: targetDomainName, // e.g., 'example.com'\n },\n ];\n\n // Iterate through the permissions and create them one by one.\n for (const permission of permissions) {\n const result = await service.permissions.create({\n requestBody: permission,\n fileId,\n fields: 'id',\n });\n\n if (result.data.id) {\n permissionIds.push(result.data.id);\n console.log(`Inserted permission id: ${result.data.id}`);\n } else {\n throw new Error('Failed to create permission');\n }\n }\n return permissionIds;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nfunction shareFile()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $realFileId = readline(\"Enter File Id: \");\n $realUser = readline(\"Enter user email address: \");\n $realDomain = readline(\"Enter domain name: \");\n $ids = array();\n $fileId = '1sTWaJ_j7PkjzaBWtNc3IzovK5hQf21FbOw9yLeeLPNQ';\n $fileId = $realFileId;\n $driveService->getClient()->setUseBatch(true);\n try {\n $batch = $driveService->createBatch();\n\n $userPermission = new Drive\\Permission(array(\n 'type' => 'user',\n 'role' => 'writer',\n 'emailAddress' => 'user@example.com'\n ));\n $userPermission['emailAddress'] = $realUser;\n $request = $driveService->permissions->create(\n $fileId, $userPermission, array('fields' => 'id'));\n $batch->add($request, 'user');\n $domainPermission = new Drive\\Permission(array(\n 'type' => 'domain',\n 'role' => 'reader',\n 'domain' => 'example.com'\n ));\n $userPermission['domain'] = $realDomain;\n $request = $driveService->permissions->create(\n $fileId, $domainPermission, array('fields' => 'id'));\n $batch->add($request, 'domain');\n $results = $batch->execute();\n\n foreach ($results as $result) {\n if ($result instanceof Google_Service_Exception) {\n // Handle error\n printf($result);\n } else {\n printf(\"Permission ID: %s\\n\", $result->id);\n array_push($ids, $result->id);\n }\n }\n } finally {\n $driveService->getClient()->setUseBatch(false);\n }\n return $ids;\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Drive.v3.Data;\nusing Google.Apis.Requests;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use-case of Drive modify permissions.\n public class ShareFile\n {\n /// <summary>\n /// Batch permission modification.\n /// </summary>\n /// <param name=\"realFileId\">File id.</param>\n /// <param name=\"realUser\">User id.</param>\n /// <param name=\"realDomain\">Domain id.</param>\n /// <returns>list of modified permissions, null otherwise.</returns>\n public static IList<String> DriveShareFile(string realFileId, string realUser, string realDomain)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n var ids = new List<String>();\n var batch = new BatchRequest(service);\n BatchRequest.OnResponse<Permission> callback = delegate(\n Permission permission,\n RequestError error,\n int index,\n HttpResponseMessage message)\n {\n if (error != null)\n {\n // Handle error\n Console.WriteLine(error.Message);\n }\n else\n {\n Console.WriteLine(\"Permission ID: \" + permission.Id);\n }\n };\n Permission userPermission = new Permission()\n {\n Type = \"user\",\n Role = \"writer\",\n EmailAddress = realUser\n };\n\n var request = service.Permissions.Create(userPermission, realFileId);\n request.Fields = \"id\";\n batch.Queue(request, callback);\n\n Permission domainPermission = new Permission()\n {\n Type = \"domain\",\n Role = \"reader\",\n Domain = realDomain\n };\n request = service.Permissions.Create(domainPermission, realFileId);\n request.Fields = \"id\";\n batch.Queue(request, callback);\n var task = batch.ExecuteAsync();\n task.Wait();\n return ids;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.613Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":483,"estimatedTokens":3606}}757{"id":"doc-configure_a_drive_ui_integration_google_drive_go-598eb9c9","source":"documentation","title":"Configure a Drive UI integration | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/enable-sdk","text":"Example:\n```text\n{\n \"action\":\"create\",\n \"folderId\":\"FOLDER_ID\",\n \"folderResourceKey\":\"FOLDER_RESOURCE_KEY\",\n \"userId\":\"USER_ID\"\n}\n```\n\nExample:\n```text\n{\n \"ids\": [\"ID\"],\n \"resourceKeys\":{\"RESOURCE_KEYS\":\"RESOURCE_KEYS\"},\n \"action\":\"open\",\n \"userId\":\"USER_ID\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.614Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":21,"estimatedTokens":72}}758{"id":"doc-integrate_with_drive_ui_s_new_button_google_driv-1663ccc6","source":"documentation","title":"Integrate with Drive UI's \"New\" button | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/integrate-create","text":"Example:\n```text\n{\n \"action\":\"create\",\n \"folderId\":\"FOLDER_ID\",\n \"folderResourceKey\":\"FOLDER_RESOURCE_KEY\",\n \"userId\":\"USER_ID\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.615Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":11,"estimatedTokens":38}}759{"id":"doc-create_and_populate_folders_google_drive_google_-9a8e41ba","source":"documentation","title":"Create and populate folders | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/folder","text":"Example:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\n\n/* Class to demonstrate use of Drive's create folder API */\npublic class CreateFolder {\n\n\n /**\n * Create new folder.\n *\n * @return Inserted folder id if successful, {@code null} otherwise.\n * @throws IOException if service account credentials file not found.\n */\n public static String createFolder() throws IOException {\n // Load pre-authorized user credentials from the environment.\n // TODO(developer) - See https://developers.google.com/identity for\n // guides on implementing OAuth2 for your application.\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n // File's metadata.\n File fileMetadata = new File();\n fileMetadata.setName(\"Test\");\n fileMetadata.setMimeType(\"application/vnd.google-apps.folder\");\n try {\n File file = service.files().create(fileMetadata)\n .setFields(\"id\")\n .execute();\n System.out.println(\"Folder ID: \" + file.getId());\n return file.getId();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to create folder: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef create_folder():\n \"\"\"Create a folder and prints the folder ID\n Returns : Folder Id\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n file_metadata = {\n \"name\": \"Invoices\",\n \"mimeType\": \"application/vnd.google-apps.folder\",\n }\n\n # pylint: disable=maybe-no-member\n file = service.files().create(body=file_metadata, fields=\"id\").execute()\n print(f'Folder ID: \"{file.get(\"id\")}\".')\n return file.get(\"id\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return None\n\n\nif __name__ == \"__main__\":\n create_folder()\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Creates a new folder in Google Drive.\n * @return {Promise<string|null|undefined>} The ID of the created folder.\n */\nasync function createFolder() {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // The metadata for the new folder.\n const fileMetadata = {\n name: 'Invoices',\n mimeType: 'application/vnd.google-apps.folder',\n };\n\n // Create the new folder.\n const file = await service.files.create({\n requestBody: fileMetadata,\n fields: 'id',\n });\n\n // Print the ID of the new folder.\n console.log('Folder Id:', file.data.id);\n return file.data.id;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nfunction createFolder()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $fileMetadata = new Drive\\DriveFile(array(\n 'name' => 'Invoices',\n 'mimeType' => 'application/vnd.google-apps.folder'));\n $file = $driveService->files->create($fileMetadata, array(\n 'fields' => 'id'));\n printf(\"Folder ID: %s\\n\", $file->id);\n return $file->id;\n\n }catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use of Drive create folder API.\n public class CreateFolder\n {\n /// <summary>\n /// Creates a new folder.\n /// </summary>\n /// <returns>created folder id, null otherwise</returns>\n public static string DriveCreateFolder()\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n // File metadata\n var fileMetadata = new Google.Apis.Drive.v3.Data.File()\n {\n Name = \"Invoices\",\n MimeType = \"application/vnd.google-apps.folder\"\n };\n\n // Create a new folder on drive.\n var request = service.Files.Create(fileMetadata);\n request.Fields = \"id\";\n var file = request.Execute();\n // Prints the created folder id.\n Console.WriteLine(\"Folder ID: \" + file.Id);\n return file.Id;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"name\": \"FOLDER_NAME\",\n \"mimeType\": \"application/vnd.google-apps.folder\"\n }'\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.FileContent;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\n\n/* Class to demonstrate Drive's upload to folder use-case. */\npublic class UploadToFolder {\n\n /**\n * Upload a file to the specified folder.\n *\n * @param realFolderId Id of the folder.\n * @return Inserted file metadata if successful, {@code null} otherwise.\n * @throws IOException if service account credentials file not found.\n */\n public static File uploadToFolder(String realFolderId) throws IOException {\n // Load pre-authorized user credentials from the environment.\n // TODO(developer) - See https://developers.google.com/identity for\n // guides on implementing OAuth2 for your application.\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n\n // File's metadata.\n File fileMetadata = new File();\n fileMetadata.setName(\"photo.jpg\");\n fileMetadata.setParents(Collections.singletonList(realFolderId));\n java.io.File filePath = new java.io.File(\"files/photo.jpg\");\n FileContent mediaContent = new FileContent(\"image/jpeg\", filePath);\n try {\n File file = service.files().create(fileMetadata, mediaContent)\n .setFields(\"id, parents\")\n .execute();\n System.out.println(\"File ID: \" + file.getId());\n return file;\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to upload file: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\nfrom googleapiclient.http import MediaFileUpload\n\n\ndef upload_to_folder(folder_id):\n \"\"\"Upload a file to the specified folder and prints file ID, folder ID\n Args: Id of the folder\n Returns: ID of the file uploaded\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n file_metadata = {\"name\": \"photo.jpg\", \"parents\": [folder_id]}\n media = MediaFileUpload(\n \"download.jpeg\", mimetype=\"image/jpeg\", resumable=True\n )\n # pylint: disable=maybe-no-member\n file = (\n service.files()\n .create(body=file_metadata, media_body=media, fields=\"id\")\n .execute()\n )\n print(f'File ID: \"{file.get(\"id\")}\".')\n return file.get(\"id\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return None\n\n\nif __name__ == \"__main__\":\n upload_to_folder(folder_id=\"1s0oKEZZXjImNngxHGnY0xed6Mw-tvspu\")\n```\n\nExample:\n```text\nimport fs from 'node:fs';\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Uploads a file to the specified folder.\n * @param {string} folderId The ID of the folder to upload the file to.\n * @return {Promise<string>} The ID of the uploaded file.\n */\nasync function uploadToFolder(folderId) {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // The request body for the file to be uploaded.\n const requestBody = {\n name: 'photo.jpg',\n parents: [folderId],\n };\n\n // The media content to be uploaded.\n const media = {\n mimeType: 'image/jpeg',\n body: fs.createReadStream('files/photo.jpg'),\n };\n\n // Upload the file to the specified folder.\n const file = await service.files.create({\n requestBody,\n media,\n fields: 'id',\n });\n\n // Print the ID of the uploaded file.\n console.log('File Id:', file.data.id);\n if (!file.data.id) {\n throw new Error('File ID not found.');\n }\n return file.data.id;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nfunction uploadToFolder($folderId)\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $fileMetadata = new Drive\\DriveFile(array(\n 'name' => 'photo.jpg',\n 'parents' => array($folderId)\n ));\n $content = file_get_contents('../files/photo.jpg');\n $file = $driveService->files->create($fileMetadata, array(\n 'data' => $content,\n 'mimeType' => 'image/jpeg',\n 'uploadType' => 'multipart',\n 'fields' => 'id'));\n printf(\"File ID: %s\\n\", $file->id);\n return $file->id;\n } catch (Exception $e) {\n echo \"Error Message: \" . $e;\n }\n}\nrequire_once 'vendor/autoload.php';\nuploadToFolder();\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use of Drive upload to folder.\n public class UploadToFolder\n {\n /// <summary>\n /// Upload a file to the specified folder.\n /// </summary>\n /// <param name=\"filePath\">Image path to upload.</param>\n /// <param name=\"folderId\">Id of the folder.</param>\n /// <returns>Inserted file metadata if successful, null otherwise</returns>\n public static Google.Apis.Drive.v3.Data.File DriveUploadToFolder\n (string filePath, string folderId)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n // Upload file photo.jpg in specified folder on drive.\n var fileMetadata = new Google.Apis.Drive.v3.Data.File()\n {\n Name = \"photo.jpg\",\n Parents = new List<string>\n {\n folderId\n }\n };\n FilesResource.CreateMediaUpload request;\n // Create a new file on drive.\n using (var stream = new FileStream(filePath,\n FileMode.Open))\n {\n // Create a new file, with metadata and stream.\n request = service.Files.Create(\n fileMetadata, stream, \"image/jpeg\");\n request.Fields = \"id\";\n request.Upload();\n }\n var file = request.ResponseBody;\n // Prints the uploaded file id.\n Console.WriteLine(\"File ID: \" + file.Id);\n return file;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is FileNotFoundException)\n {\n Console.WriteLine(\"File not found\");\n }\n else if (e is DirectoryNotFoundException)\n {\n Console.WriteLine(\"Directory Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"name\": \"photo.jpg\",\n \"parents\": [\n \"FOLDER_ID\"\n ]\n }'\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.List;\n\n/* Class to demonstrate use case for moving file to folder.*/\npublic class MoveFileToFolder {\n\n\n /**\n * @param fileId Id of file to be moved.\n * @param folderId Id of folder where the fill will be moved.\n * @return list of parent ids for the file.\n */\n public static List<String> moveFileToFolder(String fileId, String folderId)\n throws IOException {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application.*/\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n\n // Retrieve the existing parents to remove\n File file = service.files().get(fileId)\n .setFields(\"parents\")\n .execute();\n StringBuilder previousParents = new StringBuilder();\n for (String parent : file.getParents()) {\n previousParents.append(parent);\n previousParents.append(',');\n }\n try {\n // Move the file to the new folder\n file = service.files().update(fileId, null)\n .setAddParents(folderId)\n .setRemoveParents(previousParents.toString())\n .setFields(\"id, parents\")\n .execute();\n\n return file.getParents();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to move file: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef move_file_to_folder(file_id, folder_id):\n \"\"\"Move specified file to the specified folder.\n Args:\n file_id: Id of the file to move.\n folder_id: Id of the folder\n Print: An object containing the new parent folder and other meta data\n Returns : Parent Ids for the file\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # call drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n # pylint: disable=maybe-no-member\n # Retrieve the existing parents to remove\n file = service.files().get(fileId=file_id, fields=\"parents\").execute()\n previous_parents = \",\".join(file.get(\"parents\"))\n # Move the file to the new folder\n file = (\n service.files()\n .update(\n fileId=file_id,\n addParents=folder_id,\n removeParents=previous_parents,\n fields=\"id, parents\",\n )\n .execute()\n )\n return file.get(\"parents\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return None\n\n\nif __name__ == \"__main__\":\n move_file_to_folder(\n file_id=\"1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9\",\n folder_id=\"1jvTFoyBhUspwDncOTB25kb9k0Fl0EqeN\",\n )\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Moves a file to a new folder in Google Drive.\n * @param {string} fileId The ID of the file to move.\n * @param {string} folderId The ID of the folder to move the file to.\n * @return {Promise<number>} The status of the move operation.\n */\nasync function moveFileToFolder(fileId, folderId) {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // Get the file's metadata to retrieve its current parents.\n const file = await service.files.get({\n fileId,\n fields: 'parents',\n });\n\n // Get the current parents as a comma-separated string.\n const previousParents = (file.data.parents ?? []).join(',');\n\n // Move the file to the new folder.\n const result = await service.files.update({\n fileId,\n addParents: folderId,\n removeParents: previousParents,\n fields: 'id, parents',\n });\n\n // Print the status of the move operation.\n console.log(result.status);\n return result.status;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nuse Google\\Service\\Drive\\DriveFile;\nfunction moveFileToFolder($fileId,$folderId)\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $emptyFileMetadata = new DriveFile();\n // Retrieve the existing parents to remove\n $file = $driveService->files->get($fileId, array('fields' => 'parents'));\n $previousParents = join(',', $file->parents);\n // Move the file to the new folder\n $file = $driveService->files->update($fileId, $emptyFileMetadata, array(\n 'addParents' => $folderId,\n 'removeParents' => $previousParents,\n 'fields' => 'id, parents'));\n return $file->parents;\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n}\n```\n\nExample:\n```text\nusing Google;\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use-case of Drive move file to folder.\n public class MoveFileToFolder\n {\n /// <summary>\n /// Move specified file to the specified folder.\n /// </summary>\n /// <param name=\"fileId\">Id of file to be moved.</param>\n /// <param name=\"folderId\">Id of folder where the fill will be moved.</param>\n /// <returns>list of parent ids for the file, null otherwise.</returns>\n public static IList<string> DriveMoveFileToFolder(string fileId,\n string folderId)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n // Retrieve the existing parents to remove\n var getRequest = service.Files.Get(fileId);\n getRequest.Fields = \"parents\";\n var file = getRequest.Execute();\n var previousParents = String.Join(\",\", file.Parents);\n // Move the file to the new folder\n var updateRequest =\n service.Files.Update(new Google.Apis.Drive.v3.Data.File(),\n fileId);\n updateRequest.Fields = \"id, parents\";\n updateRequest.AddParents = folderId;\n updateRequest.RemoveParents = previousParents;\n file = updateRequest.Execute();\n\n return file.Parents;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is GoogleApiException)\n {\n Console.WriteLine(\"File or Folder not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\ncurl -X PATCH 'https://www.googleapis.com/drive/v3/files/FILE_ID?addParents=NEW_PARENT_ID&removeParents=PREVIOUS_PARENT_ID' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Accept: application/json'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.617Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":800,"estimatedTokens":6349}}760{"id":"doc-integrate_with_drive_ui_s_open_with_context_menu-1ddfcf7b","source":"documentation","title":"Integrate with Drive UI's \"Open with\" context menu | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/integrate-open","text":"Example:\n```text\n{\n \"ids\": [\"ID\"],\n \"resourceKeys\":{\"RESOURCE_KEYS\":\"RESOURCE_KEYS\"},\n \"action\":\"open\",\n \"userId\":\"USER_ID\"\n}\n```\n\nExample:\n```text\n{\n \"exportIds\": [\"ID\"],\n \"resourceKeys\":{\"RESOURCE_KEYS\":\"RESOURCE_KEYS\"},\n \"action\":\"open\",\n \"userId\":\"USER_ID\"\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.Arrays;\n\n/* Class to demonstrate use-case of drive's export pdf. */\npublic class ExportPdf {\n\n /**\n * Download a Document file in PDF format.\n *\n * @param realFileId file ID of any workspace document format file.\n * @return byte array stream if successful, {@code null} otherwise.\n * @throws IOException if service account credentials file not found.\n */\n public static ByteArrayOutputStream exportPdf(String realFileId) throws IOException {\n // Load pre-authorized user credentials from the environment.\n // TODO(developer) - See https://developers.google.com/identity for\n // guides on implementing OAuth2 for your application.\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n\n OutputStream outputStream = new ByteArrayOutputStream();\n try {\n service.files().export(realFileId, \"application/pdf\")\n .executeMediaAndDownloadTo(outputStream);\n\n return (ByteArrayOutputStream) outputStream;\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to export file: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport io\n\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\nfrom googleapiclient.http import MediaIoBaseDownload\n\n\ndef export_pdf(real_file_id):\n \"\"\"Download a Document file in PDF format.\n Args:\n real_file_id : file ID of any workspace document format file\n Returns : IO object with location\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n file_id = real_file_id\n\n # pylint: disable=maybe-no-member\n request = service.files().export_media(\n fileId=file_id, mimeType=\"application/pdf\"\n )\n file = io.BytesIO()\n downloader = MediaIoBaseDownload(file, request)\n done = False\n while done is False:\n status, done = downloader.next_chunk()\n print(f\"Download {int(status.progress() * 100)}.\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n file = None\n\n return file.getvalue()\n\n\nif __name__ == \"__main__\":\n export_pdf(real_file_id=\"1zbp8wAyuImX91Jt9mI-CAX_1TqkBLDEDcr2WeXBbKUY\")\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Exports a Google Doc as a PDF.\n * @param {string} fileId The ID of the file to export.\n * @return {Promise<number>} The status of the export request.\n */\nasync function exportPdf(fileId) {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // Export the file as a PDF.\n const result = await service.files.export({\n fileId,\n mimeType: 'application/pdf',\n });\n\n // Print the status of the export.\n console.log(result.status);\n return result.status;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nfunction exportPdf()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $realFileId = readline(\"Enter File Id: \");\n $fileId = '1ZdR3L3qP4Bkq8noWLJHSr_iBau0DNT4Kli4SxNc2YEo';\n $fileId = $realFileId;\n $response = $driveService->files->export($fileId, 'application/pdf', array(\n 'alt' => 'media'));\n $content = $response->getBody()->getContents();\n return $content;\n\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.619Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":186,"estimatedTokens":1318}}761{"id":"doc-implement_shared_drive_support_google_drive_goog-aca17d07","source":"documentation","title":"Implement shared drive support | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/enable-shareddrives","text":"Example:\n```text\nfiles = []\npage_token = None\nwhile True:\n response = drive_service.files().list(\n q=\"mimeType='application/vnd.google-apps.folder'\",\n spaces='drive',\n corpora='allDrives',\n supportsAllDrives=True,\n includeItemsFromAllDrives=True,\n fields='nextPageToken, files(id, name)',\n pageToken=page_token\n ).execute()\n files.extend(response.get('files', []))\n page_token = response.get('nextPageToken', None)\n if not page_token:\n break\n```\n\nExample:\n```text\nlet files = [];\nlet pageToken = null;\ndo {\n const response = await drive_service.files.list({\n q: \"mimeType='application/vnd.google-apps.folder'\",\n spaces: 'drive',\n corpora: 'allDrives',\n supportsAllDrives: true,\n includeItemsFromAllDrives: true,\n fields: 'nextPageToken, files(id, name)',\n pageToken: pageToken\n });\n files = files.concat(response.data.files);\n pageToken = response.data.nextPageToken;\n} while (pageToken);\n```\n\nExample:\n```text\nList<File> files = new ArrayList<>();\nString pageToken = null;\ndo {\n FileList result = driveService.files().list()\n .setQ(\"mimeType='application/vnd.google-apps.folder'\")\n .setSpaces(\"drive\")\n .setCorpora(\"allDrives\")\n .setSupportsAllDrives(true)\n .setIncludeItemsFromAllDrives(true)\n .setFields(\"nextPageToken, files(id, name)\")\n .setPageToken(pageToken)\n .execute();\n files.addAll(result.getFiles());\n pageToken = result.getNextPageToken();\n} while (pageToken != null);\n```\n\nExample:\n```text\ncurl -X GET \\\n 'https://www.googleapis.com/drive/v3/files?corpora=allDrives&includeItemsFromAllDrives=true&supportsAllDrives=true&fields=nextPageToken%2Cfiles(id%2Cname)' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Accept: application/json'\n```\n\nExample:\n```text\n# 1. Get the start page token for the shared drive.\nresponse = drive_service.changes().getStartPageToken(\n supportsAllDrives=True,\n driveId='SHARED_DRIVE_ID'\n).execute()\nstart_page_token = response.get('startPageToken')\n\n# 2. List changes starting from the page token.\nresponse = drive_service.changes().list(\n pageToken=start_page_token,\n supportsAllDrives=True,\n includeItemsFromAllDrives=True,\n driveId='SHARED_DRIVE_ID'\n).execute()\nchanges = response.get('changes', [])\n```\n\nExample:\n```text\n// 1. Get the start page token for the shared drive.\nconst tokenResponse = await drive_service.changes.getStartPageToken({\n supportsAllDrives: true,\n driveId: 'SHARED_DRIVE_ID'\n});\nconst startPageToken = tokenResponse.data.startPageToken;\n\n// 2. List changes starting from the page token.\nconst response = await drive_service.changes.list({\n pageToken: startPageToken,\n supportsAllDrives: true,\n includeItemsFromAllDrives: true,\n driveId: 'SHARED_DRIVE_ID'\n});\nconst changes = response.data.changes;\n```\n\nExample:\n```text\n// 1. Get the start page token for the shared drive.\nStartPageToken tokenResult = driveService.changes().getStartPageToken()\n .setSupportsAllDrives(true)\n .setDriveId(\"SHARED_DRIVE_ID\")\n .execute();\nString startPageToken = tokenResult.getStartPageToken();\n\n// 2. List changes starting from the page token.\nChangeList changesResult = driveService.changes().list(startPageToken)\n .setSupportsAllDrives(true)\n .setIncludeItemsFromAllDrives(true)\n .setDriveId(\"SHARED_DRIVE_ID\")\n .execute();\nList<Change> changes = changesResult.getChanges();\n```\n\nExample:\n```text\n# 1. Get the start page token for the shared drive.\ncurl -X GET \\\n 'https://www.googleapis.com/drive/v3/changes/startPageToken?supportsAllDrives=true&driveId=SHARED_DRIVE_ID' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Accept: application/json'\n\n# 2. List changes starting from the page token.\ncurl -X GET \\\n 'https://www.googleapis.com/drive/v3/changes?pageToken=START_PAGE_TOKEN&supportsAllDrives=true&includeItemsFromAllDrives=true&driveId=SHARED_DRIVE_ID' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Accept: application/json'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.620Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":138,"estimatedTokens":1002}}762{"id":"doc-retrieve_changes_google_drive_google_for_develop-45f3c23a","source":"documentation","title":"Retrieve changes | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/manage-changes","text":"Example:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.StartPageToken;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\n\n/* Class to demonstrate use-case of Drive's fetch start page token */\npublic class FetchStartPageToken {\n\n /**\n * Retrieve the start page token for the first time.\n *\n * @return Start page token as String.\n * @throws IOException if file is not found\n */\n public static String fetchStartPageToken() throws IOException {\n /*Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n try {\n StartPageToken response = service.changes()\n .getStartPageToken().execute();\n System.out.println(\"Start token: \" + response.getStartPageToken());\n\n return response.getStartPageToken();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to fetch start page token: \" + e.getDetails());\n throw e;\n }\n }\n\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef fetch_start_page_token():\n \"\"\"Retrieve page token for the current state of the account.\n Returns & prints : start page token\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n # pylint: disable=maybe-no-member\n response = service.changes().getStartPageToken().execute()\n print(f'Start token: {response.get(\"startPageToken\")}')\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n response = None\n\n return response.get(\"startPageToken\")\n\n\nif __name__ == \"__main__\":\n fetch_start_page_token()\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\n# TODO - PHP client currently chokes on fetching start page token\nfunction fetchStartPageToken()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $response = $driveService->changes->getStartPageToken();\n printf(\"Start token: %s\\n\", $response->startPageToken);\n return $response->startPageToken;\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use-case of Drive's fetch start page token\n public class FetchStartPageToken\n {\n /// <summary>\n /// Retrieve the starting page token.\n /// </summary>\n /// <returns>start page token as String, null otherwise.</returns>\n public static string DriveFetchStartPageToken()\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n var response = service.Changes.GetStartPageToken().Execute();\n // Prints the token value.\n Console.WriteLine(\"Start token: \" + response.StartPageTokenValue);\n return response.StartPageTokenValue;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Fetches the start page token for the current state of the account.\n * @return {Promise<string>} The start page token.\n */\nasync function fetchStartPageToken() {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive.appdata',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // Fetch the start page token.\n const res = await service.changes.getStartPageToken({});\n const token = res.data.startPageToken;\n console.log('start token: ', token);\n if (!token) {\n throw new Error('Start page token not found.');\n }\n return token;\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.ChangeList;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\n\n/* Class to demonstrate use-case of Drive's fetch changes in file. */\npublic class FetchChanges {\n /**\n * Retrieve the list of changes for the currently authenticated user.\n *\n * @param savedStartPageToken Last saved start token for this user.\n * @return Saved token after last page.\n * @throws IOException if file is not found\n */\n public static String fetchChanges(String savedStartPageToken) throws IOException {\n\n /*Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application.*/\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n try {\n // Begin with our last saved start token for this user or the\n // current token from getStartPageToken()\n String pageToken = savedStartPageToken;\n while (pageToken != null) {\n ChangeList changes = service.changes().list(pageToken)\n .execute();\n for (com.google.api.services.drive.model.Change change : changes.getChanges()) {\n // Process change\n System.out.println(\"Change found for file: \" + change.getFileId());\n }\n if (changes.getNewStartPageToken() != null) {\n // Last page, save this token for the next polling interval\n savedStartPageToken = changes.getNewStartPageToken();\n }\n pageToken = changes.getNextPageToken();\n }\n\n return savedStartPageToken;\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to fetch changes: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef fetch_changes(saved_start_page_token):\n \"\"\"Retrieve the list of changes for the currently authenticated user.\n prints changed file's ID\n Args:\n saved_start_page_token : StartPageToken for the current state of the\n account.\n Returns: saved start page token.\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n # Begin with our last saved start token for this user or the\n # current token from getStartPageToken()\n page_token = saved_start_page_token\n # pylint: disable=maybe-no-member\n\n while page_token is not None:\n response = (\n service.changes().list(pageToken=page_token, spaces=\"drive\").execute()\n )\n for change in response.get(\"changes\"):\n # Process change\n print(f'Change found for file: {change.get(\"fileId\")}')\n if \"newStartPageToken\" in response:\n # Last page, save this token for the next polling interval\n saved_start_page_token = response.get(\"newStartPageToken\")\n page_token = response.get(\"nextPageToken\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n saved_start_page_token = None\n\n return saved_start_page_token\n\n\nif __name__ == \"__main__\":\n # saved_start_page_token is the token number\n fetch_changes(saved_start_page_token=209)\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\n# TODO - PHP client currently chokes on fetching start page token\nfunction fetchChanges()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n # Begin with our last saved start token for this user or the\n # current token from getStartPageToken()\n $savedStartPageToken = readLine(\"Enter Start Page Token: \");\n $pageToken = $savedStartPageToken;\n while ($pageToken != null) {\n $response = $driveService->changes->listChanges($pageToken, array(\n 'spaces' => 'drive'\n ));\n foreach ($response->changes as $change) {\n // Process change\n printf(\"Change found for file: %s\", $change->fileId);\n }\n if ($response->newStartPageToken != null) {\n // Last page, save this token for the next polling interval\n $savedStartPageToken = $response->newStartPageToken;\n }\n $pageToken = $response->nextPageToken;\n }\n echo $savedStartPageToken;\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n\n}\nrequire_once 'vendor/autoload.php';\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use-case of Drive's fetch changes in file.\n public class FetchChanges\n {\n /// <summary>\n /// Retrieve the list of changes for the currently authenticated user.\n /// prints changed file's ID\n /// </summary>\n /// <param name=\"savedStartPageToken\">last saved start token for this user.</param>\n /// <returns>saved token for the current state of the account, null otherwise.</returns>\n public static string DriveFetchChanges(string savedStartPageToken)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n // Begin with our last saved start token for this user or the\n // current token from GetStartPageToken()\n string pageToken = savedStartPageToken;\n while (pageToken != null)\n {\n var request = service.Changes.List(pageToken);\n request.Spaces = \"drive\";\n var changes = request.Execute();\n foreach (var change in changes.Changes)\n {\n // Process change\n Console.WriteLine(\"Change found for file: \" + change.FileId);\n }\n\n if (changes.NewStartPageToken != null)\n {\n // Last page, save this token for the next polling interval\n savedStartPageToken = changes.NewStartPageToken;\n }\n pageToken = changes.NextPageToken;\n }\n return savedStartPageToken;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Fetches the list of changes for the currently authenticated user.\n * @param {string} savedStartPageToken The page token obtained from `fetch_start_page_token.js`.\n */\nasync function fetchChanges(savedStartPageToken) {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive.readonly',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // The page token for the next page of changes.\n let pageToken = savedStartPageToken;\n\n // Loop to fetch all changes, handling pagination.\n do {\n const result = await service.changes.list({\n pageToken: savedStartPageToken,\n fields: '*',\n });\n\n // Process the changes.\n (result.data.changes ?? []).forEach((change) => {\n console.log('change found for file: ', change.fileId);\n });\n\n // Update the page token for the next iteration.\n pageToken = result.data.newStartPageToken ?? '';\n } while (pageToken);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.621Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":475,"estimatedTokens":3991}}763{"id":"doc-add_the_save_to_drive_button_google_drive_google-d679f681","source":"documentation","title":"Add the \"Save to Drive\" button | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/savetodrive","text":"Example:\n```text\n<script src=\"https://apis.google.com/js/platform.js\" async defer></script>\n<div class=\"g-savetodrive\"\n data-src=\"//example.com/path/to/myfile.pdf\"\n data-filename=\"My Statement.pdf\"\n data-sitename=\"My Company Name\">\n</div>\n```\n\nExample:\n```text\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Headers: Range\nAccess-Control-Expose-Headers: Cache-Control, Content-Encoding, Content-Range\n```\n\nExample:\n```text\n<!DOCTYPE html>\n <html>\n <head>\n <title>Save to Drive Demo: Explicit Load</title>\n <link rel=\"canonical\" href=\"http://www.example.com\">\n <script src=\"https://apis.google.com/js/platform.js\" async defer>\n {parsetags: 'explicit'}\n </script>\n </head>\n <body>\n <div id=\"container\">\n <div class=\"g-savetodrive\"\n data-src=\"//example.com/path/to/myfile.pdf\"\n data-filename=\"My Statement.pdf\"\n data-sitename=\"My Company Name\">\n <div>\n </div>\n <script type=\"text/javascript\">\n gapi.savetodrive.go('container');\n </script>\n </body>\n </html>\n```\n\nExample:\n```text\n<!DOCTYPE html>\n <html>\n <head>\n <title>Save to Drive Demo: Explicit Render</title>\n <link rel=\"canonical\" href=\"http://www.example.com\">\n <script>\n window.___gcfg = {\n parsetags: 'explicit'\n };\n </script>\n <script src=\"https://apis.google.com/js/platform.js\" async defer></script>\n </head>\n <body>\n <a href=\"javascript:void(0)\" id=\"render-link\">Render the Save to Drive button</a>\n <div id=\"savetodrive-div\"></div>\n <script>\n function renderSaveToDrive() {\n gapi.savetodrive.render('savetodrive-div', {\n src: '//example.com/path/to/myfile.pdf',\n filename: 'My Statement.pdf',\n sitename: 'My Company Name'\n });\n }\n document.getElementById('render-link').addEventListener('click', renderSaveToDrive);\n </script>\n </body>\n </html>\n```\n\nExample:\n```text\n<!DOCTYPE html>\n <html>\n <head>\n <title>Save to Drive Demo: Async Load with Language</title>\n <link rel=\"canonical\" href=\"http://www.example.com\">\n </head>\n <body>\n <div class=\"g-savetodrive\"\n data-src=\"//example.com/path/to/myfile.pdf\"\n data-filename=\"My Statement.pdf\"\n data-sitename=\"My Company Name\">\n </div>\n\n <script type=\"text/javascript\">\n window.___gcfg = {\n lang: 'en-US'\n };\n </script>\n <script src = 'https://apis.google.com/js/platform.js' async defer></script>\n\n </body>\n </html>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.622Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":101,"estimatedTokens":688}}764{"id":"doc-notifications_for_resource_changes_google_drive_-904473ee","source":"documentation","title":"Notifications for resource changes | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/push","text":"Example:\n```text\nhttps://www.googleapis.com/API_NAME/API_VERSION/RESOURCE_PATH/watch\n```\n\nExample:\n```text\nPOST https://www.googleapis.com/drive/v3/files/fileId/watch\nAuthorization: Bearer CURRENT_USER_AUTH_TOKEN\nContent-Type: application/json\n\n{\n \"id\": \"01234567-89ab-cdef-0123456789ab\",\n \"type\": \"web_hook\",\n \"address\": \"https://mydomain.com/notifications\",\n ...\n \"token\": \"target=myApp-myFilesChannelDest\",\n \"expiration\": 1426325213000\n}\n```\n\nExample:\n```text\nPOST https://www.googleapis.com/drive/v3/changes/watch\nAuthorization: Bearer CURRENT_USER_AUTH_TOKEN\nContent-Type: application/json\n\n{\n \"id\": \"4ba78bf0-6a47-11e2-bcfd-0800200c9a77\",\n \"type\": \"web_hook\",\n \"address\": \"https://mydomain.com/notifications\",\n ...\n \"token\": \"target=myApp-myChangesChannelDest\",\n \"expiration\": 1426325213000\n}\n```\n\nExample:\n```text\n{\n \"kind\": \"api#channel\",\n \"id\": \"01234567-89ab-cdef-0123456789ab\",\n \"resourceId\": \"o3hgv1538sdjfh\",\n \"resourceUri\": \"https://www.googleapis.com/drive/v3/files/o3hgv1538sdjfh\",\n \"token\": \"target=myApp-myFilesChannelDest\",\n \"expiration\": 1426325213000\n}\n```\n\nExample:\n```text\nPOST https://mydomain.com/notifications // Your receiving URL.\nX-Goog-Channel-ID: channel-ID-value\nX-Goog-Channel-Token: channel-token-value\nX-Goog-Channel-Expiration: expiration-date-and-time // In human-readable format. Present only if the channel expires.\nX-Goog-Resource-ID: identifier-for-the-watched-resource\nX-Goog-Resource-URI: version-specific-URI-of-the-watched-resource\nX-Goog-Resource-State: sync\nX-Goog-Message-Number: 1\n```\n\nExample:\n```text\nPOST https://mydomain.com/notifications\nContent-Type: application/json; utf-8\nContent-Length: 0\nX-Goog-Channel-ID: 4ba78bf0-6a47-11e2-bcfd-0800200c9a66\nX-Goog-Channel-Token: 3a98f1a2b3c4d5e6f7\nX-Goog-Channel-Expiration: Tue, 19 Nov 2013 01:13:52 GMT\nX-Goog-Resource-ID: ret08u3rv24htgh289g\nX-Goog-Resource-URI: https://www.googleapis.com/drive/v3/files/ret08u3rv24htgh289g\nX-Goog-Resource-State: add\nX-Goog-Message-Number: 10\n```\n\nExample:\n```text\nPOST https://mydomain.com/notifications\nContent-Type: application/json; utf-8\nContent-Length: 0\nX-Goog-Channel-ID: 4ba78bf0-6a47-11e2-bcfd-0800200c9a66\nX-Goog-Channel-Token: 3a98f1a2b3c4d5e6f7\nX-Goog-Channel-Expiration: Tue, 19 Nov 2013 01:13:52 GMT\nX-Goog-Resource-ID: ret08u3rv24htgh289g\nX-Goog-Resource-URI: https://www.googleapis.com/drive/v3/files/ret08u3rv24htgh289g\nX-Goog-Resource-State: update\nX-Goog-Changed: content,properties\nX-Goog-Message-Number: 11\n```\n\nExample:\n```text\nPOST https://mydomain.com/notifications\nContent-Type: application/json; utf-8\nContent-Length: 0\nX-Goog-Channel-ID: 8bd90be9-3a58-3122-ab43-9823188a5b43\nX-Goog-Channel-Token: 245t1234tt83trrt333\nX-Goog-Channel-Expiration: Tue, 19 Nov 2013 01:13:52 GMT\nX-Goog-Resource-ID: ret987df98743md8g\nX-Goog-Resource-URI: https://www.googleapis.com/drive/v3/changes\nX-Goog-Resource-State: changed\nX-Goog-Message-Number: 23\n```\n\nExample:\n```text\nX-Goog-Resource-State: update\nX-Goog-Changed: content, permissions\n```\n\nExample:\n```text\nhttps://www.googleapis.com/drive/v3/channels/stop\n```\n\nExample:\n```text\nPOST https://www.googleapis.com/drive/v3/channels/stop\n \nAuthorization: Bearer CURRENT_USER_AUTH_TOKEN\nContent-Type: application/json\n\n{\n \"id\": \"4ba78bf0-6a47-11e2-bcfd-0800200c9a66\",\n \"resourceId\": \"ret08u3rv24htgh289g\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.623Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":129,"estimatedTokens":837}}765{"id":"doc-display_the_sharing_dialog_google_drive_google_f-9158680d","source":"documentation","title":"Display the sharing dialog | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/share-button","text":"Example:\n```text\n<head>\n...\n<script type=\"text/javascript\" src=\"https://apis.google.com/js/api.js\"></script>\n<script type=\"text/javascript\">\n init = function() {\n s = new gapi.drive.share.ShareClient();\n s.setOAuthToken('<OAUTH_TOKEN>');\n s.setItemIds(['<FILE_ID>']);\n }\n window.onload = function() {\n gapi.load('drive-share', init);\n }\n</script>\n</head>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.624Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":19,"estimatedTokens":103}}766{"id":"doc-migrate_to_drive_api_v3_google_drive_google_for_-3b96ba33","source":"documentation","title":"Migrate to Drive API v3 | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/migrate-to-v3","text":"Example:\n```text\nvar DISCOVERY_DOCS = [\"https://www.googleapis.com/discovery/v1/apis/drive/v3/rest\"];\n```\n\nExample:\n```text\nservice = build('drive', 'v3', credentials=creds)\n```\n\nExample:\n```text\nconst drive = google.drive({version: 'v3', auth});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.625Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":16,"estimatedTokens":66}}767{"id":"doc-remove_a_label_from_a_file_google_drive_google_f-4de4256d","source":"documentation","title":"Remove a label from a file | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/remove-label","text":"Example:\n```text\nModifyLabelsRequest modifyLabelsRequest = new ModifyLabelsRequest()\n .setLabelModifications(ImmutableList.of(\n new LabelModification()\n .setLabelId(\"LABEL_ID\")\n .setRemoveLabel(true)));\n\nModifyLabelsResponse modifyLabelsResponse = driveService.files()\n .modifyLabels(\"FILE_ID\", modifyLabelsRequest)\n .execute();\n```\n\nExample:\n```text\nlabel_modification = {\n 'labelId': 'LABEL_ID',\n 'removeLabel': True\n}\n\nmodified_labels = drive_service.files().modifyLabels(\n fileId=\"FILE_ID\",\n body={'labelModifications': [label_modification]}\n).execute()\n```\n\nExample:\n```text\n/**\n * Remove a label on a Drive file\n * @return{obj} updated label data\n **/\nasync function removeLabel() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n const service = google.drive({version: 'v3', auth});\n const labelModification = {\n 'labelId': 'LABEL_ID',\n 'removeLabel': true,\n };\n const labelModificationRequest = {\n 'labelModifications': [labelModification],\n };\n try {\n const updateResponse = await service.files.modifyLabels({\n fileId: 'FILE_ID',\n requestBody: labelModificationRequest,\n });\n return updateResponse;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.625Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":64,"estimatedTokens":385}}768{"id":"doc-set_a_label_field_on_a_file_google_drive_google_-263ed79f","source":"documentation","title":"Set a label field on a file | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/set-label","text":"Example:\n```text\nLabelFieldModification fieldModification = new LabelFieldModification()\n .setFieldId(\"FIELD_ID\")\n .setSetTextValues(ImmutableList.of(\"VALUE\"));\n\nModifyLabelsRequest modifyLabelsRequest = new ModifyLabelsRequest()\n .setLabelModifications(ImmutableList.of(\n new LabelModification()\n .setLabelId(\"LABEL_ID\")\n .setFieldModifications(ImmutableList.of(fieldModification))));\n\nModifyLabelsResponse modifyLabelsResponse = driveService.files()\n .modifyLabels(\"FILE_ID\", modifyLabelsRequest)\n .execute();\n```\n\nExample:\n```text\nfield_modification = {\n 'fieldId': 'FIELD_ID',\n 'setTextValues': ['VALUE']\n}\n\nlabel_modification = {\n 'labelId': 'LABEL_ID',\n 'fieldModifications': [field_modification]\n}\n\nmodified_labels = drive_service.files().modifyLabels(\n fileId=\"FILE_ID\",\n body={'labelModifications': [label_modification]}\n).execute()\n```\n\nExample:\n```text\n/**\n * Set a label with a text field on a Drive file\n * @return{obj} updated label data\n **/\nasync function setLabelTextField() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n const service = google.drive({version: 'v3', auth});\n const fieldModification = {\n 'fieldId': 'FIELD_ID',\n 'setTextValues': ['VALUE'],\n };\n const labelModification = {\n 'labelId': 'LABEL_ID',\n 'fieldModifications': [fieldModification],\n };\n const labelModificationRequest = {\n 'labelModifications': [labelModification],\n };\n try {\n const updateResponse = await service.files.modifyLabels({\n fileId: 'FILE_ID',\n requestBody: labelModificationRequest,\n });\n return updateResponse;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.626Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":77,"estimatedTokens":493}}769{"id":"doc-improve_performance_google_drive_google_for_deve-e7c4c6ba","source":"documentation","title":"Improve performance | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/performance","text":"Example:\n```text\nAccept-Encoding: gzip\nUser-Agent: my program (gzip)\n```\n\nExample:\n```text\nPOST https://www.googleapis.com/...\nX-HTTP-Method-Override: PATCH\n...\n```\n\nExample:\n```text\nPOST https://www.googleapis.com/batch/drive/v3\nAccept-Encoding: gzip\nUser-Agent: Google-HTTP-Java-Client/1.20.0 (gzip)\nContent-Type: multipart/mixed; boundary=END_OF_PART\nContent-Length: 963\n\n--END_OF_PART\nContent-Length: 337\nContent-Type: application/http\ncontent-id: 1\ncontent-transfer-encoding: binary\n\nPOST https://www.googleapis.com/drive/v3/files/fileId/permissions?fields=id\nAuthorization: Bearer authorization_token\nContent-Length: 70\nContent-Type: application/json; charset=UTF-8\n\n{\n \"emailAddress\":\"example@appsrocks.com\",\n \"role\":\"writer\",\n \"type\":\"user\"\n}\n--END_OF_PART\nContent-Length: 353\nContent-Type: application/http\ncontent-id: 2\ncontent-transfer-encoding: binary\n\nPOST https://www.googleapis.com/drive/v3/files/fileId/permissions?fields=id&sendNotificationEmail=false\nAuthorization: Bearer authorization_token\nContent-Length: 58\nContent-Type: application/json; charset=UTF-8\n\n{\n \"domain\":\"appsrocks.com\",\n \"role\":\"reader\",\n \"type\":\"domain\"\n}\n--END_OF_PART--\n```\n\nExample:\n```text\nHTTP/1.1 200 OK\nAlt-Svc: quic=\":443\"; p=\"1\"; ma=604800\nServer: GSE\nAlternate-Protocol: 443:quic,p=1\nX-Frame-Options: SAMEORIGIN\nContent-Encoding: gzip\nX-XSS-Protection: 1; mode=block\nContent-Type: multipart/mixed; boundary=batch_6VIxXCQbJoQ_AATxy_GgFUk\nTransfer-Encoding: chunked\nX-Content-Type-Options: nosniff\nDate: Fri, 13 Nov 2015 19:28:59 GMT\nCache-Control: private, max-age=0\nVary: X-Origin\nVary: Origin\nExpires: Fri, 13 Nov 2015 19:28:59 GMT\n\n--batch_6VIxXCQbJoQ_AATxy_GgFUk\nContent-Type: application/http\nContent-ID: response-1\n\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=UTF-8\nDate: Fri, 13 Nov 2015 19:28:59 GMT\nExpires: Fri, 13 Nov 2015 19:28:59 GMT\nCache-Control: private, max-age=0\nContent-Length: 35\n\n{\n \"id\": \"12218244892818058021i\"\n}\n\n--batch_6VIxXCQbJoQ_AATxy_GgFUk\nContent-Type: application/http\nContent-ID: response-2\n\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=UTF-8\nDate: Fri, 13 Nov 2015 19:28:59 GMT\nExpires: Fri, 13 Nov 2015 19:28:59 GMT\nCache-Control: private, max-age=0\nContent-Length: 35\n\n{\n \"id\": \"04109509152946699072k\"\n}\n\n--batch_6VIxXCQbJoQ_AATxy_GgFUk--\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.630Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":108,"estimatedTokens":580}}770{"id":"doc-manage_shared_drives_google_drive_google_for_dev-87ce0f18","source":"documentation","title":"Manage shared drives | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/manage-shareddrives","text":"Example:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.Drive;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.UUID;\n\n/* class to demonstrate use-case of Drive's create drive. */\npublic class CreateDrive {\n\n /**\n * Create a drive.\n *\n * @return Newly created drive id.\n * @throws IOException if service account credentials file not found.\n */\n public static String createDrive() throws IOException {\n /*Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application.*/\n GoogleCredentials credentials =\n GoogleCredentials.getApplicationDefault().createScoped(Arrays.asList(DriveScopes.DRIVE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n com.google.api.services.drive.Drive service =\n new com.google.api.services.drive.Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n\n Drive driveMetadata = new Drive();\n driveMetadata.setName(\"Project Resources\");\n String requestId = UUID.randomUUID().toString();\n try {\n Drive drive = service.drives().create(requestId,\n driveMetadata)\n .execute();\n System.out.println(\"Drive ID: \" + drive.getId());\n\n return drive.getId();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to create drive: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport uuid\n\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef create_drive():\n \"\"\"Create a drive.\n Returns:\n Id of the created drive\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n drive_metadata = {\"name\": \"Project Resources\"}\n request_id = str(uuid.uuid4())\n # pylint: disable=maybe-no-member\n drive = (\n service.drives()\n .create(body=drive_metadata, requestId=request_id, fields=\"id\")\n .execute()\n )\n print(f'Drive ID: {drive.get(\"id\")}')\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n drive = None\n\n return drive.get(\"id\")\n\n\nif __name__ == \"__main__\":\n create_drive()\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\nimport {v4 as uuid} from 'uuid';\n\n/**\n * Creates a new shared drive.\n * @return {Promise<string>} The ID of the created shared drive.\n */\nasync function createDrive() {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // The metadata for the new shared drive.\n const driveMetadata = {\n name: 'Project resources',\n };\n\n // A unique request ID to avoid creating duplicate shared drives.\n const requestId = uuid();\n\n // Create the new shared drive.\n const Drive = await service.drives.create({\n requestBody: driveMetadata,\n requestId,\n fields: 'id',\n });\n\n // Print the ID of the new shared drive.\n console.log('Drive Id:', Drive.data.id);\n if (!Drive.data.id) {\n throw new Error('Drive ID not found.');\n }\n return Drive.data.id;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nuse Ramsey\\Uuid\\Uuid;\nfunction createDrive()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n\n $driveMetadata = new Drive\\Drive(array(\n 'name' => 'Project Resources'));\n $requestId = Uuid::uuid4()->toString();\n $drive = $driveService->drives->create($requestId, $driveMetadata, array(\n 'fields' => 'id'));\n printf(\"Drive ID: %s\\n\", $drive->id);\n return $drive->id;\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n } \n\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Drive.v3.Data;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use of Drive's create drive.\n public class CreateDrive\n {\n /// <summary>\n /// Create a drive.\n /// </summary>\n /// <returns>newly created drive Id.</returns>\n public static string DriveCreateDrive()\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n var driveMetadata = new Drive()\n {\n Name = \"Project Resources\"\n };\n var requestId = Guid.NewGuid().ToString();\n var request = service.Drives.Create(driveMetadata, requestId);\n request.Fields = \"id\";\n var drive = request.Execute();\n Console.WriteLine(\"Drive ID: \" + drive.Id);\n return drive.Id;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.Drive;\nimport com.google.api.services.drive.model.DriveList;\nimport com.google.api.services.drive.model.Permission;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/* class to demonstrate use-case of Drive's shared drive without an organizer. */\npublic class RecoverDrive {\n\n /**\n * Find all shared drives without an organizer and add one.\n *\n * @param realUser User's email id.\n * @return All shared drives without an organizer.\n * @throws IOException if shared drive not found.\n */\n public static List<Drive> recoverDrives(String realUser)\n throws IOException {\n /*Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application.*/\n GoogleCredentials credentials =\n GoogleCredentials.getApplicationDefault().createScoped(Arrays.asList(DriveScopes.DRIVE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n com.google.api.services.drive.Drive service =\n new com.google.api.services.drive.Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n List<Drive> drives = new ArrayList<Drive>();\n\n // Find all shared drives without an organizer and add one.\n // Note: This example does not capture all cases. Shared drives\n // that have an empty group as the sole organizer, or an\n // organizer outside the organization are not captured. A\n // more exhaustive approach would evaluate each shared drive\n // and the associated permissions and groups to ensure an active\n // organizer is assigned.\n String pageToken = null;\n Permission newOrganizerPermission = new Permission()\n .setType(\"user\")\n .setRole(\"organizer\");\n\n newOrganizerPermission.setEmailAddress(realUser);\n\n\n do {\n DriveList result = service.drives().list()\n .setQ(\"organizerCount = 0\")\n .setFields(\"nextPageToken, drives(id, name)\")\n .setUseDomainAdminAccess(true)\n .setPageToken(pageToken)\n .execute();\n for (Drive drive : result.getDrives()) {\n System.out.printf(\"Found drive without organizer: %s (%s)\\n\",\n drive.getName(), drive.getId());\n // Note: For improved efficiency, consider batching\n // permission insert requests\n Permission permissionResult = service.permissions()\n .create(drive.getId(), newOrganizerPermission)\n .setUseDomainAdminAccess(true)\n .setSupportsAllDrives(true)\n .setFields(\"id\")\n .execute();\n System.out.printf(\"Added organizer permission: %s\\n\",\n permissionResult.getId());\n\n }\n\n drives.addAll(result.getDrives());\n\n pageToken = result.getNextPageToken();\n } while (pageToken != null);\n\n return drives;\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef recover_drives(real_user):\n \"\"\"Find all shared drives without an organizer and add one.\n Args:\n real_user:User ID for the new organizer.\n Returns:\n drives object\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n drives = []\n\n # pylint: disable=maybe-no-member\n page_token = None\n new_organizer_permission = {\n \"type\": \"user\",\n \"role\": \"organizer\",\n \"emailAddress\": \"user@example.com\",\n }\n new_organizer_permission[\"emailAddress\"] = real_user\n\n while True:\n response = (\n service.drives()\n .list(\n q=\"organizerCount = 0\",\n fields=\"nextPageToken, drives(id, name)\",\n useDomainAdminAccess=True,\n pageToken=page_token,\n )\n .execute()\n )\n for drive in response.get(\"drives\", []):\n print(\n \"Found shared drive without organizer: \"\n f\"{drive.get('title')}, {drive.get('id')}\"\n )\n permission = (\n service.permissions()\n .create(\n fileId=drive.get(\"id\"),\n body=new_organizer_permission,\n useDomainAdminAccess=True,\n supportsAllDrives=True,\n fields=\"id\",\n )\n .execute()\n )\n print(f'Added organizer permission: {permission.get(\"id\")}')\n\n drives.extend(response.get(\"drives\", []))\n page_token = response.get(\"nextPageToken\", None)\n if page_token is None:\n break\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n\n return drives\n\n\nif __name__ == \"__main__\":\n recover_drives(real_user=\"gduser1@workspacesamples.dev\")\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Finds all shared drives without an organizer and adds one.\n * @param {string} userEmail The email of the user to assign ownership to.\n * @return {Promise<object[]>} A list of the recovered drives.\n */\nasync function recoverDrives(userEmail) {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // The permission to add to the shared drive.\n const newOrganizerPermission = {\n type: 'user',\n role: 'organizer',\n emailAddress: userEmail, // e.g., 'user@example.com'\n };\n\n // List all shared drives with no organizers.\n const result = await service.drives.list({\n q: 'organizerCount = 0',\n fields: 'nextPageToken, drives(id, name)',\n useDomainAdminAccess: true,\n });\n\n // Add the new organizer to each found shared drive.\n for (const drive of result.data.drives ?? []) {\n if (!drive.id) {\n continue;\n }\n\n console.log('Found shared drive without organizer:', drive.name, drive.id);\n await service.permissions.create({\n requestBody: newOrganizerPermission,\n fileId: drive.id,\n useDomainAdminAccess: true,\n supportsAllDrives: true,\n fields: 'id',\n });\n }\n return result.data.drives ?? [];\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nuse Ramsey\\Uuid\\Uuid;\nfunction recoverDrives()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n\n $realUser = readline(\"Enter user email address: \");\n\n $drives = array();\n // Find all shared drives without an organizer and add one.\n // Note: This example does not capture all cases. Shared drives\n // that have an empty group as the sole organizer, or an\n // organizer outside the organization are not captured. A\n // more exhaustive approach would evaluate each shared drive\n // and the associated permissions and groups to ensure an active\n // organizer is assigned.\n $pageToken = null;\n $newOrganizerPermission = new Drive\\Permission(array(\n 'type' => 'user',\n 'role' => 'organizer',\n 'emailAddress' => 'user@example.com'\n ));\n $newOrganizerPermission['emailAddress'] = $realUser;\n do {\n $response = $driveService->drives->listDrives(array(\n 'q' => 'organizerCount = 0',\n 'fields' => 'nextPageToken, drives(id, name)',\n 'useDomainAdminAccess' => true,\n 'pageToken' => $pageToken\n ));\n foreach ($response->drives as $drive) {\n printf(\"Found shared drive without organizer: %s (%s)\\n\",\n $drive->name, $drive->id);\n $permission = $driveService->permissions->create($drive->id,\n $newOrganizerPermission,\n array(\n 'fields' => 'id',\n 'useDomainAdminAccess' => true,\n 'supportsAllDrives' => true\n ));\n printf(\"Added organizer permission: %s\\n\", $permission->id);\n }\n array_push($drives, $response->drives);\n $pageToken = $response->pageToken;\n } while ($pageToken != null);\n return $drives;\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Drive.v3.Data;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use-case of Drive's shared drive without an organizer.\n public class RecoverDrives\n {\n /// <summary>\n /// Find all shared drives without an organizer and add one.\n /// </summary>\n /// <param name=\"realUser\">User ID for the new organizer.</param>\n /// <returns>all shared drives without an organizer.</returns>\n public static IList<Drive> DriveRecoverDrives(string realUser)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n var drives = new List<Drive>();\n // Find all shared drives without an organizer and add one.\n // Note: This example does not capture all cases. Shared drives\n // that have an empty group as the sole organizer, or an\n // organizer outside the organization are not captured. A\n // more exhaustive approach would evaluate each shared drive\n // and the associated permissions and groups to ensure an active\n // organizer is assigned.\n string pageToken = null;\n var newOrganizerPermission = new Permission()\n {\n Type = \"user\",\n Role = \"organizer\",\n EmailAddress = realUser\n };\n\n do\n {\n var request = service.Drives.List();\n request.UseDomainAdminAccess = true;\n request.Q = \"organizerCount = 0\";\n request.Fields = \"nextPageToken, drives(id, name)\";\n request.PageToken = pageToken;\n var result = request.Execute();\n foreach (var drive in result.Drives)\n {\n Console.WriteLine((\"Found abandoned shared drive: {0} ({1})\",\n drive.Name, drive.Id));\n // Note: For improved efficiency, consider batching\n // permission insert requests\n var permissionRequest = service.Permissions.Create(\n newOrganizerPermission,\n drive.Id\n );\n permissionRequest.UseDomainAdminAccess = true;\n permissionRequest.SupportsAllDrives = true;\n permissionRequest.Fields = \"id\";\n var permissionResult = permissionRequest.Execute();\n Console.WriteLine(\"Added organizer permission: {0}\", permissionResult.Id);\n }\n\n pageToken = result.NextPageToken;\n } while (pageToken != null);\n\n return drives;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.632Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":621,"estimatedTokens":5037}}771{"id":"doc-return_specific_fields_google_drive_google_for_d-c5ef41ae","source":"documentation","title":"Return specific fields | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/fields-parameter","text":"Example:\n```text\nGET https://www.googleapis.com/drive/v3/files/FILE_ID?fields=name,starred,shared\n```\n\nExample:\n```text\n{\n \"name\": \"File1\",\n \"starred\": false,\n \"shared\": true\n }\n}\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v3/files/FILE_ID?fields=name,starred,shared,permissions(kind,type,role)\n```\n\nExample:\n```text\n{\n \"name\": \"File1\",\n \"starred\": false,\n \"shared\": true,\n \"permissions\": [\n {\n \"kind\": \"drive#permission\",\n \"type\": \"user\",\n \"role\": \"owner\"\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.632Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":37,"estimatedTokens":132}}772{"id":"doc-list_labels_on_a_file_google_drive_google_for_de-e654d705","source":"documentation","title":"List labels on a file | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/list-labels","text":"Example:\n```text\nList<Label> labelList = labelsDriveClient.files()\n .listLabels(\"FILE_ID\")\n .execute()\n .getLabels();\n```\n\nExample:\n```text\nlabel_list_response = drive_service.files().listLabels(\n fileId=\"FILE_ID\"\n).execute()\n```\n\nExample:\n```text\n/**\n * Lists all the labels on a Drive file\n * @return{obj} a list of Labels\n **/\nasync function listLabels() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n const service = google.drive({version: 'v3', auth});\n try {\n const labelListResponse = await service.files.listLabels({\n fileId: 'FILE_ID',\n });\n return labelListResponse;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.633Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":45,"estimatedTokens":239}}773{"id":"doc-resolve_errors_google_drive_google_for_developer-fd1371bd","source":"documentation","title":"Resolve errors | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/handle-errors","text":"Example:\n```text\n{\n \"error\": {\n \"code\": 400,\n \"errors\": [\n {\n \"domain\": \"global\",\n \"location\": \"orderBy\",\n \"locationType\": \"parameter\",\n \"message\": \"Sorting is not supported for queries with fullText terms. Results are always in descending relevance order.\",\n \"reason\": \"badRequest\"\n }\n ],\n \"message\": \"Sorting is not supported for queries with fullText terms. Results are always in descending relevance order.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"illegalKeepForeverModification\",\n \"message\": \"Bad Request. Cannot update a revision to false that is marked as keepForever.\"\n }\n ],\n \"code\": 400,\n \"message\": \"Bad Request. Cannot update a revision to false that is marked as keepForever.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"invalidSharingRequest\",\n \"message\": \"Bad Request. User message: \\\"Sorry, the items were successfully shared but emails could not be sent to email@domain.com.\\\"\"\n }\n ],\n \"code\": 400,\n \"message\": \"Bad Request\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"invalidSharingRequest\",\n \"message\": \"Bad Request. User message: \\\"ACL change not allowed.\\\"\"\n }\n ],\n \"code\": 400,\n \"message\": \"Bad Request\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"authError\",\n \"message\": \"Invalid Credentials\",\n \"locationType\": \"header\",\n \"location\": \"Authorization\",\n }\n ],\n \"code\": 401,\n \"message\": \"Invalid Credentials\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"fileNotDownloadable\",\n \"message\": \"Only files with binary content can be downloaded. Use Export with Docs Editors files.\"\n }\n ],\n \"code\": 403,\n \"message\": \"Only files with binary content can be downloaded. Use Export with Docs Editors files.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"activeItemCreationLimitExceeded\",\n \"message\": \"This account has exceeded the creation limit of 500 million items. To create more items, permanently delete some items.\"\n }\n ],\n \"code\": 403,\n \"message\": \"This account has exceeded the creation limit of 500 million items. To create more items, permanently delete some items.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"appNotAuthorizedToFile\",\n \"message\": \"The user has not granted the app {appId} {verb} access to the file {fileId}.\"\n }\n ],\n \"code\": 403,\n \"message\": \"The user has not granted the app {appId} {verb} access to the file {fileId}.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"cannotModifyInheritedTeamDrivePermission\",\n \"message\": \"Cannot update or delete an inherited permission on a shared drive item.\"\n }\n ],\n \"code\": 403,\n \"message\": \"Cannot update or delete an inherited permission on a shared drive item.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"usageLimits\",\n \"reason\": \"dailyLimitExceeded\",\n \"message\": \"Daily Limit Exceeded\"\n }\n ],\n \"code\": 403,\n \"message\": \"Daily Limit Exceeded\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"domainPolicy\",\n \"message\": \"The domain administrators have disabled Drive apps.\"\n }\n ],\n \"code\": 403,\n \"message\": \"The domain administrators have disabled Drive apps.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"download_restricted_for_revision\",\n \"message\": \"This revision cannot be downloaded by the authenticated user.\"\n }\n ],\n \"code\": 403,\n \"message\": \"This revision cannot be downloaded by the authenticated user.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"fileNotExportable\",\n \"message\": \"Google Vids does not support files.export. Use files.download with Vids files.\"\n }\n ],\n \"code\": 403,\n \"message\": \"Google Vids does not support files.export. Use files.download with Vids files.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"fileOwnerNotMemberOfTeamDrive\",\n \"message\": \"Cannot move a file into a shared drive as a writer when the owner of the file is not a member of that shared drive.\"\n }\n ],\n \"code\": 403,\n \"message\": \"Cannot move a file into a shared drive as a writer when the owner of the file is not a member of that shared drive.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"fileWriterTeamDriveMoveInDisabled\",\n \"message\": \"The domain administrator has not allowed writers to move items into a shared drive.\"\n }\n ],\n \"code\": 403,\n \"message\": \"The domain administrator has not allowed writers to move items into a shared drive.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"insufficientFilePermissions\",\n \"message\": \"The user does not have sufficient permissions for file {fileId}.\"\n }\n ],\n \"code\": 403,\n \"message\": \"The user does not have sufficient permissions for file {fileId}.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"myDriveHierarchyDepthLimitExceeded\",\n \"message\": \"Your My Drive can't contain more than 100 levels of folders. For details, see https://developers.google.com/workspace/drive/api/guides/handle-errors#nested-folder-levels.\"\n }\n ],\n \"code\": 403,\n \"message\": \"Your My Drive can't contain more than 100 levels of folders. For details, see https://developers.google.com/workspace/drive/api/guides/handle-errors#nested-folder-levels.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"numChildrenInNonRootLimitExceeded\",\n \"message\": \"The limit for this folder's number of children (files and folders) has been exceeded.\"\n }\n ],\n \"code\": 403,\n \"message\": \"The limit for this folder's number of children (files and folders) has been exceeded.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"usageLimits\",\n \"message\": \"Rate Limit Exceeded\",\n \"reason\": \"rateLimitExceeded\",\n }\n ],\n \"code\": 403,\n \"message\": \"Rate Limit Exceeded\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"message\": \"Rate limit exceeded. User message: \\\"These item(s) could not be shared because a rate limit was exceeded: filename\",\n \"reason\": \"sharingRateLimitExceeded\",\n }\n ],\n \"code\": 403,\n \"message\": \"Rate Limit Exceeded\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"message\": \"The user's Drive storage quota has been exceeded.\",\n \"reason\": \"storageQuotaExceeded\",\n }\n ],\n \"code\": 403,\n \"message\": \"The user's Drive storage quota has been exceeded.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"teamDriveFileLimitExceeded\",\n \"message\": \"The file limit for this shared drive has been exceeded.\"\n }\n ],\n \"code\": 403,\n \"message\": \"The file limit for this shared drive has been exceeded.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"teamDriveHierarchyTooDeep\",\n \"message\": \"The shared drive hierarchy depth will exceed the limit.\"\n }\n ],\n \"code\": 403,\n \"message\": \"The shared drive hierarchy depth will exceed the limit.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"teamDriveMembershipRequired\",\n \"message\": \"The attempted action requires shared drive membership.\"\n }\n ],\n \"code\": 403,\n \"message\": \"The attempted action requires shared drive membership.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"teamDrivesFolderMoveInNotSupported\",\n \"message\": \"Moving folders into shared drives is not supported.\"\n }\n ],\n \"code\": 403,\n \"message\": \"Moving folders into shared drives is not supported.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"teamDrivesParentLimit\",\n \"message\": \"A shared drive item must have exactly one parent.\"\n }\n ],\n \"code\": 403,\n \"message\": \"A shared drive item must have exactly one parent.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"usageLimits\",\n \"reason\": \"UrlLeaseLimitExceeded\",\n \"message\": \"Too many pending uploads for this snapshot. Please finish or cancel some before creating more.\"\n }\n ],\n \"code\": 403,\n \"message\": \"Too many pending uploads for this snapshot. Please finish or cancel some before creating more.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"usageLimits\",\n \"reason\": \"userRateLimitExceeded\",\n \"message\": \"User Rate Limit Exceeded\"\n }\n ],\n \"code\": 403,\n \"message\": \"User Rate Limit Exceeded\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"notFound\",\n \"message\": \"File not found {fileId}\"\n }\n ],\n \"code\": 404,\n \"message\": \"File not found: {fileId}\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"usageLimits\",\n \"reason\": \"rateLimitExceeded\",\n \"message\": \"Rate Limit Exceeded\"\n }\n ],\n \"code\": 429,\n \"message\": \"Rate Limit Exceeded\"s\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.635Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":30,"totalLines":515,"estimatedTokens":2537}}774{"id":"doc-return_a_label_from_a_file_resource_google_drive-454adb19","source":"documentation","title":"Return a label from a file resource | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/return-labels","text":"Example:\n```text\nFile file = driveService.files()\n .get(\"FILE_ID\")s\n .setIncludeLabels(\"LABEL_ID,LABEL_ID\")\n .setFields(\"labelInfo\")\n .execute();\n```\n\nExample:\n```text\nfile = drive_service.files().get(\n fileId=\"FILE_ID\",\n includeLabels=\"LABEL_ID,LABEL_ID\",\n fields=\"labelInfo\"\n).execute()\n```\n\nExample:\n```text\n/**\n * Get a Drive file with specific labels\n * @return{obj} file with labelInfo\n **/\nasync function getFileWithSpecificLabels() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n const service = google.drive({version: 'v3', auth});\n try {\n const file = await service.files.get({\n fileId: 'FILE_ID',\n includeLabels: 'LABEL_ID,LABEL_ID',\n fields: 'labelInfo',\n });\n return file;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.637Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":50,"estimatedTokens":270}}775{"id":"doc-guide_to_drive_api_v2_google_drive_google_for_de-64c0b602","source":"documentation","title":"Guide to Drive API v2 | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/v2-guide","text":"Example:\n```text\n{\n 'key': 'additionalID',\n 'value': 'ID',\n 'visibility': 'PRIVATE'\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.637Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":30}}776{"id":"doc-unset_a_label_field_on_a_file_google_drive_googl-0025b2c5","source":"documentation","title":"Unset a label field on a file | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/unset-label","text":"Example:\n```text\nLabelFieldModification fieldModification = new LabelFieldModification()\n .setFieldId(\"FIELD_ID\")\n .setUnsetValues(true);\n\nModifyLabelsRequest modifyLabelsRequest = new ModifyLabelsRequest()\n .setLabelModifications(ImmutableList.of(\n new LabelModification()\n .setLabelId(\"LABEL_ID\")\n .setFieldModifications(ImmutableList.of(fieldModification))));\n\nModifyLabelsResponse modifyLabelsResponse = driveService.files()\n .modifyLabels(\"FILE_ID\", modifyLabelsRequest)\n .execute();\n```\n\nExample:\n```text\nfield_modification = {\n 'fieldId': 'FIELD_ID',\n 'unsetValues': True\n}\n\nlabel_modification = {\n 'labelId': 'LABEL_ID',\n 'fieldModifications': [field_modification]\n}\n\nmodified_labels = drive_service.files().modifyLabels(\n fileId=\"FILE_ID\",\n body={'labelModifications': [label_modification]}\n).execute()\n```\n\nExample:\n```text\n/**\n * Unset a label with a field on a Drive file\n * @return{obj} updated label data\n **/\nasync function unsetLabelField() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n const service = google.drive({version: 'v3', auth});\n const fieldModification = {\n 'fieldId': 'FIELD_ID',\n 'unsetValues': true,\n };\n const labelModification = {\n 'labelId': 'LABEL_ID',\n 'fieldModifications': [fieldModification],\n };\n const labelModificationRequest = {\n 'labelModifications': [labelModification],\n };\n try {\n const updateResponse = await service.files.modifyLabels({\n fileId: 'FILE_ID',\n requestBody: labelModificationRequest,\n });\n return updateResponse;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.638Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":77,"estimatedTokens":482}}777{"id":"doc-node_js_quickstart_google_drive_google_for_devel-904d3d3d","source":"documentation","title":"Node.js quickstart | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/labels/quickstart/nodejs","text":"Example:\n```text\nnpm install googleapis@113 @google-cloud/local-auth@2.1.1 --save\n```\n\nExample:\n```text\nconst fs = require('fs');\n const readline = require('readline');\n const {google} = require('googleapis');\n\n // If modifying these scopes, delete token.json.\n const SCOPES = ['https://www.googleapis.com/auth/drive.labels.readonly'];\n // The file token.json stores the user's access and refresh tokens, and is\n // created automatically when the authorization flow completes for the first\n // time.\n const TOKEN_PATH = 'token.json';\n\n // Load client secrets from a local file.\n fs.readFile('credentials.json', (err, content) => {\n if (err) return console.log('Error loading client secret file:', err);\n // Authorize a client with credentials, then call the Google Drive Labels\n // API.\n authorize(JSON.parse(content), listDriveLabels);\n });\n\n /**\n * Create an OAuth2 client with the given credentials, and then execute the\n * given callback function.\n * @param {Object} credentials The authorization client credentials.\n * @param {function} callback The callback to call with the authorized client.\n */\n function authorize(credentials, callback) {\n const {client_secret, client_id, redirect_uris} = credentials.installed;\n const oAuth2Client = new google.auth.OAuth2(\n client_id, client_secret, redirect_uris[0]);\n\n // Check if we have previously stored a token.\n fs.readFile(TOKEN_PATH, (err, token) => {\n if (err) return getNewToken(oAuth2Client, callback);\n oAuth2Client.setCredentials(JSON.parse(token));\n callback(oAuth2Client);\n });\n }\n\n /**\n * Get and store new token after prompting for user authorization, and then\n * execute the given callback with the authorized OAuth2 client.\n * @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.\n * @param {getEventsCallback} callback The callback for the authorized client.\n */\n function getNewToken(oAuth2Client, callback) {\n const authUrl = oAuth2Client.generateAuthUrl({\n access_type: 'offline',\n scope: SCOPES,\n });\n console.log('Authorize this app by visiting this url:', authUrl);\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n rl.question('Enter the code from that page here: ', (code) => {\n rl.close();\n oAuth2Client.getToken(code, (err, token) => {\n if (err) return console.error('Error retrieving access token', err);\n oAuth2Client.setCredentials(token);\n // Store the token to disk for later program executions\n fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {\n if (err) return console.error(err);\n console.log('Token stored to', TOKEN_PATH);\n });\n callback(oAuth2Client);\n });\n });\n }\n\n function listDriveLabels(auth) {\n const service = google.drivelabels({version: 'v2', auth});\n const params = {\n 'view': 'LABEL_VIEW_FULL'\n };\n service.labels.list(params, (err, res) => {\n if (err) return console.error('The API returned an error: ' + err);\n const labels = res.data.labels;\n if (labels) {\n labels.forEach((label) => {\n const name = label.name;\n const title = label.properties.title;\n console.log(`${name}\\t${title}`);\n });\n } else {\n console.log('No Labels');\n }\n });\n }\n```\n\nExample:\n```text\nnode .\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.639Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":103,"estimatedTokens":903}}778{"id":"doc-python_quickstart_google_drive_google_for_develo-40dbf853","source":"documentation","title":"Python quickstart | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/labels/quickstart/python","text":"Example:\n```text\npip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib\n```\n\nExample:\n```text\nimport os.path\n\nfrom google.auth.transport.requests import Request\nfrom google.oauth2.credentials import Credentials\nfrom google_auth_oauthlib.flow import InstalledAppFlow\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n# If modifying these scopes, delete the file token.json.\nSCOPES = ['https://www.googleapis.com/auth/drive.labels.readonly']\n\ndef main():\n \"\"\"Shows basic usage of the Drive Labels API.\n\n Prints the first page of the customer's Labels.\n \"\"\"\n creds = None\n # The file token.json stores the user's access and refresh tokens, and is\n # created automatically when the authorization flow completes for the first\n # time.\n if os.path.exists('token.json'):\n creds = Credentials.from_authorized_user_file('token.json', SCOPES)\n # If there are no (valid) credentials available, let the user log in.\n if not creds or not creds.valid:\n if creds and creds.expired and creds.refresh_token:\n creds.refresh(Request())\n else:\n flow = InstalledAppFlow.from_client_secrets_file('credentials.json',\n SCOPES)\n creds = flow.run_local_server(port=0)\n # Save the credentials for the next run\n with open('token.json', 'w') as token:\n token.write(creds.to_json())\n try:\n service = build('drivelabels', 'v2', credentials=creds)\n response = service.labels().list(\n view='LABEL_VIEW_FULL').execute()\n labels = response['labels']\n\n if not labels:\n print('No Labels')\n else:\n for label in labels:\n name = label['name']\n title = label['properties']['title']\n print(u'{0}:\\t{1}'.format(name, title))\n except HttpError as error:\n # TODO (developer) - Handle errors from Labels API.\n print(f'An error occurred: {error}')\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython quickstart.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.640Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":67,"estimatedTokens":509}}779{"id":"doc-configure_the_google_workspace_mcp_servers_googl-7c05c060","source":"documentation","title":"Configure the Google Workspace MCP servers | Google for Developers","url":"https://developers.google.com/workspace/guides/configure-mcp-servers","text":"Example:\n```text\ngcloud services enable gmail.googleapis.com \\\ndrive.googleapis.com \\\ndocs.googleapis.com \\\nsheets.googleapis.com \\\nslides.googleapis.com \\\ncalendar-json.googleapis.com \\\nchat.googleapis.com \\\npeople.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable gmailmcp.googleapis.com \\\ndrivemcp.googleapis.com \\\ndocsmcp.googleapis.com \\\nsheetsmcp.googleapis.com \\\nslidesmcp.googleapis.com \\\ncalendarmcp.googleapis.com \\\nchatmcp.googleapis.com \\\npeople.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\n{\n \"mcpServers\": {\n \"gmail\": {\n \"serverUrl\": \"https://gmailmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n },\n \"drive\": {\n \"serverUrl\": \"https://drivemcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n },\n \"docs\": {\n \"serverUrl\": \"https://docsmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n },\n \"sheets\": {\n \"serverUrl\": \"https://sheetsmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n },\n \"slides\": {\n \"serverUrl\": \"https://slidesmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n },\n \"calendar\": {\n \"serverUrl\": \"https://calendarmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n },\n \"chat\": {\n \"serverUrl\": \"https://chatmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n },\n \"people\": {\n \"serverUrl\": \"https://people.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n }\n }\n}\n```\n\nExample:\n```text\nagy\n```\n\nExample:\n```text\n/mcp\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.643Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":99,"estimatedTokens":548}}780{"id":"doc-create_a_google_cloud_project_google_workspace_g-123117a3","source":"documentation","title":"Create a Google Cloud project | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/guides/create-project","text":"Example:\n```text\ngcloud projects create PROJECT_ID\n```\n\nExample:\n```text\ngcloud billing accounts list\n```\n\nExample:\n```text\ngcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.643Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":16,"estimatedTokens":55}}781{"id":"doc-let_ai_agents_search_across_workspace_with_the_u-eb3ad2e6","source":"documentation","title":"Let AI agents search across workspace with the Universal Search MCP Server for Workspace | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/guides/universal-search-mcp","text":"Example:\n```text\ngcloud services enable gmail.googleapis.com \\\ndrive.googleapis.com \\\ncalendar-json.googleapis.com \\\nchat.googleapis.com \\\nworkspacemcp.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\n{\n \"mcpServers\": {\n \"workspace-universal\": {\n \"serverUrl\": \"https://workspacemcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n }\n }\n}\n```\n\nExample:\n```text\nagy\n```\n\nExample:\n```text\n/mcp\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.644Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":35,"estimatedTokens":129}}782{"id":"doc-use_the_google_picker_web_component_google_drive-05d849a0","source":"documentation","title":"Use the Google Picker web component | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/picker/guides/web-component","text":"Example:\n```text\nnpm i @googleworkspace/drive-picker-element\n```\n\nExample:\n```text\n<script src=\"https://unpkg.com/@googleworkspace/drive-picker-element@latest/dist/index.iife.min.js\"></script>\n```\n\nExample:\n```text\nimport \"@googleworkspace/drive-picker-element\";\n```\n\nExample:\n```text\n<drive-picker>\n <drive-picker-docs-view></drive-picker-docs-view>\n</drive-picker>\n```\n\nExample:\n```text\n{\n \"type\": \"picker-picked\",\n \"detail\": {\n \"action\": \"PICKED\",\n \"docs\": [\n {\n \"id\": ID,\n \"mimeType\": \"application/pdf\",\n \"name\": NAME,\n \"url\": \"https://drive.google.com/file/d/ID/view?usp=drive_web\",\n \"sizeBytes\": 12345\n }\n ]\n }\n}\n```\n\nExample:\n```text\n<drive-picker\n prompt=\"PROMPT\"\n origin=\"ORIGIN\"\n app-id=\"APP_ID\"\n client-id=\"CLIENT_ID\"\n>\n <drive-picker-docs-view mime-types=\"application/pdf\"></drive-picker-docs-view>\n</drive-picker>\n```\n\nExample:\n```text\n<drive-picker\n prompt=\"PROMPT\"\n origin=\"ORIGIN\"\n app-id=\"APP_ID\"\n client-id=\"CLIENT_ID\"\n>\n <drive-picker-docs-view mime-types=\"image/jpeg,image/png,video/mp4,video/quicktime\"></drive-picker-docs-view>\n</drive-picker>\n```\n\nExample:\n```text\n<drive-picker\n prompt=\"PROMPT\"\n origin=\"ORIGIN\"\n app-id=\"APP_ID\"\n client-id=\"CLIENT_ID\"\n>\n <drive-picker-docs-view owned-by-me=\"true\"></drive-picker-docs-view>\n</drive-picker>\n```\n\nExample:\n```text\n<drive-picker\n prompt=\"PROMPT\"\n origin=\"ORIGIN\"\n app-id=\"APP_ID\"\n client-id=\"CLIENT_ID\"\n>\n <drive-picker-docs-view query=\"Untitled\"></drive-picker-docs-view>\n</drive-picker>\n```\n\nExample:\n```text\n<drive-picker\n prompt=\"PROMPT\"\n origin=\"ORIGIN\"\n app-id=\"APP_ID\"\n client-id=\"CLIENT_ID\"\n>\n <drive-picker-docs-view starred=\"true\"></drive-picker-docs-view>\n</drive-picker>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.645Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":102,"estimatedTokens":439}}783{"id":"doc-create_access_credentials_google_workspace_googl-2dd06add","source":"documentation","title":"Create access credentials | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/guides/create-credentials","text":"Example:\n```text\ngcloud iam service-accounts create SERVICE_ACCOUNT_NAME \\\n --display-name=\"SERVICE_ACCOUNT_NAME\"SERVICE_ACCOUNT_NAMESERVICE_ACCOUNT_NAME\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.646Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":7,"estimatedTokens":43}}784{"id":"doc-integrate_the_google_picker_into_web_apps_google-ce52d3c1","source":"documentation","title":"Integrate the Google Picker into web apps | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/picker/guides/web-picker","text":"Example:\n```text\n<script>\n let tokenClient;\n let accessToken = null;\n let pickerInited = false;\n let gisInited = false;\n\n // Use the API Loader script to load google.picker.\n function onApiLoad() {\n gapi.load('picker', onPickerApiLoad);\n }\n\n function onPickerApiLoad() {\n pickerInited = true;\n }\n\n function gisLoaded() {\n // Replace with your client ID and required scopes.\n tokenClient = google.accounts.oauth2.initTokenClient({\n client_id: 'CLIENT_ID',\n scope: 'SCOPES',\n callback: '', // defined later\n });\n gisInited = true;\n }\n </script>\n <!-- Load the Google API Loader script. -->\n <script async defer src=\"https://apis.google.com/js/api.js\" onload=\"onApiLoad()\"></script>\n <script async defer src=\"https://accounts.google.com/gsi/client\" onload=\"gisLoaded()\"></script>\n```\n\nExample:\n```text\n// Create and render a Google Picker object for selecting from Drive.\n function createPicker() {\n const showPicker = () => {\n // Replace with your API key and App ID.\n const picker = new google.picker.PickerBuilder()\n .addView(google.picker.ViewId.DOCS)\n .setOAuthToken(accessToken)\n .setDeveloperKey('API_KEY')\n .setCallback(pickerCallback)\n .setAppId('APP_ID')\n .build();\n picker.setVisible(true);\n }\n\n // Request an access token.\n tokenClient.callback = async (response) => {\n if (response.error !== undefined) {\n throw (response);\n }\n accessToken = response.access_token;\n showPicker();\n };\n\n if (accessToken === null) {\n // Prompt the user to select a Google Account and ask for consent to share their data\n // when establishing a new session.\n tokenClient.requestAccessToken({prompt: 'consent'});\n } else {\n // Skip display of account chooser and consent dialog for an existing session.\n tokenClient.requestAccessToken({prompt: ''});\n }\n }\n```\n\nExample:\n```text\n// A callback implementation.\n function pickerCallback(data) {\n let url = 'nothing';\n if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {\n const doc = data[google.picker.Response.DOCUMENTS][0];\n url = doc[google.picker.Document.URL];\n }\n const message = `You picked: ${url}`;\n document.getElementById('result').textContent = message;\n }\n```\n\nExample:\n```text\nconst picker = new google.picker.PickerBuilder()\n .addViewGroup(\n new google.picker.ViewGroup(google.picker.ViewId.DOCS)\n .addView(google.picker.ViewId.DOCUMENTS)\n .addView(google.picker.ViewId.PRESENTATIONS))\n .setOAuthToken(oauthToken)\n .setDeveloperKey(developerKey)\n .setAppId(cloudProjectNumber)\n .setCallback(pickerCallback)\n .build();\n```\n\nExample:\n```text\nconst picker = new google.picker.PickerBuilder()\n .addView(google.picker.ViewId.SPREADSHEETS)\n .enableFeature(google.picker.Feature.NAV_HIDDEN)\n .setDeveloperKey(developerKey)\n .setCallback(pickerCallback)\n .build();\n```\n\nExample:\n```text\n// Create a Google Picker builder.\n const builder = new google.picker.PickerBuilder()\n .setDeveloperKey('API_KEY')\n .setAppId('APP_ID')\n .setOAuthToken(accessToken)\n .addView(google.picker.ViewId.DOCS)\n .setCallback(pickerCallback);\n\n // Create an iframe and use .toUri as the source.\n const pickerContainer = document.getElementById('IFRAME_CONTAINER_ID');\n const iframe = document.createElement('iframe');\n\n iframe.setAttribute(\"src\", builder.toUri().toString());\n iframe.style.width = \"100%\";\n iframe.style.height = \"600px\";\n iframe.style.border = \"none\";\n\n // Attach it to your page.\n pickerContainer.appendChild(iframe);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.647Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":130,"estimatedTokens":989}}785{"id":"doc-enable_google_workspace_apis_google_for_develope-87cb642c","source":"documentation","title":"Enable Google Workspace APIs | Google for Developers","url":"https://developers.google.com/workspace/guides/enable-apis","text":"Example:\n```text\ngcloud services enable API_SERVICE_ID\n```\n\nExample:\n```text\ngcloud services enable admin.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable alertcenter.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable script.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable caldav.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable calendar-json.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable chat.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable classroom.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable cloudidentity.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable cloudsearch.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable docs.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable drive.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable driveactivity.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable drivelabels.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable forms.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable gmail.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable groupsmigration.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable groupssettings.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable gsuiteaddons.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable keep.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable licensing.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable appsmarket.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable appsmarket-component.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable meet.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable people.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable gmailpostmastertools.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable reseller.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable sheets.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable slides.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable tasks.googleapis.com\n```\n\nExample:\n```text\ngcloud services enable vault.googleapis.com\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.647Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":31,"totalLines":156,"estimatedTokens":542}}786{"id":"doc-troubleshoot_authentication_and_authorization_is-c76c990f","source":"documentation","title":"Troubleshoot authentication and authorization issues | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/labels/troubleshoot-authentication-authorization","text":"Example:\n```text\npip show six | grep \"Location:\" | cut -d \" \" -f2\n```\n\nExample:\n```text\nexport PYTHONPATH=$PYTHONPATH:INSTALL_PATH\n```\n\nExample:\n```text\nsource ~/.bashrc\n```\n\nExample:\n```text\npip install --upgrade httplib2\n```\n\nExample:\n```text\nCannot uninstall 'six'. It is a distutils installed project and thus we\ncannot accurately determine which files belong to it which would lead to\nonly a partial uninstall.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.648Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":28,"estimatedTokens":109}}787{"id":"doc-create_and_publish_a_label_google_drive_google_f-79832376","source":"documentation","title":"Create and publish a label | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/labels/guides/create-label","text":"Example:\n```text\nlabel_body = {\n 'labelType': 'ADMIN',\n 'properties': {\n 'title': 'TITLE'\n },\n 'fields': [{\n 'properties': {\n 'displayName': 'DISPLAY_NAME'\n },\n 'selectionOptions': {\n 'listOptions': {},\n 'choices': [{\n 'properties': {\n 'displayName': 'CHOICE_1'\n }\n }, {\n 'properties': {\n 'displayName': 'CHOICE_2'\n }\n }]\n }\n }]\n}\nresponse = service.labels().create(\n body=label_body, useAdminAccess=True).execute()\n```\n\nExample:\n```text\nvar label = {\n 'labelType': 'ADMIN',\n 'properties': {\n 'title': 'TITLE'\n },\n 'fields': [{\n 'properties': {\n 'displayName': 'DISPLAY_NAME'\n },\n 'selectionOptions': {\n 'listOptions': {},\n 'choices': [{\n 'properties': {\n 'displayName': 'CHOICE_1'\n }\n }, {\n 'properties': {\n 'displayName': 'CHOICE_2'\n }\n }]\n }\n }]\n};\n\nservice.labels.create({\n requestBody: label,\n useAdminAccess: true\n}, (err, res) => {\n if (err) return console.error('The API returned an error: ' + err);\n console.log(res);\n});\n```\n\nExample:\n```text\nservice.labels().publish(\n name='labels/ID',\n body={\n 'useAdminAccess': True\n }\n).execute()\n```\n\nExample:\n```text\nservice.labels.publish({\n name: 'labels/ID',\n requestBody: {\n useAdminAccess: true\n }\n}, (err, res) => {\n if (err) return console.error('The API returned an error: ' + err);\n console.log(res);\n});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.649Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":88,"estimatedTokens":402}}788{"id":"doc-integrate_the_google_picker_into_desktop_and_mob-bdad14ea","source":"documentation","title":"Integrate the Google Picker into desktop and mobile apps | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/picker/guides/desktop-mobile-picker","text":"Example:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth? \\\nclient_id=CLIENT_ID \\\n&scope=https://www.googleapis.com/auth/drive.file \\\n&redirect_uri=REDIRECT_URI \\\n&response_type=code \\\n&access_type=offline \\\n&prompt=consent \\\n&trigger_onepick=true\n```\n\nExample:\n```text\nhttps://REDIRECT_URI?picked_file_ids=PICKED_FILE_IDS&code=CODE&scope=SCOPES\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.650Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":18,"estimatedTokens":92}}789{"id":"doc-disable_enable_and_delete_a_label_google_drive_g-f87d6bac","source":"documentation","title":"Disable, enable, and delete a label | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/labels/guides/disable-delete-label","text":"Example:\n```text\nservice.labels().disable(\n name='labels/ID',\n body={\n 'useAdminAccess': True\n }\n).execute()\n```\n\nExample:\n```text\nservice.labels.disable({\n name: 'labels/ID',\n requestBody: {\n useAdminAccess: true\n }\n}, (err, res) => {\n if (err) return console.error('The API returned an error: ' + err);\n console.log(res);\n});\n```\n\nExample:\n```text\nservice.labels().enable(\n name='labels/ID',\n body={\n 'useAdminAccess': True\n }\n).execute()\n```\n\nExample:\n```text\nservice.labels.enable({\n name: 'labels/ID',\n requestBody: {\n useAdminAccess: true\n }\n}, (err, res) => {\n if (err) return console.error('The API returned an error: ' + err);\n console.log(res);\n});\n```\n\nExample:\n```text\nresponse = service.labels().delete(\n name='labels/ID',\n useAdminAccess=True\n).execute()\n```\n\nExample:\n```text\nservice.labels.delete({\n name: 'labels/ID',\n useAdminAccess: true\n}, (err, res) => {\n if (err) return console.error('The API returned an error: ' + err);\n console.log(res);\n});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.650Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":66,"estimatedTokens":261}}790{"id":"doc-update_a_label_google_drive_google_for_developer-a7ad2b12","source":"documentation","title":"Update a label | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/labels/guides/update-label","text":"Example:\n```text\nbody = {\n 'useAdminAccess': True,\n 'requests': [\n {\n 'updateLabel': {\n 'properties': {\n 'title': 'TITLE'\n },\n 'updateMask': 'title'\n }\n },\n {\n 'createField': {\n 'field': {\n 'properties': {\n 'displayName': 'DISPLAY_NAME'\n },\n 'textOptions': {}\n }\n }\n }\n ],\n 'view': 'LABEL_VIEW_FULL'\n}\n\nresponse = service.labels().delta(\n body=body,\n name='labels/ID'\n).execute()\n```\n\nExample:\n```text\nvar body = {\n 'useAdminAccess': true,\n 'requests': [\n {\n 'updateLabel': {\n 'properties': {\n 'title': 'TITLE'\n },\n 'updateMask': 'title'\n }\n },\n {\n 'createField': {\n 'field': {\n 'properties': {\n 'displayName': 'DISPLAY_NAME'\n },\n 'textOptions': {}\n }\n }\n }\n ],\n 'view': 'LABEL_VIEW_FULL'\n};\n\nservice.labels.delta({\n name: 'labels/ID',\n requestBody: body\n}, (err, res) => {\n if (err) return console.error('The API returned an error: ' + err);\n console.log(res);\n});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.652Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":70,"estimatedTokens":318}}791{"id":"doc-use_google_picker_api_features_in_web_apps_googl-b05705f3","source":"documentation","title":"Use Google Picker API features in web apps | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/picker/guides/web-picker-sample","text":"Example:\n```text\n<!DOCTYPE html>\n<html>\n<head>\n <title>Google Picker API Quickstart</title>\n <meta charset=\"utf-8\" />\n</head>\n<body>\n<p>Google Picker API Quickstart</p>\n\n<!--Add buttons to initiate auth sequence and sign out.-->\n<button id=\"authorize_button\" onclick=\"handleAuthClick()\">Authorize</button>\n<button id=\"signout_button\" onclick=\"handleSignoutClick()\">Sign Out</button>\n\n<pre id=\"content\" style=\"white-space: pre-wrap;\"></pre>\n```\n\nExample:\n```text\n<script type=\"text/javascript\">\n /* exported gapiLoaded */\n /* exported gisLoaded */\n /* exported handleAuthClick */\n /* exported handleSignoutClick */\n\n // Authorization scopes required by the API; multiple scopes can be\n // included, separated by spaces.\n const SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly';\n\n // Replace with your client ID and API key from https://console.cloud.google.com/.\n const CLIENT_ID = 'CLIENT_ID';\n const API_KEY = 'API_KEY';\n\n // Replace with your project number from https://console.cloud.google.com/.\n const APP_ID = 'APP_ID';\n\n let tokenClient;\n let accessToken = null;\n let pickerInited = false;\n let gisInited = false;\n\n document.getElementById('authorize_button').style.visibility = 'hidden';\n document.getElementById('signout_button').style.visibility = 'hidden';\n\n /**\n * Callback after api.js is loaded.\n */\n function gapiLoaded() {\n gapi.load('client:picker', initializePicker);\n }\n\n /**\n * Callback after the API client is loaded. Loads the\n * discovery doc to initialize the API.\n */\n async function initializePicker() {\n await gapi.client.load('https://www.googleapis.com/discovery/v1/apis/drive/v3/rest');\n pickerInited = true;\n maybeEnableButtons();\n }\n\n /**\n * Callback after Google Identity Services are loaded.\n */\n function gisLoaded() {\n tokenClient = google.accounts.oauth2.initTokenClient({\n client_id: CLIENT_ID,\n scope: SCOPES,\n callback: '', // defined later\n });\n gisInited = true;\n maybeEnableButtons();\n }\n\n /**\n * Enables user interaction after all libraries are loaded.\n */\n function maybeEnableButtons() {\n if (pickerInited && gisInited) {\n document.getElementById('authorize_button').style.visibility = 'visible';\n }\n }\n\n /**\n * Sign in the user upon button click.\n */\n function handleAuthClick() {\n tokenClient.callback = async (response) => {\n if (response.error !== undefined) {\n throw (response);\n }\n accessToken = response.access_token;\n document.getElementById('signout_button').style.visibility = 'visible';\n document.getElementById('authorize_button').innerText = 'Refresh';\n await createPicker();\n };\n\n if (accessToken === null) {\n // Prompt the user to select a Google Account and ask for consent to share their data\n // when establishing a new session.\n tokenClient.requestAccessToken({prompt: 'consent'});\n } else {\n // Skip display of account chooser and consent dialog for an existing session.\n tokenClient.requestAccessToken({prompt: ''});\n }\n }\n\n /**\n * Sign out the user upon button click.\n */\n function handleSignoutClick() {\n if (accessToken) {\n google.accounts.oauth2.revoke(accessToken);\n accessToken = null;\n document.getElementById('content').innerText = '';\n document.getElementById('authorize_button').innerText = 'Authorize';\n document.getElementById('signout_button').style.visibility = 'hidden';\n }\n }\n\n /**\n * Create and render a Google Picker object for searching images.\n */\n function createPicker() {\n const view = new google.picker.View(google.picker.ViewId.DOCS);\n view.setMimeTypes('image/png,image/jpeg,image/jpg');\n const picker = new google.picker.PickerBuilder()\n .enableFeature(google.picker.Feature.NAV_HIDDEN)\n .enableFeature(google.picker.Feature.MULTISELECT_ENABLED)\n .setDeveloperKey(API_KEY)\n .setAppId(APP_ID)\n .setOAuthToken(accessToken)\n .addView(view)\n .addView(new google.picker.DocsUploadView())\n .setCallback(pickerCallback)\n .build();\n picker.setVisible(true);\n }\n\n /**\n * Displays the file details of the user's selection.\n * @param {object} data - Contains the user selection from the Google Picker.\n */\n async function pickerCallback(data) {\n if (data.action === google.picker.Action.PICKED) {\n let text = `Google Picker response: \\n${JSON.stringify(data, null, 2)}\\n`;\n const selectedDoc = data[google.picker.Response.DOCUMENTS][0];\n const fileId = selectedDoc[google.picker.Document.ID];\n console.log(fileId);\n const res = await gapi.client.drive.files.get({\n 'fileId': fileId,\n 'fields': '*',\n });\n text += `Drive API response for first document: \\n${JSON.stringify(res.result, null, 2)}\\n`;\n window.document.getElementById('content').innerText = text;\n }\n }\n</script>\n<script async defer src=\"https://apis.google.com/js/api.js\" onload=\"gapiLoaded()\"></script>\n<script async defer src=\"https://accounts.google.com/gsi/client\" onload=\"gisLoaded()\"></script>\n```\n\nExample:\n```text\n</body>\n</html>\n```\n\nExample:\n```text\n/**\n * Create and render a Google Picker object for searching images.\n */\nfunction createPicker() {\n // Define what types of files the Picker should show (e.g., images)\n const view = new google.picker.View(google.picker.ViewId.DOCS);\n view.setMimeTypes('image/png,image/jpeg,image/jpg');\n\n // Build and display the picker.\n const picker = new google.picker.PickerBuilder()\n .enableFeature(google.picker.Feature.NAV_HIDDEN)\n .enableFeature(google.picker.Feature.MULTISELECT_ENABLED)\n .setDeveloperKey('API_KEY')\n .setAppId('APP_ID')\n .setOAuthToken('ACCESS_TOKEN')\n .addView(view)\n .addView(new google.picker.DocsUploadView()) // Adds an upload tab\n .setCallback(pickerCallback)\n .build();\n\n picker.setVisible(true);\n}\n\n/**\n * Displays the file details of the user's selection.\n * @param {object} data - Contains the user selection from the Google Picker.\n */\nasync function pickerCallback(data) {\n if (data.action === google.picker.Action.PICKED) {\n let text = `Google Picker response: \\n${JSON.stringify(data, null, 2)}\\n`;\n\n // Extract the ID of the first selected document.\n const selectedDoc = data[google.picker.Response.DOCUMENTS][0];\n const fileId = selectedDoc[google.picker.Document.ID];\n console.log(\"Selected File ID:\", fileId);\n\n // Optional: Fetch metadata using the Drive API based on the selected file ID.\n const res = await gapi.client.drive.files.get({\n 'fileId': fileId,\n 'fields': '*',\n });\n\n text += `Drive API response for first document: \\n${JSON.stringify(res.result, null, 2)}\\n`;\n // Update your UI with the results\n console.log(text);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.653Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":221,"estimatedTokens":1720}}792{"id":"doc-method_privilegedprivatekeydecrypt_google_worksp-a8684a3a","source":"documentation","title":"Method: privilegedprivatekeydecrypt | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/privileged-private-key-decrypt","text":"Example:\n```text\n{\n \"authentication\": string,\n \"algorithm\": string,\n \"encrypted_data_encryption_key\": string,\n \"rsa_oaep_label\": string,\n \"reason\": string,\n \"spki_hash\": string,\n \"spki_hash_algorithm\": string,\n \"wrapped_private_key\": string\n}\n```\n\nExample:\n```text\n{\n \"data_encryption_key\": string\n}\n```\n\nExample:\n```text\nPOST https://mykacls.example.org/v1/privilegedprivatekeydecrypt\n\n{\n \"wrapped_private_key\": \"wHrlNOTI9mU6PBdqiq7EQA...\",\n \"encrypted_data_encryption_key\": \"dGVzdCB3cmFwcGVkIGRlaw...\",\n \"authentication\": \"eyJhbGciOi...\",\n \"spki_hash\": \"LItGzrmjSFD57QdrY1dcLwYmSwBXzhQLAA6zVcen+r0=\",\n \"spki_hash_algorithm\": \"SHA-256\",\n \"algorithm\": \"RSA/ECB/PKCS1Padding\",\n \"reason\": \"admin decrypt\"\n}\n```\n\nExample:\n```text\n{\n \"data_encryption_key\": \"akRQtv3nr+jUhcFL6JmKzB+WzUxbkkMyW5kQsqGUAFc\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.653Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":44,"estimatedTokens":209}}793{"id":"doc-method_privilegedunwrap_google_workspace_google_-0adf517b","source":"documentation","title":"Method: privilegedunwrap | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/privileged-unwrap","text":"Example:\n```text\n{\n \"authentication\": string,\n \"reason\": string,\n \"resource_name\": string,\n \"wrapped_key\": string\n}\n```\n\nExample:\n```text\n{\n \"key\": string\n}\n```\n\nExample:\n```text\nPOST https://mykacls.example.com/v1/takeout_unwrap\n\n{\n \"wrapped_key\": \"7qTh6Mp+svVwYPlnZMyuj8WHTrM59wl/UI50jo61Qt/QubZ9tfsUc1sD62xdg3zgxC9quV4r+y7AkbfIDhbmxGqP64pWbZgFzOkP0JcSn+1xm/CB2E5IknKsAbwbYREGpiHM3nzZu+eLnvlfbzvTnJuJwBpLoPYQcnPvcgm+5gU1j1BjUaNKS/uDn7VbVm7hjbKA3wkniORC2TU2MiHElutnfrEVZ8wQfrCEpuWkOXs98H8QxUK4pBM2ea1xxGj7vREAZZg1x/Ci/E77gHxymnZ/ekhUIih6Pwu75jf+dvKcMnpmdLpwAVlE1G4dNginhFVyV/199llf9jmHasQQuaMFzQ9UMWGjA1Hg2KsaD9e3EL74A5fLkKc2EEmBD5v/aP+1RRZ3ISbTOXvxqYIFCdSFSCfPbUhkc9I2nHS0obEH7Q7KiuagoDqV0cTNXWfCGJ1DtIlGQ9IA6mPDAjX8Lg==\",\n \"authentication\": \"eyJhbGciOi…\"\n \"reason\": \"{client:'takeout' op:'read'}\"\n \"resource_name\": \"item123\"\n}\n```\n\nExample:\n```text\n{\n \"key\": \"0saNxttLMQULfXuTbRFJzi/QJokN1jW16u0yaNvvLdQ=\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.654Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":37,"estimatedTokens":236}}794{"id":"doc-method_privatekeysign_google_workspace_google_fo-db19fa61","source":"documentation","title":"Method: privatekeysign | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/private-key-sign","text":"Example:\n```text\n{\n \"authentication\": string,\n \"authorization\": string,\n \"algorithm\": string,\n \"digest\": string,\n \"rsa_pss_salt_length\": integer,\n \"reason\": string,\n \"wrapped_private_key\": string\n}\n```\n\nExample:\n```text\n{\n \"signature\": string\n}\n```\n\nExample:\n```text\n{\n \"wrapped_private_key\": \"wHrlNOTI9mU6PBdqiq7EQA...\",\n \"digest\": \"EOBc7nc+7JdIDeb0DVTHriBAbo/dfHFZJgeUhOyo67o=\",\n \"authorization\": \"eyJhbGciOi...\",\n \"authentication\": \"eyJhbGciOi...\",\n \"algorithm\": \"SHA256withRSA\",\n \"reason\": \"sign\"\n}\n```\n\nExample:\n```text\n{\n \"signature\": \"LpyCSy5ddy82PIp/87JKaMF4Jmt1KdrbfT1iqpB7uhVd3OwZiu+oq8kxIzB7Lr0iX4aOcxM6HiUyMrGP2PG8x0HkpykbUKQxBVcfm6SLdsqigT9ho5RYw20M6ZXNWVRetFSleKex4SRilTRny38e2ju/lUy0KDaCt1hDUT89nLZ1wsO3D1F3xk8J7clXv5fe7GPRd1ojo82Ny0iyVO7y7h1lh2PACHUFXOMzsdURYFCnxhKAsadccCxpCxKh5x8p78PdoenwY1tnT3/X4O/4LAGfT4fo98Frxy/xtI49WDRNZi6fsL6BQT4vS/WFkybBX9tXaenCqlRBDyZSFhatPQ==\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.655Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":40,"estimatedTokens":231}}795{"id":"doc-method_privatekeydecrypt_google_workspace_google-727a8686","source":"documentation","title":"Method: privatekeydecrypt | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/private-key-decrypt","text":"Example:\n```text\n{\n \"authentication\": string,\n \"authorization\": string,\n \"algorithm\": string,\n \"encrypted_data_encryption_key\": string,\n \"rsa_oaep_label\": string,\n \"reason\": string,\n \"wrapped_private_key\": string\n}\n```\n\nExample:\n```text\n{\n \"data_encryption_key\": string\n}\n```\n\nExample:\n```text\nPOST https://mykacls.example.org/v1/privatekeydecrypt\n\n{\n \"wrapped_private_key\": \"wHrlNOTI9mU6PBdqiq7EQA...\",\n \"encrypted_data_encryption_key\": \"dGVzdCB3cmFwcGVkIGRlaw...\",\n \"authorization\": \"eyJhbGciOi...\",\n \"authentication\": \"eyJhbGciOi...\",\n \"algorithm\": \"RSA/ECB/PKCS1Padding\",\n \"reason\": \"decrypt\"\n}\n```\n\nExample:\n```text\n{\n \"data_encryption_key\": \"akRQtv3nr+jUhcFL6JmKzB+WzUxbkkMyW5kQsqGUAFc=\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.657Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":42,"estimatedTokens":182}}796{"id":"doc-configure_security_for_google_workspace_mcp_serv-8f52b1b3","source":"documentation","title":"Configure security for Google Workspace MCP servers | Google for Developers","url":"https://developers.google.com/workspace/guides/configure-mcp-security","text":"Example:\n```text\ngcloud init\n```\n\nExample:\n```text\ngcloud config set api_endpoint_overrides/modelarmor \"https://modelarmor.LOCATION.rep.googleapis.com/\"\n```\n\nExample:\n```text\ngcloud model-armor floorsettings update \\\n--full-uri='projects/PROJECT_ID/locations/global/floorSetting' \\\n--enable-floor-setting-enforcement=TRUE \\\n--add-integrated-services=GOOGLE_MCP_SERVER \\\n--google-mcp-server-enforcement-type=INSPECT_AND_BLOCK \\\n--enable-google-mcp-server-cloud-logging \\\n--malicious-uri-filter-settings-enforcement=ENABLED \\\n--add-rai-settings-filters='[{\"confidenceLevel\": \"MEDIUM_AND_ABOVE\", \"filterType\": \"DANGEROUS\"}]'\n```\n\nExample:\n```text\ngcloud model-armor floorsettings update \\\n --full-uri='projects/PROJECT_ID/locations/global/floorSetting' \\\n --remove-integrated-services=GOOGLE_MCP_SERVER\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.658Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":30,"estimatedTokens":205}}797{"id":"doc-method_rewrap_google_workspace_google_for_develo-a6f100b2","source":"documentation","title":"Method: rewrap | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/rewrap","text":"Example:\n```text\n{\n \"authorization\": string,\n \"original_kacls_url\": string,\n \"reason\": string,\n \"wrapped_key\": string\n}\n```\n\nExample:\n```text\n{\n \"resource_key_hash\": string,\n \"wrapped_key\": string\n}\n```\n\nExample:\n```text\nPOST https://mykacls.example.com/v1/rewrap\n\n{\n \"wrapped_key\": \"7qTh6Mp+svVwYPlnZMyuj8WHTrM59wl/UI50jo61Qt/QubZ9tfsUc1sD62xdg3zgxC9quV4r+y7AkbfIDhbmxGqP64pWbZgFzOkP0JcSn+1xm/CB2E5IknKsAbwbYREGpiHM3nzZu+eLnvlfbzvTnJuJwBpLoPYQcnPvcgm+5gU1j1BjUaNKS/uDn7VbVm7hjbKA3wkniORC2TU2MiHElutnfrEVZ8wQfrCEpuWkOXs98H8QxUK4pBM2ea1xxGj7vREAZZg1x/Ci/E77gHxymnZ/ekhUIih6Pwu75jf+dvKcMnpmdLpwAVlE1G4dNginhFVyV/199llf9jmHasQQuaMFzQ9UMWGjA1Hg2KsaD9e3EL74A5fLkKc2EEmBD5v/aP+1RRZ3ISbTOXvxqYIFCdSFSCfPbUhkc9I2nHS0obEH7Q7KiuagoDqV0cTNXWfCGJ1DtIlGQ9IA6mPDAjX8Lg==\",\n \"authorization\": \"eyJhbGciOi...\",\n \"original_kacls_url\": \"https://original.example.com/kacls/v1\",\n \"reason\": \"{client:'drive' op:'read'}\"\n}\n```\n\nExample:\n```text\n{\n \"wrapped_key\": \"3qTh6Mp+svPwYPlnZMyuj8WHTrM59wl/UI50jo61Qt/QubZ9tfsUc1sD62xdg3zgxC9quV4r+y7AkbfIDhbmxGqP64pWbZgFzOkP0JcSn+1xm/CB2E5IknKsAbwbYREGpiHM3nzZu+eLnvlfbzvTnJuJwBpLoPYQcnPvcgm+5gU1j1BjUaNKS/uDn7VbVm7hjbKA3wkniORC2TU2MiHElutnfrEVZ8wQfrCEpuWkOXs98H8QxUK4pBM2ea1xxGj7vREAZZg1x/Ci/E77gHxymnZ/ekhUIih6Pwu75jf+dvKcMnpmdLpwAVlE1G4dNginhFVyV/199llf9jmHasQQuaMFzQ9UMWGjA1Hg2KsaD9e3EL74A5fLkKc2EEmBD5v/aP+1RRZ3ISbTOXvxqYIFCdSFSCfPbUhkc9I2nHS0obEH7Q7KiuagoDqV0cTNXWfCGJ1DtIlGQ9IA6mPDAjX8Lg==\",\n \"resource_key_hash\": \"SXOyPekBAUI95zuZSuJzsBlK4nO5SuJK4nNCPem5SuI=\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.658Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":39,"estimatedTokens":381}}798{"id":"doc-build_a_google_workspace_add_on_with_apps_script-9c98d2be","source":"documentation","title":"Build a Google Workspace add-on with Apps Script | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/apps-script/add-ons/cats-quickstart","text":"Example:\n```text\n/**\n * This simple Google Workspace add-on shows a random image of a cat in the\n * sidebar. When opened manually (the homepage card), some static text is\n * overlayed on the image, but when contextual cards are opened a new cat image\n * is shown with the text taken from that context (such as a message's subject\n * line) overlaying the image. There is also a button that updates the card with\n * a new random cat image.\n *\n * Click \"File > Make a copy...\" to copy the script, and \"Publish > Deploy from\n * manifest > Install add-on\" to install it.\n */\n\n/**\n * The maximum number of characters that can fit in the cat image.\n */\nvar MAX_MESSAGE_LENGTH = 40;\n\n/**\n * Callback for rendering the homepage card.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction onHomepage(e) {\n console.log(e);\n var hour = Number(Utilities.formatDate(new Date(), e.userTimezone.id, 'H'));\n var message;\n if (hour >= 6 && hour < 12) {\n message = 'Good morning';\n } else if (hour >= 12 && hour < 18) {\n message = 'Good afternoon';\n } else {\n message = 'Good night';\n }\n message += ' ' + e.hostApp;\n return createCatCard(message, true);\n}\n\n/**\n * Creates a card with an image of a cat, overlayed with the text.\n * @param {String} text The text to overlay on the image.\n * @param {Boolean} isHomepage True if the card created here is a homepage;\n * false otherwise. Defaults to false.\n * @return {CardService.Card} The assembled card.\n */\nfunction createCatCard(text, isHomepage) {\n // Explicitly set the value of isHomepage as false if null or undefined.\n if (!isHomepage) {\n isHomepage = false;\n }\n\n // Use the \"Cat as a service\" API to get the cat image. Add a \"time\" URL\n // parameter to act as a cache buster.\n var now = new Date();\n // Replace forward slashes in the text, as they break the CataaS API.\n var caption = text.replace(/\\//g, ' ');\n var imageUrl =\n Utilities.formatString('https://cataas.com/cat/says/%s?time=%s',\n encodeURIComponent(caption), now.getTime());\n var image = CardService.newImage()\n .setImageUrl(imageUrl)\n .setAltText('Meow')\n\n // Create a button that changes the cat image when pressed.\n // Note: Action parameter keys and values must be strings.\n var action = CardService.newAction()\n .setFunctionName('onChangeCat')\n .setParameters({text: text, isHomepage: isHomepage.toString()});\n var button = CardService.newTextButton()\n .setText('Change cat')\n .setOnClickAction(action)\n .setTextButtonStyle(CardService.TextButtonStyle.FILLED);\n var buttonSet = CardService.newButtonSet()\n .addButton(button);\n\n // Create a footer to be shown at the bottom.\n var footer = CardService.newFixedFooter()\n .setPrimaryButton(CardService.newTextButton()\n .setText('Powered by cataas.com')\n .setOpenLink(CardService.newOpenLink()\n .setUrl('https://cataas.com')));\n\n // Assemble the widgets and return the card.\n var section = CardService.newCardSection()\n .addWidget(image)\n .addWidget(buttonSet);\n var card = CardService.newCardBuilder()\n .addSection(section)\n .setFixedFooter(footer);\n\n if (!isHomepage) {\n // Create the header shown when the card is minimized,\n // but only when this card is a contextual card. Peek headers\n // are never used by non-contexual cards like homepages.\n var peekHeader = CardService.newCardHeader()\n .setTitle('Contextual Cat')\n .setImageUrl('https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png')\n .setSubtitle(text);\n card.setPeekCardHeader(peekHeader)\n }\n\n return card.build();\n}\n\n/**\n * Callback for the \"Change cat\" button.\n * @param {Object} e The event object, documented {@link\n * https://developers.google.com/gmail/add-ons/concepts/actions#action_event_objects\n * here}.\n * @return {CardService.ActionResponse} The action response to apply.\n */\nfunction onChangeCat(e) {\n console.log(e);\n // Get the text that was shown in the current cat image. This was passed as a\n // parameter on the Action set for the button.\n var text = e.parameters.text;\n\n // The isHomepage parameter is passed as a string, so convert to a Boolean.\n var isHomepage = e.parameters.isHomepage === 'true';\n\n // Create a new card with the same text.\n var card = createCatCard(text, isHomepage);\n\n // Create an action response that instructs the add-on to replace\n // the current card with the new one.\n var navigation = CardService.newNavigation()\n .updateCard(card);\n var actionResponse = CardService.newActionResponseBuilder()\n .setNavigation(navigation);\n return actionResponse.build();\n}\n\n/**\n * Truncate a message to fit in the cat image.\n * @param {string} message The message to truncate.\n * @return {string} The truncated message.\n */\nfunction truncate(message) {\n if (message.length > MAX_MESSAGE_LENGTH) {\n message = message.slice(0, MAX_MESSAGE_LENGTH);\n message = message.slice(0, message.lastIndexOf(' ')) + '...';\n }\n return message;\n}\n```\n\nExample:\n```text\n/**\n * Callback for rendering the card for a specific Gmail message.\n * @param {Object} e The event object.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction onGmailMessage(e) {\n console.log(e);\n // Get the ID of the message the user has open.\n var messageId = e.gmail.messageId;\n\n // Get an access token scoped to the current message and use it for GmailApp\n // calls.\n var accessToken = e.gmail.accessToken;\n GmailApp.setCurrentMessageAccessToken(accessToken);\n\n // Get the subject of the email.\n var message = GmailApp.getMessageById(messageId);\n var subject = message.getThread().getFirstMessageSubject();\n\n // Remove labels and prefixes.\n subject = subject\n .replace(/^([rR][eE]|[fF][wW][dD])\\:\\s*/, '')\n .replace(/^\\[.*?\\]\\s*/, '');\n\n // If neccessary, truncate the subject to fit in the image.\n subject = truncate(subject);\n\n return createCatCard(subject);\n}\n\n/**\n * Callback for rendering the card for the compose action dialog.\n * @param {Object} e The event object.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction onGmailCompose(e) {\n console.log(e);\n var header = CardService.newCardHeader()\n .setTitle('Insert cat')\n .setSubtitle('Add a custom cat image to your email message.');\n // Create text input for entering the cat's message.\n var input = CardService.newTextInput()\n .setFieldName('text')\n .setTitle('Caption')\n .setHint('What do you want the cat to say?');\n // Create a button that inserts the cat image when pressed.\n var action = CardService.newAction()\n .setFunctionName('onGmailInsertCat');\n var button = CardService.newTextButton()\n .setText('Insert cat')\n .setOnClickAction(action)\n .setTextButtonStyle(CardService.TextButtonStyle.FILLED);\n var buttonSet = CardService.newButtonSet()\n .addButton(button);\n // Assemble the widgets and return the card.\n var section = CardService.newCardSection()\n .addWidget(input)\n .addWidget(buttonSet);\n var card = CardService.newCardBuilder()\n .setHeader(header)\n .addSection(section);\n return card.build();\n}\n\n/**\n * Callback for inserting a cat into the Gmail draft.\n * @param {Object} e The event object.\n * @return {CardService.UpdateDraftActionResponse} The draft update response.\n */\nfunction onGmailInsertCat(e) {\n console.log(e);\n // Get the text that was entered by the user.\n var text = e.formInput.text;\n // Use the \"Cat as a service\" API to get the cat image. Add a \"time\" URL\n // parameter to act as a cache buster.\n var now = new Date();\n var imageUrl = 'https://cataas.com/cat';\n if (text) {\n // Replace forward slashes in the text, as they break the CataaS API.\n var caption = text.replace(/\\//g, ' ');\n imageUrl += Utilities.formatString('/says/%s?time=%s',\n encodeURIComponent(caption), now.getTime());\n }\n var imageHtmlContent = '<img style=\"display: block; max-height: 300px;\" src=\"'\n + imageUrl + '\"/>';\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftBodyAction(CardService.newUpdateDraftBodyAction()\n .addUpdateContent(imageHtmlContent,CardService.ContentType.MUTABLE_HTML)\n .setUpdateType(CardService.UpdateDraftBodyType.IN_PLACE_INSERT))\n .build();\n return response;\n}\n```\n\nExample:\n```text\n/**\n * Callback for rendering the card for a specific Calendar event.\n * @param {Object} e The event object.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction onCalendarEventOpen(e) {\n console.log(e);\n var calendar = CalendarApp.getCalendarById(e.calendar.calendarId);\n // The event metadata doesn't include the event's title, so using the\n // calendar.readonly scope and fetching the event by it's ID.\n var event = calendar.getEventById(e.calendar.id);\n if (!event) {\n // This is a new event still being created.\n return createCatCard('A new event! Am I invited?');\n }\n var title = event.getTitle();\n // If necessary, truncate the title to fit in the image.\n title = truncate(title);\n return createCatCard(title);\n}\n```\n\nExample:\n```text\n/**\n * Callback for rendering the card for specific Drive items.\n * @param {Object} e The event object.\n * @return {CardService.Card} The card to show to the user.\n */\nfunction onDriveItemsSelected(e) {\n console.log(e);\n var items = e.drive.selectedItems;\n // Include at most 5 items in the text.\n items = items.slice(0, 5);\n var text = items.map(function(item) {\n var title = item.title;\n // If neccessary, truncate the title to fit in the image.\n title = truncate(title);\n return title;\n }).join('\\n');\n return createCatCard(text);\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/New_York\",\n \"dependencies\": {\n },\n \"exceptionLogging\": \"STACKDRIVER\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/calendar.addons.execute\",\n \"https://www.googleapis.com/auth/calendar.readonly\",\n \"https://www.googleapis.com/auth/drive.addons.metadata.readonly\",\n \"https://www.googleapis.com/auth/gmail.addons.current.action.compose\",\n \"https://www.googleapis.com/auth/gmail.addons.current.message.readonly\",\n \"https://www.googleapis.com/auth/gmail.addons.execute\",\n \"https://www.googleapis.com/auth/script.locale\"],\n \"runtimeVersion\": \"V8\",\n \"addOns\": {\n \"common\": {\n \"name\": \"Cats\",\n \"logoUrl\": \"https://www.gstatic.com/images/icons/material/system/1x/pets_black_48dp.png\",\n \"useLocaleFromApp\": true,\n \"homepageTrigger\": {\n \"runFunction\": \"onHomepage\",\n \"enabled\": true\n },\n \"universalActions\": [{\n \"label\": \"Learn more about Cataas\",\n \"openLink\": \"https://cataas.com\"\n }]\n },\n \"gmail\": {\n \"contextualTriggers\": [{\n \"unconditional\": {\n },\n \"onTriggerFunction\": \"onGmailMessage\"\n }],\n \"composeTrigger\": {\n \"selectActions\": [{\n \"text\": \"Insert cat\",\n \"runFunction\": \"onGmailCompose\"\n }],\n \"draftAccess\": \"NONE\"\n }\n },\n \"drive\": {\n \"onItemsSelectedTrigger\": {\n \"runFunction\": \"onDriveItemsSelected\"\n }\n },\n \"calendar\": {\n \"eventOpenTrigger\": {\n \"runFunction\": \"onCalendarEventOpen\"\n }\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.660Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":347,"estimatedTokens":2831}}799{"id":"doc-method_privilegedwrap_google_workspace_google_fo-f6ce39c0","source":"documentation","title":"Method: privilegedwrap | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/privileged-wrap","text":"Example:\n```text\n{\n \"authentication\": string,\n \"key\": string,\n \"perimeter_id\": string,\n \"reason\": string,\n \"resource_name\": string\n}\n```\n\nExample:\n```text\n{\n \"wrapped_key\": string\n}\n```\n\nExample:\n```text\nPOST https://mykacls.example.com/v1/privilegedwrap\n\n{\n \"key\":\"wHrlNOTI9mU6PBdqiq7EQA==\",\n \"resource_name\": \"wdwqd…\",\n \"authentication\": \"eyJhbGciOi…\",\n \"reason\": \"admin import\"\n}\n```\n\nExample:\n```text\n{\n \"wrapped_key\": \"3qTh6Mp+svPwYPlnZMyuj8WHTrM59wl/UI50jo61Qt/QubZ9tfsUc1sD62xdg3zgxC9quV4r+y7AkbfIDhbmxGqP64pWbZgFzOkP0JcSn+1xm/CB2E5IknKsAbwbYREGpiHM3nzZu+eLnvlfbzvTnJuJwBpLoPYQcnPvcgm+5gU1j1BjUaNKS/uDn7VbVm7hjbKA3wkniORC2TU2MiHElutnfrEVZ8wQfrCEpuWkOXs98H8QxUK4pBM2ea1xxGj7vREAZZg1x/Ci/E77gHxymnZ/ekhUIih6Pwu75jf+dvKcMnpmdLpwAVlE1G4dNginhFVyV/199llf9jmHasQQuaMFzQ9UMWGjA1Hg2KsaD9e3EL74A5fLkKc2EEmBD5v/aP+1RRZ3ISbTOXvxqYIFCdSFSCfPbUhkc9I2nHS0obEH7Q7KiuagoDqV0cTNXWfCGJ1DtIlGQ9IA6mPDAjX8Lg==\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.660Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":38,"estimatedTokens":234}}800{"id":"doc-manage_client_side_encrypted_files_with_the_driv-bbf7fead","source":"documentation","title":"Manage client-side encrypted files with the Drive API | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/guides/handle-cse-files","text":"Example:\n```text\n+-------------------+\n| Magic header |\n+-------------------+\n| Encrypted Chunk 1 |\n+-------------------+\n| Encrypted Chunk 2 |\n+-------------------+\n| ... |\n+-------------------+\n| Encrypted Chunk N |\n+-------------------+\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.661Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":16,"estimatedTokens":69}}801{"id":"doc-method_delegate_google_workspace_google_for_deve-1db6c876","source":"documentation","title":"Method: delegate | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/delegate","text":"Example:\n```text\n{\n \"authentication\": string,\n \"authorization\": string,\n \"reason\": string\n}\n```\n\nExample:\n```text\n{\n \"delegated_authentication\": string\n}\n```\n\nExample:\n```text\nPOST https://mykacls.example.com/v1/delegate\n{\n \"authentication\": \"eyJhbGciOi...\",\n \"authorization\": \"eyJhbGciOi...delegated_to\\\":\\\"other_entity_id\\\",\\\"resource_name\\\":\\\"meeting_id\\\"...}\",\n \"reason\": \"{client:'meet' op:'delegate_access'}\"\n}\n```\n\nExample:\n```text\n{\n \"delegated_authentication\": \"eyJhbGciOi...delegated_to_from_authz_token...resource_name_from_authz_token...}\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.662Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":34,"estimatedTokens":145}}802{"id":"doc-method_unwrap_google_workspace_google_for_develo-17ecbf51","source":"documentation","title":"Method: unwrap | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/unwrap","text":"Example:\n```text\n{\n \"authentication\": string,\n \"authorization\": string,\n \"reason\": string,\n \"wrapped_key\": string\n}\n```\n\nExample:\n```text\n{\n \"key\": string\n}\n```\n\nExample:\n```text\nPOST https://mykacls.example.com/v1/unwrap\n\n{\n \"wrapped_key\": \"7qTh6Mp+svVwYPlnZMyuj8WHTrM59wl/UI50jo61Qt/QubZ9tfsUc1sD62xdg3zgxC9quV4r+y7AkbfIDhbmxGqP64pWbZgFzOkP0JcSn+1xm/CB2E5IknKsAbwbYREGpiHM3nzZu+eLnvlfbzvTnJuJwBpLoPYQcnPvcgm+5gU1j1BjUaNKS/uDn7VbVm7hjbKA3wkniORC2TU2MiHElutnfrEVZ8wQfrCEpuWkOXs98H8QxUK4pBM2ea1xxGj7vREAZZg1x/Ci/E77gHxymnZ/ekhUIih6Pwu75jf+dvKcMnpmdLpwAVlE1G4dNginhFVyV/199llf9jmHasQQuaMFzQ9UMWGjA1Hg2KsaD9e3EL74A5fLkKc2EEmBD5v/aP+1RRZ3ISbTOXvxqYIFCdSFSCfPbUhkc9I2nHS0obEH7Q7KiuagoDqV0cTNXWfCGJ1DtIlGQ9IA6mPDAjX8Lg==\",\n \"authorization\": \"eyJhbGciOi…\"\n \"authentication\": \"eyJhbGciOi…\"\n \"reason\": \"{client:'drive' op:'read'}\"\n}\n```\n\nExample:\n```text\n{\n \"key\": \"0saNxttLMQULfXuTbRFJzi/QJokN1jW16u0yaNvvLdQ=\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.662Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":37,"estimatedTokens":235}}803{"id":"doc-method_digest_google_workspace_google_for_develo-1c8f1234","source":"documentation","title":"Method: digest | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/digest","text":"Example:\n```text\n{\n \"authorization\": string,\n \"reason\": string,\n \"wrapped_key\": string\n}\n```\n\nExample:\n```text\n{\n \"resource_key_hash\": string\n}\n```\n\nExample:\n```text\nPOST https://mykacls.example.com/v1/digest\n\n{\n \"wrapped_key\": \"7qTh6Mp+svVwYPlnZMyuj8WHTrM59wl/UI50jo61Qt/QubZ9tfsUc1sD62xdg3zgxC9quV4r+y7AkbfIDhbmxGqP64pWbZgFzOkP0JcSn+1xm/CB2E5IknKsAbwbYREGpiHM3nzZu+eLnvlfbzvTnJuJwBpLoPYQcnPvcgm+5gU1j1BjUaNKS/uDn7VbVm7hjbKA3wkniORC2TU2MiHElutnfrEVZ8wQfrCEpuWkOXs98H8QxUK4pBM2ea1xxGj7vREAZZg1x/Ci/E77gHxymnZ/ekhUIih6Pwu75jf+dvKcMnpmdLpwAVlE1G4dNginhFVyV/199llf9jmHasQQuaMFzQ9UMWGjA1Hg2KsaD9e3EL74A5fLkKc2EEmBD5v/aP+1RRZ3ISbTOXvxqYIFCdSFSCfPbUhkc9I2nHS0obEH7Q7KiuagoDqV0cTNXWfCGJ1DtIlGQ9IA6mPDAjX8Lg==\",\n \"authorization\": \"eyJhbGciOi...\",\n \"reason\": \"{client:'drive' op:'read'}\"\n}\n```\n\nExample:\n```text\n{\n \"resource_key_hash\": \"qClT153ghqBOLPpdMsc4S4n6okPrRaLPBYT0zRcn+go=\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.663Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":35,"estimatedTokens":227}}804{"id":"doc-method_wrap_google_workspace_google_for_develope-48741434","source":"documentation","title":"Method: wrap | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/wrap","text":"Example:\n```text\n{\n \"authentication\": string,\n \"authorization\": string,\n \"key\": string,\n \"reason\": string\n}\n```\n\nExample:\n```text\n{\n \"wrapped_key\": string\n}\n```\n\nExample:\n```text\nPOST https://mykacls.example.com/v1/wrap\n\n{\n \"key\":\"wHrlNOTI9mU6PBdqiq7EQA==\",\n \"authorization\": \"eyJhbGciOi…\"\n \"authentication\": \"eyJhbGciOi…\"\n \"reason\": \"{client:'drive' op:'update'}\"\n}\n```\n\nExample:\n```text\n{\n \"wrapped_key\": \"3qTh6Mp+svPwYPlnZMyuj8WHTrM59wl/UI50jo61Qt/QubZ9tfsUc1sD62xdg3zgxC9quV4r+y7AkbfIDhbmxGqP64pWbZgFzOkP0JcSn+1xm/CB2E5IknKsAbwbYREGpiHM3nzZu+eLnvlfbzvTnJuJwBpLoPYQcnPvcgm+5gU1j1BjUaNKS/uDn7VbVm7hjbKA3wkniORC2TU2MiHElutnfrEVZ8wQfrCEpuWkOXs98H8QxUK4pBM2ea1xxGj7vREAZZg1x/Ci/E77gHxymnZ/ekhUIih6Pwu75jf+dvKcMnpmdLpwAVlE1G4dNginhFVyV/199llf9jmHasQQuaMFzQ9UMWGjA1Hg2KsaD9e3EL74A5fLkKc2EEmBD5v/aP+1RRZ3ISbTOXvxqYIFCdSFSCfPbUhkc9I2nHS0obEH7Q7KiuagoDqV0cTNXWfCGJ1DtIlGQ9IA6mPDAjX8Lg==\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.664Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":37,"estimatedTokens":230}}805{"id":"doc-node_js_quickstart_google_drive_google_for_devel-5ff57842","source":"documentation","title":"Node.js quickstart | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/quickstart/nodejs","text":"Example:\n```text\nnpm install googleapis@105 @google-cloud/local-auth@2.1.0 --save\n```\n\nExample:\n```text\nimport path from 'node:path';\nimport process from 'node:process';\nimport {authenticate} from '@google-cloud/local-auth';\nimport {google} from 'googleapis';\n\n// The scope for reading file metadata.\nconst SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'];\n// The path to the credentials file.\nconst CREDENTIALS_PATH = path.join(process.cwd(), 'credentials.json');\n\n/**\n * Lists the names and IDs of up to 10 files.\n */\nasync function listFiles() {\n // Authenticate with Google and get an authorized client.\n const auth = await authenticate({\n scopes: SCOPES,\n keyfilePath: CREDENTIALS_PATH,\n });\n\n // Create a new Drive API client.\n const drive = google.drive({version: 'v3', auth});\n // Get the list of files.\n const result = await drive.files.list({\n pageSize: 10,\n fields: 'nextPageToken, files(id, name)',\n });\n const files = result.data.files;\n if (!files || files.length === 0) {\n console.log('No files found.');\n return;\n }\n\n console.log('Files:');\n // Print the name and ID of each file.\n files.forEach((file) => {\n console.log(`${file.name} (${file.id})`);\n });\n}\n\nawait listFiles();\n```\n\nExample:\n```text\nnode .\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.664Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":56,"estimatedTokens":324}}806{"id":"doc-upload_file_data_google_drive_google_for_develop-94480af0","source":"documentation","title":"Upload file data | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/manage-uploads","text":"Example:\n```text\n/**\n * Uploads a file without metadata.\n *\n * @param {Blob|File} file The file to upload.\n * @param {string} accessToken A valid OAuth 2.0 access token.\n * @return {Promise<Object>} The uploaded file metadata.\n */\nasync function uploadFileSimple(file, accessToken) {\n const response = await fetch(\n 'https://www.googleapis.com/upload/drive/v3/files?uploadType=media',\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': file.type,\n },\n body: file,\n }\n );\n if (!response.ok) {\n throw new Error(`Upload failed: ${response.statusText}`);\n }\n return response.json();\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.FileContent;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\n\n/* Class to demonstrate use of Drive insert file API */\npublic class UploadBasic {\n\n /**\n * Upload new file.\n *\n * @return Inserted file metadata if successful, {@code null} otherwise.\n * @throws IOException if service account credentials file not found.\n */\n public static String uploadBasic() throws IOException {\n // Load pre-authorized user credentials from the environment.\n // TODO(developer) - See https://developers.google.com/identity for\n // guides on implementing OAuth2 for your application.\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n // Upload file photo.jpg on drive.\n File fileMetadata = new File();\n fileMetadata.setName(\"photo.jpg\");\n // File's content.\n java.io.File filePath = new java.io.File(\"files/photo.jpg\");\n // Specify media type and file-path for file.\n FileContent mediaContent = new FileContent(\"image/jpeg\", filePath);\n try {\n File file = service.files().create(fileMetadata, mediaContent)\n .setFields(\"id\")\n .execute();\n System.out.println(\"File ID: \" + file.getId());\n return file.getId();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to upload file: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\nfrom googleapiclient.http import MediaFileUpload\n\n\ndef upload_basic():\n \"\"\"Insert new file.\n Returns : Id's of the file uploaded\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n file_metadata = {\"name\": \"download.jpeg\"}\n media = MediaFileUpload(\"download.jpeg\", mimetype=\"image/jpeg\")\n # pylint: disable=maybe-no-member\n file = (\n service.files()\n .create(body=file_metadata, media_body=media, fields=\"id\")\n .execute()\n )\n print(f'File ID: {file.get(\"id\")}')\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n file = None\n\n return file.get(\"id\")\n\n\nif __name__ == \"__main__\":\n upload_basic()\n```\n\nExample:\n```text\nimport fs from 'node:fs';\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Uploads a file to Google Drive.\n * @return {Promise<string|null|undefined>} The ID of the uploaded file.\n */\nasync function uploadBasic() {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // The request body for the file to be uploaded.\n const requestBody = {\n name: 'photo.jpg',\n fields: 'id',\n };\n\n // The media content to be uploaded.\n const media = {\n mimeType: 'image/jpeg',\n body: fs.createReadStream('files/photo.jpg'),\n };\n\n // Upload the file.\n const file = await service.files.create({\n requestBody,\n media,\n });\n\n // Print the ID of the uploaded file.\n console.log('File Id:', file.data.id);\n return file.data.id;\n}\n```\n\nExample:\n```text\n/**\n * Uploads a file along with its metadata.\n *\n * @param {Blob|File} file The file to upload.\n * @param {string} accessToken A valid OAuth 2.0 access token.\n * @return {Promise<Object>} The uploaded file metadata.\n */\nasync function uploadFileMultipart(file, accessToken) {\n const metadata = {\n name: file.name,\n };\n\n const boundary = 'foo_bar_baz';\n const delimiter = `\\r\\n--${boundary}\\r\\n`;\n const closeDelimiter = `\\r\\n--${boundary}--`;\n\n const requestBody = new Blob([\n `--${boundary}\\r\\n`,\n 'Content-Type: application/json; charset=UTF-8\\r\\n\\r\\n',\n JSON.stringify(metadata),\n delimiter,\n `Content-Type: ${file.type}\\r\\n\\r\\n`,\n file,\n closeDelimiter,\n ]);\n\n const response = await fetch(\n 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart',\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': `multipart/related; boundary=${boundary}`,\n },\n body: requestBody,\n }\n );\n if (!response.ok) {\n throw new Error(`Upload failed: ${response.statusText}`);\n }\n return response.json();\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\n# TODO - PHP client currently chokes on fetching start page token\nfunction uploadBasic()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $fileMetadata = new Drive\\DriveFile(array(\n 'name' => 'photo.jpg'));\n $content = file_get_contents('../files/photo.jpg');\n $file = $driveService->files->create($fileMetadata, array(\n 'data' => $content,\n 'mimeType' => 'image/jpeg',\n 'uploadType' => 'multipart',\n 'fields' => 'id'));\n printf(\"File ID: %s\\n\", $file->id);\n return $file->id;\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n } \n\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use of Drive insert file API\n public class UploadBasic\n {\n /// <summary>\n /// Upload new file.\n /// </summary>\n /// <param name=\"filePath\">Image path to upload.</param>\n /// <returns>Inserted file metadata if successful, null otherwise.</returns>\n public static string DriveUploadBasic(string filePath)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n // Upload file photo.jpg on drive.\n var fileMetadata = new Google.Apis.Drive.v3.Data.File()\n {\n Name = \"photo.jpg\"\n };\n FilesResource.CreateMediaUpload request;\n // Create a new file on drive.\n using (var stream = new FileStream(filePath,\n FileMode.Open))\n {\n // Create a new file, with metadata and stream.\n request = service.Files.Create(\n fileMetadata, stream, \"image/jpeg\");\n request.Fields = \"id\";\n request.Upload();\n }\n\n var file = request.ResponseBody;\n // Prints the uploaded file id.\n Console.WriteLine(\"File ID: \" + file.Id);\n return file.Id;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is FileNotFoundException)\n {\n Console.WriteLine(\"File not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\nHTTP/1.1 200 OK\nLocation: https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&upload_id=xa298sd_sdlkj2\nContent-Length: 0\n```\n\nExample:\n```text\n/**\n * Initiates a resumable upload session and returns the session URI.\n *\n * @param {Blob|File} file The file to upload.\n * @param {string} accessToken A valid OAuth 2.0 access token.\n * @return {Promise<string>} The resumable session URI.\n */\nasync function initiateResumableUpload(file, accessToken) {\n const metadata = {\n name: file.name,\n mimeType: file.type,\n };\n\n const response = await fetch(\n 'https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable',\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${accessToken}`,\n 'Content-Type': 'application/json; charset=UTF-8',\n 'X-Upload-Content-Type': file.type,\n 'X-Upload-Content-Length': file.size,\n },\n body: JSON.stringify(metadata),\n }\n );\n if (!response.ok) {\n throw new Error(`Failed to initiate upload: ${response.statusText}`);\n }\n return response.headers.get('Location');\n}\n```\n\nExample:\n```text\n/**\n * Uploads the entire file in a single request using the session URI.\n *\n * @param {string} sessionUrl The resumable session URI.\n * @param {Blob|File} file The file to upload.\n * @return {Promise<Object>} The uploaded file metadata.\n */\nasync function uploadFileSingleRequest(sessionUrl, file) {\n const response = await fetch(sessionUrl, {\n method: 'PUT',\n headers: {\n 'Content-Length': file.size,\n },\n body: file,\n });\n if (!response.ok) {\n throw new Error(`Upload failed: ${response.status} ${response.statusText}`);\n }\n return response.json();\n}\n```\n\nExample:\n```text\n/**\n * Uploads a file in chunks of a specified size.\n *\n * @param {string} sessionUrl The resumable session URI.\n * @param {Blob|File} file The file to upload.\n * @param {number} chunkSize Chunk size in bytes (must be a multiple of 256 KB).\n * @return {Promise<Object>} The uploaded file metadata.\n */\nasync function uploadFileChunked(sessionUrl, file, chunkSize = 1024 * 1024) {\n let start = 0;\n while (start < file.size) {\n const end = Math.min(start + chunkSize, file.size);\n const chunk = file.slice(start, end);\n const chunkLength = end - start;\n\n const response = await fetch(sessionUrl, {\n method: 'PUT',\n headers: {\n 'Content-Length': chunkLength,\n 'Content-Range': `bytes ${start}-${end - 1}/${file.size}`,\n },\n body: chunk,\n });\n\n if (response.status === 308) {\n // 308 Resume Incomplete indicates chunk was received successfully.\n start = end;\n } else if (response.ok) {\n // 200 OK or 201 Created indicates the upload is fully complete.\n return response.json();\n } else {\n throw new Error(`Upload failed: ${response.status} ${response.statusText}`);\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Resumes an interrupted upload by querying the status and uploading remaining bytes.\n *\n * @param {string} sessionUrl The resumable session URI.\n * @param {Blob|File} file The file being uploaded.\n * @return {Promise<Object>} The uploaded file metadata.\n */\nasync function resumeUpload(sessionUrl, file) {\n // 1. Query the upload status by sending an empty PUT request\n const statusResponse = await fetch(sessionUrl, {\n method: 'PUT',\n headers: {\n 'Content-Range': `bytes */${file.size}`,\n },\n });\n\n if (statusResponse.ok) {\n // Already completed\n return statusResponse.json();\n }\n\n if (statusResponse.status !== 308) {\n throw new Error(`Failed to query upload status: ${statusResponse.statusText}`);\n }\n\n // 2. Parse the Range header to determine received bytes\n const rangeHeader = statusResponse.headers.get('Range');\n let startOffset = 0;\n if (rangeHeader) {\n const parts = rangeHeader.split('-');\n startOffset = parseInt(parts[1], 10) + 1;\n }\n\n // 3. Upload the remaining content of the file\n const remainingChunk = file.slice(startOffset);\n const response = await fetch(sessionUrl, {\n method: 'PUT',\n headers: {\n 'Content-Length': remainingChunk.size,\n 'Content-Range': `bytes ${startOffset}-${file.size - 1}/${file.size}`,\n },\n body: remainingChunk,\n });\n\n if (!response.ok) {\n throw new Error(`Resume upload failed: ${response.statusText}`);\n }\n return response.json();\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.FileContent;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\n\n/* Class to demonstrate Drive's upload with conversion use-case. */\npublic class UploadWithConversion {\n\n /**\n * Upload file with conversion.\n *\n * @return Inserted file id if successful, {@code null} otherwise.\n * @throws IOException if service account credentials file not found.\n */\n public static String uploadWithConversion() throws IOException {\n // Load pre-authorized user credentials from the environment.\n // TODO(developer) - See https://developers.google.com/identity for\n // guides on implementing OAuth2 for your application.\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n\n // File's metadata.\n File fileMetadata = new File();\n fileMetadata.setName(\"My Report\");\n fileMetadata.setMimeType(\"application/vnd.google-apps.spreadsheet\");\n\n java.io.File filePath = new java.io.File(\"files/report.csv\");\n FileContent mediaContent = new FileContent(\"text/csv\", filePath);\n try {\n File file = service.files().create(fileMetadata, mediaContent)\n .setFields(\"id\")\n .execute();\n System.out.println(\"File ID: \" + file.getId());\n return file.getId();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to move file: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\nfrom googleapiclient.http import MediaFileUpload\n\n\ndef upload_with_conversion():\n \"\"\"Upload file with conversion\n Returns: ID of the file uploaded\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n file_metadata = {\n \"name\": \"My Report\",\n \"mimeType\": \"application/vnd.google-apps.spreadsheet\",\n }\n media = MediaFileUpload(\"report.csv\", mimetype=\"text/csv\", resumable=True)\n # pylint: disable=maybe-no-member\n file = (\n service.files()\n .create(body=file_metadata, media_body=media, fields=\"id\")\n .execute()\n )\n print(f'File with ID: \"{file.get(\"id\")}\" has been uploaded.')\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n file = None\n\n return file.get(\"id\")\n\n\nif __name__ == \"__main__\":\n upload_with_conversion()\n```\n\nExample:\n```text\nimport fs from 'node:fs';\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Uploads a file to Google Drive and converts it to a Google Sheet.\n * @return {Promise<string|null|undefined>} The ID of the uploaded file.\n */\nasync function uploadWithConversion() {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // The metadata for the file to be uploaded and converted.\n const fileMetadata = {\n name: 'My Report',\n // The MIME type to convert the file to.\n mimeType: 'application/vnd.google-apps.spreadsheet',\n };\n\n // The media content to be uploaded.\n const media = {\n mimeType: 'text/csv',\n body: fs.createReadStream('files/report.csv'),\n };\n\n // Upload the file with conversion.\n const file = await service.files.create({\n requestBody: fileMetadata,\n media,\n fields: 'id',\n });\n\n // Print the ID of the uploaded file.\n console.log('File Id:', file.data.id);\n return file.data.id;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nfunction uploadWithConversion()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $fileMetadata = new Drive\\DriveFile(array(\n 'name' => 'My Report',\n 'mimeType' => 'application/vnd.google-apps.spreadsheet'));\n $content = file_get_contents('../files/report.csv');\n $file = $driveService->files->create($fileMetadata, array(\n 'data' => $content,\n 'mimeType' => 'text/csv',\n 'uploadType' => 'multipart',\n 'fields' => 'id'));\n printf(\"File ID: %s\\n\", $file->id);\n return $file->id;\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate Drive's upload with conversion use-case.\n public class UploadWithConversion\n {\n /// <summary>\n /// Upload file with conversion.\n /// </summary>\n /// <param name=\"filePath\">Id of the spreadsheet file.</param>\n /// <returns>Inserted file id if successful, null otherwise.</returns>\n public static string DriveUploadWithConversion(string filePath)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n // Upload file My Report on drive.\n var fileMetadata = new Google.Apis.Drive.v3.Data.File()\n {\n Name = \"My Report\",\n MimeType = \"application/vnd.google-apps.spreadsheet\"\n };\n FilesResource.CreateMediaUpload request;\n // Create a new drive.\n using (var stream = new FileStream(filePath,\n FileMode.Open))\n {\n // Create a new file, with metadata and stream.\n request = service.Files.Create(\n fileMetadata, stream, \"text/csv\");\n request.Fields = \"id\";\n request.Upload();\n }\n\n var file = request.ResponseBody;\n // Prints the uploaded file id.\n Console.WriteLine(\"File ID: \" + file.Id);\n return file.Id;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is FileNotFoundException)\n {\n Console.WriteLine(\"File not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.667Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":17,"totalLines":750,"estimatedTokens":5663}}807{"id":"doc-manage_matters_google_vault_google_for_developer-f379eb98","source":"documentation","title":"Manage matters | Google Vault | Google for Developers","url":"https://developers.google.com/workspace/vault/guides/matters","text":"Example:\n```text\nMatter matter = new Matter();\nmatter.setName(\"Matter Name\");\nmatter.setDescription(\"Matter Description\");\nMatter createdMatter = client.matters().create(matter).execute();\n```\n\nExample:\n```text\ndef create_matter(service):\n matter_content = {\n 'name': 'Matter Name',\n 'description': 'Matter Description',\n }\n matter = service.matters().create(body=matter_content).execute()\n return matter\n```\n\nExample:\n```text\nclient.matters().get(matterId).execute(); // Returns BASIC view.\nclient.matters().get(matterId).setView(\"BASIC\").execute();\nclient.matters().get(matterId).setView(\"FULL\").execute();\n```\n\nExample:\n```text\nmatter_id = getMatterId()\nservice.matters().get(matterId=matter_id).execute(); // Returns BASIC view.\nservice.matters().get(matterId=matter_id, view='BASIC').execute();\nservice.matters().get(matterId=matter_id, view='FULL').execute();\n```\n\nExample:\n```text\nList mattersList = client.matters().list().execute().getMatters();\n```\n\nExample:\n```text\nmattersList = service.matters().list().execute()\n```\n\nExample:\n```text\nListMattersResponse firstPageResponse = client.matters().list().setPageSize(20).execute();\n\nString nextPageToken = firstPageResponse.getNextPageToken();\nif (nextPageToken != null) {\n client.matters().list().setPageToken(nextPageToken).setPageSize(20).execute();\n}\n```\n\nExample:\n```text\nlist_response1 = service.matters().list(\n view='FULL', pageSize=10).execute()\nfor matter in list_response1['matters']:\n print(matter)\n\nif 'nextPageToken' in list_response1:\n list_response2 = service.matters().list(\n pageSize=10, pageToken=list_response1['nextPageToken']).execute()\n for matter in list_response2['matters']:\n print(matter)\n```\n\nExample:\n```text\n// Only get open matters.\nList openMattersList = client.matters().list().setState(\"OPEN\")\n .execute().getMatters();\n\n// Only get closed matters.\nList closedMattersList = client.matters().list().setState(\"CLOSED\")\n .execute().getMatters();\n\n// Only get deleted matters.\nList deletedMattersList = client.matters().list().setState(\"DELETED\")\n .execute().getMatters();\n```\n\nExample:\n```text\n# Only get open matters.\nopenMattersList = client.matters().list(\n state='OPEN').execute()\n\n# Only get closed matters.\nclosedMattersList = client.matters().list(\n state='CLOSED').execute()\n\n# Only get deleted matters.\ndeletedMattersList = client.matters().list(\n state='DELETED').execute()\n```\n\nExample:\n```text\nString matterId = \"matterId\";\nMatter matter = new Matter().setName(\"New Name\")\n .setDescription(\"New Description\");\nclient.matters().update(matterId, matter).execute();\n```\n\nExample:\n```text\ndef update_matter(service, matter_id):\n wanted_matter = {\n 'name': 'New Matter Name',\n 'description': 'New Description'\n }\n updated_matter = service.matters().update(\n matterId=matter_id, body=wanted_matter).execute()\n return updated_matter\n```\n\nExample:\n```text\nString matterId = \"matterId\";\n// If the matter still has holds, this operation will fail.\nclient.matters().close(matterId, new CloseMatterRequest()).execute();\n```\n\nExample:\n```text\ndef close_matter(service, matter_id):\n close_response = service.matters().close(\n matterId=matter_id, body={}).execute()\n return close_response['matter']\n```\n\nExample:\n```text\nMatter matter = client.matters().get(matterId).execute();\n\n// Delete the matter.\nclient.matters().delete(matter.getMatterId());\n// Undelete the matter.\nclient.matters().undelete(matter.getMatterId(), new UndeleteRequest());\n// Reopen the matter.\nclient.matters().reopen(matter.getMatterId(), new ReopenMatterRequest());\n```\n\nExample:\n```text\ndef reopen_matter(service, matter_id):\n reopen_response = service.matters().reopen(\n matterId=matter_id, body={}).execute()\n return reopen_response['matter']\n\ndef delete_matter(service, matter_id):\n service.matters().delete(matterId=matter_id).execute()\n return get_matter(matter_id)\n\ndef undelete_matter(service, matter_id):\n undeleted_matter = service.matters().undelete(\n matterId=matter_id, body={}).execute()\n return undeleted_matter\n```\n\nExample:\n```text\nString matterId = \"Matter Id\";\nString accountId = \"Account Id\";\n\n// List permissions for a matter.\nMatter matter = client.matters().get(matterId).setView(\"FULL\").execute();\nList matterPermissions = matter.getMatterPermissions();\n\n// Add a user to the permission set.\nclient\n .matters()\n .addPermissions(matterId)\n .setMatterPermissionAccountId(accountId)\n .setMatterPermissionRole(\"COLLABORATOR\")\n .execute();\n\n// Remove a user from the permission set.\nclient\n .matters()\n .removePermissions(matterId)\n .setAccountId(accountId)\n .execute();\n```\n\nExample:\n```text\ndef list_matter_permission(service, matter_id):\n matter = service.matters().get(matterId=matter_id, view='FULL').execute()\n return matter['matterPermissions']\n\ndef add_matter_permission(service, matter_id, account_id):\n permission = service.matters().addPermissions(\n matterId=matter_id,\n matterPermission_accountId=account_id,\n matterPermission_role='COLLABORATOR',\n sendEmails='False',\n ccMe='False').execute()\n return permission\n\ndef remove_matter_permission(service, matter_id, account_id):\n service.matters().removePermissions(\n matterId=matter_id, accountId=account_id).execute()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.669Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":208,"estimatedTokens":1351}}808{"id":"doc-homepages_google_workspace_add_ons_google_for_de-9f137570","source":"documentation","title":"Homepages | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/concepts/homepages","text":"Example:\n```text\n{\n \"addOns\": {\n \"common\": {\n \"homepageTrigger\": {\n \"runFunction\": \"myFunction\",\n \"enabled\": true\n }\n }\n }\n}\n```\n\nExample:\n```text\n{\n ...\n \"addOns\": {\n ...\n \"common\": {\n \"homepageTrigger\": { \"runFunction\": \"buildHomePage\" }\n },\n \"calendar\": {\n \"homepageTrigger\": { \"runFunction\": \"buildCalendarHomepage\" }\n },\n \"drive\": {\n \"homepageTrigger\": { \"runFunction\": \"buildDriveHomepage\" }\n },\n \"gmail\": {\n \"homepageTrigger\": { \"enabled\": false }\n },\n ...\n }\n}\n```\n\nExample:\n```text\n{\n \"addOns\": {\n \"common\": {},\n \"calendar\": {\n \"homepageTrigger\": { \"runFunction\": \"myCalendarFunction\" }\n },\n \"drive\": {\n \"homepageTrigger\": { \"runFunction\": \"myDriveFunction\" }\n },\n \"gmail\": {},\n ...\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.674Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":55,"estimatedTokens":208}}809{"id":"doc-widgets_google_workspace_add_ons_google_for_deve-02cf5fbe","source":"documentation","title":"Widgets | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/concepts/widgets","text":"Example:\n```text\nvar fixedFooter = CardService.newFixedFooter()\n .setPrimaryButton(\n CardService.newTextButton()\n .setText(\"Primary\")\n .setOpenLink(CardService.newOpenLink()\n .setUrl(\"https://www.google.com\")))\n .setSecondaryButton(\n CardService.newTextButton()\n .setText(\"Secondary\")\n .setOnClickAction(\n CardService.newAction()\n .setFunctionName(\n \"secondaryCallback\")));\n\nvar card = CardService.newCardBuilder()\n // (...)\n .setFixedFooter(fixedFooter)\n .build();\n```\n\nExample:\n```text\nvar peekHeader = CardService.newCardHeader()\n .setTitle('Contextual Cat')\n .setImageUrl('https://www.gstatic.com/images/\n icons/material/system/1x/pets_black_48dp.png')\n .setSubtitle(text);\n\n. . .\n\nvar card = CardService.newCardBuilder()\n .setDisplayStyle(CardService.DisplayStyle.PEEK)\n .setPeekCardHeader(peekHeader);\n```\n\nExample:\n```text\nvar decoratedText = CardService.newDecoratedText()\n // (...)\n .setSwitch(CardService.newSwitch()\n .setFieldName('form_input_switch_key')\n .setValue('switch_is_on')\n .setControlType(\n CardService.SwitchControlType.CHECK_BOX));\n```\n\nExample:\n```text\nvar dateOnlyPicker = CardService.newDatePicker()\n .setTitle(\"Enter a date\")\n .setFieldName(\"date_field\")\n // Set default value as May 24 2019. Either a\n // number or string is acceptable.\n .setValueInMsSinceEpoch(1558668600000)\n .setOnChangeAction(CardService.newAction()\n .setFunctionName(\"handleDateChange\"));\n\nvar timeOnlyPicker = CardService.newTimePicker()\n .setTitle(\"Enter a time\")\n .setFieldName(\"time_field\")\n // Set default value as 23:30.\n .setHours(23)\n .setMinutes(30)\n .setOnChangeAction(CardService.newAction()\n .setFunctionName(\"handleTimeChange\"));\n\nvar dateTimePicker = CardService.newDateTimePicker()\n .setTitle(\"Enter a date and time\")\n .setFieldName(\"date_time_field\")\n // Set default value as May 24 2019 03:30 AM UTC.\n // Either a number or string is acceptable.\n .setValueInMsSinceEpoch(1558668600000)\n // EDT time is 4 hours behind UTC.\n .setTimeZoneOffsetInMins(-4 * 60)\n .setOnChangeAction(CardService.newAction()\n .setFunctionName(\"handleDateTimeChange\"));\n```\n\nExample:\n```text\nfunction handleDateTimeChange(event) {\n var dateTimeInput =\n event.commonEventObject.formInputs[\"myDateTimePickerWidgetID\"];\n var msSinceEpoch = dateTimeInput.msSinceEpoch;\n var hasDate = dateTimeInput.hasDate;\n var hasTime = dateTimeInput.hadTime;\n\n // The following requires you to configure the add-on to read user locale\n // and timezone.\n // See:\n // https://developers.google.com/workspace/add-ons/how-tos/access-user-locale\n var userTimezoneId = event.userTimezone.id;\n\n // Format and log the date-time selected using the user's timezone.\n var formattedDateTime;\n if (hasDate && hasTime) {\n formattedDateTime = Utilities.formatDate(\n new Date(msSinceEpoch), userTimezoneId, \"yyy/MM/dd hh:mm:ss\");\n } else if (hasDate) {\n formattedDateTime = Utilities.formatDate(\n new Date(msSinceEpoch), userTimezoneId, \"yyy/MM/dd\")\n + \", Time unspecified\";\n } else if (hasTime) {\n formattedDateTime = \"Date unspecified, \"\n + Utilities.formatDate(\n new Date(msSinceEpoch), userTimezoneId, \"hh:mm a\");\n }\n\n if (formattedDateTime) {\n console.log(formattedDateTime);\n }\n}\n```\n\nExample:\n```text\nvar gridItem = CardService.newGridItem()\n .setIdentifier(\"item_001\")\n .setTitle(\"Lucian R.\")\n .setSubtitle(\"Chief Information Officer\")\n .setImage(imageComponent);\n\nvar cropStyle = CardService.newImageCropStyle()\n .setImageCropType(CardService.ImageCropType.RECTANGLE_4_3);\n\nvar imageComponent = CardService.newImageComponent()\n .setImageUrl(\"https://developers.google.com/workspace/\n images/cymbal/people/person1.jpeg\")\n .setCropStyle(cropStyle)\n\nvar grid = CardService.newGrid()\n .setTitle(\"Recently viewed\")\n .addItem(gridItem)\n .setNumColumns(2)\n .setOnClickAction(CardService.newAction()\n .setFunctionName(\"handleGridItemClick\"));\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.675Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":141,"estimatedTokens":1047}}810{"id":"doc-authorization_scopes_for_editor_add_ons_google_w-e614a769","source":"documentation","title":"Authorization scopes for Editor add-ons | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/concepts/editor-scopes","text":"Example:\n```text\n{\n ...\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/script.container.ui\",\n \"https://www.googleapis.com/auth/spreadsheets\"\n ],\n ...\n }\n```\n\nExample:\n```text\n/**\n * @OnlyCurrentDoc\n */\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.677Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":20,"estimatedTokens":60}}811{"id":"doc-configure_your_app_in_the_google_workspace_marke-14855913","source":"documentation","title":"Configure your app in the Google Workspace Marketplace SDK | Google for Developers","url":"https://developers.google.com/workspace/marketplace/enable-configure-sdk","text":"Example:\n```text\nYou are missing at least one of the following required permissions: Project workspacemarketplace.appconfiguration.view / workspacemarketplace.appconfiguration.update\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.681Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":50}}812{"id":"doc-translate_text_in_a_google_docs_document_google_-b7881e12","source":"documentation","title":"Translate text in a Google Docs document | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/editors/docs/quickstart/translate","text":"Example:\n```text\n/**\n * @OnlyCurrentDoc\n *\n * The above comment directs Apps Script to limit the scope of file\n * access for this add-on. It specifies that this add-on will only\n * attempt to read or modify the files in which the add-on is used,\n * and not all of the user's files. The authorization request message\n * presented to users will reflect this limited scope.\n */\n\n/**\n * Creates a menu entry in the Google Docs UI when the document is opened.\n * This method is only used by the regular add-on, and is never called by\n * the mobile add-on version.\n *\n * @param {object} e The event parameter for a simple onOpen trigger. To\n * determine which authorization mode (ScriptApp.AuthMode) the trigger is\n * running in, inspect e.authMode.\n */\nfunction onOpen(e) {\n DocumentApp.getUi()\n .createAddonMenu()\n .addItem(\"Start\", \"showSidebar\")\n .addToUi();\n}\n\n/**\n * Runs when the add-on is installed.\n * This method is only used by the regular add-on, and is never called by\n * the mobile add-on version.\n *\n * @param {object} e The event parameter for a simple onInstall trigger. To\n * determine which authorization mode (ScriptApp.AuthMode) the trigger is\n * running in, inspect e.authMode. (In practice, onInstall triggers always\n * run in AuthMode.FULL, but onOpen triggers may be AuthMode.LIMITED or\n * AuthMode.NONE.)\n */\nfunction onInstall(e) {\n onOpen(e);\n}\n\n/**\n * Opens a sidebar in the document containing the add-on's user interface.\n * This method is only used by the regular add-on, and is never called by\n * the mobile add-on version.\n */\nfunction showSidebar() {\n const ui =\n HtmlService.createHtmlOutputFromFile(\"sidebar\").setTitle(\"Translate\");\n DocumentApp.getUi().showSidebar(ui);\n}\n\n/**\n * Gets the text the user has selected. If there is no selection,\n * this function displays an error message.\n *\n * @return {Array.<string>} The selected text.\n */\nfunction getSelectedText() {\n const selection = DocumentApp.getActiveDocument().getSelection();\n const text = [];\n if (selection) {\n const elements = selection.getSelectedElements();\n for (let i = 0; i < elements.length; ++i) {\n if (elements[i].isPartial()) {\n const element = elements[i].getElement().asText();\n const startIndex = elements[i].getStartOffset();\n const endIndex = elements[i].getEndOffsetInclusive();\n\n text.push(element.getText().substring(startIndex, endIndex + 1));\n } else {\n const element = elements[i].getElement();\n // Only translate elements that can be edited as text; skip images and\n // other non-text elements.\n if (element.editAsText) {\n const elementText = element.asText().getText();\n // This check is necessary to exclude images, which return a blank\n // text element.\n if (elementText) {\n text.push(elementText);\n }\n }\n }\n }\n }\n if (!text.length) throw new Error(\"Please select some text.\");\n return text;\n}\n\n/**\n * Gets the stored user preferences for the origin and destination languages,\n * if they exist.\n * This method is only used by the regular add-on, and is never called by\n * the mobile add-on version.\n *\n * @return {Object} The user's origin and destination language preferences, if\n * they exist.\n */\nfunction getPreferences() {\n const userProperties = PropertiesService.getUserProperties();\n return {\n originLang: userProperties.getProperty(\"originLang\"),\n destLang: userProperties.getProperty(\"destLang\"),\n };\n}\n\n/**\n * Gets the user-selected text and translates it from the origin language to the\n * destination language. The languages are notated by their two-letter short\n * form. For example, English is 'en', and Spanish is 'es'. The origin language\n * may be specified as an empty string to indicate that Google Translate should\n * auto-detect the language.\n *\n * @param {string} origin The two-letter short form for the origin language.\n * @param {string} dest The two-letter short form for the destination language.\n * @param {boolean} savePrefs Whether to save the origin and destination\n * language preferences.\n * @return {Object} Object containing the original text and the result of the\n * translation.\n */\nfunction getTextAndTranslation(origin, dest, savePrefs) {\n if (savePrefs) {\n PropertiesService.getUserProperties()\n .setProperty(\"originLang\", origin)\n .setProperty(\"destLang\", dest);\n }\n const text = getSelectedText().join(\"\\n\");\n return {\n text: text,\n translation: translateText(text, origin, dest),\n };\n}\n\n/**\n * Replaces the text of the current selection with the provided text, or\n * inserts text at the current cursor location. (There will always be either\n * a selection or a cursor.) If multiple elements are selected, only inserts the\n * translated text in the first element that can contain text and removes the\n * other elements.\n *\n * @param {string} newText The text with which to replace the current selection.\n */\nfunction insertText(newText) {\n const selection = DocumentApp.getActiveDocument().getSelection();\n if (selection) {\n let replaced = false;\n const elements = selection.getSelectedElements();\n if (\n elements.length === 1 &&\n elements[0].getElement().getType() ===\n DocumentApp.ElementType.INLINE_IMAGE\n ) {\n throw new Error(\"Can't insert text into an image.\");\n }\n for (let i = 0; i < elements.length; ++i) {\n if (elements[i].isPartial()) {\n const element = elements[i].getElement().asText();\n const startIndex = elements[i].getStartOffset();\n const endIndex = elements[i].getEndOffsetInclusive();\n element.deleteText(startIndex, endIndex);\n if (!replaced) {\n element.insertText(startIndex, newText);\n replaced = true;\n } else {\n // This block handles a selection that ends with a partial element. We\n // want to copy this partial text to the previous element so we don't\n // have a line-break before the last partial.\n const parent = element.getParent();\n const remainingText = element.getText().substring(endIndex + 1);\n parent.getPreviousSibling().asText().appendText(remainingText);\n // We cannot remove the last paragraph of a doc. If this is the case,\n // just remove the text within the last paragraph instead.\n if (parent.getNextSibling()) {\n parent.removeFromParent();\n } else {\n element.removeFromParent();\n }\n }\n } else {\n const element = elements[i].getElement();\n if (!replaced && element.editAsText) {\n // Only translate elements that can be edited as text, removing other\n // elements.\n element.clear();\n element.asText().setText(newText);\n replaced = true;\n } else {\n // We cannot remove the last paragraph of a doc. If this is the case,\n // just clear the element.\n if (element.getNextSibling()) {\n element.removeFromParent();\n } else {\n element.clear();\n }\n }\n }\n }\n } else {\n const cursor = DocumentApp.getActiveDocument().getCursor();\n const surroundingText = cursor.getSurroundingText().getText();\n const surroundingTextOffset = cursor.getSurroundingTextOffset();\n\n // If the cursor follows or preceds a non-space character, insert a space\n // between the character and the translation. Otherwise, just insert the\n // translation.\n let textToInsert = newText;\n if (surroundingText) {\n if (surroundingTextOffset > 0) {\n if (surroundingText.charAt(surroundingTextOffset - 1) !== \" \") {\n textToInsert = ` ${textToInsert}`;\n }\n }\n if (surroundingTextOffset < surroundingText.length) {\n if (surroundingText.charAt(surroundingTextOffset) !== \" \") {\n textToInsert += \" \";\n }\n }\n }\n cursor.insertText(textToInsert);\n }\n}\n\n/**\n * Given text, translate it from the origin language to the destination\n * language. The languages are notated by their two-letter short form. For\n * example, English is 'en', and Spanish is 'es'. The origin language may be\n * specified as an empty string to indicate that Google Translate should\n * auto-detect the language.\n *\n * @param {string} text text to translate.\n * @param {string} origin The two-letter short form for the origin language.\n * @param {string} dest The two-letter short form for the destination language.\n * @return {string} The result of the translation, or the original text if\n * origin and dest languages are the same.\n */\nfunction translateText(text, origin, dest) {\n if (origin === dest) return text;\n return LanguageApp.translate(text, origin, dest);\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n<head>\n <base target=\"_top\">\n <link rel=\"stylesheet\" href=\"https://ssl.gstatic.com/docs/script/css/add-ons1.css\">\n <!-- The CSS package above applies Google styling to buttons and other elements. -->\n\n <style>\n .branding-below {\n bottom: 56px;\n top: 0;\n }\n .branding-text {\n left: 7px;\n position: relative;\n top: 3px;\n }\n .col-contain {\n overflow: hidden;\n }\n .col-one {\n float: left;\n width: 50%;\n }\n .logo {\n vertical-align: middle;\n }\n .radio-spacer {\n height: 20px;\n }\n .width-100 {\n width: 100%;\n }\n </style>\n <title></title>\n</head>\n<body>\n<div class=\"sidebar branding-below\">\n <form>\n <div class=\"block col-contain\">\n <div class=\"col-one\">\n <b>Selected text</b>\n <div>\n <input type=\"radio\" name=\"origin\" id=\"radio-origin-auto\" value=\"\" checked=\"checked\">\n <label for=\"radio-origin-auto\">Auto-detect</label>\n </div>\n <div>\n <input type=\"radio\" name=\"origin\" id=\"radio-origin-en\" value=\"en\">\n <label for=\"radio-origin-en\">English</label>\n </div>\n <div>\n <input type=\"radio\" name=\"origin\" id=\"radio-origin-fr\" value=\"fr\">\n <label for=\"radio-origin-fr\">French</label>\n </div>\n <div>\n <input type=\"radio\" name=\"origin\" id=\"radio-origin-de\" value=\"de\">\n <label for=\"radio-origin-de\">German</label>\n </div>\n <div>\n <input type=\"radio\" name=\"origin\" id=\"radio-origin-ja\" value=\"ja\">\n <label for=\"radio-origin-ja\">Japanese</label>\n </div>\n <div>\n <input type=\"radio\" name=\"origin\" id=\"radio-origin-es\" value=\"es\">\n <label for=\"radio-origin-es\">Spanish</label>\n </div>\n </div>\n <div>\n <b>Translate into</b>\n <div class=\"radio-spacer\">\n </div>\n <div>\n <input type=\"radio\" name=\"dest\" id=\"radio-dest-en\" value=\"en\">\n <label for=\"radio-dest-en\">English</label>\n </div>\n <div>\n <input type=\"radio\" name=\"dest\" id=\"radio-dest-fr\" value=\"fr\">\n <label for=\"radio-dest-fr\">French</label>\n </div>\n <div>\n <input type=\"radio\" name=\"dest\" id=\"radio-dest-de\" value=\"de\">\n <label for=\"radio-dest-de\">German</label>\n </div>\n <div>\n <input type=\"radio\" name=\"dest\" id=\"radio-dest-ja\" value=\"ja\" checked=\"checked\">\n <label for=\"radio-dest-ja\">Japanese</label>\n </div>\n <div>\n <input type=\"radio\" name=\"dest\" id=\"radio-dest-es\" value=\"es\">\n <label for=\"radio-dest-es\">Spanish</label>\n </div>\n </div>\n </div>\n <div class=\"block form-group\">\n <label for=\"translated-text\"><b>Translation</b></label>\n <textarea class=\"width-100\" id=\"translated-text\" rows=\"10\"></textarea>\n </div>\n <div class=\"block\">\n <input type=\"checkbox\" id=\"save-prefs\">\n <label for=\"save-prefs\">Use these languages by default</label>\n </div>\n <div class=\"block\" id=\"button-bar\">\n <button class=\"blue\" id=\"run-translation\">Translate</button>\n <button id=\"insert-text\">Insert</button>\n </div>\n </form>\n</div>\n\n<div class=\"sidebar bottom\">\n <img alt=\"Add-on logo\" class=\"logo\" src=\"https://www.gstatic.com/images/branding/product/1x/translate_48dp.png\" width=\"27\" height=\"27\">\n <span class=\"gray branding-text\">Translate sample by Google</span>\n</div>\n\n<script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js\"></script>\n<script>\n /**\n * On document load, assign click handlers to each button and try to load the\n * user's origin and destination language preferences if previously set.\n */\n $(function() {\n $('#run-translation').click(runTranslation);\n $('#insert-text').click(insertText);\n google.script.run.withSuccessHandler(loadPreferences)\n .withFailureHandler(showError).getPreferences();\n });\n\n /**\n * Callback function that populates the origin and destination selection\n * boxes with user preferences from the server.\n *\n * @param {Object} languagePrefs The saved origin and destination languages.\n */\n function loadPreferences(languagePrefs) {\n $('input:radio[name=\"origin\"]')\n .filter('[value=' + languagePrefs.originLang + ']')\n .attr('checked', true);\n $('input:radio[name=\"dest\"]')\n .filter('[value=' + languagePrefs.destLang + ']')\n .attr('checked', true);\n }\n\n /**\n * Runs a server-side function to translate the user-selected text and update\n * the sidebar UI with the resulting translation.\n */\n function runTranslation() {\n this.disabled = true;\n $('#error').remove();\n const origin = $('input[name=origin]:checked').val();\n const dest = $('input[name=dest]:checked').val();\n const savePrefs = $('#save-prefs').is(':checked');\n google.script.run\n .withSuccessHandler(\n function(textAndTranslation, element) {\n $('#translated-text').val(textAndTranslation.translation);\n element.disabled = false;\n })\n .withFailureHandler(\n function(msg, element) {\n showError(msg, $('#button-bar'));\n element.disabled = false;\n })\n .withUserObject(this)\n .getTextAndTranslation(origin, dest, savePrefs);\n }\n\n /**\n * Runs a server-side function to insert the translated text into the document\n * at the user's cursor or selection.\n */\n function insertText() {\n this.disabled = true;\n $('#error').remove();\n google.script.run\n .withSuccessHandler(\n function(returnSuccess, element) {\n element.disabled = false;\n })\n .withFailureHandler(\n function(msg, element) {\n showError(msg, $('#button-bar'));\n element.disabled = false;\n })\n .withUserObject(this)\n .insertText($('#translated-text').val());\n }\n\n /**\n * Inserts a div that contains an error message after a given element.\n *\n * @param {string} msg The error message to display.\n * @param {DOMElement} element The element after which to display the error.\n */\n function showError(msg, element) {\n const div = $('<div id=\"error\" class=\"error\">' + msg + '</div>');\n $(element).after(div);\n }\n</script>\n</body>\n</html>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.684Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":448,"estimatedTokens":3854}}813{"id":"doc-respond_to_google_chat_app_commands_google_works-db7530ae","source":"documentation","title":"Respond to Google Chat app commands | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/quick-commands","text":"Example:\n```text\n// The ID of the slash command \"/about\".\n// You must use the same ID in the Google Chat API configuration.\nconst ABOUT_COMMAND_ID = 1;\n\n/**\n * Handle requests from Google Workspace add on\n *\n * @param {Object} req Request sent by Google Chat\n * @param {Object} res Response to be sent back to Google Chat\n */\nhttp('avatarApp', (req, res) => {\n const chatEvent = req.body.chat;\n let message;\n if (chatEvent.appCommandPayload) {\n message = handleAppCommand(chatEvent);\n } else {\n message = handleMessage(chatEvent);\n }\n res.send({ hostAppDataAction: { chatDataAction: { createMessageAction: {\n message: message\n }}}});\n});\n\n/**\n * Responds to an APP_COMMAND event in Google Chat.\n *\n * @param {Object} event the event object from Google Chat\n * @return the response message object.\n */\nfunction handleAppCommand(event) {\n switch (event.appCommandPayload.appCommandMetadata.appCommandId) {\n case ABOUT_COMMAND_ID:\n return {\n text: 'The Avatar app replies to Google Chat messages.'\n };\n }\n}\n```\n\nExample:\n```text\n# The ID of the slash command \"/about\".\n# You must use the same ID in the Google Chat API configuration.\nABOUT_COMMAND_ID = 1\n\n@functions_framework.http\ndef avatar_app(req: flask.Request) -> Mapping[str, Any]:\n \"\"\"Handle requests from Google Workspace add on\n\n Args:\n flask.Request req: the request sent by Google Chat\n\n Returns:\n Mapping[str, Any]: the response to be sent back to Google Chat\n \"\"\"\n chat_event = req.get_json(silent=True)[\"chat\"]\n if chat_event and \"appCommandPayload\" in chat_event:\n message = handle_app_command(chat_event)\n else:\n message = handle_message(chat_event)\n return { \"hostAppDataAction\": { \"chatDataAction\": { \"createMessageAction\": {\n \"message\": message\n }}}}\n\ndef handle_app_command(event: Mapping[str, Any]) -> Mapping[str, Any]:\n \"\"\"Responds to an APP_COMMAND event in Google Chat.\n\n Args:\n Mapping[str, Any] event: the event object from Google Chat\n\n Returns:\n Mapping[str, Any]: the response message object.\n \"\"\"\n if event[\"appCommandPayload\"][\"appCommandMetadata\"][\"appCommandId\"] == ABOUT_COMMAND_ID:\n return {\n \"text\": \"The Avatar app replies to Google Chat messages.\",\n }\n return {}\n```\n\nExample:\n```text\n// The ID of the slash command \"/about\".\n// You must use the same ID in the Google Chat API configuration.\nprivate static final int ABOUT_COMMAND_ID = 1;\n\nprivate static final Gson gson = new Gson();\n\n/**\n * Handle requests from Google Workspace add on\n * \n * @param request the request sent by Google Chat\n * @param response the response to be sent back to Google Chat\n */\n@Override\npublic void service(HttpRequest request, HttpResponse response) throws Exception {\n JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);\n JsonObject chatEvent = event.getAsJsonObject(\"chat\");\n Message message;\n if (chatEvent.has(\"appCommandPayload\")) {\n message = handleAppCommand(chatEvent);\n } else {\n message = handleMessage(chatEvent);\n }\n JsonObject createMessageAction = new JsonObject();\n createMessageAction.add(\"message\", gson.fromJson(gson.toJson(message), JsonObject.class));\n JsonObject chatDataAction = new JsonObject();\n chatDataAction.add(\"createMessageAction\", createMessageAction);\n JsonObject hostAppDataAction = new JsonObject();\n hostAppDataAction.add(\"chatDataAction\", chatDataAction);\n JsonObject dataActions = new JsonObject();\n dataActions.add(\"hostAppDataAction\", hostAppDataAction);\n response.getWriter().write(gson.toJson(dataActions));\n}\n\n/**\n * Handles an APP_COMMAND event in Google Chat.\n *\n * @param event the event object from Google Chat\n * @return the response message object.\n */\nprivate Message handleAppCommand(JsonObject event) throws Exception {\n switch (event.getAsJsonObject(\"appCommandPayload\")\n .getAsJsonObject(\"appCommandMetadata\").get(\"appCommandId\").getAsInt()) {\n case ABOUT_COMMAND_ID:\n return new Message()\n .setText(\"The Avatar app replies to Google Chat messages.\");\n default:\n return null;\n }\n}\n```\n\nExample:\n```text\n// The ID of the slash command \"/about\".\n// You must use the same ID in the Google Chat API configuration.\nconst ABOUT_COMMAND_ID = 1;\n\n/**\n * Responds to an APP_COMMAND event in Google Chat.\n *\n * @param {Object} event the event object from Google Chat\n */\nfunction onAppCommand(event) {\n // Executes the app command logic based on ID.\n switch (event.chat.appCommandPayload.appCommandMetadata.appCommandId) {\n case ABOUT_COMMAND_ID:\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: 'The Avatar app replies to Google Chat messages.'\n }}}}};\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event from Google Chat.\n *\n * @param {Object} event The interaction event from Google Chat.\n * @param {Object} res The HTTP response object.\n * @return {Object} The JSON response message with a confirmation.\n */\nfunction onAppCommand(event, res) {\n // Collect the command ID and type from the event metadata.\n const {appCommandId, appCommandType} =\n event.chat.appCommandPayload.appCommandMetadata;\n\n if (appCommandType === 'MESSAGE_ACTION' &&\n appCommandId === REMIND_ME_COMMAND_ID) {\n\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n const messageText = event.chat.appCommandPayload.message.text;\n\n // Return a response that includes details from the original message.\n return res.json({\n \"hostAppDataAction\": {\n \"chatDataAction\": {\n \"createMessageAction\": {\n \"message\": {\n \"text\": `Setting a reminder for message: \"${messageText}\"`\n }\n }\n }\n }\n });\n }\n}\n```\n\nExample:\n```text\ndef on_app_command(event):\n \"\"\"Responds to an APP_COMMAND interaction event from Google Chat.\n\n Args:\n event (dict): The interaction event from Google Chat.\n\n Returns:\n dict: The JSON response message with a confirmation.\n \"\"\"\n # Collect the command ID and type from the event metadata.\n payload = event.get('chat', {}).get('appCommandPayload', {})\n metadata = payload.get('appCommandMetadata', {})\n if metadata.get('appCommandType') == 'MESSAGE_ACTION' and \\\n metadata.get('appCommandId') == REMIND_ME_COMMAND_ID:\n\n # Message actions can access the context of the message they were\n # invoked on, such as the text or sender of that message.\n message_text = payload.get('message', {}).get('text')\n\n # Return a response that includes details from the original message.\n return {\n \"hostAppDataAction\": {\n \"chatDataAction\": {\n \"createMessageAction\": {\n \"message\": {\n \"text\": f'Setting a reminder for message: \"{message_text}\"'\n }\n }\n }\n }\n }\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event from Google Chat.\n *\n * @param event The interaction event from Google Chat.\n * @param response The HTTP response object.\n */\nvoid onAppCommand(JsonObject event, HttpResponse response) throws Exception {\n // Collect the command ID and type from the event metadata.\n JsonObject payload = event.getAsJsonObject(\"chat\").getAsJsonObject(\"appCommandPayload\");\n JsonObject metadata = payload.getAsJsonObject(\"appCommandMetadata\");\n String appCommandType = metadata.get(\"appCommandType\").getAsString();\n\n if (appCommandType.equals(\"MESSAGE_ACTION\")) {\n int commandId = metadata.get(\"appCommandId\").getAsInt();\n if (commandId == REMIND_ME_COMMAND_ID) {\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n String messageText = payload.getAsJsonObject(\"message\").get(\"text\").getAsString();\n\n // Return a response that includes details from the original message.\n JsonObject responseMessage = new JsonObject();\n responseMessage.addProperty(\"text\", \"Setting a reminder for message: \" + messageText);\n\n JsonObject createMessageAction = new JsonObject();\n createMessageAction.add(\"message\", responseMessage);\n\n JsonObject chatDataAction = new JsonObject();\n chatDataAction.add(\"createMessageAction\", createMessageAction);\n\n JsonObject hostAppDataAction = new JsonObject();\n hostAppDataAction.add(\"chatDataAction\", chatDataAction);\n\n JsonObject finalResponse = new JsonObject();\n finalResponse.add(\"hostAppDataAction\", hostAppDataAction);\n\n response.getWriter().write(finalResponse.toString());\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event in Google Chat.\n *\n * @param {Object} event The interaction event from Google Chat.\n * @return {Object} The JSON response message with a confirmation.\n */\nfunction onAppCommand(event) {\n // Collect the command ID and type from the event metadata.\n const {appCommandId, appCommandType} =\n event.chat.appCommandPayload.appCommandMetadata;\n\n if (appCommandType === 'MESSAGE_ACTION' &&\n appCommandId === REMIND_ME_COMMAND_ID) {\n\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n const messageText = event.chat.appCommandPayload.message.text;\n\n // Return a response that includes details from the original message.\n return CardService.newChatResponseBuilder()\n .setText(\"Setting a reminder for message: \" + messageText)\n .build();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.686Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":301,"estimatedTokens":2422}}814{"id":"doc-build_a_homepage_for_a_google_chat_app_google_fo-71036a08","source":"documentation","title":"Build a homepage for a Google Chat app | Google for Developers","url":"https://developers.google.com/workspace/chat/send-app-home-card-message","text":"Example:\n```text\napp.post('/', async (req, res) => {\n let event = req.body.chat;\n\n let body = {};\n if (event.type === 'APP_HOME') {\n // App home is requested\n body = { action: { navigations: [{\n pushCard: getHomeCard()\n }]}}\n } else if (event.type === 'SUBMIT_FORM') {\n // The update button from app home is clicked\n commonEvent = req.body.commonEventObject;\n if (commonEvent && commonEvent.invokedFunction === 'updateAppHome') {\n body = updateAppHome()\n }\n }\n\n return res.json(body);\n});\n\n// Create the app home card\nfunction getHomeCard() {\n return { sections: [{ widgets: [\n { textParagraph: {\n text: \"Here is the app home 🏠 It's \" + new Date().toTimeString()\n }},\n { buttonList: { buttons: [{\n text: \"Update app home\",\n onClick: { action: {\n function: \"updateAppHome\"\n }}\n }]}}\n ]}]};\n}\n```\n\nExample:\n```text\n@app.route('/', methods=['POST'])\ndef post() -> Mapping[str, Any]:\n \"\"\"Handle requests from Google Chat\n\n Returns:\n Mapping[str, Any]: the response\n \"\"\"\n event = request.get_json()\n match event['chat'].get('type'):\n\n case 'APP_HOME':\n # App home is requested\n body = { \"action\": { \"navigations\": [{\n \"pushCard\": get_home_card()\n }]}}\n\n case 'SUBMIT_FORM':\n # The update button from app home is clicked\n event_object = event.get('commonEventObject')\n if event_object is not None:\n if 'update_app_home' == event_object.get('invokedFunction'):\n body = update_app_home()\n\n case _:\n # Other response types are not supported\n body = {}\n\n return json.jsonify(body)\n\n\ndef get_home_card() -> Mapping[str, Any]:\n \"\"\"Create the app home card\n\n Returns:\n Mapping[str, Any]: the card\n \"\"\"\n return { \"sections\": [{ \"widgets\": [\n { \"textParagraph\": {\n \"text\": \"Here is the app home 🏠 It's \" +\n datetime.datetime.now().isoformat()\n }},\n { \"buttonList\": { \"buttons\": [{\n \"text\": \"Update app home\",\n \"onClick\": { \"action\": {\n \"function\": \"update_app_home\"\n }}\n }]}}\n ]}]}\n```\n\nExample:\n```text\n// Process Google Chat events\n@PostMapping(\"/\")\n@ResponseBody\npublic GenericJson onEvent(@RequestBody JsonNode event) throws Exception {\n switch (event.at(\"/chat/type\").asText()) {\n case \"APP_HOME\":\n // App home is requested\n GenericJson navigation = new GenericJson();\n navigation.set(\"pushCard\", getHomeCard());\n\n GenericJson action = new GenericJson();\n action.set(\"navigations\", List.of(navigation));\n\n GenericJson response = new GenericJson();\n response.set(\"action\", action);\n return response;\n case \"SUBMIT_FORM\":\n // The update button from app home is clicked\n if (event.at(\"/commonEventObject/invokedFunction\").asText().equals(\"updateAppHome\")) {\n return updateAppHome();\n }\n }\n\n return new GenericJson();\n}\n\n// Create the app home card\nGoogleAppsCardV1Card getHomeCard() {\n return new GoogleAppsCardV1Card()\n .setSections(List.of(new GoogleAppsCardV1Section()\n .setWidgets(List.of(\n new GoogleAppsCardV1Widget()\n .setTextParagraph(new GoogleAppsCardV1TextParagraph()\n .setText(\"Here is the app home 🏠 It's \" + new Date())),\n new GoogleAppsCardV1Widget()\n .setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()\n .setText(\"Update app home\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setAction(new GoogleAppsCardV1Action()\n .setFunction(\"updateAppHome\"))))))))));\n}\n```\n\nExample:\n```text\n/**\n * Responds to a APP_HOME event in Google Chat.\n */\nfunction onAppHome() {\n return { action: { navigations: [{\n pushCard: getHomeCard()\n }]}};\n}\n\n/**\n * Returns the app home card.\n */\nfunction getHomeCard() {\n return { sections: [{ widgets: [\n { textParagraph: {\n text: \"Here is the app home 🏠 It's \" + new Date().toTimeString()\n }},\n { buttonList: { buttons: [{\n text: \"Update app home\",\n onClick: { action: {\n function: \"updateAppHome\"\n }}\n }]}}\n ]}]};\n}\n```\n\nExample:\n```text\n// Update the app home\nfunction updateAppHome() {\n return { renderActions: { action: { navigations: [{\n updateCard: getHomeCard()\n }]}}}\n};\n```\n\nExample:\n```text\ndef update_app_home() -> Mapping[str, Any]:\n \"\"\"Update the app home\n\n Returns:\n Mapping[str, Any]: the update card render action\n \"\"\"\n return { \"renderActions\": { \"action\": { \"navigations\": [{\n \"updateCard\": get_home_card()\n }]}}}\n```\n\nExample:\n```text\n// Update the app home\nGenericJson updateAppHome() {\n GenericJson navigation = new GenericJson();\n navigation.set(\"updateCard\", getHomeCard());\n\n GenericJson action = new GenericJson();\n action.set(\"navigations\", List.of(navigation));\n\n GenericJson renderActions = new GenericJson();\n renderActions.set(\"action\", action);\n\n GenericJson response = new GenericJson();\n response.set(\"renderActions\", renderActions);\n return response;\n}\n```\n\nExample:\n```text\n/**\n * Updates the home app.\n */\nfunction updateAppHome() {\n return { renderActions: { action: { navigations: [{\n updateCard: getHomeCard()\n }]}}};\n}\n```\n\nExample:\n```text\n{ renderActions: { action: { navigations: [{ updateCard: { sections: [{\n header: \"Add new contact\",\n widgets: [{ \"textInput\": {\n label: \"Name\",\n type: \"SINGLE_LINE\",\n name: \"contactName\"\n }}, { textInput: {\n label: \"Address\",\n type: \"MULTIPLE_LINE\",\n name: \"address\"\n }}, { decoratedText: {\n text: \"Add to favorites\",\n switchControl: {\n controlType: \"SWITCH\",\n name: \"saveFavorite\"\n }\n }}, { decoratedText: {\n text: \"Merge with existing contacts\",\n switchControl: {\n controlType: \"SWITCH\",\n name: \"mergeContact\",\n selected: true\n }\n }}, { buttonList: { buttons: [{\n text: \"Next\",\n onClick: { action: { function: \"openSequentialDialog\" }}\n }]}}]\n}]}}]}}}\n```\n\nExample:\n```text\n{ renderActions: { action: {\n navigations: [{ endNavigation: { action: \"CLOSE_DIALOG\" }}]\n}}}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.688Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":258,"estimatedTokens":1522}}815{"id":"doc-respond_to_google_chat_app_commands_google_works-f067838a","source":"documentation","title":"Respond to Google Chat app commands | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/chat/slash-commands","text":"Example:\n```text\n// The ID of the slash command \"/about\".\n// You must use the same ID in the Google Chat API configuration.\nconst ABOUT_COMMAND_ID = 1;\n\n/**\n * Handle requests from Google Workspace add on\n *\n * @param {Object} req Request sent by Google Chat\n * @param {Object} res Response to be sent back to Google Chat\n */\nhttp('avatarApp', (req, res) => {\n const chatEvent = req.body.chat;\n let message;\n if (chatEvent.appCommandPayload) {\n message = handleAppCommand(chatEvent);\n } else {\n message = handleMessage(chatEvent);\n }\n res.send({ hostAppDataAction: { chatDataAction: { createMessageAction: {\n message: message\n }}}});\n});\n\n/**\n * Responds to an APP_COMMAND event in Google Chat.\n *\n * @param {Object} event the event object from Google Chat\n * @return the response message object.\n */\nfunction handleAppCommand(event) {\n switch (event.appCommandPayload.appCommandMetadata.appCommandId) {\n case ABOUT_COMMAND_ID:\n return {\n text: 'The Avatar app replies to Google Chat messages.'\n };\n }\n}\n```\n\nExample:\n```text\n# The ID of the slash command \"/about\".\n# You must use the same ID in the Google Chat API configuration.\nABOUT_COMMAND_ID = 1\n\n@functions_framework.http\ndef avatar_app(req: flask.Request) -> Mapping[str, Any]:\n \"\"\"Handle requests from Google Workspace add on\n\n Args:\n flask.Request req: the request sent by Google Chat\n\n Returns:\n Mapping[str, Any]: the response to be sent back to Google Chat\n \"\"\"\n chat_event = req.get_json(silent=True)[\"chat\"]\n if chat_event and \"appCommandPayload\" in chat_event:\n message = handle_app_command(chat_event)\n else:\n message = handle_message(chat_event)\n return { \"hostAppDataAction\": { \"chatDataAction\": { \"createMessageAction\": {\n \"message\": message\n }}}}\n\ndef handle_app_command(event: Mapping[str, Any]) -> Mapping[str, Any]:\n \"\"\"Responds to an APP_COMMAND event in Google Chat.\n\n Args:\n Mapping[str, Any] event: the event object from Google Chat\n\n Returns:\n Mapping[str, Any]: the response message object.\n \"\"\"\n if event[\"appCommandPayload\"][\"appCommandMetadata\"][\"appCommandId\"] == ABOUT_COMMAND_ID:\n return {\n \"text\": \"The Avatar app replies to Google Chat messages.\",\n }\n return {}\n```\n\nExample:\n```text\n// The ID of the slash command \"/about\".\n// You must use the same ID in the Google Chat API configuration.\nprivate static final int ABOUT_COMMAND_ID = 1;\n\nprivate static final Gson gson = new Gson();\n\n/**\n * Handle requests from Google Workspace add on\n * \n * @param request the request sent by Google Chat\n * @param response the response to be sent back to Google Chat\n */\n@Override\npublic void service(HttpRequest request, HttpResponse response) throws Exception {\n JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);\n JsonObject chatEvent = event.getAsJsonObject(\"chat\");\n Message message;\n if (chatEvent.has(\"appCommandPayload\")) {\n message = handleAppCommand(chatEvent);\n } else {\n message = handleMessage(chatEvent);\n }\n JsonObject createMessageAction = new JsonObject();\n createMessageAction.add(\"message\", gson.fromJson(gson.toJson(message), JsonObject.class));\n JsonObject chatDataAction = new JsonObject();\n chatDataAction.add(\"createMessageAction\", createMessageAction);\n JsonObject hostAppDataAction = new JsonObject();\n hostAppDataAction.add(\"chatDataAction\", chatDataAction);\n JsonObject dataActions = new JsonObject();\n dataActions.add(\"hostAppDataAction\", hostAppDataAction);\n response.getWriter().write(gson.toJson(dataActions));\n}\n\n/**\n * Handles an APP_COMMAND event in Google Chat.\n *\n * @param event the event object from Google Chat\n * @return the response message object.\n */\nprivate Message handleAppCommand(JsonObject event) throws Exception {\n switch (event.getAsJsonObject(\"appCommandPayload\")\n .getAsJsonObject(\"appCommandMetadata\").get(\"appCommandId\").getAsInt()) {\n case ABOUT_COMMAND_ID:\n return new Message()\n .setText(\"The Avatar app replies to Google Chat messages.\");\n default:\n return null;\n }\n}\n```\n\nExample:\n```text\n// The ID of the slash command \"/about\".\n// You must use the same ID in the Google Chat API configuration.\nconst ABOUT_COMMAND_ID = 1;\n\n/**\n * Responds to an APP_COMMAND event in Google Chat.\n *\n * @param {Object} event the event object from Google Chat\n */\nfunction onAppCommand(event) {\n // Executes the app command logic based on ID.\n switch (event.chat.appCommandPayload.appCommandMetadata.appCommandId) {\n case ABOUT_COMMAND_ID:\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: 'The Avatar app replies to Google Chat messages.'\n }}}}};\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event from Google Chat.\n *\n * @param {Object} event The interaction event from Google Chat.\n * @param {Object} res The HTTP response object.\n * @return {Object} The JSON response message with a confirmation.\n */\nfunction onAppCommand(event, res) {\n // Collect the command ID and type from the event metadata.\n const {appCommandId, appCommandType} =\n event.chat.appCommandPayload.appCommandMetadata;\n\n if (appCommandType === 'MESSAGE_ACTION' &&\n appCommandId === REMIND_ME_COMMAND_ID) {\n\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n const messageText = event.chat.appCommandPayload.message.text;\n\n // Return a response that includes details from the original message.\n return res.json({\n \"hostAppDataAction\": {\n \"chatDataAction\": {\n \"createMessageAction\": {\n \"message\": {\n \"text\": `Setting a reminder for message: \"${messageText}\"`\n }\n }\n }\n }\n });\n }\n}\n```\n\nExample:\n```text\ndef on_app_command(event):\n \"\"\"Responds to an APP_COMMAND interaction event from Google Chat.\n\n Args:\n event (dict): The interaction event from Google Chat.\n\n Returns:\n dict: The JSON response message with a confirmation.\n \"\"\"\n # Collect the command ID and type from the event metadata.\n payload = event.get('chat', {}).get('appCommandPayload', {})\n metadata = payload.get('appCommandMetadata', {})\n if metadata.get('appCommandType') == 'MESSAGE_ACTION' and \\\n metadata.get('appCommandId') == REMIND_ME_COMMAND_ID:\n\n # Message actions can access the context of the message they were\n # invoked on, such as the text or sender of that message.\n message_text = payload.get('message', {}).get('text')\n\n # Return a response that includes details from the original message.\n return {\n \"hostAppDataAction\": {\n \"chatDataAction\": {\n \"createMessageAction\": {\n \"message\": {\n \"text\": f'Setting a reminder for message: \"{message_text}\"'\n }\n }\n }\n }\n }\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event from Google Chat.\n *\n * @param event The interaction event from Google Chat.\n * @param response The HTTP response object.\n */\nvoid onAppCommand(JsonObject event, HttpResponse response) throws Exception {\n // Collect the command ID and type from the event metadata.\n JsonObject payload = event.getAsJsonObject(\"chat\").getAsJsonObject(\"appCommandPayload\");\n JsonObject metadata = payload.getAsJsonObject(\"appCommandMetadata\");\n String appCommandType = metadata.get(\"appCommandType\").getAsString();\n\n if (appCommandType.equals(\"MESSAGE_ACTION\")) {\n int commandId = metadata.get(\"appCommandId\").getAsInt();\n if (commandId == REMIND_ME_COMMAND_ID) {\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n String messageText = payload.getAsJsonObject(\"message\").get(\"text\").getAsString();\n\n // Return a response that includes details from the original message.\n JsonObject responseMessage = new JsonObject();\n responseMessage.addProperty(\"text\", \"Setting a reminder for message: \" + messageText);\n\n JsonObject createMessageAction = new JsonObject();\n createMessageAction.add(\"message\", responseMessage);\n\n JsonObject chatDataAction = new JsonObject();\n chatDataAction.add(\"createMessageAction\", createMessageAction);\n\n JsonObject hostAppDataAction = new JsonObject();\n hostAppDataAction.add(\"chatDataAction\", chatDataAction);\n\n JsonObject finalResponse = new JsonObject();\n finalResponse.add(\"hostAppDataAction\", hostAppDataAction);\n\n response.getWriter().write(finalResponse.toString());\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event in Google Chat.\n *\n * @param {Object} event The interaction event from Google Chat.\n * @return {Object} The JSON response message with a confirmation.\n */\nfunction onAppCommand(event) {\n // Collect the command ID and type from the event metadata.\n const {appCommandId, appCommandType} =\n event.chat.appCommandPayload.appCommandMetadata;\n\n if (appCommandType === 'MESSAGE_ACTION' &&\n appCommandId === REMIND_ME_COMMAND_ID) {\n\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n const messageText = event.chat.appCommandPayload.message.text;\n\n // Return a response that includes details from the original message.\n return CardService.newChatResponseBuilder()\n .setText(\"Setting a reminder for message: \" + messageText)\n .build();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.690Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":301,"estimatedTokens":2422}}816{"id":"doc-fact_check_statements_with_an_adk_ai_agent_and_g-881ecc23","source":"documentation","title":"Fact-check statements with an ADK AI agent and Gemini model | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/custom-functions/fact-check","text":"Example:\n```text\ngcloud projects create PROJECT_ID\n```\n\nExample:\n```text\ngcloud billing accounts list\n```\n\nExample:\n```text\ngcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID\n```\n\nExample:\n```text\ngcloud config set project PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable aiplatform.googleapis.com\n```\n\nExample:\n```text\ngcloud iam service-accounts create SERVICE_ACCOUNT_NAME \\\n --display-name=\"SERVICE_ACCOUNT_NAME\"SERVICE_ACCOUNT_NAMESERVICE_ACCOUNT_NAME\n```\n\nExample:\n```text\ngcloud auth application-default logingcloud config set project PROJECT_IDgcloud auth application-default set-quota-project PROJECT_ID\n```\n\nExample:\n```text\nunzip adk-samples-main.zipcd adk-samples-main/python/agents/llm-auditor\n```\n\nExample:\n```text\ngcloud storage buckets create gs://CLOUD_STORAGE_BUCKET_NAME --project=PROJECT_ID --location=PROJECT_LOCATION\n```\n\nExample:\n```text\nexport GOOGLE_GENAI_USE_VERTEXAI=trueexport GOOGLE_CLOUD_PROJECT=PROJECT_IDexport GOOGLE_CLOUD_LOCATION=PROJECT_LOCATIONexport GOOGLE_CLOUD_STORAGE_BUCKET=CLOUD_STORAGE_BUCKET_NAME\n```\n\nExample:\n```text\npython3 -m venv myenvsource myenv/bin/activatepoetry install --with deploymentpython3 deployment/deploy.py --create\n```\n\nExample:\n```text\npython3 deployment/deploy.py --list\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.691Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":62,"estimatedTokens":323}}817{"id":"doc-get_app_installation_and_licensing_details_googl-04289e70","source":"documentation","title":"Get app installation and licensing details | Google Workspace Marketplace | Google for Developers","url":"https://developers.google.com/workspace/marketplace/example-calls-marketplace-api","text":"Example:\n```text\ncurl -H \"Authorization: Bearer {TOKEN}\" https://appsmarket.googleapis.com/appsmarket/v2/userLicense/APPLICATION_ID/user1@cymbalgroup.com\n```\n\nExample:\n```text\n{\n \"kind\": \"appsmarket#userLicense\",\n \"enabled\": true,\n \"state\": \"ACTIVE\",\n \"editionId\": \"default_edition\",\n \"customerId\": \"user1@cymbalgroup.com\",\n \"applicationId\": \"APPLICATION_ID\",\n \"id\": \"USER_LICENSE_ID\",\n \"userId\": \"user1@cymbalgroup.com\"\n}\n```\n\nExample:\n```text\ncurl -H \"Authorization: Bearer {TOKEN}\" https://appsmarket.googleapis.com/appsmarket/v2/customerLicense/APPLICATION_ID/cymbalgroup.com\n```\n\nExample:\n```text\n{\n \"kind\": \"appsmarket#customerLicense\",\n \"id\": \"CUSTOMER_LICENSE_ID\",\n \"applicationId\": \"APPLICATION_ID\",\n \"customerId\": \"cymbalgroup.com\",\n \"state\": \"ACTIVE\",\n \"editions\": [\n {\n \"editionId\": \"default_edition\",\n \"seatCount\": -1\n }\n ]\n}\n```\n\nExample:\n```text\ncurl -H \"Authorization: Bearer {TOKEN}\" https://appsmarket.googleapis.com/appsmarket/v2/userLicense/APPLICATION_ID/user3@cymbalgroup.com\n```\n\nExample:\n```text\n{\n \"kind\": \"appsmarket#userLicense\",\n \"enabled\": false,\n \"state\": \"ACTIVE\",\n \"editionId\": \"default_edition\",\n \"customerId\": \"cymbalgroup.com\",\n \"applicationId\": \"APPLICATION_ID\",\n \"id\": \"USER_LICENSE_ID\",\n \"userId\": \"user3@cymbalgroup.com\"\n}\n```\n\nExample:\n```text\ncurl -H \"Authorization: Bearer {TOKEN}\" https://appsmarket.googleapis.com/appsmarket/v2/userLicense/APPLICATION_ID/user2@cymbalgroup.com\n```\n\nExample:\n```text\n{\n \"kind\": \"appsmarket#userLicense\",\n \"enabled\": true,\n \"state\": \"ACTIVE\",\n \"editionId\": \"default_edition\",\n \"customerId\": \"cymbalgroup.com\",\n \"applicationId\": \"APPLICATION_ID\",\n \"id\": \"USER_LICENSE_ID\",\n \"userId\": \"user2@cymbalgroup.com\"\n}\n```\n\nExample:\n```text\n{\n \"kind\": \"appsmarket#userLicense\",\n \"enabled\": false,\n \"state\": \"UNLICENSED\",\n \"applicationId\": \"APPLICATION_ID\",\n \"id\": \"USER_LICENSE_ID\",\n \"userId\": \"user2@cymbalgroup.com\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.692Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":92,"estimatedTokens":489}}818{"id":"doc-google_workspace_developer_tools_google_for_deve-ec70cbda","source":"documentation","title":"Google Workspace Developer Tools | Google for Developers","url":"https://developers.google.com/workspace/guides/developer-tools","text":"Example:\n```text\ncode --install-extension google-workspace.google-workspace-developer-tools\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.692Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":28}}819{"id":"doc-configure_the_sheets_mcp_server_google_sheets_go-2a6e1704","source":"documentation","title":"Configure the Sheets MCP server | Google Sheets | Google for Developers","url":"https://developers.google.com/workspace/sheets/api/guides/configure-mcp-server","text":"Example:\n```text\ngcloud services enable sheets.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable sheetsmcp.googleapis.com --project=PROJECT_ID\n```\n\nExample:\n```text\n{\n \"mcpServers\": {\n \"sheets\": {\n \"serverUrl\": \"https://sheetsmcp.googleapis.com/mcp/v1\",\n \"oauth\": {\n \"clientId\": \"OAUTH_CLIENT_ID\",\n \"clientSecret\": \"OAUTH_CLIENT_SECRET\"\n }\n }\n }\n}\n```\n\nExample:\n```text\nagy\n```\n\nExample:\n```text\n/mcp\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.696Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":36,"estimatedTokens":122}}820{"id":"doc-google_sheets_api_overview_google_for_developers-6766d67f","source":"documentation","title":"Google Sheets API Overview | Google for Developers","url":"https://developers.google.com/workspace/sheets/api/guides/concepts","text":"Example:\n```text\nhttps://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit?gid=SHEET_ID#gid=SHEET_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.696Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":30}}821{"id":"doc-extend_google_sheets_apps_script_google_for_deve-c1a7991f","source":"documentation","title":"Extend Google Sheets | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/sheets","text":"Example:\n```text\nfunction logProductInfo() {\n const sheet = SpreadsheetApp.getActiveSheet();\n const data = sheet.getDataRange().getValues();\n for (let i = 0; i < data.length; i++) {\n Logger.log('Product name: ' + data[i][0]);\n Logger.log('Product number: ' + data[i][1]);\n }\n}\n```\n\nExample:\n```text\nfunction addProduct() {\n const sheet = SpreadsheetApp.getActiveSheet();\n sheet.appendRow(['Cotton Sweatshirt XL', 'css004']);\n}\n```\n\nExample:\n```text\nfunction formatMySpreadsheet() {\n // Set the font style of the cells in the range of B2:C2 to be italic.\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const sheet = ss.getSheets()[0];\n const cell = sheet.getRange('B2:C2');\n cell.setFontStyle('italic');\n}\n```\n\nExample:\n```text\nfunction validateMySpreadsheet() {\n // Set a rule for the cell B4 to be a number between 1 and 100.\n const cell = SpreadsheetApp.getActive().getRange('B4');\n const rule = SpreadsheetApp.newDataValidation()\n .requireNumberBetween(1, 100)\n .setAllowInvalid(false)\n .setHelpText('Number must be between 1 and 100.')\n .build();\n cell.setDataValidation(rule);\n}\n```\n\nExample:\n```text\nfunction newChart() {\n // Generate a chart representing the data in the range of A1:B15.\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const sheet = ss.getSheets()[0];\n\n const chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B15'))\n .setPosition(5, 5, 0, 0)\n .build();\n\n sheet.insertChart(chart);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.697Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":63,"estimatedTokens":384}}822{"id":"doc-choose_google_drive_api_scopes_google_for_develo-4328737d","source":"documentation","title":"Choose Google Drive API scopes | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/api-specific-auth","text":"Example:\n```text\nList<String> SCOPES = Arrays.asList(\n DriveScopes.DRIVE_FILE,\n DriveScopes.DRIVE_METADATA_READONLY\n);\n```\n\nExample:\n```text\nSCOPES = [\n \"https://www.googleapis.com/auth/drive.file\",\n \"https://www.googleapis.com/auth/drive.metadata.readonly\",\n]\n```\n\nExample:\n```text\nconst SCOPES = [\n 'https://www.googleapis.com/auth/drive.file',\n 'https://www.googleapis.com/auth/drive.metadata.readonly'\n];\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.698Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":25,"estimatedTokens":108}}823{"id":"doc-javascript_quickstart_google_drive_google_for_de-3479c45e","source":"documentation","title":"JavaScript quickstart | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/quickstart/js","text":"Example:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <title>Drive API Quickstart</title>\n <meta charset=\"utf-8\" />\n </head>\n <body>\n <p>Drive API Quickstart</p>\n\n <!--Add buttons to initiate auth sequence and sign out-->\n <button id=\"authorize_button\" onclick=\"handleAuthClick()\">Authorize</button>\n <button id=\"signout_button\" onclick=\"handleSignoutClick()\">Sign Out</button>\n\n <pre id=\"content\" style=\"white-space: pre-wrap;\"></pre>\n\n <script type=\"text/javascript\">\n /* exported gapiLoaded */\n /* exported gisLoaded */\n /* exported handleAuthClick */\n /* exported handleSignoutClick */\n\n // TODO(developer): Set to client ID from the Developer Console\n const CLIENT_ID = '<YOUR_CLIENT_ID>';\n\n // Discovery doc URL for APIs used by the quickstart\n const DISCOVERY_DOC = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';\n\n // Authorization scopes required by the API; multiple scopes can be\n // included, separated by spaces.\n const SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly';\n\n let tokenClient;\n let gapiInited = false;\n let gisInited = false;\n\n document.getElementById('authorize_button').style.visibility = 'hidden';\n document.getElementById('signout_button').style.visibility = 'hidden';\n\n /**\n * Callback after api.js is loaded.\n */\n function gapiLoaded() {\n gapi.load('client', initializeGapiClient);\n }\n\n /**\n * Callback after the API client is loaded. Loads the\n * discovery doc to initialize the API.\n */\n async function initializeGapiClient() {\n await gapi.client.init({\n discoveryDocs: [DISCOVERY_DOC],\n });\n gapiInited = true;\n maybeEnableButtons();\n }\n\n /**\n * Callback after Google Identity Services are loaded.\n */\n function gisLoaded() {\n tokenClient = google.accounts.oauth2.initTokenClient({\n client_id: CLIENT_ID,\n scope: SCOPES,\n callback: '', // defined later\n });\n gisInited = true;\n maybeEnableButtons();\n }\n\n /**\n * Enables user interaction after all libraries are loaded.\n */\n function maybeEnableButtons() {\n if (gapiInited && gisInited) {\n document.getElementById('authorize_button').style.visibility = 'visible';\n }\n }\n\n /**\n * Sign in the user upon button click.\n */\n function handleAuthClick() {\n tokenClient.callback = async (resp) => {\n if (resp.error !== undefined) {\n throw (resp);\n }\n document.getElementById('signout_button').style.visibility = 'visible';\n document.getElementById('authorize_button').innerText = 'Refresh';\n await listFiles();\n };\n\n if (gapi.client.getToken() === null) {\n // Prompt the user to select a Google Account and ask for consent to share their data\n // when establishing a new session.\n tokenClient.requestAccessToken({prompt: 'consent'});\n } else {\n // Skip display of account chooser and consent dialog for an existing session.\n tokenClient.requestAccessToken({prompt: ''});\n }\n }\n\n /**\n * Sign out the user upon button click.\n */\n function handleSignoutClick() {\n const token = gapi.client.getToken();\n if (token !== null) {\n google.accounts.oauth2.revoke(token.access_token);\n gapi.client.setToken('');\n document.getElementById('content').innerText = '';\n document.getElementById('authorize_button').innerText = 'Authorize';\n document.getElementById('signout_button').style.visibility = 'hidden';\n }\n }\n\n /**\n * Print metadata for first 10 files.\n */\n async function listFiles() {\n let response;\n try {\n response = await gapi.client.drive.files.list({\n 'pageSize': 10,\n 'fields': 'files(id, name)',\n });\n } catch (err) {\n document.getElementById('content').innerText = err.message;\n return;\n }\n const files = response.result.files;\n if (!files || files.length == 0) {\n document.getElementById('content').innerText = 'No files found.';\n return;\n }\n // Flatten to string to display\n const output = files.reduce(\n (str, file) => `${str}${file.name} (${file.id})\\n`,\n 'Files:\\n');\n document.getElementById('content').innerText = output;\n }\n </script>\n <script async defer src=\"https://apis.google.com/js/api.js\" onload=\"gapiLoaded()\"></script>\n <script async defer src=\"https://accounts.google.com/gsi/client\" onload=\"gisLoaded()\"></script>\n </body>\n</html>\n```\n\nExample:\n```text\nnpm install http-server\n```\n\nExample:\n```text\nnpx http-server -p 8000\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.699Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":161,"estimatedTokens":1243}}824{"id":"doc-create_and_manage_files_google_drive_google_for_-72f004d7","source":"documentation","title":"Create and manage files | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/create-file","text":"Example:\n```text\n/**\n * Create an empty file.\n * @return {string} The created file's ID.\n */\nasync function createEmptyFile() {\n // Get credentials and build service\n // TODO(developer): Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});\n const service = google.drive({version: 'v3', auth});\n\n try {\n const response = await service.files.create({});\n console.log('File ID: ' + response.data.id);\n return response.data.id;\n } catch (err) {\n // TODO(developer): Handle error\n console.error(err);\n }\n}\n```\n\nExample:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files' \\\n -H 'Authorization: Bearer ACCESS_TOKEN'\n```\n\nExample:\n```text\n/**\n * Pre-generate unique file IDs.\n */\nasync function generateFileIds() {\n // Get credentials and build service\n // TODO(developer): Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});\n const service = google.drive({version: 'v3', auth});\n\n try {\n const response = await service.files.generateIds({\n count: 10,\n space: 'drive'\n });\n const ids = response.data.ids;\n console.log('Generated IDs:');\n for (const id of ids) {\n console.log(id);\n }\n } catch (err) {\n // TODO(developer): Handle error\n console.error(err);\n }\n}\n```\n\nExample:\n```text\ncurl 'https://www.googleapis.com/drive/v3/files/generateIds?count=10&space=drive' \\\n -H 'Authorization: Bearer ACCESS_TOKEN'\n```\n\nExample:\n```text\n/**\n * Copy an existing file.\n * @return {string} The copied file's ID.\n */\nasync function copyFile() {\n // Get credentials and build service\n // TODO(developer): Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});\n const service = google.drive({version: 'v3', auth});\n\n try {\n const response = await service.files.copy({\n fileId: 'FILE_ID',\n requestBody: {\n name: 'FILE_COPY_NAME'\n }\n });\n console.log('Copied file ID: ' + response.data.id);\n return response.data.id;\n } catch (err) {\n // TODO(developer): Handle error\n console.error(err);\n }\n}\n```\n\nExample:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files/FILE_ID/copy' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"name\": \"FILE_COPY_NAME\"\n }'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.700Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":114,"estimatedTokens":686}}825{"id":"doc-download_and_export_files_google_drive_google_fo-82150e43","source":"documentation","title":"Download and export files | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/manage-downloads","text":"Example:\n```text\n/**\n * Downloads a file from Drive.\n * @param {string} fileId The ID of the file to download.\n * @return {Blob} The file content as a Blob.\n */\nfunction downloadFile(fileId) {\n var url = 'https://www.googleapis.com/drive/v3/files/' + fileId + '?alt=media';\n var response = UrlFetchApp.fetch(url, {\n headers: {\n 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()\n }\n });\n return response.getBlob();\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.Arrays;\n\n/* Class to demonstrate use-case of drive's download file. */\npublic class DownloadFile {\n\n /**\n * Download a Document file in PDF format.\n *\n * @param realFileId file ID of any workspace document format file.\n * @return byte array stream if successful, {@code null} otherwise.\n * @throws IOException if service account credentials file not found.\n */\n public static ByteArrayOutputStream downloadFile(String realFileId) throws IOException {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application.*/\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n\n try {\n OutputStream outputStream = new ByteArrayOutputStream();\n\n service.files().get(realFileId)\n .executeMediaAndDownloadTo(outputStream);\n\n return (ByteArrayOutputStream) outputStream;\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to move file: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport io\n\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\nfrom googleapiclient.http import MediaIoBaseDownload\n\n\ndef download_file(real_file_id):\n \"\"\"Downloads a file\n Args:\n real_file_id: ID of the file to download\n Returns : IO object with location.\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n file_id = real_file_id\n\n # pylint: disable=maybe-no-member\n request = service.files().get_media(fileId=file_id)\n file = io.BytesIO()\n downloader = MediaIoBaseDownload(file, request)\n done = False\n while done is False:\n status, done = downloader.next_chunk()\n print(f\"Download {int(status.progress() * 100)}.\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n file = None\n\n return file.getvalue()\n\n\nif __name__ == \"__main__\":\n download_file(real_file_id=\"1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9\")\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Downloads a file from Google Drive.\n * @param {string} fileId The ID of the file to download.\n * @return {Promise<number>} The status of the download.\n */\nasync function downloadFile(fileId) {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // Download the file.\n const file = await service.files.get({\n fileId,\n alt: 'media',\n });\n\n // Print the status of the download.\n console.log(file.status);\n return file.status;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nfunction downloadFile()\n {\n try {\n\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $realFileId = readline(\"Enter File Id: \");\n $fileId = '0BwwA4oUTeiV1UVNwOHItT0xfa2M';\n $fileId = $realFileId;\n $response = $driveService->files->get($fileId, array(\n 'alt' => 'media'));\n $content = $response->getBody()->getContents();\n return $content;\n\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Download;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use-case of drive's download file.\n public class DownloadFile\n {\n /// <summary>\n /// Download a Document file in PDF format.\n /// </summary>\n /// <param name=\"fileId\">file ID of any workspace document format file.</param>\n /// <returns>byte array stream if successful, null otherwise.</returns>\n public static MemoryStream DriveDownloadFile(string fileId)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential\n .GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n var request = service.Files.Get(fileId);\n var stream = new MemoryStream();\n\n // Add a handler which will be notified on progress changes.\n // It will notify on each chunk download and when the\n // download is completed or failed.\n request.MediaDownloader.ProgressChanged +=\n progress =>\n {\n switch (progress.Status)\n {\n case DownloadStatus.Downloading:\n {\n Console.WriteLine(progress.BytesDownloaded);\n break;\n }\n case DownloadStatus.Completed:\n {\n Console.WriteLine(\"Download complete.\");\n break;\n }\n case DownloadStatus.Failed:\n {\n Console.WriteLine(\"Download failed.\");\n break;\n }\n }\n };\n request.Download(stream);\n\n return stream;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\ncurl -L \"https://www.googleapis.com/drive/v3/files/FILE_ID?alt=media\" \\\n --header \"Authorization: Bearer ACCESS_TOKEN\" \\\n --output \"FILE_NAME\"\n```\n\nExample:\n```text\nconst fs = require('fs');\n\nconst dest = fs.createWriteStream('/path/to/dest/file.ext');\nconst response = await service.files.get(\n { fileId, alt: 'media' },\n { responseType: 'stream' }\n);\nresponse.data\n .on('end', () => {\n console.log('Download complete.');\n })\n .on('error', (err) => {\n console.error('Error downloading file.', err);\n })\n .pipe(dest);\n```\n\nExample:\n```text\nconst file = await service.files.get({\n fileId,\n alt: 'media',\n}, { responseType: 'arraybuffer' });\n\n// Convert the ArrayBuffer to a Node.js Buffer object.\nconst buffer = Buffer.from(file.data);\n```\n\nExample:\n```text\nRange: bytes=500-999\n```\n\nExample:\n```text\ncurl -L \"https://www.googleapis.com/drive/v3/files/FILE_ID/revisions/REVISION_ID?alt=media\" \\\n --header \"Authorization: Bearer ACCESS_TOKEN\" \\\n --output \"FILE_NAME\"\n```\n\nExample:\n```text\ncurl \"https://www.googleapis.com/drive/v3/files/FILE_ID?fields=webContentLink\" \\\n --header \"Authorization: Bearer ACCESS_TOKEN\" \\\n --header \"Accept: application/json\"\n```\n\nExample:\n```text\ncurl --request POST \"https://www.googleapis.com/drive/v3/files/FILE_ID/download?mimeType=video/mp4\" \\\n --header \"Authorization: Bearer ACCESS_TOKEN\" \\\n --header \"Content-Length: 0\" \\\n --header \"Accept: application/json\"\n```\n\nExample:\n```text\n/**\n * Exports a Google Workspace document.\n * @param {string} fileId The ID of the file to export.\n * @param {string} mimeType The MIME type to export to.\n * @return {Blob} The exported content as a Blob.\n */\nfunction exportPdf(fileId, mimeType) {\n var url = 'https://www.googleapis.com/drive/v3/files/' + fileId + '/export?mimeType=' + encodeURIComponent(mimeType);\n var response = UrlFetchApp.fetch(url, {\n headers: {\n 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()\n }\n });\n return response.getBlob();\n}\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.Arrays;\n\n/* Class to demonstrate use-case of drive's export pdf. */\npublic class ExportPdf {\n\n /**\n * Download a Document file in PDF format.\n *\n * @param realFileId file ID of any workspace document format file.\n * @return byte array stream if successful, {@code null} otherwise.\n * @throws IOException if service account credentials file not found.\n */\n public static ByteArrayOutputStream exportPdf(String realFileId) throws IOException {\n // Load pre-authorized user credentials from the environment.\n // TODO(developer) - See https://developers.google.com/identity for\n // guides on implementing OAuth2 for your application.\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n\n OutputStream outputStream = new ByteArrayOutputStream();\n try {\n service.files().export(realFileId, \"application/pdf\")\n .executeMediaAndDownloadTo(outputStream);\n\n return (ByteArrayOutputStream) outputStream;\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to export file: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport io\n\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\nfrom googleapiclient.http import MediaIoBaseDownload\n\n\ndef export_pdf(real_file_id):\n \"\"\"Download a Document file in PDF format.\n Args:\n real_file_id : file ID of any workspace document format file\n Returns : IO object with location\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n file_id = real_file_id\n\n # pylint: disable=maybe-no-member\n request = service.files().export_media(\n fileId=file_id, mimeType=\"application/pdf\"\n )\n file = io.BytesIO()\n downloader = MediaIoBaseDownload(file, request)\n done = False\n while done is False:\n status, done = downloader.next_chunk()\n print(f\"Download {int(status.progress() * 100)}.\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n file = None\n\n return file.getvalue()\n\n\nif __name__ == \"__main__\":\n export_pdf(real_file_id=\"1zbp8wAyuImX91Jt9mI-CAX_1TqkBLDEDcr2WeXBbKUY\")\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Exports a Google Doc as a PDF.\n * @param {string} fileId The ID of the file to export.\n * @return {Promise<number>} The status of the export request.\n */\nasync function exportPdf(fileId) {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // Export the file as a PDF.\n const result = await service.files.export({\n fileId,\n mimeType: 'application/pdf',\n });\n\n // Print the status of the export.\n console.log(result.status);\n return result.status;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nfunction exportPdf()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $realFileId = readline(\"Enter File Id: \");\n $fileId = '1ZdR3L3qP4Bkq8noWLJHSr_iBau0DNT4Kli4SxNc2YEo';\n $fileId = $realFileId;\n $response = $driveService->files->export($fileId, 'application/pdf', array(\n 'alt' => 'media'));\n $content = $response->getBody()->getContents();\n return $content;\n\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Download;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use of Drive export pdf\n public class ExportPdf\n {\n /// <summary>\n /// Download a Document file in PDF format.\n /// </summary>\n /// <param name=\"fileId\">Id of the file.</param>\n /// <returns>Byte array stream if successful, null otherwise</returns>\n public static MemoryStream DriveExportPdf(string fileId)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n var request = service.Files.Export(fileId, \"application/pdf\");\n var stream = new MemoryStream();\n // Add a handler which will be notified on progress changes.\n // It will notify on each chunk download and when the\n // download is completed or failed.\n request.MediaDownloader.ProgressChanged +=\n progress =>\n {\n switch (progress.Status)\n {\n case DownloadStatus.Downloading:\n {\n Console.WriteLine(progress.BytesDownloaded);\n break;\n }\n case DownloadStatus.Completed:\n {\n Console.WriteLine(\"Download complete.\");\n break;\n }\n case DownloadStatus.Failed:\n {\n Console.WriteLine(\"Download failed.\");\n break;\n }\n }\n };\n request.Download(stream);\n return stream;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\ncurl -L \"https://www.googleapis.com/drive/v3/files/FILE_ID/export?mimeType=application/pdf\" \\\n --header \"Authorization: Bearer ACCESS_TOKEN\" \\\n --output \"FILE_NAME.pdf\"\n```\n\nExample:\n```text\ncurl \"https://www.googleapis.com/drive/v3/files/FILE_ID?fields=id,name,exportLinks\" \\\n --header \"Authorization: Bearer ACCESS_TOKEN\" \\\n --header \"Accept: application/json\"\n```\n\nExample:\n```text\ncurl \"https://www.googleapis.com/drive/v3/files/FILE_ID/revisions/REVISION_ID?fields=id,name,exportLinks\" \\\n --header \"Authorization: Bearer ACCESS_TOKEN\" \\\n --header \"Accept: application/json\"\n```\n\nExample:\n```text\ncurl --request POST \"https://www.googleapis.com/drive/v3/files/FILE_ID/download?mimeType=MIME_TYPE&revisionId=REVISION_ID\" \\\n --header \"Authorization: Bearer ACCESS_TOKEN\" \\\n --header \"Content-Length: 0\" \\\n --header \"Accept: application/json\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.701Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":23,"totalLines":628,"estimatedTokens":4787}}826{"id":"doc-update_a_space_google_chat_google_for_developers-4514c6da","source":"documentation","title":"Update a space | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/update-spaces","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = ['https://www.googleapis.com/auth/chat.spaces'];\n\n// This sample shows how to update a space with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n space: {\n // Replace SPACE_NAME here\n name: 'spaces/SPACE_NAME',\n displayName: 'New space display name',\n },\n // The field paths to update. Separate multiple values with commas or use `*`\n // to update all field paths.\n updateMask: {\n // The field paths to update.\n paths: ['display_name'],\n },\n };\n\n // Make the request\n const response = await chatClient.updateSpace(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.spaces\"]\n\n# This sample shows how to update a space with user credential\ndef update_space_with_user_cred():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.UpdateSpaceRequest(\n space = {\n # Replace SPACE_NAME here\n 'name': 'spaces/SPACE_NAME',\n 'display_name': 'New space display name'\n },\n # The field paths to update. Separate multiple values with commas.\n update_mask = 'displayName'\n )\n\n # Make the request\n response = client.update_space(request)\n\n # Handle the response\n print(response)\n\nupdate_space_with_user_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.UpdateSpaceRequest;\nimport com.google.chat.v1.Space;\nimport com.google.protobuf.FieldMask;\n\n// This sample shows how to update space with user credential.\npublic class UpdateSpaceUserCred {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.spaces\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n UpdateSpaceRequest.Builder request = UpdateSpaceRequest.newBuilder()\n .setSpace(Space.newBuilder()\n // Replace SPACE_NAME here.\n .setName(\"spaces/SPACE_NAME\")\n .setDisplayName(\"New space display name\"))\n .setUpdateMask(FieldMask.newBuilder()\n // The field paths to update.\n .addPaths(\"display_name\"));\n Space response = chatServiceClient.updateSpace(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to update a space with user credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.spaces'\n * referenced in the manifest file (appsscript.json).\n */\nfunction updateSpaceUserCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here\n const name = \"spaces/SPACE_NAME\";\n const space = {\n displayName: \"New space display name\",\n };\n // The field paths to update. Separate multiple values with commas or use\n // `*` to update all field paths.\n const updateMask = \"displayName\";\n\n // Make the request\n const response = Chat.Spaces.patch(space, name, {\n updateMask: updateMask,\n });\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\nfrom apiclient.discovery import build\n\n# Define your app's authorization scopes.\n# When modifying these scopes, delete the file token.json, if it exists.\nSCOPES = [\"https://www.googleapis.com/auth/chat.app.spaces\"]\n\ndef main():\n '''\n Authenticates with Chat API using app authentication,\n then updates the specified space description and guidelines.\n '''\n\n # Specify service account details.\n creds = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n )\n\n # Build a service endpoint for Chat API.\n chat = build('chat', 'v1', credentials=creds)\n\n # Use the service endpoint to call Chat API.\n result = chat.spaces().patch(\n\n # The space to update, and the updated space details.\n #\n # Replace {space} with a space name.\n # Obtain the space name from the spaces resource of Chat API,\n # or from a space's URL.\n name='spaces/SPACE',\n updateMask='spaceDetails',\n body={\n\n 'spaceDetails': {\n 'description': 'This description was updated with Chat API!',\n 'guidelines': 'These guidelines were updated with Chat API!'\n }\n\n }\n\n ).execute()\n\n # Prints details about the updated space.\n print(result)\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython3 chat_space_update_app.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.703Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":190,"estimatedTokens":1277}}827{"id":"doc-annotate_emails_in_the_promotions_tab_gmail_goog-2865c29f","source":"documentation","title":"Annotate emails in the Promotions tab | Gmail | Google for Developers","url":"https://developers.google.com/workspace/gmail/promotab/overview","text":"Example:\n```text\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n<html>\n <head>\n <script type=\"application/ld+json\">\n [\n // Build the first image preview in your product carousel:\n {\n \"@context\": \"http://schema.org/\",\n \"@type\": \"PromotionCard\",\n \"image\": \"IMAGE_URL1\",\n \"url\": \"PROMO_URL1\",\n\n // Optionally, include the following PromotionCard properties:\n \"headline\": \"HEADLINE1\",\n \"price\": PRICE1,\n \"priceCurrency\": \"PRICE_CURRENCY1\",\n \"discountValue\": DISCOUNT_VALUE1,\n \"position\": POSITION\n },\n\n // Build the second image preview in your product carousel:\n {\n \"@context\": \"http://schema.org/\",\n \"@type\": \"PromotionCard\",\n \"image\": \"IMAGE_URL2\",\n \"url\": \"PROMO_URL2\",\n\n // Optionally, include the following PromotionCard properties:\n \"headline\": \"HEADLINE2\",\n \"price\": PRICE2,\n \"priceCurrency\": \"PRICE_CURRENCY2\",\n \"discountValue\": DISCOUNT_VALUE2,\n \"position\": POSITION\n }\n\n // To include more image previews, add additional PromotionCard objects.\n // You can include up to 10 image previews in a product carousel.\n\n ]\n </script>\n </head>\n\n <body>\n // The message of your email\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n<html>\n <head>\n // Build the first image preview in your product carousel:\n <div itemscope itemtype=\"http://schema.org/PromotionCard\">\n <meta itemprop=\"image\" content=\"IMAGE_URL1\"/>\n <meta itemprop=\"url\" content=\"PROMO_URL1\"/>\n\n // Optionally, include the following PromotionCard properties:\n <meta itemprop=\"headline\" content=\"HEADLINE1\"/>\n <meta itemprop=\"price\" content=\"PRICE1\"/>\n <meta itemprop=\"priceCurrency\" content=\"PRICE_CURRENCY1\"/>\n <meta itemprop=\"discountValue\" content=\"DISCOUNT_VALUE1\"/>\n <meta itemprop=\"position\" content=\"POSITION\"/>\n </div>\n\n // Build the second image preview in your product carousel:\n <div itemscope itemtype=\"http://schema.org/PromotionCard\">\n <meta itemprop=\"image\" content=\"IMAGE_URL2\"/>\n <meta itemprop=\"url\" content=\"PROMO_URL2\"/>\n\n // Optionally, include the following PromotionCard properties:\n <meta itemprop=\"headline\" content=\"HEADLINE2\"/>\n <meta itemprop=\"price\" content=\"PRICE2\"/>\n <meta itemprop=\"priceCurrency\" content=\"PRICE_CURRENCY2\"/>\n <meta itemprop=\"discountValue\" content=\"DISCOUNT_VALUE2\"/>\n <meta itemprop=\"position\" content=\"POSITION\"/>\n </div>\n\n // To include more image previews, add additional PromotionCard objects.\n // You can include up to 10 image previews in a product carousel.\n </head>\n\n <body>\n // The message of your email\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n<html>\n <head>\n <script type=\"application/ld+json\">\n [{\n \"@context\": \"http://schema.org/\",\n \"@type\": \"PromotionCard\",\n \"image\": \"IMAGE_URL\",\n \"url\": \"PROMO_URL\",\n\n // Optionally, include the following PromotionCard properties:\n \"headline\": \"HEADLINE\",\n \"price\": PRICE,\n \"priceCurrency\": \"PRICE_CURRENCY\",\n \"discountValue\": DISCOUNT_VALUE\n }]\n </script>\n </head>\n\n <body>\n // The message of your email\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n<html>\n <head>\n // Build the first image preview in your product carousel:\n <div itemscope itemtype=\"http://schema.org/PromotionCard\">\n <meta itemprop=\"image\" content=\"IMAGE_URL\"/>\n <meta itemprop=\"url\" content=\"PROMO_URL\"/>\n\n // Optionally, include the following PromotionCard properties:\n <meta itemprop=\"headline\" content=\"HEADLINE\"/>\n <meta itemprop=\"price\" content=\"PRICE\"/>\n <meta itemprop=\"priceCurrency\" content=\"PRICE_CURRENCY\"/>\n <meta itemprop=\"discountValue\" content=\"DISCOUNT_VALUE\"/>\n </div>\n\n </head>\n\n <body>\n // The message of your email\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n<html>\n <head>\n <script type=\"application/ld+json\">\n [{\n \"@context\": \"http://schema.org/\",\n \"@type\": \"DiscountOffer\",\n \"description\": \"DESCRIPTION\",\n \"discountCode\": \"DISCOUNT_CODE\",\n \"availabilityStarts\": \"START_DATE_TIME\",\n \"availabilityEnds\": \"END_DATE_TIME\"\n }]\n </script>\n </head>\n\n <body>\n // The message of your email\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n<html>\n <head>\n <div itemscope itemtype=\"http://schema.org/DiscountOffer\">\n <meta itemprop=\"description\" content=\"DESCRIPTION\"/>\n <meta itemprop=\"discountCode\" content=\"DISCOUNT_CODE\"/>\n <meta itemprop=\"availabilityStarts\" content=\"START_DATE_TIME\"/>\n <meta itemprop=\"availabilityEnds\" content=\"END_DATE_TIME\"/>\n </div>\n </head>\n\n <body>\n // The message of your email.\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n<html>\n <head>\n <script type=\"application/ld+json\">\n [{\n \"@context\": \"http://schema.org/\",\n \"@type\": \"DiscountOffer\",\n \"description\": \"DESCRIPTION\",\n \"discountCode\": \"DISCOUNT_CODE\",\n \"availabilityStarts\": \"START_DATE_TIME\",\n \"availabilityEnds\": \"END_DATE_TIME\",\n \"offerPageUrl\": \"OFFER_PAGE_URL\",\n \"merchantHomepageUrl\": \"MERCHANT_HOMEPAGE_URL\"\n }]\n </script>\n </head>\n\n <body>\n // The message of your email\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n<html>\n <head>\n <div itemscope itemtype=\"http://schema.org/DiscountOffer\">\n <meta itemprop=\"description\" content=\"DESCRIPTION\"/>\n <meta itemprop=\"discountCode\" content=\"DISCOUNT_CODE\"/>\n <meta itemprop=\"availabilityStarts\" content=\"START_DATE_TIME\"/>\n <meta itemprop=\"availabilityEnds\" content=\"END_DATE_TIME\"/>\n <meta itemprop=\"offerpageurl\" content=\"OFFER_PAGE_URL\"/>\n <meta itemprop=\"merchantHomepageUrl\" content=\"MERCHANT_HOMEPAGE_URL\"/>\n </div>\n </head>\n\n <body>\n // The message of your email.\n </body>\n</html>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.704Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":232,"estimatedTokens":1583}}828{"id":"doc-remove_a_member_from_a_space_google_chat_google_-d180f44f","source":"documentation","title":"Remove a member from a space | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/delete-members","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = ['https://www.googleapis.com/auth/chat.delete'];\n\n// This sample shows how to delete a space with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here\n name: 'spaces/SPACE_NAME',\n };\n\n // Make the request\n const response = await chatClient.deleteSpace(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\n{\n \"name\": \"spaces/SPACE_NAME/members/MEMBER_NAME\",\n \"state\": \"NOT_A_MEMBER\"\n}\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\nfrom apiclient.discovery import build\n\n# Define your app's authorization scopes.\n# When modifying these scopes, delete the file token.json, if it exists.\nSCOPES = [\"https://www.googleapis.com/auth/chat.app.memberships\"]\n\ndef main():\n '''\n Authenticates with Chat API using app authentication,\n then deletes the specified membership.\n '''\n\n # Specify service account details.\n creds = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n )\n\n # Build a service endpoint for Chat API.\n chat = build('chat', 'v1', credentials=creds)\n\n # Use the service endpoint to call Chat API.\n result = chat.spaces().members().delete(\n\n # The membership to delete.\n #\n # Replace SPACE with a space name.\n # Obtain the space name from the spaces resource of Chat API,\n # or from a space's URL.\n #\n # Replace MEMBER with a membership name.\n # Obtain the membership name from the memberships resource of\n # Chat API. To delete a Chat app's membership, replace MEMBER\n # with app; an alias for the app calling the API.\n name='spaces/SPACE/members/MEMBER'\n\n ).execute()\n\n # Print Chat API's response in your command line interface.\n # When deleting a membership, the response body is empty.\n print(result)\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython3 chat_membership_delete_app.py\n```\n\nExample:\n```text\n{\n \"name\": \"spaces/SPACE/members/MEMBER\",\n \"state\": \"NOT_A_MEMBER\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.705Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":100,"estimatedTokens":600}}829{"id":"doc-invite_or_add_a_user_google_group_or_google_chat-c9017a40","source":"documentation","title":"Invite or add a user, Google Group, or Google Chat app to a space | Google for Developers","url":"https://developers.google.com/workspace/chat/create-members","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.memberships',\n];\n\n// This sample shows how to create membership with user credential for a human\n// user\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here.\n parent: 'spaces/SPACE_NAME',\n membership: {\n member: {\n // Replace USER_NAME here\n name: 'users/USER_NAME',\n // User type for the membership\n type: 'HUMAN',\n },\n },\n };\n\n // Make the request\n const response = await chatClient.createMembership(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.memberships\"]\n\n# This sample shows how to create membership with user credential for a human\n# user\ndef create_membership_with_user_cred():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.CreateMembershipRequest(\n # Replace SPACE_NAME here\n parent = \"spaces/SPACE_NAME\",\n membership = {\n \"member\": {\n # Replace USER_NAME here\n \"name\": \"users/USER_NAME\",\n # user type for the membership\n \"type_\": \"HUMAN\"\n }\n }\n )\n\n # Make the request\n response = client.create_membership(request)\n\n # Handle the response\n print(response)\n\ncreate_membership_with_user_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.CreateMembershipRequest;\nimport com.google.chat.v1.Membership;\nimport com.google.chat.v1.SpaceName;\nimport com.google.chat.v1.User;\n\n// This sample shows how to create membership with user credential for a human\n// user.\npublic class CreateMembershipUserCred {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.memberships\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n CreateMembershipRequest.Builder request = CreateMembershipRequest.newBuilder()\n // replace SPACE_NAME here\n .setParent(\"spaces/SPACE_NAME\")\n .setMembership(Membership.newBuilder()\n .setMember(User.newBuilder()\n // replace USER_NAME here\n .setName(\"users/USER_NAME\")\n // user type for the membership\n .setType(User.Type.HUMAN)));\n Membership response = chatServiceClient.createMembership(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to create membership with user credential for a human user\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.memberships'\n * referenced in the manifest file (appsscript.json).\n */\nfunction createMembershipUserCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here.\n const parent = \"spaces/SPACE_NAME\";\n const membership = {\n member: {\n // TODO(developer): Replace USER_NAME here\n name: \"users/USER_NAME\",\n // User type for the membership\n type: \"HUMAN\",\n },\n };\n\n // Make the request\n const response = Chat.Spaces.Members.create(membership, parent);\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.memberships',\n];\n\n// This sample shows how to create membership with user credential for a group\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here.\n parent: 'spaces/SPACE_NAME',\n membership: {\n groupMember: {\n // Replace GROUP_NAME here\n name: 'groups/GROUP_NAME',\n },\n },\n };\n\n // Make the request\n const response = await chatClient.createMembership(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.memberships\"]\n\n# This sample shows how to create membership with user credential for a group\ndef create_membership_with_user_cred_for_group():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.CreateMembershipRequest(\n # Replace SPACE_NAME here\n parent = \"spaces/SPACE_NAME\",\n membership = {\n \"groupMember\": {\n # Replace GROUP_NAME here\n \"name\": \"groups/GROUP_NAME\"\n }\n }\n )\n\n # Make the request\n response = client.create_membership(request)\n\n # Handle the response\n print(response)\n\ncreate_membership_with_user_cred_for_group()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.CreateMembershipRequest;\nimport com.google.chat.v1.Membership;\nimport com.google.chat.v1.SpaceName;\nimport com.google.chat.v1.Group;\n\n// This sample shows how to create membership with user credential for a group.\npublic class CreateMembershipUserCredForGroup {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.memberships\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n CreateMembershipRequest.Builder request = CreateMembershipRequest.newBuilder()\n // replace SPACE_NAME here\n .setParent(\"spaces/SPACE_NAME\")\n .setMembership(Membership.newBuilder()\n .setGroupMember(Group.newBuilder()\n // replace GROUP_NAME here\n .setName(\"groups/GROUP_NAME\")));\n Membership response = chatServiceClient.createMembership(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to create membership with user credential for a group\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.memberships'\n * referenced in the manifest file (appsscript.json).\n */\nfunction createMembershipUserCredForGroup() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here.\n const parent = \"spaces/SPACE_NAME\";\n const membership = {\n groupMember: {\n // TODO(developer): Replace GROUP_NAME here\n name: \"groups/GROUP_NAME\",\n },\n };\n\n // Make the request\n const response = Chat.Spaces.Members.create(membership, parent);\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.memberships.app',\n];\n\n// This sample shows how to create an app membership.\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here.\n parent: 'spaces/SPACE_NAME',\n membership: {\n member: {\n // Member name for app membership, do not change this\n name: 'users/app',\n // User type for the membership\n type: 'BOT',\n },\n },\n };\n\n // Make the request\n const response = await chatClient.createMembership(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.memberships.app\"]\n\n# This sample shows how to create membership with app credential for an app\ndef create_membership_with_user_cred_for_app():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.CreateMembershipRequest(\n # Replace SPACE_NAME here\n parent = \"spaces/SPACE_NAME\",\n membership = {\n \"member\": {\n # member name for app membership, do not change this.\n \"name\": \"users/app\",\n # user type for the membership\n \"type_\": \"BOT\"\n }\n }\n )\n\n # Make the request\n response = client.create_membership(request)\n\n # Handle the response\n print(response)\n\ncreate_membership_with_user_cred_for_app()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.CreateMembershipRequest;\nimport com.google.chat.v1.Membership;\nimport com.google.chat.v1.SpaceName;\nimport com.google.chat.v1.User;\n\n// This sample shows how to create membership with user credential for the\n// calling app.\npublic class CreateMembershipUserCredForApp {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.memberships.app\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n CreateMembershipRequest.Builder request = CreateMembershipRequest.newBuilder()\n // replace SPACE_NAME here\n .setParent(\"spaces/SPACE_NAME\")\n .setMembership(Membership.newBuilder()\n .setMember(User.newBuilder()\n // member name for app membership, do not change this.\n .setName(\"users/app\")\n // user type for the membership\n .setType(User.Type.BOT)));\n Membership response = chatServiceClient.createMembership(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to create membership with app credential for an app\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.memberships.app'\n * referenced in the manifest file (appsscript.json).\n */\nfunction createMembershipUserCredForApp() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here.\n const parent = \"spaces/SPACE_NAME\";\n const membership = {\n member: {\n // Member name for app membership, do not change this\n name: \"users/app\",\n // User type for the membership\n type: \"BOT\",\n },\n };\n\n // Make the request\n const response = Chat.Spaces.Members.create(membership, parent);\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\nfrom apiclient.discovery import build\n\n# Define your app's authorization scopes.\n# When modifying these scopes, delete the file token.json, if it exists.\nSCOPES = [\"https://www.googleapis.com/auth/chat.app.memberships\"]\n\ndef main():\n '''\n Authenticates with Chat API using app authentication,\n then adds a user to a Chat space by creating a membership.\n '''\n\n # Specify service account details.\n creds = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n )\n\n # Build a service endpoint for Chat API.\n chat = build('chat', 'v1', credentials=creds)\n\n # Use the service endpoint to call Chat API.\n result = chat.spaces().members().create(\n\n # The space in which to create a membership.\n parent = 'spaces/SPACE',\n\n # Specify which user the membership is for.\n body = {\n 'member': {\n 'name':'users/USER',\n 'type': 'HUMAN'\n }\n }\n\n ).execute()\n\n # Prints details about the created membership.\n print(result)\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython3 chat_membership_app_create.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.707Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":463,"estimatedTokens":3125}}830{"id":"doc-delete_a_space_google_chat_google_for_developers-eef9b5f2","source":"documentation","title":"Delete a space | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/delete-spaces","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = ['https://www.googleapis.com/auth/chat.delete'];\n\n// This sample shows how to delete a space with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here\n name: 'spaces/SPACE_NAME',\n };\n\n // Make the request\n const response = await chatClient.deleteSpace(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\nfrom apiclient.discovery import build\n\n# Define your app's authorization scopes.\n# When modifying these scopes, delete the file token.json, if it exists.\nSCOPES = [\"https://www.googleapis.com/auth/chat.app.delete\"]\n\ndef main():\n '''\n Authenticates with Chat API using app authentication,\n then deletes the specified space.\n '''\n\n # Specify service account details.\n creds = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n )\n\n # Build a service endpoint for Chat API.\n chat = build('chat', 'v1', credentials=creds)\n\n # Use the service endpoint to call Chat API.\n result = chat.spaces().delete(\n\n # The space to delete.\n #\n # Replace SPACE with a space name.\n # Obtain the space name from the spaces resource of Chat API,\n # or from a space's URL.\n name='spaces/SPACE'\n\n ).execute()\n\n # Print Chat API's response in your command line interface.\n # When deleting a space, the response body is empty.\n print(result)\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython3 chat_space_delete_app.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.708Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":79,"estimatedTokens":476}}831{"id":"doc-smart_chips_google_sheets_google_for_developers-8a516ad3","source":"documentation","title":"Smart chips | Google Sheets | Google for Developers","url":"https://developers.google.com/workspace/sheets/api/guides/chips","text":"Example:\n```text\n{\n \"updateCells\": {\n \"rows\": [\n {\n \"values\": [\n {\n \"userEnteredValue\": {\n \"stringValue\": \"@ is the owner of @.\"\n },\n \"chipRuns\": [\n {\n \"chip\": {\n \"personProperties\": {\n \"email\": \"johndoe@gmail.com\",\n \"displayFormat\": \"DEFAULT\"\n }\n }\n },\n {\n \"startIndex\": 18,\n \"chip\": {\n \"richLinkProperties\": {\n \"uri\": \"https://docs.google.com/document/d/YOUR_DOCUMENT_ID/edit\"\n }\n }\n }\n ]\n }\n ]\n }\n ],\n \"fields\": \"userEnteredValue,chipRuns\",\n \"range\": {\n \"startRowIndex\": 0,\n \"startColumnIndex\": 0\n }\n }\n }\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.709Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":43,"estimatedTokens":300}}832{"id":"doc-tables_google_sheets_google_for_developers-de010436","source":"documentation","title":"Tables | Google Sheets | Google for Developers","url":"https://developers.google.com/workspace/sheets/api/guides/tables","text":"Example:\n```text\n{\n \"addTable\": {\n \"table\": {\n \"name\": \"Project Tracker\",\n \"tableId\": \"123\",\n \"range\": {\n \"sheetId\": 0,\n \"startColumnIndex\": 0,\n \"endColumnIndex\": 5,\n \"startRowIndex\": 0,\n \"endRowIndex\": 5,\n },\n \"columnProperties\": [\n {\n \"columnIndex\": 0,\n \"columnName\": \"Column 1\",\n \"columnType\": \"PERCENT\"\n },\n {\n \"columnIndex\": 1,\n \"columnName\": \"Column 2\",\n \"columnType\": \"DROPDOWN\",\n \"dataValidationRule\": {\n \"condition\": {\n \"type\": \"ONE_OF_LIST\",\n \"values\": [\n {\n \"userEnteredValue\": \"Not Started\"\n },\n {\n \"userEnteredValue\": \"In Progress\"\n },\n {\n \"userEnteredValue\": \"Complete\"\n }\n ]\n }\n }\n }\n ],\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.709Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":48,"estimatedTokens":251}}833{"id":"doc-create_a_named_space_in_google_chat_google_for_d-b180357e","source":"documentation","title":"Create a named space in Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/create-spaces","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.spaces.create',\n];\n\n// This sample shows how to create a named space with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n space: {\n spaceType: 'SPACE',\n // Replace DISPLAY_NAME here.\n displayName: 'DISPLAY_NAME',\n },\n };\n\n // Make the request\n const response = await chatClient.createSpace(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.spaces.create\"]\n\ndef create_space_with_user_cred():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.CreateSpaceRequest(\n space = {\n \"space_type\": 'SPACE',\n # Replace DISPLAY_NAME here.\n \"display_name\": 'DISPLAY_NAME'\n }\n )\n\n # Make the request\n response = client.create_space(request)\n\n # Handle the response\n print(response)\n\ncreate_space_with_user_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.CreateSpaceRequest;\nimport com.google.chat.v1.Space;\n\n// This sample shows how to create space with user credential.\npublic class CreateSpaceUserCred {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.spaces.create\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n CreateSpaceRequest.Builder request = CreateSpaceRequest.newBuilder()\n .setSpace(Space.newBuilder()\n .setSpaceType(Space.SpaceType.SPACE)\n // Replace DISPLAY_NAME here.\n .setDisplayName(\"DISPLAY_NAME\"));\n Space response = chatServiceClient.createSpace(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to create space with user credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.spaces.create'\n * referenced in the manifest file (appsscript.json).\n */\nfunction createSpaceUserCred() {\n // Initialize request argument(s)\n const space = {\n spaceType: \"SPACE\",\n // TODO(developer): Replace DISPLAY_NAME here\n displayName: \"DISPLAY_NAME\",\n };\n\n // Make the request\n const response = Chat.Spaces.create(space);\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\nfrom apiclient.discovery import build\n\n# Define your app's authorization scopes.\n# When modifying these scopes, delete the file token.json, if it exists.\nSCOPES = [\"https://www.googleapis.com/auth/chat.app.spaces.create\"]\n\ndef main():\n '''\n Authenticates with Chat API using app authentication,\n then creates a Chat space.\n '''\n\n # Specify service account details.\n creds = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n )\n\n # Build a service endpoint for Chat API.\n chat = build('chat', 'v1', credentials=creds)\n\n # Use the service endpoint to call Chat API.\n result = chat.spaces().create(\n\n # Details about the space to create.\n body = {\n\n # To create a named space, set spaceType to SPACE.\n 'spaceType': 'SPACE',\n\n # The user-visible name of the space.\n 'displayName': 'API-made',\n\n # The customer ID of the Workspace domain.\n 'customer': 'CUSTOMER'\n }\n\n ).execute()\n\n # Prints details about the created space.\n print(result)\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython3 chat_space_create_named_app.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.710Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":171,"estimatedTokens":1050}}834{"id":"doc-analytics_data_service_apps_script_google_for_de-e54036ac","source":"documentation","title":"Analytics Data Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/advanced/analyticsdata","text":"Example:\n```text\n/**\n * Runs a report of a Google Analytics 4 property ID. Creates a sheet with the\n * report.\n */\nfunction runReport() {\n /**\n * TODO(developer): Uncomment this variable and replace with your\n * Google Analytics 4 property ID before running the sample.\n */\n const propertyId = \"YOUR-GA4-PROPERTY-ID\";\n\n try {\n const metric = AnalyticsData.newMetric();\n metric.name = \"activeUsers\";\n\n const dimension = AnalyticsData.newDimension();\n dimension.name = \"city\";\n\n const dateRange = AnalyticsData.newDateRange();\n dateRange.startDate = \"2020-03-31\";\n dateRange.endDate = \"today\";\n\n const request = AnalyticsData.newRunReportRequest();\n request.dimensions = [dimension];\n request.metrics = [metric];\n request.dateRanges = dateRange;\n\n const report = AnalyticsData.Properties.runReport(\n request,\n `properties/${propertyId}`,\n );\n if (!report.rows) {\n console.log(\"No rows returned.\");\n return;\n }\n\n const spreadsheet = SpreadsheetApp.create(\"Google Analytics Report\");\n const sheet = spreadsheet.getActiveSheet();\n\n // Append the headers.\n const dimensionHeaders = report.dimensionHeaders.map((dimensionHeader) => {\n return dimensionHeader.name;\n });\n const metricHeaders = report.metricHeaders.map((metricHeader) => {\n return metricHeader.name;\n });\n const headers = [...dimensionHeaders, ...metricHeaders];\n\n sheet.appendRow(headers);\n\n // Append the results.\n const rows = report.rows.map((row) => {\n const dimensionValues = row.dimensionValues.map((dimensionValue) => {\n return dimensionValue.value;\n });\n const metricValues = row.metricValues.map((metricValues) => {\n return metricValues.value;\n });\n return [...dimensionValues, ...metricValues];\n });\n\n sheet.getRange(2, 1, report.rows.length, headers.length).setValues(rows);\n\n console.log(\"Report spreadsheet created: %s\", spreadsheet.getUrl());\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.715Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":74,"estimatedTokens":529}}835{"id":"doc-event_types_google_calendar_google_for_developer-91a81b4c","source":"documentation","title":"Event types | Google Calendar | Google for Developers","url":"https://developers.google.com/calendar/api/guides/event-types","text":"Example:\n```text\nconst CALENDAR_ID = 'CALENDAR_ID' || 'primary';\n\n/** Lists default events. */\nfunction listDefaultEvents() {\n listEvents('default');\n}\n\n/** Lists birthday events. */\nfunction listBirthdays() {\n listEvents('birthday');\n}\n\n/** Lists events from Gmail. */\nfunction listEventsFromGmail() {\n listEvents('fromGmail');\n}\n\n/**\n * Lists events with the given event type. If no type is specified, lists all events.\n * See https://developers.google.com/workspace/calendar/api/v3/reference/events/list\n */\nfunction listEvents(eventType = undefined) {\n // Query parameters for the list request.\n const optionalArgs = {\n eventTypes: eventType ? [eventType] : undefined,\n singleEvents: true,\n timeMax: '2024-07-30T00:00:00+01:00',\n timeMin: '2024-07-29T00:00:00+01:00',\n }\n try {\n var response = Calendar.Events.list(CALENDAR_ID, optionalArgs);\n response.items.forEach(event => console.log(event));\n } catch (exception) {\n console.log(exception.message);\n }\n}\n\n/**\n * Reads the event with the given eventId.\n * See https://developers.google.com/workspace/calendar/api/v3/reference/events/get\n */\nfunction readEvent() {\n try {\n var response = Calendar.Events.get(CALENDAR_ID, 'EVENT_ID');\n console.log(response);\n } catch (exception) {\n console.log(exception.message);\n }\n}\n\n/** Creates a default event. */\nfunction createDefaultEvent() {\n const event = {\n start: { dateTime: '2024-07-30T10:30:00+01:00'},\n end: { dateTime: '2024-07-30T12:30:00+01:00'},\n description: 'Created from Apps Script.',\n eventType: 'default',\n summary: 'Sample event',\n }\n createEvent(event);\n}\n\n/** Creates a birthday event. */\nfunction createBirthday() {\n const event = {\n start: { date: '2024-01-29' },\n end: { date: '2024-01-30' },\n eventType: 'birthday',\n recurrence: [\"RRULE:FREQ=YEARLY\"],\n summary: \"My friend's birthday\",\n transparency: \"transparent\",\n visibility: \"private\",\n }\n createEvent(event);\n}\n\n/**\n * Creates a Calendar event.\n * See https://developers.google.com/workspace/calendar/api/v3/reference/events/insert\n */\nfunction createEvent(event) {\n\n try {\n var response = Calendar.Events.insert(event, CALENDAR_ID);\n console.log(response);\n } catch (exception) {\n console.log(exception.message);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.716Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":94,"estimatedTokens":580}}836{"id":"doc-manage_long_running_operations_google_drive_goog-7d3dece5","source":"documentation","title":"Manage long-running operations | Google Drive | Google for Developers","url":"https://developers.google.com/drive/api/guides/long-running-operations","text":"Example:\n```text\nFILE_ID\n```\n\nExample:\n```text\n{\n \"done\": true,\n \"metadata\": {\n \"@type\": \"type.googleapis.com/google.apps.drive.v3.DownloadFileMetadata\",\n \"resourceKey\": \"RESOURCE_KEY\"\n },\n \"name\": \"NAME\",\n \"response\": {\n \"@type\": \"type.googleapis.com/google.apps.drive.v3.DownloadFileResponse\",\n \"downloadUri\": \"DOWNLOAD_URI\",\n \"partialDownloadAllowed\": false\n }\n}\n```\n\nExample:\n```text\noperations.get(name='NAME');\n```\n\nExample:\n```text\ncurl -i -H \\\n 'Authorization: Bearer $(gcloud auth print-access-token)\" \\\n 'https://googleapis.com/drive/v3/operations/NAME?alt=json'\n```\n\nExample:\n```text\nFILE_IDREVISION_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.718Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":40,"estimatedTokens":166}}837{"id":"doc-class_datasourcespec_apps_script_google_for_deve-668b7f2d","source":"documentation","title":"Class DataSourceSpec | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/data-source-spec","text":"Example:\n```text\nconst dataSourceTable = SpreadsheetApp.getActive()\n .getSheetByName('Data Sheet 1')\n .getDataSourceTables()[0];\nconst spec = dataSourceTable.getDataSource().getSpec();\nif (spec.getType() === SpreadsheetApp.DataSourceType.BIGQUERY) {\n const bqSpec = spec.asBigQuery();\n Logger.log('Project ID: %s\\n', bqSpec.getProjectId());\n Logger.log('Raw query string: %s\\n', bqSpec.getRawQuery());\n}\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec().asLooker();\n\nif (spec.getType() === SpreadsheetApp.DataSourceType.LOOKER) {\n const lookerSpec = spec.asLooker();\n Logger.log('Looker instance URL: %s\\n', lookerSpec.getInstanceUrl());\n}\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec().asLooker();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\n\nconst newSpec = spec.copy();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst parameters = spec.getParameters();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst type = spec.getType();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.719Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":68,"estimatedTokens":471}}838{"id":"doc-use_connected_sheets_apps_script_google_for_deve-2dd383de","source":"documentation","title":"Use Connected Sheets | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/sheets/connected-sheets","text":"Example:\n```text\nfunction addDataSource() {\n SpreadsheetApp.enableBigQueryExecution();\n var spreadsheet = SpreadsheetApp.getActive();\n }\n```\n\nExample:\n```text\nfunction addDataSource() {\n SpreadsheetApp.enableLookerExecution();\n var spreadsheet = SpreadsheetApp.getActive();\n }\n```\n\nExample:\n```text\n{ ...\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/bigquery.readonly\",\n \"https://www.googleapis.com/auth/spreadsheets\",\n \"https://www.googleapis.com/auth/drive\" ],\n... }\n```\n\nExample:\n```text\n// For operations that fetch data from BigQuery, enableBigQueryExecution() must be called.\nSpreadsheetApp.enableBigQueryExecution();\nvar spreadsheet = SpreadsheetApp.create('Test connected sheets');\nLogger.log('New test spreadsheet: %s', spreadsheet.getUrl());\n\n// Build data source spec by selecting a table.\nvar dataSourceSpec = SpreadsheetApp.newDataSourceSpec()\n .asBigQuery()\n .setProjectId('<YOUR_PROJECT_ID>')\n .setTableProjectId('bigquery-public-data')\n .setDatasetId('ncaa_basketball')\n .setTableId('mbb_historical_tournament_games')\n .build();\n// Add data source and its associated data source sheet.\nvar dataSourceSheet = spreadsheet.insertDataSourceSheet(dataSourceSpec);\nvar dataSource = dataSourceSheet.getDataSource();\n```\n\nExample:\n```text\n// For operations that fetch data from Looker, enableLookerExecution() must be called.\nSpreadsheetApp.enableLookerExecution();\nvar spreadsheet = SpreadsheetApp.create('Test connected sheets');\nLogger.log('New test spreadsheet: %s', spreadsheet.getUrl());\n\n// Build data source spec by selecting a table.\nvar dataSourceSpec = SpreadsheetApp.newDataSourceSpec()\n .asLooker()\n .setInstanceUrl('<INSTANCE_URL>')\n .setModelName('<MODEL_NAME>')\n .setExploreName('<EXPLORE_NAME>')\n .build();\n// Add data source and its associated data source sheet.\nvar dataSourceSheet = spreadsheet.insertDataSourceSheet(dataSourceSpec);\nvar dataSource = dataSourceSheet.getDataSource();\n```\n\nExample:\n```text\nvar rootCell = spreadsheet.insertSheet('pivotTableSheet').getRange('A1');\n\n// Add data source pivot table and set data source specific configurations.\nvar dataSourcePivotTable = rootCell.createDataSourcePivotTable(dataSource);\nvar rowGroup = dataSourcePivotTable.addRowGroup('season');\nrowGroup.sortDescending().setGroupLimit(5);\ndataSourcePivotTable.addColumnGroup('win_school_ncaa');\ndataSourcePivotTable.addPivotValue('win_pts',\nSpreadsheetApp.PivotTableSummarizeFunction.AVERAGE);\ndataSourcePivotTable.addPivotValue('game_date',\nSpreadsheetApp.PivotTableSummarizeFunction.COUNTA);\nvar filterCriteria = SpreadsheetApp.newFilterCriteria()\n .whenTextEqualToAny(['Duke', 'North Carolina'])\n .build();\ndataSourcePivotTable.addFilter('win_school_ncaa', filterCriteria);\n\n// Get a regular pivot table instance and set shared configurations.\nvar pivotTable = dataSourcePivotTable.asPivotTable();\npivotTable.setValuesDisplayOrientation(SpreadsheetApp.Dimension.ROWS);\n```\n\nExample:\n```text\nvar status = dataSourcePivotTable.getStatus();\nLogger.log('Initial state: %s', status.getExecutionState());\n\ndataSourcePivotTable.refreshData();\n\nstatus = dataSourcePivotTable.waitForCompletion(/* timeoutInSeconds= */ 60);\nLogger.log('Ending state: %s', status.getExecutionState());\nif (status.getExecutionState() == SpreadsheetApp.DataExecutionState.ERROR) {\n Logger.log('Error: %s (%s)', status.getErrorCode(),\n status.getErrorMessage());\n}\n```\n\nExample:\n```text\n// Add data source with query parameter.\nfunction addDataSource() {\n SpreadsheetApp.enableBigQueryExecution();\n var spreadsheet = SpreadsheetApp.getActive();\n\n // Add a new sheet and use A1 cell as the parameter cell.\n var parameterCell = spreadsheet.insertSheet('parameterSheet').getRange('A1');\n parameterCell.setValue('Duke');\n\n // Add data source with query parameter.\n var dataSourceSpec = SpreadsheetApp.newDataSourceSpec()\n .asBigQuery()\n .setProjectId('<YOUR_PROJECT_ID>')\n .setRawQuery('select * from `bigquery-public-data`.`ncaa_basketball`.`mbb_historical_tournament_games` WHERE win_school_ncaa = @SCHOOL')\n .setParameterFromCell('SCHOOL', 'parameterSheet!A1')\n .build();\n var dataSourceSheet = spreadsheet.insertDataSourceSheet(dataSourceSpec);\n dataSourceSheet.asSheet().setName('ncaa_data');\n}\n\n// Function used to configure event trigger to refresh data source sheet.\nfunction refreshOnParameterEdit(e) {\n var editedRange = e.range;\n if (editedRange.getSheet().getName() != 'parameterSheet') {\n return;\n }\n // Check that the edited range includes A1.\n if (editedRange.getRow() > 1 || editedRange.getColumn() > 1) {\n return;\n }\n\n var spreadsheet = e.source;\n SpreadsheetApp.enableBigQueryExecution();\n spreadsheet.getSheetByName('ncaa_data').asDataSourceSheet().refreshData();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.720Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":143,"estimatedTokens":1203}}839{"id":"doc-class_lookerdatasourcespec_apps_script_google_fo-33601ae1","source":"documentation","title":"Class LookerDataSourceSpec | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/looker-data-source-spec","text":"Example:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\nconst lookerDataSourceSpec = ss.getDataSources()[0].getSpec().asLooker();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\n\nconst newSpec = spec.copy();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\nconst lookerDataSourceSpec = ss.getDataSources()[0].getSpec().asLooker();\nconst exploreName = lookerDataSourceSpec.getExploreName();\nLogger.log(exploreName);\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\nconst lookerDataSourceSpec = ss.getDataSources()[0].getSpec().asLooker();\nconst instanceUrl = lookerDataSourceSpec.getInstanceUrl();\nLogger.log(instanceUrl);\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\nconst lookerDataSourceSpec = ss.getDataSources()[0].getSpec().asLooker();\nconst modelName = lookerDataSourceSpec.getModelName();\nLogger.log(modelName);\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst parameters = spec.getParameters();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst type = spec.getType();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.721Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":78,"estimatedTokens":505}}840{"id":"doc-google_chat_api_client_libraries_google_for_deve-6243d654","source":"documentation","title":"Google Chat API client libraries | Google for Developers","url":"https://developers.google.com/workspace/chat/libraries","text":"Example:\n```text\nnpm install @google-apps/chat\n```\n\nExample:\n```text\npython -m venv <your-env>source <your-env>/bin/activatepip install google-apps-chat\n```\n\nExample:\n```text\n<dependencyManagement>\n <dependencies>\n <dependency>\n <groupId>com.google.cloud</groupId>\n <artifactId>libraries-bom</artifactId>\n <version>26.42.0</version>\n <type>pom</type>\n <scope>import</scope>\n </dependency>\n </dependencies>\n </dependencyManagement>\n\n <dependencies>\n <dependency>\n <groupId>com.google.cloud</groupId>\n <artifactId>google-cloud-chat</artifactId>\n </dependency>\n```\n\nExample:\n```text\n<dependency>\n <groupId>com.google.cloud</groupId>\n <artifactId>google-cloud-chat</artifactId>\n <version>0.10.0</version>\n</dependency>\n```\n\nExample:\n```text\n<dependency>\n <groupId>com.google.cloud</groupId>\n <artifactId>google-cloud-chat</artifactId>\n <version>0.9.0</version>\n</dependency>\n```\n\nExample:\n```text\nimplementation 'com.google.cloud:google-cloud-chat:0.10.0'\n```\n\nExample:\n```text\nlibraryDependencies += \"com.google.cloud\" % \"google-cloud-chat\" % \"0.10.0\"\n```\n\nExample:\n```text\nimport \"cloud.google.com/go\"\n```\n\nExample:\n```text\ngo get cloud.google.com/go/chat\n```\n\nExample:\n```text\ngem install google-apps-chat\n```\n\nExample:\n```text\ncomposer require google/apps-chat\n```\n\nExample:\n```text\npip install --upgrade google-api-python-client\n```\n\nExample:\n```text\neasy_install --upgrade google-api-python-client\n```\n\nExample:\n```text\npython setup.py install\n```\n\nExample:\n```text\ngem install google-api-client\n```\n\nExample:\n```text\ngem update -y google-api-client\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.722Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":105,"estimatedTokens":411}}841{"id":"doc-get_details_about_a_membership_google_chat_googl-d6662fca","source":"documentation","title":"Get details about a membership | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/get-members","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.memberships.readonly',\n];\n\n// This sample shows how to get membership with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME and MEMBER_NAME here\n name: 'spaces/SPACE_NAME/members/MEMBER_NAME',\n };\n\n // Make the request\n const response = await chatClient.getMembership(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.memberships.readonly\"]\n\n# This sample shows how to get membership with user credential\ndef get_membership_with_user_cred():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.GetMembershipRequest(\n # Replace SPACE_NAME and MEMBER_NAME here\n name = 'spaces/SPACE_NAME/members/MEMBER_NAME',\n )\n\n # Make the request\n response = client.get_membership(request)\n\n # Handle the response\n print(response)\n\nget_membership_with_user_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.GetMembershipRequest;\nimport com.google.chat.v1.Membership;\n\n// This sample shows how to get membership with user credential.\npublic class GetMembershipUserCred {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.memberships.readonly\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n GetMembershipRequest.Builder request = GetMembershipRequest.newBuilder()\n // replace SPACE_NAME and MEMBERSHIP_NAME here\n .setName(\"spaces/SPACE_NAME/members/MEMBERSHIP_NAME\");\n Membership response = chatServiceClient.getMembership(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to get membership with user credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.memberships.readonly'\n * referenced in the manifest file (appsscript.json).\n */\nfunction getMembershipUserCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME and MEMBER_NAME here\n const name = \"spaces/SPACE_NAME/members/MEMBER_NAME\";\n\n // Make the request\n const response = Chat.Spaces.Members.get(name);\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nimport {createClientWithAppCredentials} from './authentication-utils.js';\n\n// This sample shows how to get membership with app credential\nasync function main() {\n // Create a client\n const chatClient = createClientWithAppCredentials();\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME and MEMBER_NAME here\n name: 'spaces/SPACE_NAME/members/MEMBER_NAME',\n };\n\n // Make the request\n const response = await chatClient.getMembership(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_app_credentials\nfrom google.apps import chat_v1 as google_chat\n\n# This sample shows how to get membership with app credential\ndef get_membership_with_app_cred():\n # Create a client\n client = create_client_with_app_credentials()\n\n # Initialize request argument(s)\n request = google_chat.GetMembershipRequest(\n # Replace SPACE_NAME and MEMBER_NAME here\n name = 'spaces/SPACE_NAME/members/MEMBER_NAME',\n )\n\n # Make the request\n response = client.get_membership(request)\n\n # Handle the response\n print(response)\n\nget_membership_with_app_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.GetMembershipRequest;\nimport com.google.chat.v1.Membership;\n\n// This sample shows how to get membership with app credential.\npublic class GetMembershipAppCred {\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithAppCredentials()) {\n GetMembershipRequest.Builder request = GetMembershipRequest.newBuilder()\n // replace SPACE_NAME and MEMBERSHIP_NAME here\n .setName(\"spaces/SPACE_NAME/members/MEMBERSHIP_NAME\");\n Membership response = chatServiceClient.getMembership(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to get membership with app credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.bot'\n * used by service accounts.\n */\nfunction getMembershipAppCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME and MEMBER_NAME here\n const name = \"spaces/SPACE_NAME/members/MEMBER_NAME\";\n const parameters = {};\n\n // Make the request\n const response = Chat.Spaces.Members.get(\n name,\n parameters,\n getHeaderWithAppCredentials(),\n );\n\n // Handle the response\n console.log(response);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.724Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":206,"estimatedTokens":1386}}842{"id":"doc-class_lookerdatasourcespecbuilder_apps_script_go-42530dad","source":"documentation","title":"Class LookerDataSourceSpecBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/looker-data-source-spec-builder","text":"Example:\n```text\nconst lookerDataSourceSpecBuilder =\n SpreadsheetApp.newDataSourceSpec().asLooker();\n```\n\nExample:\n```text\nconst bigQueryDataSourceSpec = SpreadsheetApp.newDataSourceSpec().asBigQuery();\n// TODO(developer): Replace with the required dataset, project and table IDs.\nbigQueryDataSourceSpec.setDatasetId('my data set id');\nbigQueryDataSourceSpec.setProjectId('my project id');\nbigQueryDataSourceSpec.setTableId('my table id');\n\nbigQueryDataSourceSpec.build();\n```\n\nExample:\n```text\nconst lookerDataSourceSpecBuilder =\n SpreadsheetApp.newDataSourceSpec().asLooker();\nconst lookerSpec = lookerDataSourceSpecBuilder.setExploreName('my explore name')\n .setInstanceUrl('my instance url')\n .setModelName('my model name')\n .build();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\n\nconst newSpec = spec.copy();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\nconst lookerDataSourceSpec = ss.getDataSources()[0].getSpec().asLooker();\nconst exploreName = lookerDataSourceSpec.getExploreName();\nLogger.log(exploreName);\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\nconst lookerDataSourceSpec = ss.getDataSources()[0].getSpec().asLooker();\nconst instanceUrl = lookerDataSourceSpec.getInstanceUrl();\nLogger.log(instanceUrl);\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\nconst lookerDataSourceSpec = ss.getDataSources()[0].getSpec().asLooker();\nconst modelName = lookerDataSourceSpec.getModelName();\nLogger.log(modelName);\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst parameters = spec.getParameters();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst type = spec.getType();\n```\n\nExample:\n```text\nconst specBuilder = SpreadsheetApp.newDataSourceSpec();\nspecBuilder.removeAllParameters();\n```\n\nExample:\n```text\nconst specBuilder = SpreadsheetApp.newDataSourceSpec();\nspecBuilder.removeParameter('x');\n```\n\nExample:\n```text\nconst lookerDataSourceSpecBuilder =\n SpreadsheetApp.newDataSourceSpec().asLooker();\n// TODO(developer): replace explore name with your own\nlookerDataSourceSpecBuilder.setExploreName('my explore name');\n```\n\nExample:\n```text\nconst lookerDataSourceSpecBuilder =\n SpreadsheetApp.newDataSourceSpec().asLooker();\n// TODO(developer): replace instance url with your own\nlookerDataSourceSpecBuilder.setInstanceUrl('my instance url');\n```\n\nExample:\n```text\nconst lookerDataSourceSpecBuilder =\n SpreadsheetApp.newDataSourceSpec().asLooker();\n// TODO(developer): replace model name with your own\nlookerDataSourceSpecBuilder.setModelName('my model name');\n```\n\nExample:\n```text\nconst specBuilder = SpreadsheetApp.newDataSourceSpec().asBigQuery();\nspecBuilder.setParameterFromCell('x', 'A1');\nconst bigQuerySpec = specBuilder.build();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.726Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":138,"estimatedTokens":916}}843{"id":"doc-class_calendareventseries_apps_script_google_for-1c358151","source":"documentation","title":"Class CalendarEventSeries | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/calendar/calendar-event-series","text":"Example:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Adds an email notification for 15 minutes before the event.\nevent.addEmailReminder(15);\n```\n\nExample:\n```text\n// Example 1: Add a guest to one event\nfunction addAttendeeToEvent() {\n // Replace the below values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to add\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar containing\n // event\n const eventId = '123abc'; // ID of event instance\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n const event = calendar.getEventById(eventId);\n if (event === null) {\n // Event not found\n console.log('Event not found', eventId);\n return;\n }\n event.addGuest(attendeeEmail);\n}\n\n// Example 2: Add a guest to all events on a calendar within a specified\n// timeframe\nfunction addAttendeeToAllEvents() {\n // Replace the following values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to add\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar with the\n // events\n const startDate =\n new Date('YYYY-MM-DD'); // The first date to add the guest to the events\n const endDate =\n new Date('YYYY-MM-DD'); // The last date to add the guest to the events\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n // Get the events within the specified timeframe\n const calEvents = calendar.getEvents(startDate, endDate);\n console.log(calEvents.length); // Checks how many events are found\n // Loop through all events and add the attendee to each of them\n for (let i = 0; i < calEvents.length; i++) {\n const event = calEvents[i];\n event.addGuest(attendeeEmail);\n }\n}\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Adds a pop-up notification for 15 minutes before the event.\nevent.addPopupReminder(15);\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Determines whether people can add themselves as guests to the event and logs\n// it.\nconsole.log(event.anyoneCanAddSelf());\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Gets the color of the calendar event and logs it.\nconst eventColor = event.getColor();\nconsole.log(eventColor);\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Gets a list of the creators of the event and logs it.\nconsole.log(event.getCreators());\n```\n\nExample:\n```text\n// Opens the calendar by using its ID.\n// To get the user's default calendar use CalendarApp.getDefault() instead.\n// TODO(developer): Replace the calendar ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 8:10 AM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 08:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the date that the\n // event was created and logs it.\n const eventCreated = event.getDateCreated();\n console.log(eventCreated);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// To get the user's default calendar use CalendarApp.getDefault() instead.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 4:00 PM and 5:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 16:00:00'),\n new Date('Feb 04, 2023 17:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the description of the\n // event.\n event.setDescription('Important meeting');\n\n // Gets the description of the event and logs it.\n const description = event.getDescription();\n console.log(description);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:00 PM and 6:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 15:00:00'),\n new Date('Feb 04, 2023 18:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, adds email reminders for\n // the user to be sent at 4 and 7 minutes before the event.\n event.addEmailReminder(4);\n event.addEmailReminder(7);\n\n // Gets the minute values for all email reminders that are set up for the user\n // for this event and logs it.\n const emailReminder = event.getEmailReminders();\n console.log(emailReminder);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the default calendar and logs all out-of-office events for the current day.\nconst calendar = CalendarApp.getDefaultCalendar();\nconst events = calendar.getEventsForDay(new Date());\nconsole.log(events.filter(e => e.getEventType() === CalendarApp.EventType.OUT_OF_OFFICE));\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Gets a guest by email address.\nconst guestEmailId = event.getGuestByEmail('alex@example.com');\n\n// If the email address corresponds to an event guest, logs the email address.\nif (guestEmailId) {\n console.log(guestEmailId.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Adds two guests to the event by using their email addresses.\nevent.addGuest('alex@example.com');\nevent.addGuest('cruz@example.com');\n\n// Gets the guests list for the event.\nconst guestList = event.getGuestList();\n\n// Loops through the list to get all the guests and logs their email addresses.\nfor (const guest of guestList) {\n console.log(guest.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Gets the guests list for the event, including the owner of the event.\nconst guestList = event.getGuestList(true);\n\n// Loops through the list to get all the guests and logs it.\nfor (const guest of guestList) {\n console.log(guest.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 5th, 2023 that takes place\n// between 9:00 AM and 9:25 AM.\n// For an event series, use calendar.getEventSeriesById('abc123456@google.com');\n// and replace the series ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 05, 2023 09:00:00'),\n new Date('Jan 05, 2023 09:25:00'),\n )[0];\n\n// Gets the ID of the event and logs it.\nconsole.log(event.getId());\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:00 PM and 5:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:00:00'),\n new Date('Feb 01, 2023 17:00:00'),\n )[0];\n\n// Gets the date the event was last updated and logs it.\nconst eventUpdatedDate = event.getLastUpdated();\nconsole.log(eventUpdatedDate);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the location of the\n // event to Mumbai.\n event.setLocation('Mumbai');\n\n // Gets the location of the event and logs it.\n const eventLocation = event.getLocation();\n console.log(eventLocation);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the event status of\n // the effective user and logs it.\n const myStatus = event.getMyStatus();\n console.log(myStatus.toString());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 4:00 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 16:00:00'),\n new Date('Feb 25,2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the ID of the calendar\n // where the event was originally created and logs it.\n const calendarId = event.getOriginalCalendarId();\n console.log(calendarId);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:05 PM and 5:35 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 17:05:00'),\n new Date('Feb 04, 2023 17:35:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, adds two pop-up reminders\n // to the event. The first reminder pops up 5 minutes before the event starts\n // and the second reminder pops up 3 minutes before the event starts.\n event.addPopupReminder(3);\n event.addPopupReminder(5);\n\n // Gets the minute values for all pop-up reminders for the event and logs it.\n const popUpReminder = event.getPopupReminders();\n console.log(popUpReminder);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@example.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, logs the title of the\n // event.\n console.log(event.getTitle());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Gets the first event from the default calendar for today.\nconst today = new Date();\nconst event = CalendarApp.getDefaultCalendar().getEventsForDay(today)[0];\n// Gets the event's transparency and logs it.\nconst transparency = event.getTransparency();\nLogger.log(transparency);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the visibility of the\n // event and logs it.\n const eventVisibility = event.getVisibility();\n console.log(eventVisibility.toString());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether guests\n // can invite other guests and logs it.\n console.log(event.guestsCanInviteOthers());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // guests can't modify it.\n event.setGuestsCanModify(false);\n\n // Determines whether guests can modify the event and logs it.\n console.log(event.guestsCanModify());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether guests\n // can see other guests and logs it.\n console.log(event.guestsCanSeeGuests());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether you're\n // the owner of the event and logs it.\n console.log(event.isOwnedByMe());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 1,2023 16:10:00'),\n new Date('Feb 1,2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, removes all reminders from\n // the event.\n event.removeAllReminders();\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Example 1: Remove a guest from one event\nfunction removeGuestFromEvent() {\n // Replace the below values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to remove\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar containing\n // event\n const eventId = '123abc'; // ID of event instance\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n const event = calendar.getEventById(eventId);\n if (event === null) {\n // Event not found\n console.log('Event not found', eventId);\n return;\n }\n event.removeGuest(attendeeEmail);\n}\n\n// Example 2: Remove a guest from all events on a calendar within a specified\n// timeframe\nfunction removeGuestFromAllEvents() {\n // Replace the following values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to remove\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar with the\n // events\n const startDate = new Date(\n 'YYYY-MM-DD'); // The first date to remove the guest from the events\n const endDate = new Date(\n 'YYYY-MM-DD'); // The last date to remove the attendee from the events\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n // Get the events within the specified timeframe\n const calEvents = calendar.getEvents(startDate, endDate);\n console.log(calEvents.length); // Checks how many events are found\n // Loop through all events and remove the attendee from each of them\n for (let i = 0; i < calEvents.length; i++) {\n const event = calEvents[i];\n event.removeGuest(attendeeEmail);\n }\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 1, 2023 16:10:00'),\n new Date('Feb 1, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, resets the reminders using\n // the calendar's default settings.\n event.resetRemindersToDefault();\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 15th, 2023 that takes\n// place between 3:30 PM and 4:30 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 15, 2023 15:30:00'),\n new Date('Feb 15, 2023 16:30:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // non-guests can't add themselves to the event.\n event.setAnyoneCanAddSelf(false);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the color of the\n // calendar event to green.\n event.setColor(CalendarApp.EventColor.GREEN);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:05 PM and 5:35 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 17:05:00'),\n new Date('Feb 04, 2023 17:35:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the description of the\n // event to 'Meeting.'\n event.setDescription('Meeting');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own. You must have edit access to\n// the calendar.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // guests can invite other guests.\n event.setGuestsCanInviteOthers(true);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the location of the\n // event to Noida.\n event.setLocation('Noida');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event status for\n // the current user to maybe.\n event.setMyStatus(CalendarApp.GuestStatus.MAYBE);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Sets the events in a series to take place every Wednesday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().getEventSeriesById(\n '123456789@example.com',\n);\nconst startDate = new Date('January 2, 2013 03:00:00 PM EST');\nconst recurrence = CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014'));\neventSeries.setRecurrence(recurrence, startDate);\n```\n\nExample:\n```text\n// Sets the events in a series to take place from 3pm to 4pm every Tuesday and\n// Thursday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().getEventSeriesById(\n '123456789@example.com',\n);\nconst startTime = new Date('January 1, 2013 03:00:00 PM EST');\nconst endTime = new Date('January 1, 2013 04:00:00 PM EST');\nconst recurrence =\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekdays(\n [CalendarApp.Weekday.TUESDAY, CalendarApp.Weekday.THURSDAY])\n .until(new Date('January 1, 2014'));\neventSeries.setRecurrence(recurrence, startTime, endTime);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@example.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, changes its title to\n // Event1.\n event.setTitle('Event1');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n// Sets the event's transparency to TRANSPARENT.\nevent.setTransparency(CalendarApp.EventTransparency.TRANSPARENT);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.730Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":39,"totalLines":1005,"estimatedTokens":8315}}844{"id":"doc-class_calendarevent_apps_script_google_for_devel-4eea0e8e","source":"documentation","title":"Class CalendarEvent | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/calendar/calendar-event","text":"Example:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Adds an email notification for 15 minutes before the event.\nevent.addEmailReminder(15);\n```\n\nExample:\n```text\n// Example 1: Add a guest to one event\nfunction addAttendeeToEvent() {\n // Replace the below values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to add\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar containing\n // event\n const eventId = '123abc'; // ID of event instance\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n const event = calendar.getEventById(eventId);\n if (event === null) {\n // Event not found\n console.log('Event not found', eventId);\n return;\n }\n event.addGuest(attendeeEmail);\n}\n\n// Example 2: Add a guest to all events on a calendar within a specified\n// timeframe\nfunction addAttendeeToAllEvents() {\n // Replace the following values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to add\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar with the\n // events\n const startDate =\n new Date('YYYY-MM-DD'); // The first date to add the guest to the events\n const endDate =\n new Date('YYYY-MM-DD'); // The last date to add the guest to the events\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n // Get the events within the specified timeframe\n const calEvents = calendar.getEvents(startDate, endDate);\n console.log(calEvents.length); // Checks how many events are found\n // Loop through all events and add the attendee to each of them\n for (let i = 0; i < calEvents.length; i++) {\n const event = calEvents[i];\n event.addGuest(attendeeEmail);\n }\n}\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Adds a pop-up notification for 15 minutes before the event.\nevent.addPopupReminder(15);\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Determines whether people can add themselves as guests to the event and logs\n// it.\nconsole.log(event.anyoneCanAddSelf());\n```\n\nExample:\n```text\n// Gets an event by its ID.\n// TODO(developer): Replace the string with the ID of the event that you want to\n// delete.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Deletes the event.\nevent.deleteEvent();\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Creates an event named 'My all-day event' for May 16, 2023.\nconst event = calendar.createAllDayEvent(\n 'My all-day event',\n new Date('May 16, 2023'),\n);\n\n// Gets the event's end date and logs it.\nconst endDate = event.getAllDayEndDate();\nconsole.log(endDate);\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Creates an event named 'My all-day event' for May 16, 2023.\nconst event = calendar.createAllDayEvent(\n 'My all-day event',\n new Date('May 16, 2023'),\n);\n\n// Gets the event's start date and logs it.\nconst startDate = event.getAllDayStartDate();\nconsole.log(startDate);\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Gets the color of the calendar event and logs it.\nconst eventColor = event.getColor();\nconsole.log(eventColor);\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Gets a list of the creators of the event and logs it.\nconsole.log(event.getCreators());\n```\n\nExample:\n```text\n// Opens the calendar by using its ID.\n// To get the user's default calendar use CalendarApp.getDefault() instead.\n// TODO(developer): Replace the calendar ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 8:10 AM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 08:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the date that the\n // event was created and logs it.\n const eventCreated = event.getDateCreated();\n console.log(eventCreated);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// To get the user's default calendar use CalendarApp.getDefault() instead.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 4:00 PM and 5:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 16:00:00'),\n new Date('Feb 04, 2023 17:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the description of the\n // event.\n event.setDescription('Important meeting');\n\n // Gets the description of the event and logs it.\n const description = event.getDescription();\n console.log(description);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:00 PM and 6:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 15:00:00'),\n new Date('Feb 04, 2023 18:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, adds email reminders for\n // the user to be sent at 4 and 7 minutes before the event.\n event.addEmailReminder(4);\n event.addEmailReminder(7);\n\n // Gets the minute values for all email reminders that are set up for the user\n // for this event and logs it.\n const emailReminder = event.getEmailReminders();\n console.log(emailReminder);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:00 PM and 5:00 PM.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:00:00'),\n new Date('Feb 01, 2023 17:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the date and time at\n // which the event ends and logs it.\n console.log(event.getEndTime());\n} else {\n // If no event exists within the given time frame, logs that info to the\n // console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 18th, 2023 that takes\n// place between 1:00 PM and 2:00 PM.\nconst event = calendar.getEvents(\n new Date('Feb 18, 2023 13:00:00'),\n new Date('Feb 18, 2023 14:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the event series for\n // the event and sets the color to pale green.\n event.getEventSeries().setColor(CalendarApp.EventColor.PALE_GREEN);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the default calendar and logs all out-of-office events for the current day.\nconst calendar = CalendarApp.getDefaultCalendar();\nconst events = calendar.getEventsForDay(new Date());\nconsole.log(events.filter(e => e.getEventType() === CalendarApp.EventType.OUT_OF_OFFICE));\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Gets a guest by email address.\nconst guestEmailId = event.getGuestByEmail('alex@example.com');\n\n// If the email address corresponds to an event guest, logs the email address.\nif (guestEmailId) {\n console.log(guestEmailId.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Adds two guests to the event by using their email addresses.\nevent.addGuest('alex@example.com');\nevent.addGuest('cruz@example.com');\n\n// Gets the guests list for the event.\nconst guestList = event.getGuestList();\n\n// Loops through the list to get all the guests and logs their email addresses.\nfor (const guest of guestList) {\n console.log(guest.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Gets the guests list for the event, including the owner of the event.\nconst guestList = event.getGuestList(true);\n\n// Loops through the list to get all the guests and logs it.\nfor (const guest of guestList) {\n console.log(guest.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 5th, 2023 that takes place\n// between 9:00 AM and 9:25 AM.\n// For an event series, use calendar.getEventSeriesById('abc123456@google.com');\n// and replace the series ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 05, 2023 09:00:00'),\n new Date('Jan 05, 2023 09:25:00'),\n )[0];\n\n// Gets the ID of the event and logs it.\nconsole.log(event.getId());\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:00 PM and 5:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:00:00'),\n new Date('Feb 01, 2023 17:00:00'),\n )[0];\n\n// Gets the date the event was last updated and logs it.\nconst eventUpdatedDate = event.getLastUpdated();\nconsole.log(eventUpdatedDate);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the location of the\n // event to Mumbai.\n event.setLocation('Mumbai');\n\n // Gets the location of the event and logs it.\n const eventLocation = event.getLocation();\n console.log(eventLocation);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the event status of\n // the effective user and logs it.\n const myStatus = event.getMyStatus();\n console.log(myStatus.toString());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 4:00 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 16:00:00'),\n new Date('Feb 25,2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the ID of the calendar\n // where the event was originally created and logs it.\n const calendarId = event.getOriginalCalendarId();\n console.log(calendarId);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:05 PM and 5:35 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 17:05:00'),\n new Date('Feb 04, 2023 17:35:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, adds two pop-up reminders\n // to the event. The first reminder pops up 5 minutes before the event starts\n // and the second reminder pops up 3 minutes before the event starts.\n event.addPopupReminder(3);\n event.addPopupReminder(5);\n\n // Gets the minute values for all pop-up reminders for the event and logs it.\n const popUpReminder = event.getPopupReminders();\n console.log(popUpReminder);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\n// Gets the date and time at which this calendar event begins and logs it.\nconst startTime = event.getStartTime();\nconsole.log(startTime);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@example.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, logs the title of the\n // event.\n console.log(event.getTitle());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Gets the first event from the default calendar for today.\nconst today = new Date();\nconst event = CalendarApp.getDefaultCalendar().getEventsForDay(today)[0];\n// Gets the event's transparency and logs it.\nconst transparency = event.getTransparency();\nLogger.log(transparency);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the visibility of the\n // event and logs it.\n const eventVisibility = event.getVisibility();\n console.log(eventVisibility.toString());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether guests\n // can invite other guests and logs it.\n console.log(event.guestsCanInviteOthers());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // guests can't modify it.\n event.setGuestsCanModify(false);\n\n // Determines whether guests can modify the event and logs it.\n console.log(event.guestsCanModify());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether guests\n // can see other guests and logs it.\n console.log(event.guestsCanSeeGuests());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\n// Determines whether this event is an all-day event and logs it.\nconsole.log(event.isAllDayEvent());\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether you're\n // the owner of the event and logs it.\n console.log(event.isOwnedByMe());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for Januart 31st, 2023 that takes\n// place between 9:00 AM and 10:00 AM.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:00:00'),\n new Date('Jan 31, 2023 10:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether the\n // event is part of an event series and logs it.\n console.log(event.isRecurringEvent());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 1,2023 16:10:00'),\n new Date('Feb 1,2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, removes all reminders from\n // the event.\n event.removeAllReminders();\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Example 1: Remove a guest from one event\nfunction removeGuestFromEvent() {\n // Replace the below values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to remove\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar containing\n // event\n const eventId = '123abc'; // ID of event instance\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n const event = calendar.getEventById(eventId);\n if (event === null) {\n // Event not found\n console.log('Event not found', eventId);\n return;\n }\n event.removeGuest(attendeeEmail);\n}\n\n// Example 2: Remove a guest from all events on a calendar within a specified\n// timeframe\nfunction removeGuestFromAllEvents() {\n // Replace the following values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to remove\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar with the\n // events\n const startDate = new Date(\n 'YYYY-MM-DD'); // The first date to remove the guest from the events\n const endDate = new Date(\n 'YYYY-MM-DD'); // The last date to remove the attendee from the events\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n // Get the events within the specified timeframe\n const calEvents = calendar.getEvents(startDate, endDate);\n console.log(calEvents.length); // Checks how many events are found\n // Loop through all events and remove the attendee from each of them\n for (let i = 0; i < calEvents.length; i++) {\n const event = calEvents[i];\n event.removeGuest(attendeeEmail);\n }\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 1, 2023 16:10:00'),\n new Date('Feb 1, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, resets the reminders using\n // the calendar's default settings.\n event.resetRemindersToDefault();\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 17th, 2023 that takes\n// place between 4:00 PM and 5:00 PM.\nconst event = calendar.getEvents(\n new Date('Feb 17, 2023 16:00:00'),\n new Date('Feb 17, 2023 17:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the date of the event\n // and updates it to an all-day event.\n event.setAllDayDate(new Date('Feb 17, 2023'));\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 18th, 2023 that takes\n// place between 4:00 PM and 5:00 PM.\nconst event = calendar.getEvents(\n new Date('Feb 18, 2023 16:00:00'),\n new Date('Feb 18, 2023 17:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event to be an\n // all-day event from Feb 18th, 2023 until Feb 25th, 2023. Applying this\n // method changes a regular event into an all-day event.\n event.setAllDayDates(new Date('Feb 18, 2023'), new Date('Feb 25, 2023'));\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 15th, 2023 that takes\n// place between 3:30 PM and 4:30 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 15, 2023 15:30:00'),\n new Date('Feb 15, 2023 16:30:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // non-guests can't add themselves to the event.\n event.setAnyoneCanAddSelf(false);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the color of the\n // calendar event to green.\n event.setColor(CalendarApp.EventColor.GREEN);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:05 PM and 5:35 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 17:05:00'),\n new Date('Feb 04, 2023 17:35:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the description of the\n // event to 'Meeting.'\n event.setDescription('Meeting');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own. You must have edit access to\n// the calendar.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // guests can invite other guests.\n event.setGuestsCanInviteOthers(true);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the location of the\n // event to Noida.\n event.setLocation('Noida');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event status for\n // the current user to maybe.\n event.setMyStatus(CalendarApp.GuestStatus.MAYBE);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Declares a start time of 11:00 AM on February 20th, 2023 and an end time of\n// 12:00 PM on February 20th, 2023.\nconst startTime = new Date('Feb 20,2023 11:00:00');\nconst endTime = new Date('Feb 20, 2023 12:00:00');\n\n// Creates an all-day event on February 20th, 2023.\nconst event = calendar.createAllDayEvent('Meeting', new Date('Feb 20,2023'));\n\n// Updates the all-day event to a regular event by setting a start and end time\n// for the event.\nevent.setTime(startTime, endTime);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@example.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, changes its title to\n // Event1.\n event.setTitle('Event1');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n// Sets the event's transparency to TRANSPARENT.\nevent.setTransparency(CalendarApp.EventTransparency.TRANSPARENT);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.736Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":48,"totalLines":1209,"estimatedTokens":9810}}845{"id":"doc-method_signature_meetaddonclient_endactivity_goo-b3a06b79","source":"documentation","title":"Method signature MeetAddonClient.endActivity | Google Meet | Google for Developers","url":"https://developers.google.com/meet/add-ons/reference/websdk/addon_sdk.meetaddonclient.endcollaboration.md","text":"Example:\n```text\nendActivity(): Promise<void>;\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.736Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":16}}846{"id":"doc-work_with_tabs_apps_script_google_for_developers-714420dc","source":"documentation","title":"Work with tabs | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/docs/tabs","text":"Example:\n```text\n// Print the ID of Tab 3.1.2.\nconst doc = DocumentApp.getActiveDocument();\nconst tab = doc.getTabs()[2].getChildTabs()[0].getChildTabs()[1];\nconsole.log(tab.getId());\n```\n\nExample:\n```text\n// Print the text from the body of the active tab.\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\nconsole.log(body.getText());\n```\n\nExample:\n```text\n/** Logs all text contents from all tabs in the active document. */\nfunction logAllText() {\n // Generate a list of all the tabs in the document, including any\n // nested child tabs. DocumentApp.openById('abc123456') can also\n // be used instead of DocumentApp.getActiveDocument().\n const doc = DocumentApp.getActiveDocument();\n const allTabs = getAllTabs(doc);\n\n // Log the content from each tab in the document.\n for (const tab of allTabs) {\n // Get the DocumentTab from the generic Tab object.\n const documentTab = tab.asDocumentTab();\n // Get the body from the given DocumentTab.\n const body = documentTab.getBody();\n // Get the body text and log it to the console.\n console.log(body.getText());\n }\n}\n\n/**\n * Returns a flat list of all tabs in the document, in the order\n * they would appear in the UI (i.e. top-down ordering). Includes\n * all child tabs.\n */\nfunction getAllTabs(doc) {\n const allTabs = [];\n // Iterate over all tabs and recursively add any child tabs to\n // generate a flat list of Tabs.\n for (const tab of doc.getTabs()) {\n addCurrentAndChildTabs(tab, allTabs);\n }\n return allTabs;\n}\n\n/**\n * Adds the provided tab to the list of all tabs, and recurses\n * through and adds all child tabs.\n */\nfunction addCurrentAndChildTabs(tab, allTabs) {\n allTabs.push(tab);\n for (const childTab of tab.getChildTabs()) {\n addCurrentAndChildTabs(childTab, allTabs);\n }\n}\n```\n\nExample:\n```text\n/** \n * Logs all text contents from the first tab in the active \n * document. \n */\nfunction logAllText() {\n // Generate a list of all the tabs in the document, including any\n // nested child tabs.\n const doc = DocumentApp.getActiveDocument();\n const allTabs = getAllTabs(doc);\n\n // Log the content from the first tab in the document.\n const firstTab = allTabs[0];\n // Get the DocumentTab from the generic Tab object.\n const documentTab = firstTab.asDocumentTab();\n // Get the body from the DocumentTab.\n const body = documentTab.getBody();\n // Get the body text and log it to the console.\n console.log(body.getText());\n}\n```\n\nExample:\n```text\n/** Inserts text into the first tab of the active document. */\nfunction insertTextInFirstTab() {\n // Get the first tab's body.\n const doc = DocumentApp.getActiveDocument();\n const firstTab = doc.getTabs()[0];\n const firstDocumentTab = firstTab.asDocumentTab();\n const firstTabBody = firstDocumentTab.getBody();\n\n // Append a paragraph and a page break to the first tab's body\n // section.\n firstTabBody.appendParagraph(\"A paragraph.\");\n firstTabBody.appendPageBreak();\n}\n```\n\nExample:\n```text\n/**\n * Inserts text into the active/selected tab of the active\n * document.\n */\nfunction insertTextInActiveTab() {\n // Get the active/selected tab's body.\n const doc = DocumentApp.getActiveDocument();\n const activeTab = doc.getActiveTab();\n const activeDocumentTab = activeTab.asDocumentTab();\n const activeTabBody = activeDocumentTab.getBody();\n\n // Append a paragraph and a page break to the active tab's body\n // section.\n activeTabBody.appendParagraph(\"A paragraph.\");\n activeTabBody.appendPageBreak();\n}\n```\n\nExample:\n```text\n/**\n * Changes the user's selection to select all tables within the tab\n * with the provided ID.\n */\nfunction selectAllTables(tabId) {\n const doc = DocumentApp.getActiveDocument();\n const tab = doc.getTab(tabId);\n const documentTab = tab.asDocumentTab();\n\n // Build a range that encompasses all tables within the specified\n // tab.\n const rangeBuilder = documentTab.newRange();\n const tables = documentTab.getBody().getTables();\n for (let i = 0; i < tables.length; i++) {\n rangeBuilder.addElement(tables[i]);\n }\n // Set the document's selection to the tables within the specified\n // tab. Note that this actually switches the user's active tab as\n // well.\n doc.setSelection(rangeBuilder.build());\n}\n```\n\nExample:\n```text\n/**\n * Changes the user's selected tab to the tab immediately following\n * the currently selected one. Handles child tabs.\n *\n * Only changes the selection if there is a tab following the\n * currently selected one.\n */\nfunction selectNextTab() {\n const doc = DocumentApp.getActiveDocument();\n const allTabs = getAllTabs(doc);\n const activeTab = doc.getActiveTab();\n\n // Find the index of the currently active tab.\n let activeTabIndex = -1;\n for (let i = 0; i < allTabs.length; i++) {\n if (allTabs[i].getId() === activeTab.getId()) {\n activeTabIndex = i;\n }\n }\n\n // Update the user's selected tab if there is a valid next tab.\n const nextTabIndex = activeTabIndex + 1;\n if (nextTabIndex < allTabs.length) {\n doc.setActiveTab(allTabs[nextTabIndex].getId());\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.737Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":181,"estimatedTokens":1281}}847{"id":"doc-class_selectioninput_apps_script_google_for_deve-e0dbeb9d","source":"documentation","title":"Class SelectionInput | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/selection-input","text":"Example:\n```text\nconst checkboxGroup =\n CardService.newSelectionInput()\n .setType(CardService.SelectionInputType.CHECK_BOX)\n .setTitle('A group of checkboxes. Multiple selections are allowed.')\n .setFieldName('checkbox_field')\n .addItem('checkbox one title', 'checkbox_one_value', false)\n .addItem('checkbox two title', 'checkbox_two_value', true)\n .addItem('checkbox three title', 'checkbox_three_value', true)\n .setOnChangeAction(\n CardService.newAction().setFunctionName('handleCheckboxChange'),\n );\n\nconst radioGroup =\n CardService.newSelectionInput()\n .setType(CardService.SelectionInputType.RADIO_BUTTON)\n .setTitle(\n 'A group of radio buttons. Only a single selection is allowed.')\n .setFieldName('checkbox_field')\n .addItem('radio button one title', 'radio_one_value', true)\n .addItem('radio button two title', 'radio_two_value', false)\n .addItem('radio button three title', 'radio_three_value', false);\n\nconst multiSelect =\n CardService.newSelectionInput()\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setFieldName('multiselect')\n .setTitle('A multi select input example.')\n .addMultiSelectItem(\n 'Contact 1',\n 'contact-1',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact one description',\n )\n .addMultiSelectItem(\n 'Contact 2',\n 'contact-2',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact two description',\n )\n .addMultiSelectItem(\n 'Contact 3',\n 'contact-3',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact three description',\n )\n .addMultiSelectItem(\n 'Contact 4',\n 'contact-4',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact four description',\n )\n .addMultiSelectItem(\n 'Contact 5',\n 'contact-5',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact five description',\n )\n .setMultiSelectMaxSelectedItems(3)\n .setMultiSelectMinQueryLength(1);\n```\n\nExample:\n```text\nconst multiSelect =\n CardService.newSelectionInput()\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setFieldName('contacts')\n .setTitle('Selected contacts')\n .setDataSourceConfig(\n CardService.newDataSourceConfig().setPlatformDataSource(\n CardService.newPlatformDataSource().setCommonDataSource(\n CardService.CommonDataSource.USER,\n )\n )\n );\n```\n\nExample:\n```text\nconst multiSelect =\n CardService.newSelectionInput()\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setFieldName('multiselect')\n .setTitle('A multi select input example.')\n .addMultiSelectItem(\n 'Contact 1',\n 'contact-1',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact one description',\n )\n .addMultiSelectItem(\n 'Contact 2',\n 'contact-2',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact two description',\n )\n .addMultiSelectItem(\n 'Contact 3',\n 'contact-3',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact three description',\n )\n .addMultiSelectItem(\n 'Contact 4',\n 'contact-4',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact four description',\n )\n .addMultiSelectItem(\n 'Contact 5',\n 'contact-5',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact five description',\n );\n```\n\nExample:\n```text\nconst multiSelect =\n CardService.newSelectionInput()\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setFieldName('contacts')\n .setTitle('Selected contacts')\n .addMultiSelectItem(\n 'Contact 3',\n 'contact-3',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact three description',\n )\n .setMultiSelectMaxSelectedItems(5)\n .setMultiSelectMinQueryLength(2)\n .setExternalDataSource(\n CardService.newAction().setFunctionName('getContacts'),\n );\n```\n\nExample:\n```text\nconst multiSelect =\n CardService.newSelectionInput()\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setFieldName('multiselect')\n .setTitle('A multi select input example.')\n .setMultiSelectMaxSelectedItems(3)\n .addMultiSelectItem(\n 'Contact 1',\n 'contact-1',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact one description',\n )\n .addMultiSelectItem(\n 'Contact 2',\n 'contact-2',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact two description',\n )\n .addMultiSelectItem(\n 'Contact 3',\n 'contact-3',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact three description',\n )\n .addMultiSelectItem(\n 'Contact 4',\n 'contact-4',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact four description',\n )\n .addMultiSelectItem(\n 'Contact 5',\n 'contact-5',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact five description',\n );\n```\n\nExample:\n```text\nconst multiSelect =\n CardService.newSelectionInput()\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setFieldName('multiselect')\n .setTitle('A multi select input example.')\n .setMultiSelectMinQueryLength(1)\n .addMultiSelectItem(\n 'Contact 1',\n 'contact-1',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact one description',\n )\n .addMultiSelectItem(\n 'Contact 2',\n 'contact-2',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact two description',\n )\n .addMultiSelectItem(\n 'Contact 3',\n 'contact-3',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact three description',\n )\n .addMultiSelectItem(\n 'Contact 4',\n 'contact-4',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact four description',\n )\n .addMultiSelectItem(\n 'Contact 5',\n 'contact-5',\n false,\n 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',\n 'Contact five description',\n );\n```\n\nExample:\n```text\nconst multiSelect =\n CardService.newSelectionInput()\n .setType(CardService.SelectionInputType.MULTI_SELECT)\n .setFieldName('contacts')\n .setTitle('Selected contacts')\n .setPlatformDataSource(\n CardService.newPlatformDataSource().setCommonDataSource(\n CardService.CommonDataSource.USER,\n ),\n );\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.740Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":254,"estimatedTokens":2087}}848{"id":"doc-class_datasourcespecbuilder_apps_script_google_f-082aa1fc","source":"documentation","title":"Class DataSourceSpecBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/data-source-spec-builder","text":"Example:\n```text\nconst spec = SpreadsheetApp.newDataSourceSpec()\n .asBigQuery()\n .setProjectId('big_query_project')\n .setRawQuery('select @FIELD from table limit @LIMIT')\n .setParameterFromCell('FIELD', 'Sheet1!A1')\n .setParameterFromCell('LIMIT', 'namedRangeCell')\n .build();\n```\n\nExample:\n```text\nconst spec = SpreadsheetApp.newDataSourceSpec()\n .asLooker()\n .setInstanceUrl('https://looker_instance_url.com')\n .setModelName('model_name')\n .setExploreName('explore_name')\n .build();\n```\n\nExample:\n```text\nconst bigQueryDataSourceSpec = SpreadsheetApp.newDataSourceSpec().asBigQuery();\n// TODO(developer): Replace with the required dataset, project and table IDs.\nbigQueryDataSourceSpec.setDatasetId('my data set id');\nbigQueryDataSourceSpec.setProjectId('my project id');\nbigQueryDataSourceSpec.setTableId('my table id');\n\nbigQueryDataSourceSpec.build();\n```\n\nExample:\n```text\nconst lookerDataSourceSpecBuilder =\n SpreadsheetApp.newDataSourceSpec().asLooker();\nconst lookerSpec = lookerDataSourceSpecBuilder.setExploreName('my explore name')\n .setInstanceUrl('my instance url')\n .setModelName('my model name')\n .build();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\n\nconst newSpec = spec.copy();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst parameters = spec.getParameters();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst type = spec.getType();\n```\n\nExample:\n```text\nconst specBuilder = SpreadsheetApp.newDataSourceSpec();\nspecBuilder.removeAllParameters();\n```\n\nExample:\n```text\nconst specBuilder = SpreadsheetApp.newDataSourceSpec();\nspecBuilder.removeParameter('x');\n```\n\nExample:\n```text\nconst specBuilder = SpreadsheetApp.newDataSourceSpec().asBigQuery();\nspecBuilder.setParameterFromCell('x', 'A1');\nconst bigQuerySpec = specBuilder.build();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.741Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":93,"estimatedTokens":638}}849{"id":"doc-class_action_apps_script_google_for_developers-a13640c3","source":"documentation","title":"Class Action | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/action","text":"Example:\n```text\nconst image = CardService.newImage().setOnClickAction(\n CardService.newAction().setFunctionName('handleImageClick').setParameters({\n imageSrc: 'carImage'\n }),\n);\n```\n\nExample:\n```text\nconst textInput = CardService.newTextInput()\n .setFieldName('text_input_1')\n .setTitle('Text input title');\n\n// Creates a footer button that requires an input from the above TextInput\n// Widget.\nconst action = CardService.newAction()\n .setFunctionName('notificationCallback')\n .addRequiredWidget('text_input_1');\nconst fixedFooter = CardService.newFixedFooter().setPrimaryButton(\n CardService.newTextButton().setText('help').setOnClickAction(action),\n);\n```\n\nExample:\n```text\n// Creates a button with an action that requires inputs from all widgets.\nconst button = CardService.newTextButton()\n .setText('Create notification')\n .setOnClickAction(\n CardService.newAction().setAllWidgetsAreRequired(true));\n```\n\nExample:\n```text\nconst action = CardService.newAction()\n .setFunctionName('handleDialog')\n .setInteraction(CardService.Interaction.OPEN_DIALOG);\n```\n\nExample:\n```text\n// Creates a button with an action that persists the client's values as the\n// on-click action.\nconst button =\n CardService.newTextButton()\n .setText('Create notification')\n .setOnClickAction(\n CardService.newAction().setPersistValues(true).setFunctionName(\n 'functionName'),\n );\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.745Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":55,"estimatedTokens":403}}850{"id":"doc-class_textparagraph_apps_script_google_for_devel-489e3716","source":"documentation","title":"Class TextParagraph | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/text-paragraph","text":"Example:\n```text\nconst textParagraph = CardService.newTextParagraph().setText(\n 'This is a text paragraph widget. Multiple lines are allowed if needed.',\n);\n```\n\nExample:\n```text\nconst textParagraph =\n CardService.newTextParagraph()\n .setText(\n 'This is a text paragraph widget. Multiple lines are allowed if needed.',\n )\n .setMaxLines(1);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.747Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":18,"estimatedTokens":100}}851{"id":"doc-resource_key_hash_google_workspace_google_for_de-7a1b1e54","source":"documentation","title":"Resource key hash | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/resource-key-hash","text":"Example:\n```text\necho -n \"ResourceKeyDigest:my_resource:my_perimeter\" | openssl sha256 -mac HMAC -macopt hexkey:f00d -binary\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.747Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":36}}852{"id":"doc-structured_error_replies_google_workspace_google-d19e34cf","source":"documentation","title":"Structured error replies | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/structured-errors","text":"Example:\n```text\n{\n \"code\": int,\n \"message\": string,\n \"details\": string\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.748Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":24}}853{"id":"doc-method_wrapprivatekey_google_workspace_google_fo-49b6684c","source":"documentation","title":"Method: wrapprivatekey | Google Workspace | Google for Developers","url":"https://developers.google.com/workspace/cse/reference/wrap-private-key","text":"Example:\n```text\n{\n \"authentication\": string,\n \"perimeter_id\": string,\n \"private_key\": string\n}\n```\n\nExample:\n```text\n{\n \"wrapped_private_key\": string\n}\n```\n\nExample:\n```text\nPOST https://mykacls.example.org/v1/wrapprivatekey\n\n{\n \"private_key\": \"-----BEGIN RSA PRIVATE KEY-----\\\\nMIIJ......\\\\n-----END RSA PRIVATE KEY-----\",\n \"perimeter_id\": \"\"\n}\n```\n\nExample:\n```text\n{\n \"wrapped_private_key\": \"LpyCSy5ddy82PIp/87JKaMF4Jmt1KdrbfT1iqpB7uhVd3OwZiu+oq8kxIzB7Lr0iX4aOcxM6HiUyMrGP2PG8x0HkpykbUKQxBVcfm6SLdsqigT9ho5RYw20M6ZXNWVRetFSleKex4SRilTRny38e2ju/lUy0KDaCt1hDUT89nLZ1wsO3D1F3xk8J7clXv5fe7GPRd1ojo82Ny0iyVO7y7h1lh2PACHUFXOMzsdURYFCnxhKAsadccCxpCxKh5x8p78PdoenwY1tnT3/X4O/4LAGfT4fo98Frxy/xtI49WDRNZi6fsL6BQT4vS/WFkybBX9tXaenCqlRBDyZSFhatPQ==\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.748Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":34,"estimatedTokens":192}}854{"id":"doc-domain_shared_contacts_api_overview_admin_consol-ab5fbc4f","source":"documentation","title":"Domain Shared Contacts API overview | Admin console | Google for Developers","url":"https://developers.google.com/admin-sdk/domain-shared-contacts","text":"Example:\n```text\nGData-Version: 3.0\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.750Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":14}}855{"id":"doc-admin_settings_api_overview_admin_console_google-e2bf2901","source":"documentation","title":"Admin Settings API overview | Admin console | Google for Developers","url":"https://developers.google.com/admin-sdk/admin-settings","text":"Example:\n```text\nhttps://apps-apis.google.com/a/feeds/domain/2.0/{domainName}/email/gateway\n```\n\nExample:\n```text\n<atom:entry xmlns:atom='http://www.w3.org/2005/Atom'\n xmlns:apps='http://schemas.google.com/apps/2006'>\n <apps:property name='smartHost' value='smtp.out.domain.com' />\n <apps:property name='smtpMode' value='SMTP' />\n</atom:entry>\n```\n\nExample:\n```text\n<?xml version='1.0' encoding='UTF-8'?>\n<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n<id>https://apps-apis.google.com/a/feeds/domain/2.0/domainName/email/gateway</id>\n<updated>2008-12-17T23:59:23.887Z</updated>\n<link rel='self' type='application/atom+xml' href='https://apps-apis.google.com/a/feeds/domain/\n 2.0/domainName/email/gateway'/>\n<link rel='edit' type='application/atom+xml' href='https://apps-apis.google.com/a/feeds/domain/\n 2.0/domainName/email/gateway'/>\n<apps:property name='smartHost' value='smtp.out.domain.com' />\n<apps:property name='smtpMode' value='SMTP' />\n</entry>\n```\n\nExample:\n```text\nhttps://apps-apis.google.com/a/feeds/domain/2.0/{domainName}/sso/general\n```\n\nExample:\n```text\n<?xml version='1.0' encoding='UTF-8'?>\n<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n<apps:property name='samlSignonUri' value='http://www.example.com/sso/signon'/>\n...\n<apps:property name='samlLogoutUri' value='http://www.example.com/sso/logout'/>\n<apps:property name='changePasswordUri' value='http://www.example.com/sso/changepassword'/>\n<apps:property name='enableSSO' value='true'/>\n<apps:property name='ssoWhitelist' value='CIDR formatted IP address'/>\n<apps:property name='useDomainSpecificIssuer' value='false'/>\n</entry>\n```\n\nExample:\n```text\n<atom:entry xmlns:atom='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n<apps:property name='enableSSO' value='false' />\n<apps:property name='samlSignonUri' value='http://www.example.com/sso/signon' />\n<apps:property name='samlLogoutUri' value='http://www.example.com/sso/logout' />\n<apps:property name='changePasswordUri' value='http://www.example.com/sso/changepassword' />\n<apps:property name='ssoWhitelist' value='127.0.0.1/32' />\n<apps:property name='useDomainSpecificIssuer' value='false'/>\n</atom:entry>\n```\n\nExample:\n```text\n<?xml version='1.0' encoding='UTF-8'?>\n<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n...\n<apps:property name='samlSignonUri' value='http://www.example.com/sso/signon'/>\n<apps:property name='samlLogoutUri' value='http://www.example.com/sso/logout'/>\n<apps:property name='changePasswordUri' value='http://www.example.com/sso/changepassword'/>\n<apps:property name='enableSSO' value='false'/>\n<apps:property name='ssoWhitelist' value='127.0.0.1/32'/>\n<apps:property name='useDomainSpecificIssuer' value='false'/>\n</entry>\n```\n\nExample:\n```text\nhttps://apps-apis.google.com/a/feeds/domain/2.0/{domainName}/sso/signingkey\n```\n\nExample:\n```text\n<?xml version='1.0' encoding='UTF-8'?>\n<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n...\n<apps:property name='signingKey' value='yourBase64EncodedPublicKey'/>\n</entry>\n```\n\nExample:\n```text\n<atom:entry xmlns:atom='http://www.w3.org/2005/Atom' xmlns:apps=\"http://schemas.google.com/apps/2006\">\n<apps:property name='signingKey' value='yourBase64EncodedPublicKey'/>\n</atom:entry>\n```\n\nExample:\n```text\n<?xml version='1.0' encoding='UTF-8'?>\n<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>\n...\n<apps:property name='smartHost' value='smtpout.domain.com'/>\n<apps:property name='smtpMode' value='SMTP'/>\n</entry>\n```\n\nExample:\n```text\n<atom:entry xmlns:atom='http://www.w3.org/2005/Atom' xmlns:apps=\"http://schemas.google.com/apps/2006\">\n<apps:property name='smartHost' value='smtp.out.domain.com' />\n<apps:property name='smtpMode' value='SMTP' />\n</atom:entry>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.751Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":114,"estimatedTokens":984}}856{"id":"doc-overview_admin_console_google_for_developers-89db5e69","source":"documentation","title":"Overview | Admin console | Google for Developers","url":"https://developers.google.com/admin-sdk/alertcenter/guides","text":"Example:\n```text\n// First, authorize the API and create a client to make requests with.\nURL serviceAccountUrl = AuthUtils.class.getResource(\"/client_secret.json\");\nGoogleCredentials credentials = ServiceAccountCredentials\n .fromStream(serviceAccountUrl.openStream())\n .createDelegated(\"admin@xxxx.com\")\n .createScoped(Collections.singleton(\"https://www.googleapis.com/auth/apps.alerts\"));\nApacheHttpTransport transport = new ApacheHttpTransport();\nHttpCredentialsAdapter adapter = new HttpCredentialsAdapter(credentials);\nAlertCenter alertCenter = new AlertCenter.Builder(transport, new JacksonFactory(), adapter)\n .setApplicationName(\"Alert Center client\")\n .build();\n\n// List alerts in pages, printing each alert discovered.\nString pageToken = null;\ndo {\n ListAlertsResponse listResponse = service.alerts().list().setPageToken(pageToken)\n .setPageSize(20).execute();\n if (listResponse.getAlerts() != null) {\n for (Alert alert : listResponse.getAlerts()) {\n System.out.println(alert);\n }\n }\n pageToken = listResponse.getNextPageToken();\n} while (pageToken != null);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.752Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":29,"estimatedTokens":280}}857{"id":"doc-observe_meeting_events_with_python_and_the_googl-8fc55244","source":"documentation","title":"Observe meeting events with Python and the Google Meet REST API | Google for Developers","url":"https://developers.google.com/meet/api/guides/tutorial-events-python","text":"Example:\n```text\nmkdir meet-tutorialcd meet-tutorialpython3 -mvenv envsource env/bin/activate\n```\n\nExample:\n```text\nmkdir meet-tutorialcd meet-tutorialpython3 -mvenv envenv/bin/activate.bat\n```\n\nExample:\n```text\nmkdir meet-tutorialcd meet-tutorialpython3 -mvenv envenv/bin/activate.ps1\n```\n\nExample:\n```text\ngcloud projects create PROJECT_ID\n```\n\nExample:\n```text\ngcloud billing accounts list\n```\n\nExample:\n```text\ngcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID\n```\n\nExample:\n```text\npip install google-auth google-auth-oauthlib\n```\n\nExample:\n```text\nimport os\nimport json\n\nfrom google.auth.transport import requests\nfrom google.oauth2.credentials import Credentials\nfrom google_auth_oauthlib.flow import InstalledAppFlow\n\ndef authorize() -> Credentials:\n \"\"\"Ensure valid credentials for calling the Meet REST API.\"\"\"\n CLIENT_SECRET_FILE = \"./client_secret.json\"\n credentials = None\n\n if os.path.exists('token.json'):\n credentials = Credentials.from_authorized_user_file('token.json')\n\n if credentials is None:\n flow = InstalledAppFlow.from_client_secrets_file(\n CLIENT_SECRET_FILE,\n scopes=[\n 'https://www.googleapis.com/auth/meetings.space.created',\n ])\n flow.run_local_server(port=0)\n credentials = flow.credentials\n\n if credentials and credentials.expired:\n credentials.refresh(requests.Request())\n\n if credentials is not None:\n with open(\"token.json\", \"w\") as f:\n f.write(credentials.to_json())\n\n return credentials\n\nUSER_CREDENTIALS = authorize()\n```\n\nExample:\n```text\npython3 main.py\n```\n\nExample:\n```text\ngcloud config set project PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable meet.googleapis.com workspaceevents.googleapis.com pubsub.googleapis.com\n```\n\nExample:\n```text\npip install google-apps-meet\n```\n\nExample:\n```text\nfrom google.apps import meet_v2 as meet\n```\n\nExample:\n```text\ndef create_space() -> meet.Space:\n \"\"\"Create a meeting space.\"\"\"\n client = meet.SpacesServiceClient(credentials=USER_CREDENTIALS)\n request = meet.CreateSpaceRequest()\n return client.create_space(request=request)\n```\n\nExample:\n```text\ngcloud pubsub topics create workspace-events\n```\n\nExample:\n```text\ngcloud pubsub topics add-iam-policy-binding workspace-events --member='serviceAccount:meet-api-event-push@system.gserviceaccount.com' --role='roles/pubsub.publisher'\n```\n\nExample:\n```text\ngcloud pubsub subscriptions create workspace-events-sub --topic=TOPIC_NAME\n```\n\nExample:\n```text\ngcloud iam service-accounts create meet-event-listener \\\n --display-name=\"meet-event-listener\"\n```\n\nExample:\n```text\ngcloud projects add-iam-policy-binding PROJECT_ID \\\n --member=\"serviceAccount:meet-event-listener@PROJECT_ID.iam.gserviceaccount.com\" \\\n --role=\"roles/pubsub.subscriber\"\n```\n\nExample:\n```text\ngcloud auth application-default login --impersonate-service-account=SERVICE_ACCOUNT_EMAIL\n```\n\nExample:\n```text\ngcloud iam service-accounts add-iam-policy-binding \\\n SERVICE_ACCOUNT_EMAIL \\\n --member=\"user:YOUR_EMAIL\" \\\n --role=\"roles/iam.serviceAccountTokenCreator\"\n```\n\nExample:\n```text\npip install google-cloud-pubsub\n```\n\nExample:\n```text\nfrom google.cloud import pubsub_v1\n```\n\nExample:\n```text\ndef subscribe_to_space(space_name: str = None, topic_name: str = None):\n \"\"\"Subscribe to events for a meeting space.\"\"\"\n session = requests.AuthorizedSession(USER_CREDENTIALS)\n body = {\n 'targetResource': f\"//meet.googleapis.com/{space_name}\",\n \"eventTypes\": [\n \"google.workspace.meet.conference.v2.started\",\n \"google.workspace.meet.conference.v2.ended\",\n \"google.workspace.meet.participant.v2.joined\",\n \"google.workspace.meet.participant.v2.left\",\n \"google.workspace.meet.recording.v2.fileGenerated\",\n \"google.workspace.meet.transcript.v2.fileGenerated\",\n ],\n \"payloadOptions\": {\n \"includeResource\": False,\n },\n \"notificationEndpoint\": {\n \"pubsubTopic\": topic_name\n },\n \"ttl\": \"86400s\",\n }\n response = session.post(\"https://workspaceevents.googleapis.com/v1/subscriptions\", json=body)\n return response\n```\n\nExample:\n```text\ndef format_participant(participant: meet.Participant) -> str:\n \"\"\"Formats a participant for display on the console.\"\"\"\n if participant.anonymous_user:\n return f\"{participant.anonymous_user.display_name} (Anonymous)\"\n\n if participant.signedin_user:\n return f\"{participant.signedin_user.display_name} (ID: {participant.signedin_user.user})\"\n\n if participant.phone_user:\n return f\"{participant.phone_user.display_name} (Phone)\"\n\n return \"Unknown participant\"\n\n\ndef fetch_participant_from_session(session_name: str) -> meet.Participant:\n \"\"\"Fetches the participant for a session.\"\"\"\n client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)\n # Use the parent path of the session to fetch the participant details\n parsed_session_path = client.parse_participant_session_path(session_name)\n participant_resource_name = client.participant_path(\n parsed_session_path[\"conference_record\"],\n parsed_session_path[\"participant\"])\n return client.get_participant(name=participant_resource_name)\n\n\ndef on_conference_started(message: pubsub_v1.subscriber.message.Message):\n \"\"\"Display information about a conference when started.\"\"\"\n payload = json.loads(message.data)\n resource_name = payload.get(\"conferenceRecord\").get(\"name\")\n client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)\n conference = client.get_conference_record(name=resource_name)\n print(f\"Conference (ID {conference.name}) started at {conference.start_time.rfc3339()}\")\n\n\ndef on_conference_ended(message: pubsub_v1.subscriber.message.Message):\n \"\"\"Display information about a conference when ended.\"\"\"\n payload = json.loads(message.data)\n resource_name = payload.get(\"conferenceRecord\").get(\"name\")\n client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)\n conference = client.get_conference_record(name=resource_name)\n print(f\"Conference (ID {conference.name}) ended at {conference.end_time.rfc3339()}\")\n\n\ndef on_participant_joined(message: pubsub_v1.subscriber.message.Message):\n \"\"\"Display information about a participant when they join a meeting.\"\"\"\n payload = json.loads(message.data)\n resource_name = payload.get(\"participantSession\").get(\"name\")\n client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)\n session = client.get_participant_session(name=resource_name)\n participant = fetch_participant_from_session(resource_name)\n display_name = format_participant(participant)\n print(f\"{display_name} joined at {session.start_time.rfc3339()}\")\n\n\ndef on_participant_left(message: pubsub_v1.subscriber.message.Message):\n \"\"\"Display information about a participant when they leave a meeting.\"\"\"\n payload = json.loads(message.data)\n resource_name = payload.get(\"participantSession\").get(\"name\")\n client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)\n session = client.get_participant_session(name=resource_name)\n participant = fetch_participant_from_session(resource_name)\n display_name = format_participant(participant)\n print(f\"{display_name} left at {session.end_time.rfc3339()}\")\n\n\ndef on_recording_ready(message: pubsub_v1.subscriber.message.Message):\n \"\"\"Display information about a recorded meeting when artifact is ready.\"\"\"\n payload = json.loads(message.data)\n resource_name = payload.get(\"recording\").get(\"name\")\n client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)\n recording = client.get_recording(name=resource_name)\n print(f\"Recording available at {recording.drive_destination.export_uri}\")\n\n\ndef on_transcript_ready(message: pubsub_v1.subscriber.message.Message):\n \"\"\"Display information about a meeting transcript when artifact is ready.\"\"\"\n payload = json.loads(message.data)\n resource_name = payload.get(\"transcript\").get(\"name\")\n client = meet.ConferenceRecordsServiceClient(credentials=USER_CREDENTIALS)\n transcript = client.get_transcript(name=resource_name)\n print(f\"Transcript available at {transcript.docs_destination.export_uri}\")\n\n\ndef on_message(message: pubsub_v1.subscriber.message.Message) -> None:\n \"\"\"Handles an incoming event from the Google Cloud Pub/Sub API.\"\"\"\n event_type = message.attributes.get(\"ce-type\")\n handler = {\n \"google.workspace.meet.conference.v2.started\": on_conference_started,\n \"google.workspace.meet.conference.v2.ended\": on_conference_ended,\n \"google.workspace.meet.participant.v2.joined\": on_participant_joined,\n \"google.workspace.meet.participant.v2.left\": on_participant_left,\n \"google.workspace.meet.recording.v2.fileGenerated\": on_recording_ready,\n \"google.workspace.meet.transcript.v2.fileGenerated\": on_transcript_ready,\n }.get(event_type)\n\n try:\n if handler is not None:\n handler(message)\n message.ack()\n except Exception as error:\n print(\"Unable to process event\")\n print(error)\n\n\ndef listen_for_events(subscription_name: str = None):\n \"\"\"Subscribe to events on the subscription.\"\"\"\n subscriber = pubsub_v1.SubscriberClient()\n with subscriber:\n future = subscriber.subscribe(subscription_name, callback=on_message)\n print(\"Listening for events\")\n try:\n future.result()\n except KeyboardInterrupt:\n future.cancel()\n print(\"Done\")\n```\n\nExample:\n```text\nspace = create_space()\nprint(f\"Join the meeting at {space.meeting_uri}\")\n\nTOPIC_NAME = \"projects/PROJECT_ID/topics/TOPIC_ID\"\nSUBSCRIPTION_NAME = \"projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID\"\n\nsubscription = subscribe_to_space(topic_name=TOPIC_NAME, space_name=space.name)\nif (subscription.status_code) == 200:\n listen_for_events(subscription_name=SUBSCRIPTION_NAME)\nelse:\n print(f\"Subscription to Meet events failed, response data: {subscription.content}\")\n```\n\nExample:\n```text\nJoin the meeting at https://meet.google.com/abc-mnop-xyz\n```\n\nExample:\n```text\ndef subscribe_to_user(user_name: str = None, topic_name: str = None) -> requests_lib.Response:\n \"\"\"Subscribe to events for a user.\"\"\"\n session = requests.AuthorizedSession(USER_CREDENTIALS)\n body = {\n \"targetResource\": f\"//cloudidentity.googleapis.com/users/{user_name}\",\n \"eventTypes\": [\n \"google.workspace.meet.conference.v2.started\",\n \"google.workspace.meet.conference.v2.ended\",\n \"google.workspace.meet.participant.v2.joined\",\n \"google.workspace.meet.participant.v2.left\",\n \"google.workspace.meet.recording.v2.fileGenerated\",\n \"google.workspace.meet.transcript.v2.fileGenerated\",\n ],\n \"payloadOptions\": {\n \"includeResource\": False,\n },\n \"notificationEndpoint\": {\"pubsubTopic\": topic_name},\n \"ttl\": \"86400s\",\n }\n response = session.post(\n \"https://workspaceevents.googleapis.com/v1/subscriptions\", json=body\n )\n return response\n\nservice = build(\"people\", \"v1\", credentials=USER_CREDENTIALS)\nresponse = (\n service.people()\n .get(resourceName=\"people/me\", personFields=\"names,emailAddresses\")\n .execute()\n)\nresource_name = response.get(\"resourceName\")\nif resource_name.startswith(\"people/\"):\n resource_name = resource_name[len(\"people/\") :]\n\nsubscription = subscribe_to_user(topic_name=TOPIC_NAME, user_name=resource_name)\n```\n\nExample:\n```text\ndef get_space(meeting_code: str) -> meet.Space:\n \"\"\"Get a meeting space.\"\"\"\n client = meet.SpacesServiceClient(credentials=USER_CREDENTIALS)\n return client.get_space(name=\"spaces/\" + meeting_code)\n```\n\nExample:\n```text\ngcloud pubsub subscriptions delete SUBSCRIPTION_NAME\n```\n\nExample:\n```text\ngcloud pubsub topics delete TOPIC_NAME\n```\n\nExample:\n```text\ngcloud projects delete PROJECT_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.754Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":32,"totalLines":387,"estimatedTokens":3023}}858{"id":"doc-groups_settings_api_overview_admin_console_googl-1384ce0b","source":"documentation","title":"Groups Settings API overview | Admin console | Google for Developers","url":"https://developers.google.com/admin-sdk/groups-settings/get_started","text":"Example:\n```text\nGET https://www.googleapis.com/groups/v1/groups/salesgroup@example.com?alt=json\n```\n\nExample:\n```text\nGET https://www.googleapis.com/groups/v1/groups/salesgroup@example.com?alt=atom\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.756Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":11,"estimatedTokens":54}}859{"id":"doc-openid_connect_sign_in_with_google_google_for_de-1169da2b","source":"documentation","title":"OpenID Connect | Sign in with Google | Google for Developers","url":"https://developers.google.com/identity/protocols/OpenIDConnect","text":"Example:\n```text\n// Create a state token to prevent request forgery.\n// Store it in the session for later validation.\n$state = bin2hex(random_bytes(128/8));\n$app['session']->set('state', $state);\n// Set the client ID, token state, and application name in the HTML while\n// serving it.\nreturn $app['twig']->render('index.html', array(\n 'CLIENT_ID' => CLIENT_ID,\n 'STATE' => $state,\n 'APPLICATION_NAME' => APPLICATION_NAME\n));\n```\n\nExample:\n```text\n// Create a state token to prevent request forgery.\n// Store it in the session for later validation.\nString state = new BigInteger(130, new SecureRandom()).toString(32);\nrequest.session().attribute(\"state\", state);\n// Read index.html into memory, and set the client ID,\n// token state, and application name in the HTML before serving it.\nreturn new Scanner(new File(\"index.html\"), \"UTF-8\")\n .useDelimiter(\"\\\\A\").next()\n .replaceAll(\"[{]{2}\\\\s*CLIENT_ID\\\\s*[}]{2}\", CLIENT_ID)\n .replaceAll(\"[{]{2}\\\\s*STATE\\\\s*[}]{2}\", state)\n .replaceAll(\"[{]{2}\\\\s*APPLICATION_NAME\\\\s*[}]{2}\",\n APPLICATION_NAME);\n```\n\nExample:\n```text\n# Create a state token to prevent request forgery.\n# Store it in the session for later validation.\nstate = hashlib.sha256(os.urandom(1024)).hexdigest()\nsession['state'] = state\n# Set the client ID, token state, and application name in the HTML while\n# serving it.\nresponse = make_response(\n render_template('index.html',\n CLIENT_ID=CLIENT_ID,\n STATE=state,\n APPLICATION_NAME=APPLICATION_NAME))\n```\n\nExample:\n```text\nhttps://accounts.google.com/o/oauth2/v2/auth?\n response_type=code&\n client_id=424911365001.apps.googleusercontent.com&\n scope=openid%20email&\n redirect_uri=https%3A//developers.google.com/oauthplayground&\n state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2-login-demo.example.com%2FmyHome&\n login_hint=jsmith@example.com&\n nonce=0394852-3190485-2490358&\n hd=example.com\n```\n\nExample:\n```text\nhttps://developers.google.com/oauthplayground?state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foa2cb.example.com%2FmyHome&code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&scope=openid%20email%20https://www.googleapis.com/auth/userinfo.email\n```\n\nExample:\n```text\n// Ensure that there is no request forgery going on, and that the user\n// sending us this connect request is the user that was supposed to.\nif ($request->get('state') != ($app['session']->get('state'))) {\n return new Response('Invalid state parameter', 401);\n}\n```\n\nExample:\n```text\n// Ensure that there is no request forgery going on, and that the user\n// sending us this connect request is the user that was supposed to.\nif (!request.queryParams(\"state\").equals(\n request.session().attribute(\"state\"))) {\n response.status(401);\n return GSON.toJson(\"Invalid state parameter.\");\n}\n```\n\nExample:\n```text\n# Ensure that the request is not a forgery and that the user sending\n# this connect request is the expected user.\nif request.args.get('state', '') != session['state']:\n response = make_response(json.dumps('Invalid state parameter.'), 401)\n response.headers['Content-Type'] = 'application/json'\n return response\n```\n\nExample:\n```text\nPOST /token HTTP/1.1\nHost: oauth2.googleapis.com\nContent-Type: application/x-www-form-urlencoded\n\ncode=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&\nclient_id=your-client-id&\nclient_secret=your-client-secret&\nredirect_uri=https%3A//developers.google.com/oauthplayground&\ngrant_type=authorization_code\n```\n\nExample:\n```text\n{\n \"iss\": \"https://accounts.google.com\",\n \"azp\": \"1234987819200.apps.googleusercontent.com\",\n \"aud\": \"1234987819200.apps.googleusercontent.com\",\n \"sub\": \"10769150350006150715113082367\",\n \"at_hash\": \"HK6E_P6Dh8Y93mRNtsDB1Q\",\n \"hd\": \"example.com\",\n \"email\": \"jsmith@example.com\",\n \"email_verified\": \"true\",\n \"iat\": 1353601026,\n \"exp\": 1353604926,\n \"nonce\": \"0394852-3190485-2490358\"\n}\n```\n\nExample:\n```text\nscope=openid%20profile%20email\n```\n\nExample:\n```text\nhttps://accounts.google.com/.well-known/openid-configuration\n```\n\nExample:\n```text\n{\n \"issuer\": \"https://accounts.google.com\",\n \"authorization_endpoint\": \"https://accounts.google.com/o/oauth2/v2/auth\",\n \"device_authorization_endpoint\": \"https://oauth2.googleapis.com/device/code\",\n \"token_endpoint\": \"https://oauth2.googleapis.com/token\",\n \"userinfo_endpoint\": \"https://openidconnect.googleapis.com/v1/userinfo\",\n \"revocation_endpoint\": \"https://oauth2.googleapis.com/revoke\",\n \"jwks_uri\": \"https://www.googleapis.com/oauth2/v3/certs\",\n \"response_types_supported\": [\n \"code\",\n \"token\",\n \"id_token\",\n \"code token\",\n \"code id_token\",\n \"token id_token\",\n \"code token id_token\",\n \"none\"\n ],\n \"subject_types_supported\": [\n \"public\"\n ],\n \"id_token_signing_alg_values_supported\": [\n \"RS256\"\n ],\n \"scopes_supported\": [\n \"openid\",\n \"email\",\n \"profile\"\n ],\n \"token_endpoint_auth_methods_supported\": [\n \"client_secret_post\",\n \"client_secret_basic\"\n ],\n \"claims_supported\": [\n \"aud\",\n \"email\",\n \"email_verified\",\n \"exp\",\n \"family_name\",\n \"given_name\",\n \"iat\",\n \"iss\",\n \"locale\",\n \"name\",\n \"picture\",\n \"sub\"\n ],\n \"code_challenge_methods_supported\": [\n \"plain\",\n \"S256\"\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.758Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":191,"estimatedTokens":1311}}860{"id":"doc-google_docs_api_overview_google_for_developers-36c543be","source":"documentation","title":"Google Docs API overview | Google for Developers","url":"https://developers.google.com/docs/api","text":"Example:\n```text\nhttps://docs.google.com/document/d/DOCUMENT_ID/edit\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.759Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":22}}861{"id":"doc-google_sheets_api_overview_google_for_developers-5212ece5","source":"documentation","title":"Google Sheets API Overview | Google for Developers","url":"https://developers.google.com/sheets/api","text":"Example:\n```text\nhttps://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit?gid=SHEET_ID#gid=SHEET_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.760Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":30}}862{"id":"doc-manage_contacts_with_the_carddav_protocol_people-97048410","source":"documentation","title":"Manage contacts with the CardDAV protocol | People API | Google for Developers","url":"https://developers.google.com/people/carddav","text":"Example:\n```text\nhttps://www.googleapis.com/.well-known/carddav\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.761Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":21}}863{"id":"doc-overview_google_forms_google_for_developers-e361da21","source":"documentation","title":"Overview | Google Forms | Google for Developers","url":"https://developers.google.com/forms/api/guides","text":"Example:\n```text\n{\n \"formId\": \"FORM_ID\",\n \"info\": {\n \"title\": \"Famous Black Women\",\n \"description\": \"Please complete this quiz based off of this week's readings for class.\",\n \"documentTitle\": \"Famous Black Women\"\n },\n \"settings\": {\n \"quizSettings\": {\n \"isQuiz\": true\n }\n },\n \"revisionId\": \"00000021\",\n \"responderUri\": \"https://docs.google.com/forms/d/e/1FAIpQLSd0iBLPh4suZoGW938EU1WIxzObQv_jXto0nT2U8HH2KsI5dg/viewform\",\n \"items\": [\n {\n \"itemId\": \"5d9f9786\",\n \"imageItem\": {\n \"image\": {\n \"contentUri\": \"DIRECT_URL\",\n \"properties\": {\n \"alignment\": \"LEFT\"\n }\n }\n }\n },\n {\n \"itemId\": \"72b30353\",\n \"title\": \"Which African American woman authored \\\"I Know Why the Caged Bird Sings\\\"?\",\n \"questionItem\": {\n \"question\": {\n \"questionId\": \"25405d4e\",\n \"required\": true,\n \"grading\": {\n \"pointValue\": 2,\n \"correctAnswers\": {\n \"answers\": [\n {\n \"value\": \"Maya Angelou\"\n }\n ]\n }\n },\n \"choiceQuestion\": {\n \"type\": \"RADIO\",\n \"options\": [\n {\n \"value\": \"Maya Angelou\"\n },\n {\n \"value\": \"bell hooks\"\n },\n {\n \"value\": \"Alice Walker\"\n },\n {\n \"value\": \"Roxane Gay\"\n }\n ]\n }\n }\n }\n },\n {\n \"itemId\": \"0a4859c8\",\n \"title\": \"Who was the first Dominican-American woman elected to state office?\",\n \"questionItem\": {\n \"question\": {\n \"questionId\": \"37fff47a\",\n \"grading\": {\n \"pointValue\": 2,\n \"correctAnswers\": {\n \"answers\": [\n {\n \"value\": \"Grace Diaz\"\n }\n ]\n }\n },\n \"choiceQuestion\": {\n \"type\": \"RADIO\",\n \"options\": [\n {\n \"value\": \"Rosa Clemente\"\n },\n {\n \"value\": \"Grace Diaz\"\n },\n {\n \"value\": \"Juana Matias\"\n },\n {\n \"value\": \"Sabrina Matos\"\n }\n ]\n }\n }\n }\n }\n ],\n \"publishSettings\" : {\n \"isPublished\": true,\n \"isAcceptingResponses\": true\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.761Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":110,"estimatedTokens":632}}864{"id":"doc-quickstart_generate_text_using_agent_platform_ap-db64002b","source":"documentation","title":"Quickstart: Generate text using Agent Platform | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/quickstart/vertex-ai","text":"Example:\n```text\n/**\n * Main entry point to test the Agent Platform integration.\n */\nfunction main() {\n const prompt = 'What is Apps Script in one sentence?';\n\n try {\n const response = callVertexAI(prompt);\n console.log(`Response: ${response}`);\n } catch (error) {\n console.error(`Failed to call Agent Platform: ${error.message}`);\n }\n}\n\n/**\n * Calls the Gemini model on Agent Platform.\n *\n * @param {string} prompt - The user's input prompt.\n * @return {string} The text generated by the model.\n */\nfunction callVertexAI(prompt) {\n // Configuration\n const projectId = 'GOOGLE_CLOUD_PROJECT_ID';\n const region = 'us-central1';\n const modelName = 'gemini-2.5-flash';\n\n const model = `projects/${projectId}/locations/${region}/publishers/google/models/${modelName}`;\n\n const payload = {\n contents: [{\n role: 'user',\n parts: [{\n text: prompt\n }]\n }],\n generationConfig: {\n temperature: 0.1,\n maxOutputTokens: 2048\n }\n };\n\n // Execute the request using the Vertex AI Advanced Service (which wraps the Agent Platform API)\n const response = VertexAI.Endpoints.generateContent(payload, model);\n\n // Use optional chaining for safe property access\n return response?.candidates?.[0]?.content?.parts?.[0]?.text || 'No response generated.';\n}\n```\n\nExample:\n```text\nResponse: Google Apps Script is a cloud-based, JavaScript platform that lets you\nautomate, integrate, and extend Google Workspace applications like Sheets, Docs,\nand Gmail.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.763Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":59,"estimatedTokens":378}}865{"id":"doc-custom_menus_in_google_workspace_apps_script_goo-9dbb1dda","source":"documentation","title":"Custom Menus in Google Workspace | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/menus","text":"Example:\n```text\nfunction onOpen() {\n const ui = SpreadsheetApp.getUi();\n // Or DocumentApp, SlidesApp or FormApp.\n ui.createMenu('Custom Menu')\n .addItem('First item', 'menuItem1')\n .addSeparator()\n .addSubMenu(ui.createMenu('Sub-menu')\n .addItem('Second item', 'menuItem2'))\n .addToUi();\n}\n\nfunction menuItem1() {\n SpreadsheetApp.getUi() // Or DocumentApp, SlidesApp or FormApp.\n .alert('You clicked the first menu item!');\n}\n\nfunction menuItem2() {\n SpreadsheetApp.getUi() // Or DocumentApp, SlidesApp or FormApp.\n .alert('You clicked the second menu item!');\n}\n```\n\nExample:\n```text\nfunction showMessageBox() {\n SpreadsheetApp.getUi().alert('You clicked it!');\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.764Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":32,"estimatedTokens":183}}866{"id":"doc-custom_functions_in_google_sheets_apps_script_go-e49324bd","source":"documentation","title":"Custom Functions in Google Sheets | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/sheets/functions","text":"Example:\n```text\n/**\n * Multiplies an input value by 2.\n * @param {number} input The number to double.\n * @return The input multiplied by 2.\n * @customfunction\n*/\nfunction DOUBLE(input) {\n return input * 2;\n}\n```\n\nExample:\n```text\n/**\n * Multiplies the input value by 2.\n *\n * @param {number} input The value to multiply.\n * @return {number} The input multiplied by 2.\n * @customfunction\n */\nfunction DOUBLE(input) {\n return input * 2;\n}\n```\n\nExample:\n```text\n/**\n * Multiplies the input value by 2.\n *\n * @param {number|Array<Array<number>>} input The value or range of cells\n * to multiply.\n * @return The input multiplied by 2.\n * @customfunction\n */\nfunction DOUBLE(input) {\n return Array.isArray(input) ?\n input.map(row => row.map(cell => cell * 2)) :\n input * 2;\n}\n```\n\nExample:\n```text\n/**\n * Show the title and date for the first page of posts on the\n * Developer blog.\n *\n * @return Two columns of data representing posts on the\n * Developer blog.\n * @customfunction\n */\nfunction getBlogPosts() {\n var array = [];\n var url = 'https://gsuite-developers.googleblog.com/atom.xml';\n var xml = UrlFetchApp.fetch(url).getContentText();\n var document = XmlService.parse(xml);\n var root = document.getRootElement();\n var atom = XmlService.getNamespace('http://www.w3.org/2005/Atom');\n var entries = document.getRootElement().getChildren('entry', atom);\n for (var i = 0; i < entries.length; i++) {\n var title = entries[i].getChild('title', atom).getText();\n var date = entries[i].getChild('published', atom).getValue();\n array.push([title, date]);\n }\n return array;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.766Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":72,"estimatedTokens":407}}867{"id":"doc-vertex_ai_service_apps_script_google_for_develop-729c1d9a","source":"documentation","title":"Vertex AI Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/advanced/vertex-ai","text":"Example:\n```text\n/**\n * Main entry point to test the Vertex AI integration.\n */\nfunction main() {\n const prompt = 'What is Apps Script in one sentence?';\n\n try {\n const response = callVertexAI(prompt);\n console.log(`Response: ${response}`);\n } catch (error) {\n console.error(`Failed to call Vertex AI: ${error.message}`);\n }\n}\n\n/**\n * Calls the Vertex AI Gemini model.\n *\n * @param {string} prompt - The user's input prompt.\n * @return {string} The text generated by the model.\n */\nfunction callVertexAI(prompt) {\n // Configuration\n const projectId = 'GOOGLE_CLOUD_PROJECT_ID';\n const region = 'us-central1';\n const modelName = 'gemini-2.5-flash';\n\n const model = `projects/${projectId}/locations/${region}/publishers/google/models/${modelName}`;\n\n const payload = {\n contents: [{\n role: 'user',\n parts: [{\n text: prompt\n }]\n }],\n generationConfig: {\n temperature: 0.1,\n maxOutputTokens: 2048\n }\n };\n\n // Execute the request using the Vertex AI Advanced Service\n const response = VertexAI.Endpoints.generateContent(payload, model);\n\n // Use optional chaining for safe property access\n return response?.candidates?.[0]?.content?.parts?.[0]?.text || 'No response generated.';\n}\n```\n\nExample:\n```text\n/**\n * Main entry point to test the Vertex AI integration.\n */\nfunction main() {\n const prompt = 'What is Apps Script in one sentence?';\n\n try {\n const response = callVertexAI(prompt);\n console.log(`Response: ${response}`);\n } catch (error) {\n console.error(`Failed to call Vertex AI: ${error.message}`);\n }\n}\n\n/**\n * Calls the Vertex AI Gemini model.\n *\n * @param {string} prompt - The user's input prompt.\n * @return {string} The text generated by the model.\n */\nfunction callVertexAI(prompt) {\n const service = getServiceAccountService();\n\n // Configuration\n const projectId = 'GOOGLE_CLOUD_PROJECT_ID';\n const region = 'us-central1';\n const modelName = 'gemini-2.5-flash';\n\n const model = `projects/${projectId}/locations/${region}/publishers/google/models/${modelName}`;\n\n const payload = {\n contents: [{\n role: 'user',\n parts: [{\n text: prompt\n }]\n }],\n generationConfig: {\n temperature: 0.1,\n maxOutputTokens: 2048\n }\n };\n\n // Execute the request using the Vertex AI Advanced Service\n const response = VertexAI.Endpoints.generateContent(\n payload,\n model,\n {},\n // Authenticate with the service account token.\n { Authorization: `Bearer ${service.getAccessToken()}` },\n );\n\n // Use optional chaining for safe property access\n return response?.candidates?.[0]?.content?.parts?.[0]?.text || 'No response generated.';\n}\n\n/**\n * Get a new OAuth2 service for a given service account.\n */\nfunction getServiceAccountService() {\n const serviceAccountKeyString = PropertiesService.getScriptProperties().getProperty('SERVICE_ACCOUNT_KEY');\n\n if (!serviceAccountKeyString) {\n throw new Error('SERVICE_ACCOUNT_KEY property is not set. Please follow the setup instructions.');\n }\n\n const serviceAccountKey = JSON.parse(serviceAccountKeyString);\n\n const CLIENT_EMAIL = serviceAccountKey.client_email;\n const PRIVATE_KEY = serviceAccountKey.private_key;\n const SCOPES = ['https://www.googleapis.com/auth/cloud-platform'];\n\n return OAuth2.createService('ServiceAccount')\n .setTokenUrl('https://oauth2.googleapis.com/token')\n .setPrivateKey(PRIVATE_KEY)\n .setIssuer(CLIENT_EMAIL)\n .setPropertyStore(PropertiesService.getScriptProperties())\n .setScope(SCOPES);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.769Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":135,"estimatedTokens":889}}868{"id":"doc-configure_the_google_chat_api_google_for_develop-054065f2","source":"documentation","title":"Configure the Google Chat API | Google for Developers","url":"https://developers.google.com/workspace/chat/configure-chat-api","text":"Example:\n```text\nhttps://console.developers.google.com/apis/api/chat.googleapis.com/hangouts-chat?project=PROJECT_ID\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.772Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":34}}869{"id":"doc-manage_comments_google_sheets_google_for_develop-5f4d40d0","source":"documentation","title":"Manage comments | Google Sheets | Google for Developers","url":"https://developers.google.com/workspace/sheets/api/guides/comments","text":"Example:\n```text\nGET https://sheets.googleapis.com/v4/spreadsheets/SPREADSHEET_ID?commentsViewMode=COMMENTS_VIEW_MODE_INCLUDED&fields=spreadsheetId,comments,sheets(properties(sheetId,title),commentAnchors)\n```\n\nExample:\n```text\n{\n \"spreadsheetId\": \"SPREADSHEET_ID\",\n \"sheets\": [\n {\n \"properties\": {\n \"sheetId\": 0,\n \"title\": \"Sheet1\"\n },\n \"commentAnchors\": [\n {\n \"anchorId\": \"ANCHOR_ID\",\n \"range\": {\n \"sheetId\": 0,\n \"startRowIndex\": 0,\n \"endRowIndex\": 1,\n \"startColumnIndex\": 0,\n \"endColumnIndex\": 1\n }\n }\n ]\n }\n ],\n \"comments\": [\n {\n \"commentId\": \"COMMENT_ID\",\n \"anchorId\": \"ANCHOR_ID\",\n \"headPost\": {\n \"postId\": \"POST_ID\",\n \"content\": \"This is a comment thread head post.\",\n \"contentHtml\": \"The content of the post as HTML.\",\n \"author\": {\n \"displayName\": \"DISPLAY_NAME\",\n \"me\": true,\n \"user\": \"users/USER\"\n },\n \"createTime\": \"2026-07-01T10:13:12Z\",\n \"updateTime\": \"2026-07-01T10:13:12Z\"\n },\n \"replies\": [\n {\n \"postId\": \"REPLY_POST_ID\",\n \"content\": \"This is a reply to the comment.\",\n \"author\": {\n \"displayName\": \"DISPLAY_NAME\",\n \"me\": false\n },\n \"createTime\": \"2026-07-01T10:15:00Z\",\n \"updateTime\": \"2026-07-01T10:15:00Z\"\n }\n ],\n \"status\": \"OPEN\"\n }\n ],\n \"commentsViewMode\": \"COMMENTS_VIEW_MODE_INCLUDED\"\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"insertComment\": {\n \"content\": \"This is a comment added using the API.\",\n \"coordinate\": {\n \"sheetId\": 0,\n \"rowIndex\": 1,\n \"columnIndex\": 1\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"insertComment\": {\n \"content\": \"Please review the data in this cell.\",\n \"assigneeEmailAddress\": \"ASSIGNEE_EMAIL_ADDRESS\",\n \"coordinate\": {\n \"sheetId\": 0,\n \"rowIndex\": 1,\n \"columnIndex\": 1\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"addCommentReply\": {\n \"commentId\": \"COMMENT_ID\",\n \"post\": {\n \"content\": \"Replying to the comment thread.\"\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"addCommentReply\": {\n \"commentId\": \"COMMENT_ID\",\n \"post\": {\n \"commentAction\": \"RESOLVE\"\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"addCommentReply\": {\n \"commentId\": \"COMMENT_ID\",\n \"post\": {\n \"content\": \"Replying to the comment thread.\",\n \"assigneeEmail\": \"ASSIGNEE_EMAIL\"\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"updateCommentPost\": {\n \"commentId\": \"COMMENT_ID\",\n \"postId\": \"POST_ID\",\n \"content\": \"This is the updated comment text.\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"deleteComment\": {\n \"commentId\": \"COMMENT_ID\"\n }\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.773Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":179,"estimatedTokens":789}}870{"id":"doc-mcp_reference_slidesmcp_googleapis_com_google_sl-900aaff7","source":"documentation","title":"MCP Reference: slidesmcp.googleapis.com | Google Slides | Google for Developers","url":"https://developers.google.com/workspace/slides/api/reference/mcp","text":"Example:\n```text\ncurl --location 'https://slidesmcp.googleapis.com/mcp' \\\n--header 'content-type: application/json' \\\n--header 'accept: application/json, text/event-stream' \\\n--data '{\n \"method\": \"tools/list\",\n \"jsonrpc\": \"2.0\",\n \"id\": 1\n}'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.774Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":67}}871{"id":"doc-tasks_service_apps_script_google_for_developers-b901c56b","source":"documentation","title":"Tasks Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/advanced/tasks","text":"Example:\n```text\n/**\n * Lists the titles and IDs of tasksList.\n * @see https://developers.google.com/tasks/reference/rest/v1/tasklists/list\n */\nfunction listTaskLists() {\n try {\n // Returns all the authenticated user's task lists.\n const taskLists = Tasks.Tasklists.list();\n // If taskLists are available then print all tasklists.\n if (!taskLists.items) {\n console.log(\"No task lists found.\");\n return;\n }\n // Print the tasklist title and tasklist id.\n for (let i = 0; i < taskLists.items.length; i++) {\n const taskList = taskLists.items[i];\n console.log(\n 'Task list with title \"%s\" and ID \"%s\" was found.',\n taskList.title,\n taskList.id,\n );\n }\n } catch (err) {\n // TODO (developer) - Handle exception from Task API\n console.log(\"Failed with an error %s \", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Lists task items for a provided tasklist ID.\n * @param {string} taskListId The tasklist ID.\n * @see https://developers.google.com/tasks/reference/rest/v1/tasks/list\n */\nfunction listTasks(taskListId) {\n try {\n // List the task items of specified tasklist using taskList id.\n const tasks = Tasks.Tasks.list(taskListId);\n // If tasks are available then print all task of given tasklists.\n if (!tasks.items) {\n console.log(\"No tasks found.\");\n return;\n }\n // Print the task title and task id of specified tasklist.\n for (let i = 0; i < tasks.items.length; i++) {\n const task = tasks.items[i];\n console.log(\n 'Task with title \"%s\" and ID \"%s\" was found.',\n task.title,\n task.id,\n );\n }\n } catch (err) {\n // TODO (developer) - Handle exception from Task API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Adds a task to a tasklist.\n * @param {string} taskListId The tasklist to add to.\n * @see https://developers.google.com/tasks/reference/rest/v1/tasks/insert\n */\nfunction addTask(taskListId) {\n // Task details with title and notes for inserting new task\n let task = {\n title: \"Pick up dry cleaning\",\n notes: \"Remember to get this done!\",\n };\n try {\n // Call insert method with taskDetails and taskListId to insert Task to specified tasklist.\n task = Tasks.Tasks.insert(task, taskListId);\n // Print the Task ID of created task.\n console.log('Task with ID \"%s\" was created.', task.id);\n } catch (err) {\n // TODO (developer) - Handle exception from Tasks.insert() of Task API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.775Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":89,"estimatedTokens":646}}872{"id":"doc-mcp_reference_drivemcp_googleapis_com_google_dri-4b51925b","source":"documentation","title":"MCP Reference: drivemcp.googleapis.com | Google Drive | Google for Developers","url":"https://developers.google.com/workspace/drive/api/reference/mcp","text":"Example:\n```text\ncurl --location 'https://drivemcp.googleapis.com/mcp/v1' \\\n--header 'content-type: application/json' \\\n--header 'accept: application/json, text/event-stream' \\\n--data '{\n \"method\": \"tools/list\",\n \"jsonrpc\": \"2.0\",\n \"id\": 1\n}'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.775Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":68}}873{"id":"doc-format_messages_google_chat_google_for_developer-4bd11f3f","source":"documentation","title":"Format messages | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/format-messages","text":"Example:\n```text\n{\n \"text\": \"Your pizza delivery *has arrived*!\\nThank you for using _Cymbal Pizza!_\"\n }\n```\n\nExample:\n```text\n{\n \"text\": \"I can meet there at:\\nNoon\\n3 pm\\n5 pm\\nWhat time works for you?\",\n \"formattedText\": \"I can meet <http://example.com|there> at:\\n* Noon\\n* 3 pm\\n* 5 pm\\nWhat time works for *you*?\",\n }\n```\n\nExample:\n```text\n{\n \"text\": \"Hello <customEmojis/CUSTOM_EMOJI_ID>.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Hello <chat-emoji data-custom-emoji=\\\"customEmojis/CUSTOM_EMOJI_ID\\\">.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Hello <chat-emoji data-emoji-name=\\\"CUSTOM_EMOJI_NAME\\\">.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"A customer has reported an issue. Assigning ticket #942 to <users/123456789012345678901>.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Assigning ticket #942 to <chat-user data-user=\\\"users/123456789012345678901\\\">.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Assigning ticket #942 to <chat-user data-email=\\\"mahan@example.com\\\">.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Important message for <users/all>: Code freeze starts at midnight tonight!\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Important message for <chat-user data-user=\\\"users/all\\\">: Code freeze starts at midnight tonight!\"\n}\n```\n\nExample:\n```text\nThis is a code block.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.776Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":77,"estimatedTokens":325}}874{"id":"doc-make_a_google_chat_space_discoverable_to_specifi-e1e0ee3f","source":"documentation","title":"Make a Google Chat space discoverable to specific users in a Google Workspace organization | Google for Developers","url":"https://developers.google.com/workspace/chat/space-target-audience","text":"Example:\n```text\n\"accessSettings\": {\n \"accessPermissionSettings\": {\n \"discoverSpaceSetting\": {\n \"principals\": [\n { \"audience\": { \"name\": \"audiences/TARGET_AUDIENCE_ID_1\" } },\n { \"audience\": { \"name\": \"audiences/TARGET_AUDIENCE_ID_2\" } }\n ]\n },\n \"joinSpaceSetting\": {\n \"principals\": [\n { \"audience\": { \"name\": \"audiences/TARGET_AUDIENCE_ID_1\" } }\n ]\n }\n }\n }\n```\n\nExample:\n```text\n\"accessSettings\": {\n \"audience\": \"audiences/TARGET_AUDIENCE_ID\"\n }\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.777Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":27,"estimatedTokens":138}}875{"id":"doc-mcp_reference_sheetsmcp_googleapis_com_google_sh-7a8dac44","source":"documentation","title":"MCP Reference: sheetsmcp.googleapis.com | Google Sheets | Google for Developers","url":"https://developers.google.com/workspace/sheets/api/reference/mcp","text":"Example:\n```text\ncurl --location 'https://sheetsmcp.googleapis.com/mcp' \\\n--header 'content-type: application/json' \\\n--header 'accept: application/json, text/event-stream' \\\n--data '{\n \"method\": \"tools/list\",\n \"jsonrpc\": \"2.0\",\n \"id\": 1\n}'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.778Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":67}}876{"id":"doc-labels_google_calendar_google_for_developers-05b96f88","source":"documentation","title":"Labels | Google Calendar | Google for Developers","url":"https://developers.google.com/workspace/calendar/api/guides/labels","text":"Example:\n```text\n{\n \"kind\": \"calendar#calendar\",\n \"id\": \"primary\",\n \"summary\": \"My Team Calendar\",\n \"labelProperties\": {\n \"eventLabels\": [\n {\n \"id\": \"42617328-8756-4291-8273-192837465647\",\n \"backgroundColor\": \"#039be5\",\n \"name\": \"Important Project\"\n },\n {\n \"id\": \"19283746-9182-4736-8271-918273645261\",\n \"backgroundColor\": \"#33b679\",\n \"name\": \"Team Meeting\"\n }\n ]\n }\n}\n```\n\nExample:\n```text\n// Refer to the Go quickstart on how to setup the service:\n// https://developers.google.com/workspace/calendar/quickstart/go\n\n// 1. Read the calendar to get existing labels\ncalendar, err := srv.Calendars.Get(\"primary\").Do()\nif err != nil {\n log.Fatalf(\"Unable to retrieve calendar: %v\", err)\n}\n\nif calendar.LabelProperties == nil {\n calendar.LabelProperties = &calendar.LabelProperties{}\n}\nlabels := calendar.LabelProperties.EventLabels\n\n// 2. Remove a label with a specific ID (UUID format)\ntargetIdToRemove := \"11111111-2222-3333-4444-555555555555\"\nvar updatedLabels []*calendar.EventLabel\nfor _, label := range labels {\n if label.Id != targetIdToRemove {\n updatedLabels = append(updatedLabels, label)\n }\n}\nlabels = updatedLabels\n\n// 3. Add 2 new labels with UUID IDs\nlabels = append(labels, &calendar.EventLabel{\n Id: \"22222222-3333-4444-5555-666666666666\",\n BackgroundColor: \"#8e24aa\",\n Name: \"Design Work\",\n})\nlabels = append(labels, &calendar.EventLabel{\n Id: \"33333333-4444-5555-6666-777777777777\",\n BackgroundColor: \"#f4511e\",\n Name: \"Urgent Review\",\n})\n\n// 4. Update the calendar with the new list\ncalendar.LabelProperties.EventLabels = labels\nupdatedCalendar, err := srv.Calendars.Update(\"primary\", calendar).Do()\nif err != nil {\n log.Fatalf(\"Unable to update calendar: %v\", err)\n}\nfmt.Printf(\"Calendar updated: %s\\n\", updatedCalendar.Summary)\n```\n\nExample:\n```text\n// Refer to the Java quickstart on how to setup the service:\n// https://developers.google.com/workspace/calendar/quickstart/java\n\n// 1. Read the calendar to get existing labels\nCalendar calendar = service.calendars().get(\"primary\").execute();\n\nLabelProperties labelProperties = calendar.getLabelProperties();\nif (labelProperties == null) {\n labelProperties = new LabelProperties();\n}\nList<EventLabel> labels = labelProperties.getEventLabels();\nif (labels == null) {\n labels = new ArrayList<>();\n} else {\n // Create a mutable copy since the returned list might be immutable\n labels = new ArrayList<>(labels);\n}\n\n// 2. Remove a label with a specific ID (UUID format)\nString targetIdToRemove = \"11111111-2222-3333-4444-555555555555\";\nlabels.removeIf(label -> targetIdToRemove.equals(label.getId()));\n\n// 3. Add 2 new labels with UUID IDs\nlabels.add(new EventLabel()\n .setId(\"22222222-3333-4444-5555-666666666666\")\n .setBackgroundColor(\"#8e24aa\")\n .setName(\"Design Work\"));\n\nlabels.add(new EventLabel()\n .setId(\"33333333-4444-5555-6666-777777777777\")\n .setBackgroundColor(\"#f4511e\")\n .setName(\"Urgent Review\"));\n\n// 4. Update the calendar with the new list\nlabelProperties.setEventLabels(labels);\ncalendar.setLabelProperties(labelProperties);\n\nCalendar updatedCalendar = service.calendars().update(\"primary\", calendar)\n .execute();\n\nSystem.out.printf(\"Calendar updated: %s\\n\", updatedCalendar.getSummary());\n```\n\nExample:\n```text\n// Refer to the Node.js quickstart on how to setup the service:\n// https://developers.google.com/workspace/calendar/quickstart/nodejs\n\n// 1. Retrieve the calendar resource\ncalendar.calendars.get({\n calendarId: 'primary'\n}, (err, res) => {\n if (err) return console.log('The API returned an error: ' + err);\n\n // Extract existing labels from the calendar resource\n const cal = res.data;\n if (!cal.labelProperties) {\n cal.labelProperties = {};\n }\n if (!cal.labelProperties.eventLabels) {\n cal.labelProperties.eventLabels = [];\n }\n\n // 2. Remove a label with a specific ID (UUID format)\n const targetIdToRemove = '11111111-2222-3333-4444-555555555555';\n cal.labelProperties.eventLabels = cal.labelProperties.eventLabels.filter(\n label => label.id !== targetIdToRemove\n );\n\n // 3. Add 2 new labels with UUID IDs\n cal.labelProperties.eventLabels.push({\n id: '22222222-3333-4444-5555-666666666666',\n backgroundColor: '#8e24aa',\n name: 'Design Work'\n });\n cal.labelProperties.eventLabels.push({\n id: '33333333-4444-5555-6666-777777777777',\n backgroundColor: '#f4511e',\n name: 'Urgent Review'\n });\n\n // 4. Update the calendar with the new list\n calendar.calendars.update({\n calendarId: 'primary',\n resource: cal\n }, (updateErr, updateRes) => {\n if (updateErr) return console.log('Update failed: ' + updateErr);\n console.log(`Calendar updated: ${updateRes.data.summary}`);\n });\n});\n```\n\nExample:\n```text\n# Refer to the Python quickstart on how to setup the service:\n# https://developers.google.com/workspace/calendar/quickstart/python\n\n# 1. Read the calendar to get existing labels\ncalendar = service.calendars().get(calendarId='primary').execute()\n\nlabel_properties = calendar.setdefault('labelProperties', {})\nlabels = label_properties.setdefault('eventLabels', [])\n\n# 2. Remove a label with a specific ID (UUID format)\ntarget_id_to_remove = \"11111111-2222-3333-4444-555555555555\"\nlabels = [l for l in labels if l.get('id') != target_id_to_remove]\n\n# 3. Add 2 new labels with UUID IDs\nlabels.append({\n 'id': '22222222-3333-4444-5555-666666666666',\n 'backgroundColor': '#8e24aa',\n 'name': 'Design Work'\n})\nlabels.append({\n 'id': '33333333-4444-5555-6666-777777777777',\n 'backgroundColor': '#f4511e',\n 'name': 'Urgent Review'\n})\n\n# 4. Update the calendar with the new list\nlabel_properties['eventLabels'] = labels\ncalendar['labelProperties'] = label_properties\n\nupdated_calendar = service.calendars().update(\n calendarId='primary',\n body=calendar\n).execute()\n\nprint(f\"Calendar updated: {updated_calendar.get('summary')}\")\n```\n\nExample:\n```text\nPUT https://www.googleapis.com/calendar/v3/calendars/primary\nAuthorization: Bearer [YOUR_ACCESS_TOKEN]\nContent-Type: application/json\n\n{\n \"summary\": \"Updated Team Calendar\",\n \"labelProperties\": {\n \"eventLabels\": [\n {\n \"id\": \"22222222-3333-4444-5555-666666666666\",\n \"backgroundColor\": \"#8e24aa\",\n \"name\": \"Design Work\"\n },\n {\n \"id\": \"33333333-4444-5555-6666-777777777777\",\n \"backgroundColor\": \"#f4511e\",\n \"name\": \"Urgent Review\"\n }\n ]\n }\n}\n```\n\nExample:\n```text\n{\n \"kind\": \"calendar#event\",\n \"id\": \"sample-event-id\",\n \"summary\": \"Review Design Specs\",\n \"start\": {\n \"dateTime\": \"2026-07-01T10:00:00Z\"\n },\n \"end\": {\n \"dateTime\": \"2026-07-01T11:00:00Z\"\n },\n \"eventLabelId\": \"22222222-3333-4444-5555-666666666666\"\n}\n```\n\nExample:\n```text\n// Refer to the Go quickstart on how to setup the service:\n// https://developers.google.com/workspace/calendar/quickstart/go\n\nevent := &calendar.Event{\n Summary: \"Design Sync\",\n Start: &calendar.EventDateTime{\n DateTime: \"2026-07-02T14:00:00Z\",\n },\n End: &calendar.EventDateTime{\n DateTime: \"2026-07-02T15:00:00Z\",\n },\n EventLabelId: \"22222222-3333-4444-5555-666666666666\",\n}\n\ncreatedEvent, err := srv.Events.Insert(\"primary\", event).EventLabelVersion(1).Do()\nif err != nil {\n log.Fatalf(\"Unable to create event: %v\", err)\n}\nfmt.Printf(\"Event created: %s\\n\", createdEvent.HtmlLink)\n```\n\nExample:\n```text\n// Refer to the Java quickstart on how to setup the service:\n// https://developers.google.com/workspace/calendar/quickstart/java\n\nEvent event = new Event()\n .setSummary(\"Design Sync\")\n .setStart(new EventDateTime().setDateTime(new DateTime(\"2026-07-02T14:00:00Z\")))\n .setEnd(new EventDateTime().setDateTime(new DateTime(\"2026-07-02T15:00:00Z\")))\n .setEventLabelId(\"22222222-3333-4444-5555-666666666666\");\n\nEvent createdEvent = service.events().insert(\"primary\", event)\n .setEventLabelVersion(1L)\n .execute();\n\nSystem.out.printf(\"Event created: %s\\n\", createdEvent.getHtmlLink());\n```\n\nExample:\n```text\n// Refer to the Node.js quickstart on how to setup the service:\n// https://developers.google.com/workspace/calendar/quickstart/nodejs\n\nconst event = {\n summary: 'Design Sync',\n start: {\n dateTime: '2026-07-02T14:00:00Z',\n },\n end: {\n dateTime: '2026-07-02T15:00:00Z',\n },\n eventLabelId: '22222222-3333-4444-5555-666666666666',\n};\n\ncalendar.events.insert({\n calendarId: 'primary',\n resource: event,\n eventLabelVersion: 1,\n}, (err, res) => {\n if (err) return console.log('The API returned an error: ' + err);\n console.log(`Event created: ${res.data.htmlLink}`);\n});\n```\n\nExample:\n```text\n# Refer to the Python quickstart on how to setup the service:\n# https://developers.google.com/workspace/calendar/quickstart/python\n\nevent = {\n 'summary': 'Design Sync',\n 'start': {\n 'dateTime': '2026-07-02T14:00:00Z',\n },\n 'end': {\n 'dateTime': '2026-07-02T15:00:00Z',\n },\n 'eventLabelId': '22222222-3333-4444-5555-666666666666'\n}\n\nevent = service.events().insert(\n calendarId='primary',\n body=event,\n eventLabelVersion=1\n).execute()\n\nprint(f\"Event created: {event.get('htmlLink')}\")\n```\n\nExample:\n```text\nPOST https://www.googleapis.com/calendar/v3/calendars/primary/events?eventLabelVersion=1\nAuthorization: Bearer [YOUR_ACCESS_TOKEN]\nContent-Type: application/json\n\n{\n \"summary\": \"Design Sync\",\n \"start\": {\n \"dateTime\": \"2026-07-02T14:00:00Z\"\n },\n \"end\": {\n \"dateTime\": \"2026-07-02T15:00:00Z\"\n },\n \"eventLabelId\": \"22222222-3333-4444-5555-666666666666\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.779Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":355,"estimatedTokens":2402}}877{"id":"doc-manage_user_availability_for_chat_apps_google_ch-b1911f8f","source":"documentation","title":"Manage user availability for Chat apps | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/manage-user-availability","text":"Example:\n```text\nconst { ChatServiceClient } = require('@google-apps/chat').v1;\n\n// Instantiates a client\nconst chatServiceClient = new ChatServiceClient();\n\nasync function getAvailability() {\n const request = {\n // The name of the availability resource to retrieve.\n // Format: users/{user}/availability\n // The 'me' alias can be used to refer to the calling user.\n name: 'users/me/availability',\n };\n\n try {\n const response = await chatServiceClient.getAvailability(request);\n console.log(response);\n } catch (err) {\n console.error('Error retrieving availability:',\n```\n\nExample:\n```text\nfrom google.apps import chat_v1 as google_chat\n\ndef get_availability():\n # Instantiates a client\n client = google_chat.ChatServiceClient()\n\n # Prepare request\n request = google_chat.GetAvailabilityRequest(\n # Format: users/{user}/availability\n # The 'me' alias refers to the calling user.\n name=\"users/me/availability\",\n )\n\n # Call the API\n try:\n response = client.get_availability(request=request)\n print(response)\n except Exception as e:\n print(f\"Error retrieving availability: {e}\n```\n\nExample:\n```text\nimport com.google.chat.v1.Availability;\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.GetAvailabilityRequest;\n\npublic class GetAvailability {\n public static void main(String[] args) throws Exception {\n // Instantiates a client\n try (ChatServiceClient chatServiceClient = ChatServiceClient.create()) {\n GetAvailabilityRequest request = GetAvailabilityRequest.newBuilder()\n // Format: users/{user}/availability\n // The 'me' alias refers to the calling user.\n .setName(\"users/me/availability\")\n .build();\n\n Availability response = chatServiceClient.getAvailability(request);\n System.out.println(respo\n```\n\nExample:\n```text\n/**\n * Retrieves the calling user's availability details.\n */\nfunction getUserAvailability() {\n const name = 'users/me/availability';\n try {\n const availability = Chat.Users.Availability.get(name);\n console.log(availability);\n } catch (err) {\n console.error('Failed to get availability: ' +\n```\n\nExample:\n```text\nconst { ChatServiceClient } = require('@google-apps/chat').v1;\n\n// Instantiates a client\nconst chatServiceClient = new ChatServiceClient();\n\nasync function updateCustomStatus() {\n const request = {\n // The Availability resource to update.\n availability: {\n name: 'users/me/availability',\n customStatus: {\n text: 'In a meeting',\n emoji: {\n unicode: '📅'\n }\n }\n },\n // The fields to update. Must contain 'custom_status'.\n updateMask: {\n paths: ['custom_status']\n }\n };\n\n try {\n const response = await chatServiceClient.updateAvailability(request);\n console.log(response);\n } catch (err) {\n console.error('Error up\n```\n\nExample:\n```text\nfrom google.apps import chat_v1 as google_chat\nfrom google.protobuf import field_mask_pb2\n\ndef update_custom_status():\n # Instantiates a client\n client = google_chat.ChatServiceClient()\n\n # Define custom status and emoji\n custom_status = google_chat.CustomStatus(\n text=\"In a meeting\",\n emoji=google_chat.Emoji(unicode=\"📅\")\n )\n\n # Initialize availability object\n availability = google_chat.Availability(\n name=\"users/me/availability\",\n custom_status=custom_status\n )\n\n # Specify update mask\n update_mask = field_mask_pb2.FieldMask(paths=[\"custom_status\"])\n\n # Prepare request\n request = google_chat.UpdateAvailabilityRequest(\n availability=availability,\n update_mask=update_mask\n )\n\n # Call the API\n try:\n response = client.update_availability(request=request)\n print(response)\n except Exception as e:\n print(f\"Error up\n```\n\nExample:\n```text\nimport com.google.chat.v1.Availability;\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.CustomStatus;\nimport com.google.chat.v1.Emoji;\nimport com.google.chat.v1.UpdateAvailabilityRequest;\nimport com.google.protobuf.FieldMask;\n\npublic class UpdateCustomStatus {\n public static void main(String[] args) throws Exception {\n // Instantiates a client\n try (ChatServiceClient chatServiceClient = ChatServiceClient.create()) {\n CustomStatus customStatus = CustomStatus.newBuilder()\n .setText(\"In a meeting\")\n .setEmoji(Emoji.newBuilder().setUnicode(\"📅\"))\n .build();\n\n Availability availability = Availability.newBuilder()\n .setName(\"users/me/availability\")\n .setCustomStatus(customStatus)\n .build();\n\n FieldMask updateMask = FieldMask.newBuilder()\n .addPaths(\"custom_status\")\n .build();\n\n UpdateAvailabilityRequest request = UpdateAvailabilityRequest.newBuilder()\n .setAvailability(availability)\n .setUpdateMask(updateMask)\n .build();\n\n Availability response = chatServiceClient.updateAvailability(request);\n Sy\n```\n\nExample:\n```text\n/**\n * Updates the calling user's custom status message.\n */\nfunction updateCustomStatus() {\n const name = 'users/me/availability';\n const availability = {\n customStatus: {\n text: 'In a meeting',\n emoji: {\n unicode: '📅'\n }\n }\n };\n const updateMask = 'custom_status';\n\n try {\n const response = Chat.Users.Availability.patch(availability, name, {\n updateMask: updateMask\n });\n console.log(response);\n } catch (err) {\n console.error('Failed to\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.780Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":212,"estimatedTokens":1396}}878{"id":"doc-mcp_reference_gmailmcp_googleapis_com_gmail_goog-d1a72605","source":"documentation","title":"MCP Reference: gmailmcp.googleapis.com | Gmail | Google for Developers","url":"https://developers.google.com/workspace/gmail/api/reference/mcp","text":"Example:\n```text\ncurl --location 'https://gmailmcp.googleapis.com/mcp/v1' \\\n--header 'content-type: application/json' \\\n--header 'accept: application/json, text/event-stream' \\\n--data '{\n \"method\": \"tools/list\",\n \"jsonrpc\": \"2.0\",\n \"id\": 1\n}'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.788Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":68}}879{"id":"doc-mcp_reference_chatmcp_googleapis_com_google_chat-1d6bbdbd","source":"documentation","title":"MCP Reference: chatmcp.googleapis.com | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/api/reference/mcp","text":"Example:\n```text\ncurl --location 'https://chatmcp.googleapis.com/mcp/v1' \\\n--header 'content-type: application/json' \\\n--header 'accept: application/json, text/event-stream' \\\n--data '{\n \"method\": \"tools/list\",\n \"jsonrpc\": \"2.0\",\n \"id\": 1\n}'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.789Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":67}}880{"id":"doc-send_a_message_using_the_google_chat_api_google_-d7d1d0f2","source":"documentation","title":"Send a message using the Google Chat API | Google for Developers","url":"https://developers.google.com/workspace/chat/create-messages","text":"Example:\n```text\nimport {createClientWithAppCredentials} from './authentication-utils.js';\n\n// This sample shows how to create message with app credential\nasync function main() {\n // Create a client\n const chatClient = createClientWithAppCredentials();\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here.\n parent: 'spaces/SPACE_NAME',\n message: {\n text:\n '👋🌎 Hello world! I created this message by calling ' +\n \"the Chat API's `messages.create()` method.\",\n cardsV2: [\n {\n card: {\n header: {\n title: 'About this message',\n imageUrl:\n 'https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/info/default/24px.svg',\n },\n sections: [\n {\n header: 'Contents',\n widgets: [\n {\n textParagraph: {\n text:\n '🔡 <b>Text</b> which can include ' +\n 'hyperlinks 🔗, emojis 😄🎉, and @mentions 🗣️.',\n },\n },\n {\n textParagraph: {\n text:\n '🖼️ A <b>card</b> to display visual elements' +\n 'and request information such as text 🔤, ' +\n 'dates and times 📅, and selections ☑️.',\n },\n },\n {\n textParagraph: {\n text:\n '👉🔘 An <b>accessory widget</b> which adds ' +\n 'a button to the bottom of a message.',\n },\n },\n ],\n },\n {\n header: \"What's next\",\n collapsible: true,\n widgets: [\n {\n textParagraph: {\n text: \"❤️ <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages.reactions/create'>Add a reaction</a>.\",\n },\n },\n {\n textParagraph: {\n text:\n \"🔄 <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages/patch'>Update</a> \" +\n \"or ❌ <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages/delete'>delete</a> \" +\n 'the message.',\n },\n },\n ],\n },\n ],\n },\n },\n ],\n accessoryWidgets: [\n {\n buttonList: {\n buttons: [\n {\n text: 'View documentation',\n icon: {materialIcon: {name: 'link'}},\n onClick: {\n openLink: {\n url: 'https://developers.google.com/workspace/chat/create-messages',\n },\n },\n },\n ],\n },\n },\n ],\n },\n };\n\n // Make the request\n const response = await chatClient.createMessage(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_app_credentials\nfrom google.apps import chat_v1 as google_chat\n\n# This sample shows how to create message with app credential\ndef create_message_with_app_cred():\n # Create a client\n client = create_client_with_app_credentials()\n\n # Initialize request argument(s)\n request = google_chat.CreateMessageRequest(\n # Replace SPACE_NAME here.\n parent = \"spaces/SPACE_NAME\",\n message = {\n \"text\": '👋🌎 Hello world! I created this message by calling ' +\n 'the Chat API\\'s `messages.create()` method.',\n \"cards_v2\" : [{ \"card\": {\n \"header\": {\n \"title\": 'About this message',\n \"image_url\": 'https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/info/default/24px.svg'\n },\n \"sections\": [{\n \"header\": \"Contents\",\n \"widgets\": [{ \"text_paragraph\": {\n \"text\": '🔡 <b>Text</b> which can include ' +\n 'hyperlinks 🔗, emojis 😄🎉, and @mentions 🗣️.'\n }}, { \"text_paragraph\": {\n \"text\": '🖼️ A <b>card</b> to display visual elements' +\n 'and request information such as text 🔤, ' +\n 'dates and times 📅, and selections ☑️.'\n }}, { \"text_paragraph\": {\n \"text\": '👉🔘 An <b>accessory widget</b> which adds ' +\n 'a button to the bottom of a message.'\n }}\n ]}, {\n \"header\": \"What's next\",\n \"collapsible\": True,\n \"widgets\": [{ \"text_paragraph\": {\n \"text\": \"❤️ <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages.reactions/create'>Add a reaction</a>.\"\n }}, { \"text_paragraph\": {\n \"text\": \"🔄 <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages/patch'>Update</a> \" +\n \"or ❌ <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages/delete'>delete</a> \" +\n \"the message.\"\n }\n }]\n }\n ]\n }}],\n \"accessory_widgets\": [{ \"button_list\": { \"buttons\": [{\n \"text\": 'View documentation',\n \"icon\": { \"material_icon\": { \"name\": 'link' }},\n \"on_click\": { \"open_link\": {\n \"url\": 'https://developers.google.com/workspace/chat/create-messages'\n }}\n }]}}]\n }\n )\n\n # Make the request\n response = client.create_message(request)\n\n # Handle the response\n print(response)\n\ncreate_message_with_app_cred()\n```\n\nExample:\n```text\nimport com.google.apps.card.v1.Button;\nimport com.google.apps.card.v1.ButtonList;\nimport com.google.apps.card.v1.Card;\nimport com.google.apps.card.v1.Icon;\nimport com.google.apps.card.v1.MaterialIcon;\nimport com.google.apps.card.v1.OnClick;\nimport com.google.apps.card.v1.OpenLink;\nimport com.google.apps.card.v1.TextParagraph;\nimport com.google.apps.card.v1.Widget;\nimport com.google.apps.card.v1.Card.CardHeader;\nimport com.google.apps.card.v1.Card.Section;\nimport com.google.chat.v1.AccessoryWidget;\nimport com.google.chat.v1.CardWithId;\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.CreateMessageRequest;\nimport com.google.chat.v1.Message;\n\n// This sample shows how to create message with app credential.\npublic class CreateMessageAppCred {\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithAppCredentials()) {\n CreateMessageRequest.Builder request = CreateMessageRequest.newBuilder()\n // Replace SPACE_NAME here.\n .setParent(\"spaces/SPACE_NAME\")\n .setMessage(Message.newBuilder()\n .setText( \"👋🌎 Hello world! I created this message by calling \" +\n \"the Chat API\\'s `messages.create()` method.\")\n .addCardsV2(CardWithId.newBuilder().setCard(Card.newBuilder()\n .setHeader(CardHeader.newBuilder()\n .setTitle(\"About this message\")\n .setImageUrl(\"https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/info/default/24px.svg\"))\n .addSections(Section.newBuilder()\n .setHeader(\"Contents\")\n .addWidgets(Widget.newBuilder().setTextParagraph(TextParagraph.newBuilder().setText(\n \"🔡 <b>Text</b> which can include \" +\n \"hyperlinks 🔗, emojis 😄🎉, and @mentions 🗣️.\")))\n .addWidgets(Widget.newBuilder().setTextParagraph(TextParagraph.newBuilder().setText(\n \"🖼️ A <b>card</b> to display visual elements \" +\n \"and request information such as text 🔤, \" +\n \"dates and times 📅, and selections ☑️.\")))\n .addWidgets(Widget.newBuilder().setTextParagraph(TextParagraph.newBuilder().setText(\n \"👉🔘 An <b>accessory widget</b> which adds \" +\n \"a button to the bottom of a message.\"))))\n .addSections(Section.newBuilder()\n .setHeader(\"What's next\")\n .setCollapsible(true)\n .addWidgets(Widget.newBuilder().setTextParagraph(TextParagraph.newBuilder().setText(\n \"❤️ <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages.reactions/create'>Add a reaction</a>.\")))\n .addWidgets(Widget.newBuilder().setTextParagraph(TextParagraph.newBuilder().setText(\n \"🔄 <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages/patch'>Update</a> \" +\n \"or ❌ <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages/delete'>delete</a> \" +\n \"the message.\"))))))\n .addAccessoryWidgets(AccessoryWidget.newBuilder()\n .setButtonList(ButtonList.newBuilder()\n .addButtons(Button.newBuilder()\n .setText(\"View documentation\")\n .setIcon(Icon.newBuilder()\n .setMaterialIcon(MaterialIcon.newBuilder().setName(\"link\")))\n .setOnClick(OnClick.newBuilder()\n .setOpenLink(OpenLink.newBuilder()\n .setUrl(\"https://developers.google.com/workspace/chat/create-messages\")))))));\n Message response = chatServiceClient.createMessage(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to create message with app credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.bot'\n * used by service accounts.\n */\nfunction createMessageAppCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here.\n const parent = \"spaces/SPACE_NAME\";\n const message = {\n text:\n \"👋🌎 Hello world! I created this message by calling \" +\n \"the Chat API's `messages.create()` method.\",\n cardsV2: [\n {\n card: {\n header: {\n title: \"About this message\",\n imageUrl:\n \"https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/info/default/24px.svg\",\n },\n sections: [\n {\n header: \"Contents\",\n widgets: [\n {\n textParagraph: {\n text:\n \"🔡 <b>Text</b> which can include \" +\n \"hyperlinks 🔗, emojis 😄🎉, and @mentions 🗣️.\",\n },\n },\n {\n textParagraph: {\n text:\n \"🖼️ A <b>card</b> to display visual elements\" +\n \"and request information such as text 🔤, \" +\n \"dates and times 📅, and selections ☑️.\",\n },\n },\n {\n textParagraph: {\n text:\n \"👉🔘 An <b>accessory widget</b> which adds \" +\n \"a button to the bottom of a message.\",\n },\n },\n ],\n },\n {\n header: \"What's next\",\n collapsible: true,\n widgets: [\n {\n textParagraph: {\n text: \"❤️ <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages.reactions/create'>Add a reaction</a>.\",\n },\n },\n {\n textParagraph: {\n text:\n \"🔄 <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages/patch'>Update</a> \" +\n \"or ❌ <a href='https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages/delete'>delete</a> \" +\n \"the message.\",\n },\n },\n ],\n },\n ],\n },\n },\n ],\n accessoryWidgets: [\n {\n buttonList: {\n buttons: [\n {\n text: \"View documentation\",\n icon: { materialIcon: { name: \"link\" } },\n onClick: {\n openLink: {\n url: \"https://developers.google.com/workspace/chat/create-messages\",\n },\n },\n },\n ],\n },\n },\n ],\n };\n const parameters = {};\n\n // Make the request\n const response = Chat.Spaces.Messages.create(\n message,\n parent,\n parameters,\n getHeaderWithAppCredentials(),\n );\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\n{\n text: \"Rate your experience with this Chat app.\",\n accessoryWidgets: [{ buttonList: { buttons: [{\n icon: { material_icon: {\n name: \"thumb_up\"\n }},\n color: { red: 0, blue: 255, green: 0 },\n onClick: { action: {\n function: \"doUpvote\"\n }}\n }, {\n icon: { material_icon: {\n name: \"thumb_down\"\n }},\n color: { red: 0, blue: 255, green: 0 },\n onClick: { action: {\n function: \"doDownvote\"\n }}\n }]}}]\n}\n```\n\nExample:\n```text\n{\n text: \"Hello private world!\",\n privateMessageViewer: {\n name: \"users/USER_ID\"\n }\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Critical update: the server is down!\",\n \"createMessageNotificationOptions\": {\n \"notificationType\": \"NOTIFICATION_TYPE_FORCE_NOTIFY\"\n }\n}\n```\n\nExample:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.messages.create',\n];\n\n// This sample shows how to create message with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here.\n parent: 'spaces/SPACE_NAME',\n message: {\n text:\n '👋🌎 Hello world!' +\n 'Text messages can contain things like:\\n\\n' +\n '* Hyperlinks 🔗\\n' +\n '* Emojis 😄🎉\\n' +\n '* Mentions of other Chat users `@` \\n\\n' +\n 'For details, see the ' +\n '<https://developers.google.com/workspace/chat/format-messages' +\n '|Chat API developer documentation>.',\n },\n };\n\n // Make the request\n const response = await chatClient.createMessage(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.messages.create\"]\n\ndef create_message_with_user_cred():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.CreateMessageRequest(\n # Replace SPACE_NAME here.\n parent = \"spaces/SPACE_NAME\",\n message = {\n \"text\": '👋🌎 Hello world!' +\n 'Text messages can contain things like:\\n\\n' +\n '* Hyperlinks 🔗\\n' +\n '* Emojis 😄🎉\\n' +\n '* Mentions of other Chat users `@` \\n\\n' +\n 'For details, see the ' +\n '<https://developers.google.com/workspace/chat/format-messages' +\n '|Chat API developer documentation>.'\n }\n )\n\n # Make the request\n response = client.create_message(request)\n\n # Handle the response\n print(response)\n\ncreate_message_with_user_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.CreateMessageRequest;\nimport com.google.chat.v1.Message;\n\n// This sample shows how to create message with user credential.\npublic class CreateMessageUserCred {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.messages.create\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n CreateMessageRequest.Builder request = CreateMessageRequest.newBuilder()\n // Replace SPACE_NAME here.\n .setParent(\"spaces/SPACE_NAME\")\n .setMessage(Message.newBuilder()\n .setText( \"👋🌎 Hello world!\" +\n \"Text messages can contain things like:\\n\\n\" +\n \"* Hyperlinks 🔗\\n\" +\n \"* Emojis 😄🎉\\n\" +\n \"* Mentions of other Chat users `@` \\n\\n\" +\n \"For details, see the \" +\n \"<https://developers.google.com/workspace/chat/format-messages\" +\n \"|Chat API developer documentation>.\"));\n Message response = chatServiceClient.createMessage(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to create message with user credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.messages.create'\n * referenced in the manifest file (appsscript.json).\n */\nfunction createMessageUserCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here.\n const parent = \"spaces/SPACE_NAME\";\n const message = {\n text:\n \"👋🌎 Hello world!\" +\n \"Text messages can contain things like:\\n\\n\" +\n \"* Hyperlinks 🔗\\n\" +\n \"* Emojis 😄🎉\\n\" +\n \"* Mentions of other Chat users `@` \\n\\n\" +\n \"For details, see the \" +\n \"<https://developers.google.com/workspace/chat/format-messages\" +\n \"|Chat API developer documentation>.\",\n };\n\n // Make the request\n const response = Chat.Spaces.Messages.create(message, parent);\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nimport {protos} from '@google-apps/chat';\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.messages.create',\n];\n\n// This sample shows how to create message with user credential with thread key\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here.\n parent: 'spaces/SPACE_NAME',\n // Creates the message as a reply to the thread specified by thread_key\n // If it fails, the message starts a new thread instead\n messageReplyOption:\n protos.google.chat.v1.CreateMessageRequest.MessageReplyOption\n .REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD,\n message: {\n text: 'Hello with user credential!',\n thread: {\n // Thread key specifies a thread and is unique to the chat app\n // that sets it\n threadKey: 'THREAD_KEY',\n },\n },\n };\n\n // Make the request\n const response = await chatClient.createMessage(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nimport google.apps.chat_v1.CreateMessageRequest.MessageReplyOption\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.messages.create\"]\n\n# This sample shows how to create message with user credential with thread key\ndef create_message_with_user_cred_thread_key():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.CreateMessageRequest(\n # Replace SPACE_NAME here\n parent = \"spaces/SPACE_NAME\",\n # Creates the message as a reply to the thread specified by thread_key.\n # If it fails, the message starts a new thread instead.\n message_reply_option = MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD,\n message = {\n \"text\": \"Hello with user credential!\",\n \"thread\": {\n # Thread key specifies a thread and is unique to the chat app\n # that sets it.\n \"thread_key\": \"THREAD_KEY\"\n }\n }\n )\n\n # Make the request\n response = client.create_message(request)\n\n # Handle the response\n print(response)\n\ncreate_message_with_user_cred_thread_key()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.CreateMessageRequest;\nimport com.google.chat.v1.CreateMessageRequest.MessageReplyOption;\nimport com.google.chat.v1.Message;\nimport com.google.chat.v1.Thread;\n\n// This sample shows how to create message with a thread key with user\n// credential.\npublic class CreateMessageUserCredThreadKey {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.messages.create\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n CreateMessageRequest.Builder request = CreateMessageRequest.newBuilder()\n // Replace SPACE_NAME here.\n .setParent(\"spaces/SPACE_NAME\")\n // Creates the message as a reply to the thread specified by thread_key.\n // If it fails, the message starts a new thread instead.\n .setMessageReplyOption(\n MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD)\n .setMessage(Message.newBuilder()\n .setText(\"Hello with user credentials!\")\n // Thread key specifies a thread and is unique to the chat app\n // that sets it.\n .setThread(Thread.newBuilder().setThreadKey(\"THREAD_KEY\")));\n Message response = chatServiceClient.createMessage(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to create message with user credential with thread key\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.messages.create'\n * referenced in the manifest file (appsscript.json).\n */\nfunction createMessageUserCredThreadKey() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here.\n const parent = \"spaces/SPACE_NAME\";\n // Creates the message as a reply to the thread specified by thread_key\n // If it fails, the message starts a new thread instead\n const messageReplyOption = \"REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD\";\n const message = {\n text: \"Hello with user credential!\",\n thread: {\n // Thread key specifies a thread and is unique to the chat app\n // that sets it\n threadKey: \"THREAD_KEY\",\n },\n };\n\n // Make the request\n const response = Chat.Spaces.Messages.create(message, parent, {\n messageReplyOption: messageReplyOption,\n });\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.messages.create',\n];\n\n// This sample shows how to create a message with user credentials and a custom\n// message id\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here.\n parent: 'spaces/SPACE_NAME',\n // Message id lets chat apps get, update or delete a message without needing\n // to store the system assigned ID in the message's resource name\n messageId: 'client-MESSAGE-ID',\n message: {text: 'Hello with user credential!'},\n };\n\n // Make the request\n const response = await chatClient.createMessage(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.messages.create\"]\n\n# This sample shows how to create message with user credential with message id\ndef create_message_with_user_cred_message_id():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.CreateMessageRequest(\n # Replace SPACE_NAME here\n parent = \"spaces/SPACE_NAME\",\n # Message id let chat apps get, update or delete a message without needing\n # to store the system assigned ID in the message's resource name.\n message_id = \"client-MESSAGE-ID\",\n message = {\n \"text\": \"Hello with user credential!\"\n }\n )\n\n # Make the request\n response = client.create_message(request)\n\n # Handle the response\n print(response)\n\ncreate_message_with_user_cred_message_id()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.CreateMessageRequest;\nimport com.google.chat.v1.Message;\n\n// This sample shows how to create message with message id specified with user\n// credential.\npublic class CreateMessageUserCredMessageId {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.messages.create\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n CreateMessageRequest.Builder request = CreateMessageRequest.newBuilder()\n // Replace SPACE_NAME here.\n .setParent(\"spaces/SPACE_NAME\")\n .setMessage(Message.newBuilder()\n .setText(\"Hello with user credentials!\"))\n // Message ID lets chat apps get, update or delete a message without\n // needing to store the system assigned ID in the message's resource\n // name.\n .setMessageId(\"client-MESSAGE-ID\");\n Message response = chatServiceClient.createMessage(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to create message with user credential with message id\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.messages.create'\n * referenced in the manifest file (appsscript.json).\n */\nfunction createMessageUserCredMessageId() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here.\n const parent = \"spaces/SPACE_NAME\";\n // Message id lets chat apps get, update or delete a message without needing\n // to store the system assigned ID in the message's resource name\n const messageId = \"client-MESSAGE-ID\";\n const message = { text: \"Hello with user credential!\" };\n\n // Make the request\n const response = Chat.Spaces.Messages.create(message, parent, {\n messageId: messageId,\n });\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = ['https://www.googleapis.com/auth/chat.messages.create'];\n\n// This sample shows how to create a message that quotes another message.\nasync function main() {\n\n // Create a client\n const chatClient = await createClientWithUserCredentials(USER_AUTH_OAUTH_SCOPES);\n\n // Initialize request argument(s)\n const request = {\n\n // TODO(developer): Replace SPACE_NAME .\n parent: 'spaces/SPACE_NAME',\n\n message: {\n text: 'I am responding to a quoted message!',\n\n // quotedMessageMetadata lets Chat apps respond to a message by quoting it.\n quotedMessageMetadata: {\n\n // TODO(developer): Replace QUOTED_MESSAGE_NAME\n // and QUOTED_MESSAGE_LAST_UPDATE_TIME.\n name: 'QUOTED_MESSAGE_NAME',\n lastUpdateTime: 'QUOTED_MESSAGE_LAST_UPDATE_TIME'\n }\n }\n };\n\n // Make the request\n const response = await chatClient.createMessage(request);\n\n // Handle the response\n console.log(response);\n}\n\nmain().catch(console.error);\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\nfrom google.protobuf.timestamp_pb2 import Timestamp\n\nSCOPES = ['https://www.googleapis.com/auth/chat.messages.create']\n\n# This sample shows how to create a message that quotes another message.\ndef create_message_quote_message():\n '''Creates a message that quotes another message.'''\n\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Create a timestamp from the RFC-3339 string.\n # TODO(developer): Replace QUOTED_MESSAGE_LAST_UPDATE_TIME.\n last_update_time = Timestamp()\n last_update_time.FromJsonString('QUOTED_MESSAGE_LAST_UPDATE_TIME')\n\n # Initialize request argument(s)\n request = google_chat.CreateMessageRequest(\n\n # TODO(developer): Replace SPACE_NAME.\n parent='spaces/SPACE_NAME',\n\n # Create the message.\n message = google_chat.Message(\n text='I am responding to a quoted message!',\n\n # quotedMessageMetadata lets Chat apps respond to a message by quoting it.\n quoted_message_metadata=google_chat.QuotedMessageMetadata(\n\n name='QUOTED_MESSAGE_NAME',\n last_update_time=last_update_time\n )\n )\n )\n\n # Make the request\n response = client.create_message(request)\n\n # Handle the response\n print(response)\n\ncreate_message_quote_message()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.CreateMessageRequest;\nimport com.google.chat.v1.Message;\nimport com.google.chat.v1.QuotedMessageMetadata;\nimport com.google.protobuf.util.Timestamps;\nimport com.google.workspace.api.chat.samples.utils.AuthenticationUtils;\nimport java.text.ParseException;\n\n// This sample shows how to create a message that quotes another message.\npublic class CreateMessageQuoteMessage {\n public static void main(String[] args) throws Exception, ParseException {\n // Create a client.\n ChatServiceClient chatClient = AuthenticationUtils.createClientWithUserCredentials();\n\n // Initialize request argument(s).\n // TODO(developer): Replace SPACE_NAME, QUOTED_MESSAGE_NAME,\n // and QUOTED_MESSAGE_LAST_UPDATE_TIME here.\n String parent = \"spaces/SPACE_NAME\";\n String quotedMessageName = \"QUOTED_MESSAGE_NAME\";\n String lastUpdateTime = \"QUOTED_MESSAGE_LAST_UPDATE_TIME\";\n\n QuotedMessageMetadata quotedMessageMetadata =\n QuotedMessageMetadata.newBuilder()\n .setName(quotedMessageName)\n .setLastUpdateTime(Timestamps.parse(lastUpdateTime))\n .build();\n\n Message message = Message.newBuilder()\n .setText(\"I am responding to a quoted message!\")\n .setQuotedMessageMetadata(quotedMessageMetadata)\n .build();\n\n CreateMessageRequest request =\n CreateMessageRequest.newBuilder()\n .setParent(parent)\n .setMessage(message)\n .build();\n\n // Make the request.\n Message response = chatClient.createMessage(request);\n\n // Handle the response.\n System.out.println(response);\n }\n}\n```\n\nExample:\n```text\n/**\n * Creates a message that quotes another message.\n *\n * Relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.messages.create'\n * referenced in the manifest file (appsscript.json).\n */\nfunction createMessageQuoteMessage() {\n\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here.\n const parent = 'spaces/SPACE_NAME';\n\n const message = {\n\n // The text content of the message.\n text: 'I am responding to a quoted message!',\n\n // quotedMessageMetadata lets Chat apps respond to a message by quoting it.\n //\n // TODO(developer): Replace QUOTED_MESSAGE_NAME\n // and QUOTED_MESSAGE_LAST_UPDATE_TIME .\n quotedMessageMetadata: {\n name: 'QUOTED_MESSAGE_NAME',\n lastUpdateTime: 'QUOTED_MESSAGE_LAST_UPDATE_TIME',\n }\n };\n\n // Make the request\n const response = Chat.Spaces.Messages.create(message, parent);\n\n // Handle the response\n console.log(response);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.792Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":23,"totalLines":1010,"estimatedTokens":8243}}881{"id":"doc-class_form_apps_script_google_for_developers-2256ce28","source":"documentation","title":"Class Form | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/forms/form","text":"Example:\n```text\n// Open a form by ID and create a new spreadsheet.\nconst form = FormApp.openById('1234567890abcdefghijklmnopqrstuvwxyz');\nconst ss = SpreadsheetApp.create('Spreadsheet Name');\n\n// Update form properties via chaining.\nform.setTitle('Form Name')\n .setDescription('Description of form')\n .setConfirmationMessage('Thanks for responding!')\n .setAllowResponseEdits(true)\n .setAcceptingResponses(false);\n\n// Update the form's response destination.\nform.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds a checkbox grid item.\nconst item = form.addCheckboxGridItem();\n\nitem.setTitle('Where did you celebrate New Year\\'s?');\n\n// Sets the grid's rows and columns.\nitem.setRows(['New York', 'San Francisco', 'London']).setColumns([\n '2014', '2015', '2016', '2017'\n]);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds a checkbox item.\nconst item = form.addCheckboxItem();\n\n// Sets the title of the checkbox item to 'Do you prefer cats or dogs?'\nitem.setTitle('Do you prefer cats or dogs?');\n\n// Sets the choices.\nitem.setChoiceValues(['Cats', 'Dogs']);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds a date item.\nconst item = form.addDateItem();\n\n// Sets the title to 'When were you born?'\nitem.setTitle('When were you born?');\n\n// Sets the description for the date item.\nitem.setHelpText('Some helper text.');\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds a question with date and time inputs.\nconst item = form.addDateTimeItem();\n\n// Sets the title to 'When were you born?'\nitem.setTitle('When were you born?');\n\n// Sets the question as required.\nitem.setRequired(true);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds a question with a duration input.\nconst item = form.addDurationItem();\n\n// Sets the title to 'How long can you hold your breath?'\nitem.setTitle('How long can you hold your breath?');\n\n// Sets the question as required.\nitem.setRequired(true);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl('https://docs.google.com/forms/d/abc123456/edit');\n\n// Adds editor to the form.\n// TODO(developer): replace the emailAddress.\nform.addEditor('editor@uni.edu');\n```\n\nExample:\n```text\n// Opens the Forms file by its URL.\n// TODO(developer): Replace the URL with your own.\nconst oldForm = FormApp.openByUrl('https://docs.google.com/forms/d/abc123456/edit');\n\n// Gets the editors from the old form.\nconst users = oldForm.getEditors();\n\n// Creates a new form.\nconst newForm = FormApp.create('New form');\n\n// Adds the editors to a new form.\nusers.forEach(user => newForm.addEditor(user));\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl('https://docs.google.com/forms/d/abc123456/edit');\n\n// Adds editors to the form.\n// TODO(developer): replace the emailAddress.\nform.addPublishedReaders(['editor1@uni.edu', 'editor2@uni.edu']);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds a multiple choice grid.\nconst item = form.addGridItem();\n\n// Sets the title to 'Rate your interests.'\nitem.setTitle('Rate your interests');\n\n// Sets the grid's rows and columns.\nitem.setRows(['Cars', 'Computers', 'Celebrities']).setColumns([\n 'Boring', 'So-so', 'Interesting'\n]);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds an image item.\nconst item = form.addImageItem();\n\n// Gets the Google icon to use as the image.\nconst img = UrlFetchApp.fetch(\n 'https://fonts.gstatic.com/s/i/productlogos/googleg/v6/web-24dp/logo_googleg_color_1x_web_24dp.png',\n);\n\n// Sets the image, title, and description for the item.\nitem.setTitle('Google icon').setHelpText('Google icon').setImage(img);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds a dropdown list to the form.\nconst item = form.addListItem();\n\n// Sets the title to 'Do you prefer cats or dogs?'\nitem.setTitle('Do you prefer cats or dogs?');\n\n// Sets the description to 'This is description text...'\nitem.setHelpText('This is description text...');\n\n// Creates and adds choices to the dropdown list.\nitem.setChoices([item.createChoice('dog'), item.createChoice('cat')]);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds a multiple choice item to the form.\nconst item = form.addMultipleChoiceItem();\n\n// Sets the title.\nitem.setTitle('What is your favorite ice cream flavor?');\n\n// Creates some choice items.\nconst vanilla = item.createChoice('vanilla');\nconst chocolate = item.createChoice('chocolate');\nconst strawberry = item.createChoice('strawberry');\n\n// Sets the choices.\nitem.setChoices([vanilla, chocolate, strawberry]);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds page break items to create a second and third page for the form.\nconst pageTwo = form.addPageBreakItem();\nconst pageThree = form.addPageBreakItem();\n\n// Sets the titles for the pages.\npageTwo.setTitle('Page two');\npageThree.setTitle('Page three');\n\n// Upon completion of the first page, sets the form to navigate to the third\n// page.\npageTwo.setGoToPage(pageThree);\n\n// Upon completion of the second page, sets the form to navigate back to the\n// first page.\npageThree.setGoToPage(FormApp.PageNavigationType.RESTART);\n```\n\nExample:\n```text\n// Opens the form by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds the paragraph text item.\nconst item = form.addParagraphTextItem();\n\n// Sets the title to 'What is your address?'\nitem.setTitle('What is your address?');\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl('https://docs.google.com/forms/d/abc123456/edit');\n\n// Adds responder to the form.\n// TODO(developer): replace the emailAddress.\nform.addPublishedReader('responder@uni.edu');\n```\n\nExample:\n```text\n// Opens the Forms file by its URL.\n// TODO(developer): Replace the URL with your own.\nconst oldForm = FormApp.openByUrl('https://docs.google.com/forms/d/abc123456/edit');\n\n// Gets the responders from the old form.\nconst users = oldForm.getPublishedReaders();\n\n// Creates a new form.\nconst newForm = FormApp.create('New form');\n\n// Adds the responders to a new form.\nusers.forEach(user => newForm.addPublishedReader(user));\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl('https://docs.google.com/forms/d/abc123456/edit');\n\n// Adds responders to the form.\n// TODO(developer): replace the emailAddress.\nform.addPublishedReaders(['responder1@uni.edu', 'responder2@uni.edu']);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds the rating item.\nconst item = form.addRatingItem();\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds the scale item.\nconst item = form.addScaleItem();\n\n// Sets the title of the scale item to 'Choose a number.'\nitem.setTitle('Choose a number');\n\n// Sets the scale to 1-5.\nitem.setBounds(1, 5);\n\n// Sets the label for the lower and upper bounds.\nitem.setLabels('Lowest', 'Highest');\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds the section heading item.\nconst item = form.addSectionHeaderItem();\n\n// Sets the title to 'Title of new section.'\nitem.setTitle('Title of new section');\n\n// Sets the description.\nitem.setHelpText('Description of new section');\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds a single-line text item.\nconst item = form.addTextItem();\n\n// Sets the title to 'What is your name?'\nitem.setTitle('What is your name?');\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds a question with a time input.\nconst item = form.addTimeItem();\n\n// Sets the title to 'What time do you usually wake up in the morning?'\nitem.setTitle('What time do you usually wake up in the morning?');\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Adds a video item.\nconst item = form.addVideoItem();\n\n// Sets the title, description, and video.\nitem.setTitle('YouTube video')\n .setHelpText('Send content automatically via Google Sheets and Apps Script')\n .setVideoUrl('https://youtu.be/xxgQr-jSu9o');\n\n// Sets the alignment to the center.\nitem.setAlignment(FormApp.Alignment.CENTER);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Checks if the form displays a link to edit a response after submitting it.\n// The default is false. To let people edit their responses, use\n// form.setAllowResponseEdits(true).\nconst edit = form.canEditResponse();\n\n// If the form doesn't let people edit responses, logs false to the console.\nconsole.log(edit);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the form to not collect respondents' email addresses.\nform.setCollectEmail(false);\n\n// Checks whether the form collects respondents' email addresses and logs it to\n// the console.\nconst bool = form.collectsEmail();\n\nconsole.log(bool);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Gets all the items from the form.\nconst items = form.getItems();\n\n// Finds the index of a paragraph text item and deletes it by the item's index.\nconst index = items.findIndex(\n (item) => item.getType() === FormApp.ItemType.PARAGRAPH_TEXT,\n);\nif (index !== -1) {\n form.deleteItem(index);\n}\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Gets all of the items from the form.\nconst items = form.getItems();\n\n// Finds a paragraph text item and deletes it.\nconst item = items.find(\n (item) => item.getType() === FormApp.ItemType.PARAGRAPH_TEXT,\n);\nif (item) {\n form.deleteItem(item);\n}\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the confirmation message to display after someone submits the form.\nform.setConfirmationMessage('You successfully submitted the form.');\n\n// Gets the confirmation message and logs it to the console.\nconst message = form.getConfirmationMessage();\n\nconsole.log(message);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets a custom closed form message to display to the user when the form\n// no longer accepts responses.\nform.setCustomClosedFormMessage('The form is no longer accepting responses.');\n\n// Gets the custom message set for the form and logs it to the console.\nconst message = form.getCustomClosedFormMessage();\n\nconsole.log(message);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the form description.\nform.setDescription('This is the form description.');\n\n// Gets the form description and logs it to the console.\nconst description = form.getDescription();\n\nconsole.log(description);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Creates a spreadsheet to use as the response destination.\nconst ss = SpreadsheetApp.create('Test_Spreadsheet');\n\n// Updates the form's response destination.\nform.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());\n\n// Gets the ID of the form's response destination and logs it to the console.\nconst destinationId = form.getDestinationId();\n\nconsole.log(destinationId);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc1234556/edit',\n);\n\n// Gets the type of the form's response destination and logs it to the console.\nconst destinationType = form.getDestinationType().name();\n\nconsole.log(destinationType);\n```\n\nExample:\n```text\n// Opens the form by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Gets the URL that accesses the form's edit mode and logs it to the console.\nconst url = form.getEditUrl();\n\nconsole.log(url);\n```\n\nExample:\n```text\n// Opens the form by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Gets the ID of the form and logs it to the console.\nconst id = form.getId();\n\nconsole.log(id);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Gets the ID of the first item on the form.\nconst itemId = form.getItems()[0].getId();\n\n// Gets the item from the ID.\nconst item = form.getItemById(itemId);\n\n// Gets the name of the item type and logs it to the console.\nconst type = item.getType().name();\n\nconsole.log(type);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Gets the list of items in the form.\nconst items = form.getItems();\n\n// Gets the type for each item and logs them to the console.\nconst types = items.map((item) => item.getType().name());\n\nconsole.log(types);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Gets a list of all checkbox items on the form.\nconst items = form.getItems(FormApp.ItemType.CHECKBOX);\n\n// Gets the title of each checkbox item and logs them to the console.\nconst checkboxItemsTitle = items.map(\n (item) => item.asCheckboxItem().getTitle(),\n);\nconsole.log(checkboxItemsTitle);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl('https://docs.google.com/forms/d/abc123456/edit');\n\n// Gets the responders for the form.\nconst users = form.getPublishedReaders();\nusers.forEach(user => console.log(user.getEmail()));\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Gets the URL to respond to the form and logs it to the console.\nconst url = form.getPublishedUrl();\nconsole.log(url);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// Opens the form by its URL.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Gets the URL to view a summary of the form's responses and logs it to the\n// console.\nconst url = form.getSummaryUrl();\nconsole.log(url);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the title of the form to 'For_Testing.'\nform.setTitle('For_Testing');\n\n// Gets the title of the form and logs it to the console.\nconst title = form.getTitle();\nconsole.log(title);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// Opens the form by its URL.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Displays the progress bar on the form.\nform.setProgressBar(true);\n\n// Checks if the form displays a progress bar and logs it to the console.\nconsole.log(form.hasProgressBar());\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the form to display a link to submit another\n// response after someone submits the form.\nform.setShowLinkToRespondAgain(true);\n\n// Checks if the form displays a 'Submit another response' link and logs it to\n// the console.\nconsole.log(form.hasRespondAgainLink());\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the form to accept responses.\nform.setAcceptingResponses(true);\n\n// Checks if the form is accepting responses or not and logs it to the console.\nconst accepting = form.isAcceptingResponses();\nconsole.log(accepting);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Checks whether the form is published or not and logs it to the console.\nconsole.log(form.isPublished());\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the form to display a link to a summary of\n// the responses after someone submits the form.\nform.setPublishingSummary(true);\n\n// Checks if the form displays a \"See previous responses\" link and logs it to\n// the console.\nconst publishingLink = form.isPublishingSummary();\nconsole.log(publishingLink);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the form as a quiz.\nform.setIsQuiz(true);\n\n// Checks if the form is a quiz or not and logs it to the console.\nconsole.log(form.isQuiz());\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Moves the first item to be the last item.\nform.moveItem(0, form.getItems().length - 1);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Gets the first item.\nconst item = form.getItems()[0];\n\n// Moves the item to be the last item.\nform.moveItem(item, form.getItems().length - 1);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Opens a spreadsheet to use for the response destination.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Updates the form's response destination to the spreadsheet.\nform.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());\n\n// Unlinks the form from the spreadsheet.\nform.removeDestination();\n```\n\nExample:\n```text\n// Opens the Forms file by its URL.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl('https://docs.google.com/forms/d/abc123456/edit');\n\n// Removes the editor from the form.\n// TODO(developer): replace the emailAddress.\nform.removeEditor('editor@uni.edu');\n```\n\nExample:\n```text\n// Opens the Forms file by its URL.\n// TODO(developer): Replace the URL with your own.\nconst form1 = FormApp.openByUrl('https://docs.google.com/forms/d/abc123456/edit');\n\n// Gets the editors from the form.\nconst users = form1.getEditors();\n\n// Opens another form.\n// TODO(developer): Replace the URL with your own.\nconst form2 = FormApp.openByUrl('https://docs.google.com/forms/d/efg123456/edit');\n\n// Removes editors from the form.\nusers.forEach(user => form2.removeEditor(user));\n```\n\nExample:\n```text\n// Opens the Forms file by its URL.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl('https://docs.google.com/forms/d/abc123456/edit');\n\n// Removes the responders from the form.\n// TODO(developer): replace the emailAddress.\nform.removePublishedReader('responder1@uni.edu');\n```\n\nExample:\n```text\n// Opens the Forms file by its URL.\n// TODO(developer): Replace the URL with your own.\nconst form1 = FormApp.openByUrl('https://docs.google.com/forms/d/abc123456/edit');\n\n// Gets the responders from the form.\nconst users = form1.getPublishedReaders();\n\n// Opens another form.\n// TODO(developer): Replace the URL with your own.\nconst form2 = FormApp.openByUrl('https://docs.google.com/forms/d/efg123456/edit');\n\n// Removes responders from the form.\nusers.forEach(user => form2.removePublishedReader(user));\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the form to accept responses.\nform.setAcceptingResponses(true);\n\n// Checks whether the form is accepting responses or not and logs it to the\n// console.\nconsole.log(form.isAcceptingResponses());\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Shows \"Edit your response\" link after someone submits the form.\nform.setAllowResponseEdits(true);\n\n// Checks whether the option to edit the form after a user submits it is set to\n// true or not and logs it to the console.\nconsole.log(form.canEditResponse());\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the form to collect respondents' email addresses.\nform.setCollectEmail(true);\n\n// Checks whether the value is set to true or false and logs it to the console.\nconst collect = form.collectsEmail();\nconsole.log(collect);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets a custom confirmation message to display after someone submits the form.\nform.setConfirmationMessage('Your form has been successfully submitted.');\n\n// Gets the confirmation message set for the form and logs it to the console.\nconst message = form.getConfirmationMessage();\nconsole.log(message);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the form to not accept responses.\nform.setAcceptingResponses(false);\n\n// Sets a custom closed form message to display to the user.\nform.setCustomClosedFormMessage('The form is no longer accepting responses.');\n\n// Gets the custom message set for the form and logs it to the console.\nconst message = form.getCustomClosedFormMessage();\nconsole.log(message);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Makes the form a quiz.\nform.setIsQuiz(true);\n\n// Checks whether the form is a quiz or not and logs it to the console.\nconsole.log(form.isQuiz());\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Displays the progress bar on the form.\nform.setProgressBar(true);\n\n// Checks whether the form has a progress bar and logs it to the console.\nconsole.log(form.hasProgressBar());\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Publishes the form before sharing it.\nform.setPublished(true);\n\n// Checks whether the form is published or not and logs it to the console.\nconsole.log(form.isPublished());\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within a\n// Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Checks whether the form supports publishing or not and logs it to the\n// console.\nconsole.log(form.supportsAdvancedResponderPermissions());\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Checks if the form requires respondents to log in to a Google Workspace\n// account before responding and logs it to the console.\nconst login = form.requiresLogin();\nconsole.log(login);\n```\n\nExample:\n```text\n// Opens the Forms file by its URL. If you created your script from within\n// a Google Forms file, you can use FormApp.getActiveForm() instead.\n// TODO(developer): Replace the URL with your own.\nconst form = FormApp.openByUrl(\n 'https://docs.google.com/forms/d/abc123456/edit',\n);\n\n// Sets the form so that users must log in to their Google Workspace account.\nform.setRequireLogin(true);\n\n// Checks whether the form requires login or not and logs it to the console.\nconsole.log(form.requiresLogin());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.797Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":66,"totalLines":1136,"estimatedTokens":8694}}882{"id":"doc-list_sections_google_chat_google_for_developers-d42ee24c","source":"documentation","title":"List sections | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/list-sections","text":"Example:\n```text\nfrom google.cloud import chat_v1\n\ndef list_sections():\n # Create a client\n client = chat_v1.ChatServiceClient()\n\n # Initialize request\n request = chat_v1.ListSectionsRequest(\n parent=\"users/me\"\n )\n\n # Make the request\n page_result = client.list_sections(request=request)\n\n # Handle the response\n for section in page_result:\n print(section)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.798Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":22,"estimatedTokens":104}}883{"id":"doc-change_the_position_of_a_section_google_chat_goo-1719398b","source":"documentation","title":"Change the position of a section | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/position-section","text":"Example:\n```text\nfrom google.cloud import chat_v1\n\ndef position_section():\n # Create a client\n client = chat_v1.ChatServiceClient()\n\n # Initialize request\n request = chat_v1.PositionSectionRequest(\n name=\"SECTION_NAME\",\n relative_position=chat_v1.PositionSectionRequest.Position.START\n )\n\n # Make the request\n response = client.position_section(request=request)\n\n print(response)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.799Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":21,"estimatedTokens":109}}884{"id":"doc-update_a_section_google_chat_google_for_develope-a543a420","source":"documentation","title":"Update a section | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/update-section","text":"Example:\n```text\nfrom google.cloud import chat_v1\nfrom google.protobuf import field_mask_pb2\n\ndef update_section():\n # Create a client\n client = chat_v1.ChatServiceClient()\n\n # Initialize request\n request = chat_v1.UpdateSectionRequest(\n section=chat_v1.Section(\n name=\"SECTION_NAME\",\n display_name=\"NEW_SECTION_DISPLAY_NAME\"\n ),\n update_mask=field_mask_pb2.FieldMask(paths=[\"display_name\"])\n )\n\n # Make the request\n response = client.update_section(request=request)\n\n print(response)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.800Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":25,"estimatedTokens":143}}885{"id":"doc-package_google_chat_v1_google_chat_google_for_de-7aeac3ef","source":"documentation","title":"Package google.chat.v1 | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/api/reference/rpc/google.chat.v1","text":"Example:\n```text\nHello @FooBot how are you!\"\n```\n\nExample:\n```text\n\"annotations\":[{\n \"type\":\"USER_MENTION\",\n \"startIndex\":6,\n \"length\":7,\n \"userMention\": {\n \"user\": {\n \"name\":\"users/{user}\",\n \"displayName\":\"FooBot\",\n \"avatarUrl\":\"https://goo.gl/aeDtrS\",\n \"type\":\"BOT\"\n },\n \"type\":\"MENTION\"\n }\n}]\n```\n\nExample:\n```text\ncreator(\"users/me\")\n```\n\nExample:\n```text\nrole = \"ROLE_MANAGER\" OR role = \"ROLE_MEMBER\"\nmember.type = \"HUMAN\" AND role = \"ROLE_MANAGER\"\n\nmember.type != \"BOT\"\n```\n\nExample:\n```text\nmember.type = \"HUMAN\" AND member.type = \"BOT\"\nrole = \"ROLE_MANAGER\" AND role = \"ROLE_MEMBER\"\n```\n\nExample:\n```text\ncreate_time > \"2012-04-21T11:30:00-04:00\"\n\ncreate_time > \"2012-04-21T11:30:00-04:00\" AND\n thread.name = spaces/AAAAAAAAAAA/threads/123\n\ncreate_time > \"2012-04-21T11:30:00+00:00\" AND\n\ncreate_time < \"2013-01-01T00:00:00+00:00\" AND\n thread.name = spaces/AAAAAAAAAAA/threads/123\n\nthread.name = spaces/AAAAAAAAAAA/threads/123\n```\n\nExample:\n```text\nuser.name = \"users/{user}\"\nemoji.unicode = \"🙂\"\nemoji.custom_emoji.uid = \"{uid}\"\nemoji.unicode = \"🙂\" OR emoji.unicode = \"👍\"\nemoji.unicode = \"🙂\" OR emoji.custom_emoji.uid = \"{uid}\"\nemoji.unicode = \"🙂\" AND user.name = \"users/{user}\"\n(emoji.unicode = \"🙂\" OR emoji.custom_emoji.uid = \"{uid}\")\nAND user.name = \"users/{user}\"\n```\n\nExample:\n```text\nemoji.unicode = \"🙂\" AND emoji.unicode = \"👍\"\nemoji.unicode = \"🙂\" AND emoji.custom_emoji.uid = \"{uid}\"\nemoji.unicode = \"🙂\" OR user.name = \"users/{user}\"\nemoji.unicode = \"🙂\" OR emoji.custom_emoji.uid = \"{uid}\" OR\nuser.name = \"users/{user}\"\nemoji.unicode = \"🙂\" OR emoji.custom_emoji.uid = \"{uid}\"\nAND user.name = \"users/{user}\"\n```\n\nExample:\n```text\nstart_time=\"2023-08-23T19:20:33+00:00\" AND\nend_time=\"2023-08-23T19:21:54+00:00\"\n```\n\nExample:\n```text\nstart_time=\"2023-08-23T19:20:33+00:00\" AND\n(event_types:\"google.workspace.chat.space.v1.updated\" OR\nevent_types:\"google.workspace.chat.message.v1.created\")\n```\n\nExample:\n```text\nstart_time=\"2023-08-23T19:20:33+00:00\" OR\nend_time=\"2023-08-23T19:21:54+00:00\"\n```\n\nExample:\n```text\nevent_types:\"google.workspace.chat.space.v1.updated\" AND\nevent_types:\"google.workspace.chat.message.v1.created\"\n```\n\nExample:\n```text\nspace_type = \"SPACE\"\nspaceType = \"GROUP_CHAT\" OR spaceType = \"DIRECT_MESSAGE\"\n```\n\nExample:\n```text\n\"Pending reports\" AND create_time >= \"2023-01-01T00:00:00Z\"\n\nsender.name = \"users/example@gmail.com\"\n\nannotations.user_mentions.user.name:\"users/0987654321\"\n\nattachment:* AND space.name = \"spaces/ABCDEFGH\"\n\ntasks AND is_unread() AND sender.name = \"users/1234567890\"\n\n\"things to do\" \"urgent\"\n\n(sender.name = \"users/1234567890\")\nAND (create_time < \"2023-05-01T00:00:00Z\")\n\ntasks AND space.name = \"spaces/ABCDEFGH\" AND has_link()\n\n\"project one\" is_unread()\n\nspace.display_name:Project tasks\n```\n\nExample:\n```text\ncustomer = \"customers/my_customer\" AND space_type = \"SPACE\"\n\ncustomer = \"customers/my_customer\" AND space_type = \"SPACE\" AND\ndisplay_name:\"Hello World\"\n\ncustomer = \"customers/my_customer\" AND space_type = \"SPACE\" AND\n(last_active_time < \"2020-01-01T00:00:00+00:00\" OR last_active_time >\n\"2022-01-01T00:00:00+00:00\")\n\ncustomer = \"customers/my_customer\" AND space_type = \"SPACE\" AND\n(display_name:\"Hello World\" OR display_name:\"Fun event\") AND\n(last_active_time > \"2020-01-01T00:00:00+00:00\" AND last_active_time <\n\"2022-01-01T00:00:00+00:00\")\n\ncustomer = \"customers/my_customer\" AND space_type = \"SPACE\" AND\n(create_time > \"2019-01-01T00:00:00+00:00\" AND create_time <\n\"2020-01-01T00:00:00+00:00\") AND (external_user_allowed = \"true\") AND\n(space_history_state = \"HISTORY_ON\" OR space_history_state = \"HISTORY_OFF\")\n```\n\nExample:\n```text\ndisplay_name:\"Hello World\" AND space_type = \"SPACE\"\n\n(display_name:\"Hello\" OR display_name:\"Fun\") AND space_type = \"SPACE\"\n\n(external_user_allowed = \"true\" AND space_type = \"SPACE\") // Returns an\nempty response.\n\n(external_user_allowed = \"true\" AND display_name:\"Hello\" AND space_type =\n\"SPACE\")\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.812Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":171,"estimatedTokens":992}}886{"id":"doc-get_details_about_a_google_chat_space_event_goog-c06db0d7","source":"documentation","title":"Get details about a Google Chat space event | Google for Developers","url":"https://developers.google.com/workspace/chat/get-space-event","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\n// Replace SCOPE_NAME here with an authorization scope based on the event type\nconst USER_AUTH_OAUTH_SCOPES = ['SCOPE_NAME'];\n\n// This sample shows how to get space event with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME and SPACE_EVENT_NAME here\n name: 'spaces/SPACE_NAME/spaceEvents/SPACE_EVENT_NAME',\n };\n\n // Make the request\n const response = await chatClient.getSpaceEvent(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\nfrom apiclient.discovery import build\n\n# Define your app's authorization scopes.\n# Set authorization scopes based on the\n# event type. For example, if you are getting a space event\n# about a new membership, use the `chat.app.memberships` scope.\n#\n# When modifying these scopes, delete the file token.json, if it exists.\nSCOPES = [\"https://www.googleapis.com/auth/chat.app.memberships\",\n \"https://www.googleapis.com/auth/chat.app.memberships.readonly\",\n \"https://www.googleapis.com/auth/chat.app.messages.readonly\",\n \"https://www.googleapis.com/auth/chat.app.spaces\",\n \"https://www.googleapis.com/auth/chat.app.spaces.readonly\"]\n\ndef main():\n '''\n Authenticates with Chat API using app authentication,\n then lists space events from a specified space.\n '''\n\n # Specify service account details.\n creds = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n )\n\n # Build a service endpoint for Chat API.\n chat = build('chat', 'v1', credentials=creds)\n\n # Use the service endpoint to call Chat API.\n result = chat.spaces().spaceEvents().get(\n\n # The space to get event details from.\n #\n # Replace SPACE_NAME with a space name.\n # Obtain the space name from the spaces resource of Chat API,\n # or from a space's URL.\n name='spaces/SPACE_NAME/spaceEvents/SPACE_EVENT_NAME',\n\n ).execute()\n\n # Print Chat API's response in your command line interface.\n print(result)\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython3 chat_spaceevents_get_app.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.812Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":87,"estimatedTokens":616}}887{"id":"doc-move_a_space_to_a_different_section_google_chat_-45ee735a","source":"documentation","title":"Move a space to a different section | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/move-section-item","text":"Example:\n```text\nfrom google.cloud import chat_v1\n\ndef move_section_item():\n # Create a client\n client = chat_v1.ChatServiceClient()\n\n # Initialize request\n request = chat_v1.MoveSectionItemRequest(\n name=\"SECTION_ITEM_NAME\",\n target_section=\"TARGET_SECTION_NAME\"\n )\n\n # Make the request\n response = client.move_section_item(request=request)\n\n print(response)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.813Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":21,"estimatedTokens":104}}888{"id":"doc-create_a_section_google_chat_google_for_develope-e7f65cf6","source":"documentation","title":"Create a section | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/create-section","text":"Example:\n```text\nfrom google.cloud import chat_v1\n\ndef create_section():\n # Create a client\n client = chat_v1.ChatServiceClient()\n\n # Initialize request\n request = chat_v1.CreateSectionRequest(\n parent=\"users/me\",\n section=chat_v1.Section(\n display_name=\"SECTION_DISPLAY_NAME\",\n type=chat_v1.Section.SectionType.CUSTOM_SECTION\n )\n )\n\n # Make the request\n response = client.create_section(request=request)\n\n print(response)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.814Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":24,"estimatedTokens":127}}889{"id":"doc-list_spaces_in_a_section_google_chat_google_for_-becc28eb","source":"documentation","title":"List spaces in a section | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/list-section-items","text":"Example:\n```text\nfrom google.cloud import chat_v1\n\ndef list_section_items():\n # Create a client\n client = chat_v1.ChatServiceClient()\n\n # Initialize request\n request = chat_v1.ListSectionItemsRequest(\n parent=\"SECTION_NAME\"\n )\n\n # Make the request\n page_result = client.list_section_items(request=request)\n\n # Handle the response\n for item in page_result:\n print(item)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.814Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":22,"estimatedTokens":107}}890{"id":"doc-find_a_group_chat_google_chat_google_for_develop-e8290cac","source":"documentation","title":"Find a group chat | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/find-group-chats","text":"Example:\n```text\n/**\n * This sample shows how to find a group chat with specific members.\n *\n * It relies on the @google-apps/chat npm package.\n */\n// Read the documentation for more details:\n// https://developers.google.com/workspace/chat/api/reference/rpc/google.chat.v1#google.chat.v1.ChatService.FindGroupChats\n\nconst {ChatServiceClient} = require('@google-apps/chat');\nconst {auth} = require('google-auth-library');\n\nasync function main() {\n // Create a client\n const chatClient = new ChatServiceClient({\n authClient: await auth.getClient({\n scopes: ['https://www.googleapis.com/auth/chat.memberships.readonly']\n })\n });\n\n // The users to find a group chat with.\n // Don't include the caller.\n const users = [\n 'users/123456789',\n 'users/987654321'\n ];\n\n // Create the request\n const request = {\n users: users\n };\n\n // Call the API\n const response = await chatClient.findGroupChats(request);\n\n // Handle the response\n if (response.spaces && response.spaces.length > 0) {\n console.log('Found group chat:', response.spaces[0].name);\n } else {\n console.log('No group chat found.');\n }\n}\n\nmain().catch(console.error);\n```\n\nExample:\n```text\n\"\"\"\nThis sample shows how to find a group chat with specific members.\n\"\"\"\nfrom google.apps import chat_v1\nimport google.auth\n\n# Read the documentation for more details:\n# https://developers.google.com/workspace/chat/api/reference/rpc/google.chat.v1#google.chat.v1.ChatService.FindGroupChats\n\ndef find_group_chat():\n # Create a client\n scopes = [\"https://www.googleapis.com/auth/chat.memberships.readonly\"]\n credentials, _ = google.auth.default(scopes=scopes)\n client = chat_v1.ChatServiceClient(credentials=credentials)\n\n # The users to find a group chat with.\n # Don't include the caller.\n users_list = [\n \"users/123456789\",\n \"users/987654321\"\n ]\n\n # Create the request\n request = chat_v1.FindGroupChatsRequest(\n users=users_list\n )\n\n # Call the API\n response = client.find_group_chats(request)\n\n # Handle the response\n if response.spaces:\n print(f\"Found group chat: {response.spaces[0].name}\")\n else:\n print(\"No group chat found.\")\n\nif __name__ == \"__main__\":\n find_group_chat()\n```\n\nExample:\n```text\n/**\n * This sample shows how to find a group chat with specific members.\n */\nimport com.google.api.gax.core.FixedCredentialsProvider;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.ChatServiceSettings;\nimport com.google.chat.v1.FindGroupChatsRequest;\nimport com.google.chat.v1.FindGroupChatsResponse;\nimport java.util.Arrays;\nimport java.util.List;\n\n// Read the documentation for more details:\n// https://developers.google.com/workspace/chat/api/reference/rpc/google.chat.v1#google.chat.v1.ChatService.FindGroupChats\n\npublic class FindGroupChat {\n public static void main(String[] args) throws Exception {\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(\"https://www.googleapis.com/auth/chat.memberships.readonly\"));\n ChatServiceSettings settings = ChatServiceSettings.newBuilder()\n .setCredentialsProvider(FixedCredentialsProvider.create(credentials))\n .build();\n try (ChatServiceClient chatServiceClient = ChatServiceClient.create(settings)) {\n List<String> users = Arrays.asList(\n \"users/123456789\",\n \"users/987654321\"\n );\n\n FindGroupChatsRequest request = FindGroupChatsRequest.newBuilder()\n .addAllUsers(users)\n .build();\n\n FindGroupChatsResponse response = chatServiceClient.findGroupChats(request);\n\n if (!response.getSpacesList().isEmpty()) {\n System.out.printf(\"Found group chat: %s\\n\", response.getSpacesList().get(0).getName());\n } else {\n System.out.println(\"No group chat found.\");\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to find a group chat with specific members.\n */\n// Read the documentation for more details:\n// https://developers.google.com/workspace/chat/api/reference/rpc/google.chat.v1#google.chat.v1.ChatService.FindGroupChats\n\nfunction findGroupChat() {\n // The users to find a group chat with.\n // Don't include the caller.\n const users = [\n 'users/123456789',\n 'users/987654321'\n ];\n\n try {\n // Call the API\n // In Apps Script, query parameters are passed as optional arguments\n const response = Chat.Spaces.findGroupChats({\n users: users\n });\n\n if (response.spaces && response.spaces.length > 0) {\n console.log('Found group chat: ' + response.spaces[0].name);\n } else {\n console.log('No group chat found.');\n }\n } catch (err) {\n // Handle error\n console.log('Failed to find group chat: ' + err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to find a group chat with specific members and return details.\n *\n * It relies on the @google-apps/chat npm package.\n */\n// Read the documentation for more details:\n// https://developers.google.com/workspace/chat/api/reference/rpc/google.chat.v1#google.chat.v1.ChatService.FindGroupChats\n\nconst {ChatServiceClient} = require('@google-apps/chat');\nconst {auth} = require('google-auth-library');\n\nasync function main() {\n // Create a client\n const chatClient = new ChatServiceClient({\n authClient: await auth.getClient({\n scopes: [\n 'https://www.googleapis.com/auth/chat.spaces.readonly',\n 'https://www.googleapis.com/auth/chat.memberships.readonly'\n ]\n })\n });\n\n // The users to find a group chat with.\n // Don't include the caller.\n const users = [\n 'users/123456789',\n 'users/987654321'\n ];\n\n // Create the request\n const request = {\n users: users,\n spaceView: 'SPACE_VIEW_EXPANDED'\n };\n\n // Call the API\n const response = await chatClient.findGroupChats(request);\n\n // Handle the response\n if (response.spaces && response.spaces.length > 0) {\n console.log('Found group chat:', response.spaces[0].displayName);\n } else {\n console.log('No group chat found.');\n }\n}\n\nmain().catch(console.error);\n```\n\nExample:\n```text\n\"\"\"\nThis sample shows how to find a group chat with specific members and return details.\n\"\"\"\nfrom google.apps import chat_v1\nimport google.auth\n\n# Read the documentation for more details:\n# https://developers.google.com/workspace/chat/api/reference/rpc/google.chat.v1#google.chat.v1.ChatService.FindGroupChats\n\ndef find_group_chat_with_details():\n # Create a client\n scopes = [\n \"https://www.googleapis.com/auth/chat.memberships.readonly\",\n \"https://www.googleapis.com/auth/chat.spaces.readonly\"\n ]\n credentials, _ = google.auth.default(scopes=scopes)\n client = chat_v1.ChatServiceClient(credentials=credentials)\n\n # The users to find a group chat with.\n # Don't include the caller.\n users_list = [\n \"users/123456789\",\n \"users/987654321\"\n ]\n\n # Create the request\n request = chat_v1.FindGroupChatsRequest(\n users=users_list,\n space_view=chat_v1.SpaceView.SPACE_VIEW_EXPANDED\n )\n\n # Call the API\n response = client.find_group_chats(request)\n\n # Handle the response\n if response.spaces:\n print(f\"Found group chat: {response.spaces[0].display_name}\")\n else:\n print(\"No group chat found.\")\n\nif __name__ == \"__main__\":\n find_group_chat_with_details()\n```\n\nExample:\n```text\n/**\n * This sample shows how to find a group chat with specific members and return details.\n */\nimport com.google.api.gax.core.FixedCredentialsProvider;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.ChatServiceSettings;\nimport com.google.chat.v1.FindGroupChatsRequest;\nimport com.google.chat.v1.FindGroupChatsResponse;\nimport com.google.chat.v1.SpaceView;\nimport java.util.Arrays;\nimport java.util.List;\n\n// Read the documentation for more details:\n// https://developers.google.com/workspace/chat/api/reference/rpc/google.chat.v1#google.chat.v1.ChatService.FindGroupChats\n\npublic class FindGroupChatWithDetails {\n public static void main(String[] args) throws Exception {\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(\n \"https://www.googleapis.com/auth/chat.memberships.readonly\",\n \"https://www.googleapis.com/auth/chat.spaces.readonly\"\n ));\n ChatServiceSettings settings = ChatServiceSettings.newBuilder()\n .setCredentialsProvider(FixedCredentialsProvider.create(credentials))\n .build();\n try (ChatServiceClient chatServiceClient = ChatServiceClient.create(settings)) {\n List<String> users = Arrays.asList(\n \"users/123456789\",\n \"users/987654321\"\n );\n\n FindGroupChatsRequest request = FindGroupChatsRequest.newBuilder()\n .addAllUsers(users)\n .setSpaceView(SpaceView.SPACE_VIEW_EXPANDED)\n .build();\n\n FindGroupChatsResponse response = chatServiceClient.findGroupChats(request);\n\n if (!response.getSpacesList().isEmpty()) {\n System.out.printf(\"Found group chat: %s\\n\", response.getSpacesList().get(0).getDisplayName());\n } else {\n System.out.println(\"No group chat found.\");\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to find a group chat with specific members and return details.\n */\n// Read the documentation for more details:\n// https://developers.google.com/workspace/chat/api/reference/rpc/google.chat.v1#google.chat.v1.ChatService.FindGroupChats\n\nfunction findGroupChatWithDetails() {\n // The users to find a group chat with.\n // Don't include the caller.\n const users = [\n 'users/123456789',\n 'users/987654321'\n ];\n\n try {\n // Call the API\n // In Apps Script, query parameters are passed as optional arguments\n const response = Chat.Spaces.findGroupChats({\n users: users,\n spaceView: 'SPACE_VIEW_EXPANDED'\n });\n\n if (response.spaces && response.spaces.length > 0) {\n console.log('Found group chat: ' + response.spaces[0].displayName);\n } else {\n console.log('No group chat found.');\n }\n } catch (err) {\n // Handle error\n console.log('Failed to find group chat: ' + err.message);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.814Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":355,"estimatedTokens":2580}}891{"id":"doc-delete_a_section_google_chat_google_for_develope-ef0d3213","source":"documentation","title":"Delete a section | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/delete-section","text":"Example:\n```text\nfrom google.cloud import chat_v1\n\ndef delete_section():\n # Create a client\n client = chat_v1.ChatServiceClient()\n\n # Initialize request\n request = chat_v1.DeleteSectionRequest(\n name=\"SECTION_NAME\"\n )\n\n # Make the request\n client.delete_section(request=request)\n\n print(\"Section deleted\")\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.815Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":20,"estimatedTokens":89}}892{"id":"doc-list_events_from_a_google_chat_space_google_for_-bc2cb9c2","source":"documentation","title":"List events from a Google Chat space | Google for Developers","url":"https://developers.google.com/workspace/chat/list-space-events","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\n// Authorization scopes based on the event types\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.memberships.readonly',\n 'https://www.googleapis.com/auth/chat.messages.readonly',\n];\n\n// This sample shows how to list space events with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here\n parent: 'spaces/SPACE_NAME',\n // A required filter. Filters events about new memberships and messages.\n filter:\n 'eventTypes:\"google.workspace.chat.membership.v1.created\" OR eventTypes:\"google.workspace.chat.message.v1.created\"',\n };\n\n // Make the request\n const pageResult = chatClient.listSpaceEventsAsync(request);\n\n // Handle the response. Iterating over pageResult will yield results and\n // resolve additional pages automatically.\n for await (const response of pageResult) {\n console.log(response);\n }\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\nfrom apiclient.discovery import build\n\n# Define your app's authorization scopes.\n# Set authorization scopes based on the\n# event type. For example, if you are getting a space event\n# about a new membership, use the `chat.app.memberships` scope.\n#\n# When modifying these scopes, delete the file token.json, if it exists.\nSCOPES = [\"https://www.googleapis.com/auth/chat.app.memberships\",\n \"https://www.googleapis.com/auth/chat.app.memberships.readonly\",\n \"https://www.googleapis.com/auth/chat.app.messages.readonly\",\n \"https://www.googleapis.com/auth/chat.app.spaces\",\n \"https://www.googleapis.com/auth/chat.app.spaces.readonly\"]\n\ndef main():\n '''\n Authenticates with Chat API using app authentication,\n then lists space events from a specified space.\n '''\n\n # Specify service account details.\n creds = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n )\n\n # Build a service endpoint for Chat API.\n chat = build('chat', 'v1', credentials=creds)\n\n # Use the service endpoint to call Chat API.\n result = chat.spaces().spaceEvents().list(\n\n # The space to list events from.\n #\n # Replace SPACE_NAME with a space name.\n # Obtain the space name from the spaces resource of Chat API,\n # or from a space's URL.\n parent='spaces/SPACE_NAME',\n\n # A required filter. Filters events by event type.\n #\n # Update this filter to match your requirements.\n filter='eventTypes:\"google.workspace.chat.message.v1.created\"'\n\n ).execute()\n\n # Print Chat API's response in your command line interface.\n print(result)\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython3 chat_spaceevents_list_app.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.816Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":101,"estimatedTokens":757}}893{"id":"doc-list_messages_google_chat_google_for_developers-00fc8904","source":"documentation","title":"List messages | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/list-messages","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.messages.readonly',\n];\n\n// This sample shows how to list messages with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here\n parent: 'spaces/SPACE_NAME',\n };\n\n // Make the request\n const pageResult = chatClient.listMessagesAsync(request);\n\n // Handle the response. Iterating over pageResult will yield results\n // and resolve additional pages automatically.\n for await (const response of pageResult) {\n console.log(response);\n }\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.messages.readonly\"]\n\n# This sample shows how to list messages with user credential\ndef list_messages_with_user_cred():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.ListMessagesRequest(\n # Replace SPACE_NAME here\n parent = 'spaces/SPACE_NAME',\n # Number of results that will be returned at once\n page_size = 100\n )\n\n # Make the request\n page_result = client.list_messages(request)\n\n # Handle the response. Iterating over page_result will yield results and\n # resolve additional pages automatically.\n for response in page_result:\n print(response)\n\nlist_messages_with_user_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.ListMessagesRequest;\nimport com.google.chat.v1.ListMessagesResponse;\nimport com.google.chat.v1.Message;\n\n// This sample shows how to list messages with user credential.\npublic class ListMessagesUserCred {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.messages.readonly\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n ListMessagesRequest.Builder request = ListMessagesRequest.newBuilder()\n // Replace SPACE_NAME here.\n .setParent(\"spaces/SPACE_NAME\")\n // Number of results that will be returned at once.\n .setPageSize(10);\n\n // Iterate over results and resolve additional pages automatically.\n for (Message response :\n chatServiceClient.listMessages(request.build()).iterateAll()) {\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to list messages with user credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.messages.readonly'\n * referenced in the manifest file (appsscript.json).\n */\nfunction listMessagesUserCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here\n const parent = \"spaces/SPACE_NAME\";\n\n // Iterate through the response pages using page tokens\n let responsePage;\n let pageToken = null;\n do {\n // Request response pages\n responsePage = Chat.Spaces.Messages.list(parent, {\n pageSize: 10,\n pageToken: pageToken,\n });\n // Handle response pages\n if (responsePage.messages) {\n for (const message of responsePage.messages) {\n console.log(message);\n }\n }\n // Update the page token to the next one\n pageToken = responsePage.nextPageToken;\n } while (pageToken);\n}\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\nfrom apiclient.discovery import build\n\n# Define your app's authorization scopes.\n# When modifying these scopes, delete the file token.json, if it exists.\nSCOPES = [\"https://www.googleapis.com/auth/chat.app.messages.readonly\"]\n\ndef main():\n '''\n Authenticates with Chat API using app authentication,\n then lists messages from a specified space.\n '''\n\n # Specify service account details.\n creds = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n )\n\n # Build a service endpoint for Chat API.\n chat = build('chat', 'v1', credentials=creds)\n\n # Use the service endpoint to call Chat API.\n result = chat.spaces().messages().list(\n\n # The space to list messages from.\n #\n # Replace SPACE_NAME with a space name.\n # Obtain the space name from the spaces resource of Chat API,\n # or from a space's URL.\n parent='spaces/SPACE_NAME'\n\n ).execute()\n\n # Print Chat API's response in your command line interface.\n print(result)\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython3 chat_messages_list_app.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.817Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":181,"estimatedTokens":1249}}894{"id":"doc-get_details_about_a_message_google_chat_google_f-82667a88","source":"documentation","title":"Get details about a message | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/get-messages","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.messages.readonly',\n];\n\n// This sample shows how to get message with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME and MESSAGE_NAME here\n name: 'spaces/SPACE_NAME/messages/MESSAGE_NAME',\n };\n\n // Make the request\n const response = await chatClient.getMessage(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nimport google.oauth2.credentials\n\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.messages.readonly\"]\n\n# This sample shows how to get message with user credential\ndef get_message_with_user_cred():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.GetMessageRequest(\n # Replace SPACE_NAME and MESSAGE_NAME here\n name = \"spaces/SPACE_NAME/messages/MESSAGE_NAME\",\n )\n\n # Make the request\n response = client.get_message(request)\n\n # Handle the response\n print(response)\n\nget_message_with_user_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.GetMessageRequest;\nimport com.google.chat.v1.Message;\n\n// This sample shows how to get message with user credential.\npublic class GetMessageUserCred {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.messages.readonly\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n GetMessageRequest.Builder request = GetMessageRequest.newBuilder()\n // replace SPACE_NAME and MESSAGE_NAME here\n .setName(\"spaces/SPACE_NAME/members/MESSAGE_NAME\");\n Message response = chatServiceClient.getMessage(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to get message with user credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.messages.readonly'\n * referenced in the manifest file (appsscript.json).\n */\nfunction getMessageUserCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME and MESSAGE_NAME here\n const name = \"spaces/SPACE_NAME/messages/MESSAGE_NAME\";\n\n // Make the request\n const response = Chat.Spaces.Messages.get(name);\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nimport {createClientWithAppCredentials} from './authentication-utils.js';\n\n// This sample shows how to get message with app credential\nasync function main() {\n // Create a client\n const chatClient = createClientWithAppCredentials();\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME and MESSAGE_NAME here\n name: 'spaces/SPACE_NAME/messages/MESSAGE_NAME',\n };\n\n // Make the request\n const response = await chatClient.getMessage(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_app_credentials\nfrom google.apps import chat_v1 as google_chat\n\n# This sample shows how to get message with app credential\ndef get_message_with_app_cred():\n # Create a client\n client = create_client_with_app_credentials()\n\n # Initialize request argument(s)\n request = google_chat.GetMessageRequest(\n # Replace SPACE_NAME and MESSAGE_NAME here\n name = 'spaces/SPACE_NAME/messages/MESSAGE_NAME',\n )\n\n # Make the request\n response = client.get_message(request=request)\n\n # Handle the response\n print(response)\n\nget_message_with_app_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.GetMessageRequest;\nimport com.google.chat.v1.Message;\n\n// This sample shows how to get message with app credential.\npublic class GetMessageAppCred {\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithAppCredentials()) {\n GetMessageRequest.Builder request = GetMessageRequest.newBuilder()\n // replace SPACE_NAME and MESSAGE_NAME here\n .setName(\"spaces/SPACE_NAME/members/MESSAGE_NAME\");\n Message response = chatServiceClient.getMessage(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to get message with app credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.bot'\n * used by service accounts.\n */\nfunction getMessageAppCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME and MESSAGE_NAME here\n const name = \"spaces/SPACE_NAME/messages/MESSAGE_NAME\";\n const parameters = {};\n\n // Make the request\n const response = Chat.Spaces.Messages.get(\n name,\n parameters,\n getHeaderWithAppCredentials(),\n );\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\nfrom apiclient.discovery import build\n\n# Define your app's authorization scopes.\n# When modifying these scopes, delete the file token.json, if it exists.\nSCOPES = [\"https://www.googleapis.com/auth/chat.app.messages.readonly\"]\n\ndef main():\n '''\n Authenticates with Chat API using app authentication,\n then gets details about a message.\n '''\n\n # Specify service account details.\n creds = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n )\n\n # Build a service endpoint for Chat API.\n chat = build('chat', 'v1', credentials=creds)\n\n # Use the service endpoint to call Chat API.\n result = chat.spaces().messages().get(\n\n # The message to get details about.\n #\n # Replace SPACE_NAME with a space name.\n # Obtain the space name from the spaces resource of Chat API,\n # or from a space's URL.\n name='spaces/SPACE_NAME/messages/MESSAGE_NAME',\n\n ).execute()\n\n # Print Chat API's response in your command line interface.\n print(result)\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython3 chat_messages_get_admin_app.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.818Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":256,"estimatedTokens":1680}}895{"id":"doc-client_id_migration_guide_google_maps_platform_p-3861cb53","source":"documentation","title":"Client ID Migration Guide | Google Maps Platform Premium Plan | Google for Developers","url":"https://developers.google.com/maps/premium/migrate-client-id","text":"Example:\n```text\n{\n \"error_message\" : \"Requests to this API must be over SSL. Load the API with\n \\\"https://\\\" instead of \\\"http://\\\".\",\n\n \"results\" : [],\n\n \"status\" : \"REQUEST_DENIED\"\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.819Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":52}}896{"id":"doc-create_and_update_interactive_cards_google_chat_-f3fa9aa0","source":"documentation","title":"Create and update interactive cards | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/create-update-interactive-cards","text":"Example:\n```text\n/**\n * This sample shows how to create a message with a card on behalf of a user.\n */\nconst {google} = require('googleapis');\nconst {auth} = require('google-auth-library');\n\nasync function main() {\n // Create a client\n const authClient = await auth.getClient({\n scopes: ['https://www.googleapis.com/auth/chat.messages.create']\n });\n google.options({auth: authClient});\n\n // Initialize the Chat API with Developer Preview labels\n const chat = await google.discoverAPI(\n 'https://chat.googleapis.com/$discovery/rest?version=v1&labels=DEVELOPER_PREVIEW&key=API_KEY'\n );\n\n // The space to create the message in.\n const parent = 'spaces/SPACE_NAME';\n\n // Create the request\n const request = {\n parent: parent,\n requestBody: {\n text: 'Here is a card created on my behalf:',\n cardsV2: [{\n cardId: 'unique-card-id',\n card: {\n header: {\n title: 'Card Title',\n subtitle: 'Card Subtitle'\n },\n sections: [{\n widgets: [{\n textParagraph: {\n text: 'This card is attached to a user message.'\n }\n }]\n }]\n }\n }]\n }\n };\n\n // Call the API\n const response = await chat.spaces.messages.create(request);\n\n // Handle the response\n console.log(response.data);\n}\n\nmain().catch(console.error);\n```\n\nExample:\n```text\n\"\"\"\nThis sample shows how to create a message with a card on behalf of a user.\n\"\"\"\nfrom google.oauth2 import service_account\nfrom googleapiclient.discovery import build\nimport google.auth\n\ndef create_message_with_card():\n # Create a client\n scopes = [\"https://www.googleapis.com/auth/chat.messages.create\"]\n credentials, _ = google.auth.default(scopes=scopes)\n\n # Build the service endpoint for Chat API with Developer Preview labels.\n service = build(\n 'chat',\n 'v1',\n credentials=credentials,\n discoveryServiceUrl='https://chat.googleapis.com/$discovery/rest?version=v1&labels=DEVELOPER_PREVIEW&key=API_KEY'\n )\n\n # The space to create the message in.\n parent = \"spaces/SPACE_NAME\"\n\n # Create the request\n result = service.spaces().messages().create(\n parent=parent,\n body={\n 'text': 'Here is a card created on my behalf:',\n 'cardsV2': [{\n 'cardId': 'unique-card-id',\n 'card': {\n 'header': {\n 'title': 'Card Title',\n 'subtitle': 'Card Subtitle'\n },\n 'sections': [{\n 'widgets': [{\n 'textParagraph': {\n 'text': 'This card is attached to a user message.'\n }\n }]\n }]\n }\n }]\n }\n ).execute()\n\n print(result)\n\nif __name__ == \"__main__\":\n create_message_with_card()\n```\n\nExample:\n```text\n/**\n * This sample shows how to create a message with a card on behalf of a user.\n */\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.http.GenericUrl;\nimport com.google.api.client.http.HttpRequest;\nimport com.google.api.client.http.HttpRequestFactory;\nimport com.google.api.client.http.HttpTransport;\nimport com.google.api.client.http.json.JsonHttpContent;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class CreateMessageWithCard {\n public static void main(String[] args) throws Exception {\n HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();\n GsonFactory jsonFactory = GsonFactory.getDefaultInstance();\n\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(\"https://www.googleapis.com/auth/chat.messages.create\"));\n HttpRequestFactory requestFactory = transport.createRequestFactory(new HttpCredentialsAdapter(credentials));\n\n String parent = \"spaces/SPACE_NAME\";\n GenericUrl url = new GenericUrl(\"https://chat.googleapis.com/v1/\" + parent + \"/messages\");\n\n // Construct the message body\n Map<String, Object> message = new HashMap<>();\n message.put(\"text\", \"Here is a card created on my behalf:\");\n\n Map<String, Object> header = new HashMap<>();\n header.put(\"title\", \"Card Title\");\n header.put(\"subtitle\", \"Card Subtitle\");\n\n Map<String, Object> textParagraph = new HashMap<>();\n textParagraph.put(\"text\", \"This card is attached to a user message.\");\n\n Map<String, Object> widget = new HashMap<>();\n widget.put(\"textParagraph\", textParagraph);\n\n Map<String, Object> section = new HashMap<>();\n section.put(\"widgets\", Collections.singletonList(widget));\n\n Map<String, Object> card = new HashMap<>();\n card.put(\"header\", header);\n card.put(\"sections\", Collections.singletonList(section));\n\n Map<String, Object> cardWithId = new HashMap<>();\n cardWithId.put(\"cardId\", \"unique-card-id\");\n cardWithId.put(\"card\", card);\n\n message.put(\"cardsV2\", Collections.singletonList(cardWithId));\n\n HttpRequest request = requestFactory.buildPostRequest(url, new JsonHttpContent(jsonFactory, message));\n System.out.println(request.execute().parseAsString());\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to create a message with a card on behalf of a user.\n */\nfunction createMessageWithCard() {\n const parent = 'spaces/SPACE_NAME';\n const url = `https://chat.googleapis.com/v1/${parent}/messages`;\n\n const message = {\n text: 'Here is a card created on my behalf:',\n cardsV2: [{\n cardId: 'unique-card-id',\n card: {\n header: {\n title: 'Card Title',\n subtitle: 'Card Subtitle'\n },\n sections: [{\n widgets: [{\n textParagraph: {\n text: 'This card is attached to a user message.'\n }\n }]\n }]\n }\n }]\n };\n\n const options = {\n method: 'post',\n headers: {\n Authorization: 'Bearer ' + ScriptApp.getOAuthToken()\n },\n contentType: 'application/json',\n payload: JSON.stringify(message),\n muteHttpExceptions: true\n };\n\n try {\n const response = UrlFetchApp.fetch(url, options);\n console.log(response.getContentText());\n } catch (err) {\n console.log('Failed to create message: ' + err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to update cards on a message.\n */\nconst {google} = require('googleapis');\nconst {auth} = require('google-auth-library');\n\nasync function main() {\n // Create a client with app credentials\n const authClient = await auth.getClient({\n scopes: ['https://www.googleapis.com/auth/chat.bot']\n });\n google.options({auth: authClient});\n\n // Initialize the Chat API with Developer Preview labels\n const chat = await google.discoverAPI(\n 'https://chat.googleapis.com/$discovery/rest?version=v1&labels=DEVELOPER_PREVIEW&key=API_KEY'\n );\n\n // The message to update.\n const messageName = 'spaces/SPACE_NAME/messages/MESSAGE_ID';\n\n // Create the request\n const request = {\n name: messageName,\n requestBody: {\n cardsV2: [{\n cardId: 'unique-card-id',\n card: {\n header: {\n title: 'Updated Card Title',\n subtitle: 'Updated Card Subtitle'\n },\n sections: [{\n widgets: [{\n textParagraph: {\n text: 'The card content has been updated asynchronously.'\n }\n }]\n }]\n }\n }]\n }\n };\n\n // Call the API\n await chat.spaces.messages.replaceCards(request);\n console.log('Cards updated.');\n}\n\nmain().catch(console.error);\n```\n\nExample:\n```text\n\"\"\"\nThis sample shows how to update cards on a message.\n\"\"\"\nfrom google.oauth2 import service_account\nfrom googleapiclient.discovery import build\nimport google.auth\n\ndef replace_message_cards():\n # Create a client with app credentials\n scopes = [\"https://www.googleapis.com/auth/chat.bot\"]\n credentials, _ = google.auth.default(scopes=scopes)\n\n # Build the service endpoint for Chat API with Developer Preview labels.\n service = build(\n 'chat',\n 'v1',\n credentials=credentials,\n discoveryServiceUrl='https://chat.googleapis.com/$discovery/rest?version=v1&labels=DEVELOPER_PREVIEW&key=API_KEY'\n )\n\n # The message to update.\n message_name = \"spaces/SPACE_NAME/messages/MESSAGE_ID\"\n\n # Create the request\n result = service.spaces().messages().replaceCards(\n name=message_name,\n body={\n 'cardsV2': [{\n 'cardId': 'unique-card-id',\n 'card': {\n 'header': {\n 'title': 'Updated Card Title',\n 'subtitle': 'Updated Card Subtitle'\n },\n 'sections': [{\n 'widgets': [{\n 'textParagraph': {\n 'text': 'The card content has been updated asynchronously.'\n }\n }]\n }]\n }\n }]\n }\n ).execute()\n\n print(\"Cards updated.\")\n\nif __name__ == \"__main__\":\n replace_message_cards()\n```\n\nExample:\n```text\n/**\n * This sample shows how to update cards on a message.\n */\nimport com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;\nimport com.google.api.client.http.GenericUrl;\nimport com.google.api.client.http.HttpRequest;\nimport com.google.api.client.http.HttpRequestFactory;\nimport com.google.api.client.http.HttpTransport;\nimport com.google.api.client.http.json.JsonHttpContent;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ReplaceMessageCards {\n public static void main(String[] args) throws Exception {\n HttpTransport transport = GoogleNetHttpTransport.newTrustedTransport();\n GsonFactory jsonFactory = GsonFactory.getDefaultInstance();\n\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(\"https://www.googleapis.com/auth/chat.bot\"));\n HttpRequestFactory requestFactory = transport.createRequestFactory(new HttpCredentialsAdapter(credentials));\n\n String messageName = \"spaces/SPACE_NAME/messages/MESSAGE_ID\";\n GenericUrl url = new GenericUrl(\"https://chat.googleapis.com/v1/\" + messageName + \":replaceCards\");\n\n // Construct the body\n Map<String, Object> header = new HashMap<>();\n header.put(\"title\", \"Updated Card Title\");\n header.put(\"subtitle\", \"Updated Card Subtitle\");\n\n Map<String, Object> textParagraph = new HashMap<>();\n textParagraph.put(\"text\", \"The card content has been updated asynchronously.\");\n\n Map<String, Object> widget = new HashMap<>();\n widget.put(\"textParagraph\", textParagraph);\n\n Map<String, Object> section = new HashMap<>();\n section.put(\"widgets\", Collections.singletonList(widget));\n\n Map<String, Object> card = new HashMap<>();\n card.put(\"header\", header);\n card.put(\"sections\", Collections.singletonList(section));\n\n Map<String, Object> cardWithId = new HashMap<>();\n cardWithId.put(\"cardId\", \"unique-card-id\");\n cardWithId.put(\"card\", card);\n\n Map<String, Object> body = new HashMap<>();\n body.put(\"cardsV2\", Collections.singletonList(cardWithId));\n\n HttpRequest request = requestFactory.buildPostRequest(url, new JsonHttpContent(jsonFactory, body));\n request.execute();\n System.out.println(\"Cards updated.\");\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to update cards on a message.\n */\nfunction replaceMessageCards() {\n const messageName = 'spaces/SPACE_NAME/messages/MESSAGE_ID';\n const url = `https://chat.googleapis.com/v1/${messageName}:replaceCards`;\n\n const request = {\n cardsV2: [{\n cardId: 'unique-card-id',\n card: {\n header: {\n title: 'Updated Card Title',\n subtitle: 'Updated Card Subtitle'\n },\n sections: [{\n widgets: [{\n textParagraph: {\n text: 'The card content has been updated asynchronously.'\n }\n }]\n }]\n }\n }]\n };\n\n const options = {\n method: 'post',\n headers: {\n Authorization: 'Bearer ' + ScriptApp.getOAuthToken()\n },\n contentType: 'application/json',\n payload: JSON.stringify(request),\n muteHttpExceptions: true\n };\n\n try {\n const response = UrlFetchApp.fetch(url, options);\n console.log('Cards updated.');\n } catch (err) {\n console.log('Failed to update cards: ' + err.message);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.821Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":443,"estimatedTokens":3249}}897{"id":"doc-work_with_artifacts_google_meet_google_for_devel-730d2d77","source":"documentation","title":"Work with artifacts | Google Meet | Google for Developers","url":"https://developers.google.com/workspace/meet/api/guides/artifacts","text":"Example:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.GetRecordingRequest;\nimport com.google.apps.meet.v2.Recording;\nimport com.google.apps.meet.v2.RecordingName;\n\npublic class AsyncGetRecording {\n\n public static void main(String[] args) throws Exception {\n asyncGetRecording();\n }\n\n public static void asyncGetRecording() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n GetRecordingRequest request =\n GetRecordingRequest.newBuilder()\n .setName(RecordingName.of(\"[CONFERENCE_RECORD]\", \"[RECORDING]\").toString())\n .build();\n ApiFuture<Recording> future =\n conferenceRecordsServiceClient.getRecordingCallable().futureCall(request);\n // Do something.\n Recording response = future.get();\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Resource name of the recording.\n */\n// const name = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callGetRecording() {\n // Construct request\n const request = {\n name,\n };\n\n // Run request\n const response = await meetClient.getRecording(request);\n console.log(response);\n}\n\ncallGetRecording();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_get_recording():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.GetRecordingRequest(\n name=\"name_value\",\n )\n\n # Make the request\n response = await client.get_recording(request=request)\n\n # Handle the response\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/CONFERENCE_RECORD_NAME/recordings/RECORDING_NAME\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordName;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.ListRecordingsRequest;\nimport com.google.apps.meet.v2.Recording;\n\npublic class AsyncListRecordings {\n\n public static void main(String[] args) throws Exception {\n asyncListRecordings();\n }\n\n public static void asyncListRecordings() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n ListRecordingsRequest request =\n ListRecordingsRequest.newBuilder()\n .setParent(ConferenceRecordName.of(\"[CONFERENCE_RECORD]\").toString())\n .setPageSize(883849137)\n .setPageToken(\"pageToken873572522\")\n .build();\n ApiFuture<Recording> future =\n conferenceRecordsServiceClient.listRecordingsPagedCallable().futureCall(request);\n // Do something.\n for (Recording element : future.get().iterateAll()) {\n // doThingsWith(element);\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Format: `conferenceRecords/{conference_record}`\n */\n// const parent = 'abc123'\n/**\n * Maximum number of recordings to return. The service might return fewer\n * than this value.\n * If unspecified, at most 10 recordings are returned.\n * The maximum value is 100; values above 100 are coerced to 100.\n * Maximum might change in the future.\n */\n// const pageSize = 1234\n/**\n * Page token returned from previous List Call.\n */\n// const pageToken = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callListRecordings() {\n // Construct request\n const request = {\n parent,\n };\n\n // Run request\n const iterable = meetClient.listRecordingsAsync(request);\n for await (const response of iterable) {\n console.log(response);\n }\n}\n\ncallListRecordings();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_list_recordings():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.ListRecordingsRequest(\n parent=\"parent_value\",\n )\n\n # Make the request\n page_result = client.list_recordings(request=request)\n\n # Handle the response\n async for response in page_result:\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/PARENT_NAME/recordings\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.GetTranscriptRequest;\nimport com.google.apps.meet.v2.Transcript;\nimport com.google.apps.meet.v2.TranscriptName;\n\npublic class AsyncGetTranscript {\n\n public static void main(String[] args) throws Exception {\n asyncGetTranscript();\n }\n\n public static void asyncGetTranscript() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n GetTranscriptRequest request =\n GetTranscriptRequest.newBuilder()\n .setName(TranscriptName.of(\"[CONFERENCE_RECORD]\", \"[TRANSCRIPT]\").toString())\n .build();\n ApiFuture<Transcript> future =\n conferenceRecordsServiceClient.getTranscriptCallable().futureCall(request);\n // Do something.\n Transcript response = future.get();\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Resource name of the transcript.\n */\n// const name = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callGetTranscript() {\n // Construct request\n const request = {\n name,\n };\n\n // Run request\n const response = await meetClient.getTranscript(request);\n console.log(response);\n}\n\ncallGetTranscript();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_get_transcript():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.GetTranscriptRequest(\n name=\"name_value\",\n )\n\n # Make the request\n response = await client.get_transcript(request=request)\n\n # Handle the response\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/CONFERENCE_RECORD_NAME/transcripts/TRANSCRIPT_NAME\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordName;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.ListTranscriptsRequest;\nimport com.google.apps.meet.v2.Transcript;\n\npublic class AsyncListTranscripts {\n\n public static void main(String[] args) throws Exception {\n asyncListTranscripts();\n }\n\n public static void asyncListTranscripts() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n ListTranscriptsRequest request =\n ListTranscriptsRequest.newBuilder()\n .setParent(ConferenceRecordName.of(\"[CONFERENCE_RECORD]\").toString())\n .setPageSize(883849137)\n .setPageToken(\"pageToken873572522\")\n .build();\n ApiFuture<Transcript> future =\n conferenceRecordsServiceClient.listTranscriptsPagedCallable().futureCall(request);\n // Do something.\n for (Transcript element : future.get().iterateAll()) {\n // doThingsWith(element);\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Format: `conferenceRecords/{conference_record}`\n */\n// const parent = 'abc123'\n/**\n * Maximum number of transcripts to return. The service might return fewer\n * than this value.\n * If unspecified, at most 10 transcripts are returned.\n * The maximum value is 100; values above 100 are coerced to 100.\n * Maximum might change in the future.\n */\n// const pageSize = 1234\n/**\n * Page token returned from previous List Call.\n */\n// const pageToken = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callListTranscripts() {\n // Construct request\n const request = {\n parent,\n };\n\n // Run request\n const iterable = meetClient.listTranscriptsAsync(request);\n for await (const response of iterable) {\n console.log(response);\n }\n}\n\ncallListTranscripts();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_list_transcripts():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.ListTranscriptsRequest(\n parent=\"parent_value\",\n )\n\n # Make the request\n page_result = client.list_transcripts(request=request)\n\n # Handle the response\n async for response in page_result:\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/PARENT_NAME/transcripts\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.GetTranscriptEntryRequest;\nimport com.google.apps.meet.v2.TranscriptEntry;\nimport com.google.apps.meet.v2.TranscriptEntryName;\n\npublic class AsyncGetTranscriptEntry {\n\n public static void main(String[] args) throws Exception {\n asyncGetTranscriptEntry();\n }\n\n public static void asyncGetTranscriptEntry() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n GetTranscriptEntryRequest request =\n GetTranscriptEntryRequest.newBuilder()\n .setName(\n TranscriptEntryName.of(\"[CONFERENCE_RECORD]\", \"[TRANSCRIPT]\", \"[ENTRY]\")\n .toString())\n .build();\n ApiFuture<TranscriptEntry> future =\n conferenceRecordsServiceClient.getTranscriptEntryCallable().futureCall(request);\n // Do something.\n TranscriptEntry response = future.get();\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Resource name of the `TranscriptEntry`.\n */\n// const name = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callGetTranscriptEntry() {\n // Construct request\n const request = {\n name,\n };\n\n // Run request\n const response = await meetClient.getTranscriptEntry(request);\n console.log(response);\n}\n\ncallGetTranscriptEntry();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_get_transcript_entry():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.GetTranscriptEntryRequest(\n name=\"name_value\",\n )\n\n # Make the request\n response = await client.get_transcript_entry(request=request)\n\n # Handle the response\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/CONFERENCE_RECORD_NAME/transcripts/TRANSCRIPT_NAME/entries/TRANSCRIPT_ENTRY_NAME\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.ListTranscriptEntriesRequest;\nimport com.google.apps.meet.v2.TranscriptEntry;\nimport com.google.apps.meet.v2.TranscriptName;\n\npublic class AsyncListTranscriptEntries {\n\n public static void main(String[] args) throws Exception {\n asyncListTranscriptEntries();\n }\n\n public static void asyncListTranscriptEntries() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n ListTranscriptEntriesRequest request =\n ListTranscriptEntriesRequest.newBuilder()\n .setParent(TranscriptName.of(\"[CONFERENCE_RECORD]\", \"[TRANSCRIPT]\").toString())\n .setPageSize(883849137)\n .setPageToken(\"pageToken873572522\")\n .build();\n ApiFuture<TranscriptEntry> future =\n conferenceRecordsServiceClient.listTranscriptEntriesPagedCallable().futureCall(request);\n // Do something.\n for (TranscriptEntry element : future.get().iterateAll()) {\n // doThingsWith(element);\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Format:\n * `conferenceRecords/{conference_record}/transcripts/{transcript}`\n */\n// const parent = 'abc123'\n/**\n * Maximum number of entries to return. The service might return fewer than\n * this value.\n * If unspecified, at most 10 entries are returned.\n * The maximum value is 100; values above 100 are coerced to 100.\n * Maximum might change in the future.\n */\n// const pageSize = 1234\n/**\n * Page token returned from previous List Call.\n */\n// const pageToken = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callListTranscriptEntries() {\n // Construct request\n const request = {\n parent,\n };\n\n // Run request\n const iterable = meetClient.listTranscriptEntriesAsync(request);\n for await (const response of iterable) {\n console.log(response);\n }\n}\n\ncallListTranscriptEntries();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_list_transcript_entries():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.ListTranscriptEntriesRequest(\n parent=\"parent_value\",\n )\n\n # Make the request\n page_result = client.list_transcript_entries(request=request)\n\n # Handle the response\n async for response in page_result:\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/PARENT_NAME/transcripts/TRANSCRIPT_NAME/entries\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2beta/conferenceRecords/CONFERENCE_RECORD_NAME/smartNotes/SMART_NOTES_NAME\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2beta/conferenceRecords/PARENT_NAME/smartNotes\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.823Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":26,"totalLines":685,"estimatedTokens":5399}}898{"id":"doc-add_interactive_ui_elements_to_cards_google_chat-423f19de","source":"documentation","title":"Add interactive UI elements to cards | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/design-interactive-card-dialog","text":"Example:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Section Header\",\n \"widgets\": [\n {\n \"selectionInput\": {\n \"name\": \"contacts\",\n \"type\": \"DROPDOWN\",\n \"label\": \"Select contact from organization\",\n \"data_source_configs\": [\n {\n \"platformDataSource\": {\n \"commonDataSource\": \"USER\"\n },\n \"min_characters_trigger\": 1\n }\n ]\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Section Header\",\n \"widgets\": [\n {\n \"selectionInput\": {\n \"name\": \"crm_leads\",\n \"type\": \"DROPDOWN\",\n \"label\": \"Select CRM Lead\",\n \"data_source_configs\": [\n {\n \"remoteDataSource\": {\n \"function\": \"getCrmLeads\"\n },\n \"min_characters_trigger\": 2\n }\n ],\n \"items\": [\n {\n \"text\": \"Suggested Lead 1\",\n \"value\": \"lead-1\"\n }\n ]\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"selectionInput\": {\n \"name\": \"contacts\",\n \"type\": \"MULTI_SELECT\",\n \"label\": \"Selected contacts\",\n \"multiSelectMaxSelectedItems\": 5,\n \"multiSelectMinQueryLength\": 1,\n \"platformDataSource\": {\n \"commonDataSource\": \"USER\"\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"selectionInput\": {\n \"name\": \"spaces\",\n \"type\": \"MULTI_SELECT\",\n \"label\": \"Selected contacts\",\n \"multiSelectMaxSelectedItems\": 3,\n \"multiSelectMinQueryLength\": 1,\n \"platformDataSource\": {\n \"hostAppDataSource\": {\n \"chatDataSource\": {\n \"spaceDataSource\": {\n \"defaultToCurrentSpace\": true\n }\n }\n }\n }\n }\n}\n```\n\nExample:\n```text\nselectionInput: {\n name: \"contacts\",\n type: \"MULTI_SELECT\",\n label: \"Selected contacts\",\n multiSelectMaxSelectedItems: 3,\n multiSelectMinQueryLength: 1,\n externalDataSource: { function: \"getContacts\" },\n // Suggested items loaded by default.\n // The list is static here but it could be dynamic.\n items: [getContact(\"3\")]\n}\n```\n\nExample:\n```text\n'selectionInput': {\n 'name': \"contacts\",\n 'type': \"MULTI_SELECT\",\n 'label': \"Selected contacts\",\n 'multiSelectMaxSelectedItems': 3,\n 'multiSelectMinQueryLength': 1,\n 'externalDataSource': { 'function': \"getContacts\" },\n # Suggested items loaded by default.\n # The list is static here but it could be dynamic.\n 'items': [get_contact(\"3\")]\n}\n```\n\nExample:\n```text\n.setSelectionInput(new GoogleAppsCardV1SelectionInput()\n .setName(\"contacts\")\n .setType(\"MULTI_SELECT\")\n .setLabel(\"Selected contacts\")\n .setMultiSelectMaxSelectedItems(3)\n .setMultiSelectMinQueryLength(1)\n .setExternalDataSource(new GoogleAppsCardV1Action().setFunction(\"getContacts\"))\n .setItems(List.of(getContact(\"3\")))))))))));\n```\n\nExample:\n```text\n/**\n * Responds to a WIDGET_UPDATE event in Google Chat.\n *\n * @param {Object} event The event object from Chat API.\n * @return {Object} Response from the Chat app.\n */\nfunction onWidgetUpdate(event) {\n if (event.common[\"invokedFunction\"] === \"getContacts\") {\n const query = event.common.parameters[\"autocomplete_widget_query\"];\n return { actionResponse: {\n type: \"UPDATE_WIDGET\",\n updatedWidget: { suggestions: { items: [\n // The list is static here but it could be dynamic.\n getContact(\"1\"), getContact(\"2\"), getContact(\"3\"), getContact(\"4\"), getContact(\"5\")\n // Only return items based on the query from the user\n ].filter(e => !query || e.text.includes(query))}}\n }};\n }\n}\n\n/**\n * Generate a suggested contact given an ID.\n *\n * @param {String} id The ID of the contact to return.\n * @return {Object} The contact formatted as a suggested item for selectors.\n */\nfunction getContact(id) {\n return {\n value: id,\n startIconUri: \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n text: \"Contact \" + id\n };\n}\n```\n\nExample:\n```text\ndef on_widget_update(event: dict) -> dict:\n \"\"\"Responds to a WIDGET_UPDATE event in Google Chat.\"\"\"\n if \"getContacts\" == event.get(\"common\").get(\"invokedFunction\"):\n query = event.get(\"common\").get(\"parameters\").get(\"autocomplete_widget_query\")\n return { 'actionResponse': {\n 'type': \"UPDATE_WIDGET\",\n 'updatedWidget': { 'suggestions': { 'items': list(filter(lambda e: query is None or query in e[\"text\"], [\n # The list is static here but it could be dynamic.\n get_contact(\"1\"), get_contact(\"2\"), get_contact(\"3\"), get_contact(\"4\"), get_contact(\"5\")\n # Only return items based on the query from the user\n ]))}}\n }}\n\n\ndef get_contact(id: str) -> dict:\n \"\"\"Generate a suggested contact given an ID.\"\"\"\n return {\n 'value': id,\n 'startIconUri': \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n 'text': \"Contact \" + id\n }\n```\n\nExample:\n```text\n// Responds to a WIDGET_UPDATE event in Google Chat.\nMessage onWidgetUpdate(JsonNode event) {\n if (\"getContacts\".equals(event.at(\"/invokedFunction\").asText())) {\n String query = event.at(\"/common/parameters/autocomplete_widget_query\").asText();\n return new Message().setActionResponse(new ActionResponse()\n .setType(\"UPDATE_WIDGET\")\n .setUpdatedWidget(new UpdatedWidget()\n .setSuggestions(new SelectionItems().setItems(List.of(\n // The list is static here but it could be dynamic.\n getContact(\"1\"), getContact(\"2\"), getContact(\"3\"), getContact(\"4\"), getContact(\"5\")\n // Only return items based on the query from the user\n ).stream().filter(e -> query == null || e.getText().indexOf(query) > -1).toList()))));\n }\n return null;\n}\n\n// Generate a suggested contact given an ID.\nGoogleAppsCardV1SelectionItem getContact(String id) {\n return new GoogleAppsCardV1SelectionItem()\n .setValue(id)\n .setStartIconUri(\"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\")\n .setText(\"Contact \" + id);\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Select contacts\",\n \"widgets\": [\n {\n \"selectionInput\": {\n \"type\": \"MULTI_SELECT\",\n \"label\": \"Selected contacts\",\n \"name\": \"contacts\",\n \"multiSelectMaxSelectedItems\": 3,\n \"multiSelectMinQueryLength\": 1,\n \"onChangeAction\": {\n \"all_widgets_are_required\": true\n },\n \"items\": [\n {\n \"value\": \"contact-1\",\n \"startIconUri\": \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n \"text\": \"Contact 1\",\n \"bottomText\": \"Contact one description\",\n \"selected\": false\n },\n {\n \"value\": \"contact-2\",\n \"startIconUri\": \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n \"text\": \"Contact 2\",\n \"bottomText\": \"Contact two description\",\n \"selected\": false\n },\n {\n \"value\": \"contact-3\",\n \"startIconUri\": \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n \"text\": \"Contact 3\",\n \"bottomText\": \"Contact three description\",\n \"selected\": false\n },\n {\n \"value\": \"contact-4\",\n \"startIconUri\": \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n \"text\": \"Contact 4\",\n \"bottomText\": \"Contact four description\",\n \"selected\": false\n },\n {\n \"value\": \"contact-5\",\n \"startIconUri\": \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n \"text\": \"Contact 5\",\n \"bottomText\": \"Contact five description\",\n \"selected\": false\n }\n ]\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"A datetime picker widget with both date and time:\"\n }\n },\n {\n \"divider\": {}\n },\n {\n \"dateTimePicker\": {\n \"name\": \"date_time_picker_date_and_time\",\n \"label\": \"meeting\",\n \"type\": \"DATE_AND_TIME\"\n }\n },\n {\n \"textParagraph\": {\n \"text\": \"A datetime picker widget with just date:\"\n }\n },\n {\n \"divider\": {}\n },\n {\n \"dateTimePicker\": {\n \"name\": \"date_time_picker_date_only\",\n \"label\": \"Choose a date\",\n \"type\": \"DATE_ONLY\",\n \"onChangeAction\":{\n \"all_widgets_are_required\": true\n }\n }\n },\n {\n \"textParagraph\": {\n \"text\": \"A datetime picker widget with just time:\"\n }\n },\n {\n \"divider\": {}\n },\n {\n \"dateTimePicker\": {\n \"name\": \"date_time_picker_time_only\",\n \"label\": \"Select a time\",\n \"type\": \"TIME_ONLY\"\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Section Header\",\n \"collapsible\": true,\n \"uncollapsibleWidgetsCount\": 1,\n \"widgets\": [\n {\n \"selectionInput\": {\n \"name\": \"location\",\n \"label\": \"Select Color\",\n \"type\": \"DROPDOWN\",\n \"onChangeAction\": {\n \"all_widgets_are_required\": true\n },\n \"items\": [\n {\n \"text\": \"Red\",\n \"value\": \"red\",\n \"selected\": false\n },\n {\n \"text\": \"Green\",\n \"value\": \"green\",\n \"selected\": false\n },\n {\n \"text\": \"White\",\n \"value\": \"white\",\n \"selected\": false\n },\n {\n \"text\": \"Blue\",\n \"value\": \"blue\",\n \"selected\": false\n },\n {\n \"text\": \"Black\",\n \"value\": \"black\",\n \"selected\": false\n }\n ]\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Tell us about yourself\",\n \"collapsible\": true,\n \"uncollapsibleWidgetsCount\": 2,\n \"widgets\": [\n {\n \"textInput\": {\n \"name\": \"favoriteColor\",\n \"label\": \"Favorite color\",\n \"type\": \"SINGLE_LINE\",\n \"validation\": {\"character_limit\":15},\n \"onChangeAction\":{\n \"all_widgets_are_required\": true\n }\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Validate text inputs by input types\",\n \"collapsible\": true,\n \"uncollapsibleWidgetsCount\": 2,\n \"widgets\": [\n {\n \"textInput\": {\n \"name\": \"mailing_address\",\n \"label\": \"Please enter a valid email address\",\n \"type\": \"SINGLE_LINE\",\n \"validation\": {\n \"input_type\": \"EMAIL\"\n },\n \"onChangeAction\": {\n \"all_widgets_are_required\": true\n }\n }\n },\n {\n \"textInput\": {\n \"name\": \"validate_integer\",\n \"label\": \"Please enter a number\",\n \"type\": \"SINGLE_LINE\",\n \"validation\": {\n \"input_type\": \"INTEGER\"\n }\n }\n },\n {\n \"textInput\": {\n \"name\": \"validate_float\",\n \"label\": \"Please enter a number with a decimal\",\n \"type\": \"SINGLE_LINE\",\n \"validation\": {\n \"input_type\": \"FLOAT\"\n }\n }\n }\n ]\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.825Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":480,"estimatedTokens":3031}}899{"id":"doc-update_a_user_s_membership_in_a_google_chat_spac-d629e03e","source":"documentation","title":"Update a user's membership in a Google Chat space | Google for Developers","url":"https://developers.google.com/workspace/chat/update-members","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.memberships',\n];\n\n// This sample shows how to update a membership with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n membership: {\n // Replace SPACE_NAME and MEMBER_NAME here\n name: 'spaces/SPACE_NAME/members/MEMBER_NAME',\n // Replace ROLE_NAME here with ROLE_MEMBER or ROLE_MANAGER\n role: 'ROLE_NAME',\n },\n updateMask: {\n // The field paths to update.\n paths: ['role'],\n },\n };\n\n // Make the request\n const response = await chatClient.updateMembership(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\nfrom apiclient.discovery import build\n\n# Define your app's authorization scopes.\n# When modifying these scopes, delete the file token.json, if it exists.\nSCOPES = [\"https://www.googleapis.com/auth/chat.app.memberships\"]\n\ndef main():\n '''\n Authenticates with Chat API using app authentication,\n then updates a specified space member to change\n it from a regular member to a space owner.\n '''\n\n # Specify service account details.\n creds = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n )\n\n # Build a service endpoint for Chat API.\n chat = build('chat', 'v1', credentials=creds)\n\n # Use the service endpoint to call Chat API.\n result = chat.spaces().members().patch(\n\n # The membership to update, and the updated role.\n #\n # Replace SPACE with a space name.\n # Obtain the space name from the spaces resource of Chat API,\n # or from a space's URL.\n #\n # Replace MEMBERSHIP with a membership name.\n # Obtain the membership name from the membership of Chat API.\n name='spaces/SPACE/members/MEMBERSHIP',\n updateMask='role',\n\n # Replace ROLE with a MembershipRole value.\n # Obtain the MembershipRole values from the membership of Chat API.\n body={'role': 'ROLE'}\n\n ).execute()\n\n # Prints details about the updated membership.\n print(result)\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython3 chat_membership_update_to_owner_app.py\n```\n\nExample:\n```text\nfrom google.oauth2 import service_account\nfrom apiclient.discovery import build\n\n# Define your app's authorization scopes.\n# When modifying these scopes, delete the file token.json, if it exists.\nSCOPES = [\"https://www.googleapis.com/auth/chat.app.memberships\"]\n\ndef main():\n '''\n Authenticates with Chat API via user credentials,\n then updates a specified space owner to change\n it to a regular member.\n '''\n\n # Specify service account details.\n creds = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n )\n\n # Build a service endpoint for Chat API.\n chat = build('chat', 'v1', credentials=creds)\n\n # Use the service endpoint to call Chat API.\n result = chat.spaces().members().patch(\n\n # The membership to update, and the updated role.\n #\n # Replace SPACE with a space name.\n # Obtain the space name from the spaces resource of Chat API,\n # or from a space's URL.\n #\n # Replace MEMBERSHIP with a membership name.\n # Obtain the membership name from the membership of Chat API.\n name='spaces/SPACE/members/MEMBERSHIP',\n updateMask='role',\n body={'role': 'ROLE_MEMBER'}\n\n ).execute()\n\n # Prints details about the updated membership.\n print(result)\n\nif __name__ == '__main__':\n main()\n```\n\nExample:\n```text\npython3 chat_membership_update_to_member_app.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.827Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":151,"estimatedTokens":1000}}900{"id":"doc-class_maps_apps_script_google_for_developers-a753d26a","source":"documentation","title":"Class Maps | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/maps/maps","text":"Example:\n```text\n// Decodes a string representation of the latitudes and longitudes of\n// Minneapolis and Milwaukee respectively.\nconst polyline = 'qvkpG`qhxPbgyI_zq_@';\nconst points = Maps.decodePolyline(polyline);\nfor (let i = 0; i < points.length; i += 2) {\n Logger.log('%s, %s', points[i], points[i + 1]);\n}\n```\n\nExample:\n```text\n// The latitudes and longitudes of New York and Boston respectively.\nconst points = [40.77, -73.97, 42.34, -71.04];\nconst polyline = Maps.encodePolyline(points);\n```\n\nExample:\n```text\nMaps.resetAuthenticationApiKey();\n```\n\nExample:\n```text\nMaps.setAuthenticationByApiKey('BBdgJpSbLtAtmkBFjgLt310qT6iekggfDdVqLC0');\n```\n\nExample:\n```text\nMaps.setAuthenticationByApiKey('BBdgJpSbLtAtmkBFjgLt310qT6iekggfDdVqLC0',\n'7_pry-Skg0PKxds-7nvdl91mB5=');\n```\n\nExample:\n```text\nMaps.setAuthentication('gme-123456789', 'VhSEZvOXVSdnlxTnpJcUE');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.828Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":40,"estimatedTokens":221}}901{"id":"doc-get_a_user_s_space_notification_settings_google_-32adc8a2","source":"documentation","title":"Get a user's space notification settings | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/get-space-notification-setting","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.users.spacesettings',\n];\n\n// This sample shows how to get the space notification setting for the calling\n// user\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s), replace the SPACE_NAME with an actual space\n // name.\n const request = {\n name: 'users/me/spaces/SPACE_NAME/spaceNotificationSetting',\n };\n\n // Make the request\n const response = await chatClient.getSpaceNotificationSetting(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.829Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":33,"estimatedTokens":196}}902{"id":"doc-update_a_user_s_space_notification_settings_goog-ced2f06b","source":"documentation","title":"Update a user's space notification settings | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/update-space-notification-setting","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.users.spacesettings',\n];\n\n// This sample shows how to update the space notification setting for the\n// calling user\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s), replace the SPACE_NAME with an actual space\n // name.\n const request = {\n spaceNotificationSetting: {\n name: 'users/me/spaces/SPACE_NAME/spaceNotificationSetting',\n notificationSetting: 'ALL',\n muteSetting: 'UNMUTED',\n },\n updateMask: {paths: ['notification_setting', 'mute_setting']},\n };\n\n // Make the request\n const response = await chatClient.updateSpaceNotificationSetting(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.830Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":38,"estimatedTokens":240}}903{"id":"doc-get_details_about_a_space_google_chat_google_for-eed81804","source":"documentation","title":"Get details about a space | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/get-spaces","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.spaces.readonly',\n];\n\n// This sample shows how to get space with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here\n name: 'spaces/SPACE_NAME',\n };\n\n // Make the request\n const response = await chatClient.getSpace(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.spaces.readonly\"]\n\n# This sample shows how to get space with user credential\ndef get_space_with_user_cred():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.GetSpaceRequest(\n # Replace SPACE_NAME here\n name = \"spaces/SPACE_NAME\",\n )\n\n # Make the request\n response = client.get_space(request)\n\n # Handle the response\n print(response)\n\nget_space_with_user_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.GetSpaceRequest;\nimport com.google.chat.v1.Space;\n\n// This sample shows how to get space with user credential.\npublic class GetSpaceUserCred {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.spaces.readonly\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n GetSpaceRequest.Builder request = GetSpaceRequest.newBuilder()\n // Replace SPACE_NAME here\n .setName(\"spaces/SPACE_NAME\");\n Space response = chatServiceClient.getSpace(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to get space with user credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.spaces.readonly'\n * referenced in the manifest file (appsscript.json).\n */\nfunction getSpaceUserCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here\n const name = \"spaces/SPACE_NAME\";\n\n // Make the request\n const response = Chat.Spaces.get(name);\n\n // Handle the response\n console.log(response);\n}\n```\n\nExample:\n```text\nimport {createClientWithAppCredentials} from './authentication-utils.js';\n\n// This sample shows how to get space with app credential\nasync function main() {\n // Create a client\n const chatClient = createClientWithAppCredentials();\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here\n name: 'spaces/SPACE_NAME',\n };\n\n // Make the request\n const response = await chatClient.getSpace(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_app_credentials\nfrom google.apps import chat_v1 as google_chat\n\n# This sample shows how to get space with app credential\ndef get_space_with_app_cred():\n # Create a client\n client = create_client_with_app_credentials()\n\n # Initialize request argument(s)\n request = google_chat.GetSpaceRequest(\n # Replace SPACE_NAME here\n name = \"spaces/SPACE_NAME\",\n )\n\n # Make the request\n response = client.get_space(request)\n\n # Handle the response\n print(response)\n\nget_space_with_app_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.GetSpaceRequest;\nimport com.google.chat.v1.Space;\n\n// This sample shows how to get space with app credential.\npublic class GetSpaceAppCred {\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithAppCredentials()) {\n GetSpaceRequest.Builder request = GetSpaceRequest.newBuilder()\n // Replace SPACE_NAME here\n .setName(\"spaces/SPACE_NAME\");\n Space response = chatServiceClient.getSpace(request.build());\n\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to get space with app credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.bot'\n * used by service accounts.\n */\nfunction getSpaceAppCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here\n const name = \"spaces/SPACE_NAME\";\n const parameters = {};\n\n // Make the request\n const response = Chat.Spaces.get(\n name,\n parameters,\n getHeaderWithAppCredentials(),\n );\n\n // Handle the response\n console.log(response);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.834Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":206,"estimatedTokens":1259}}904{"id":"doc-migrate_scripts_to_the_v8_runtime_apps_script_go-64c7af32","source":"documentation","title":"Migrate scripts to the V8 runtime | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/v8-runtime/migration","text":"Example:\n```text\n// Rhino runtime\nvar obj = {a: 1, b: 2, c: 3};\n\n// Don't use 'for each' in V8\nfor each (var value in obj) {\n Logger.log(\"value = %s\", value);\n}\n```\n\nExample:\n```text\n// V8 runtime\nvar obj = {a: 1, b: 2, c: 3};\n\nfor (var key in obj) { // OK in V8\n var value = obj[key];\n Logger.log(\"value = %s\", value);\n}\n```\n\nExample:\n```text\nfunction class() {} // Syntax error in V8.\nvar obj = { class: 1 }; // Allowed.\n```\n\nExample:\n```text\n// Rhino runtime\nconst x = 1;\nx = 2; // No error\nconsole.log(x); // Outputs 1\n```\n\nExample:\n```text\n// V8 runtime\nconst x = 1;\nx = 2; // Throws TypeError\nconsole.log(x); // Never executed\n```\n\nExample:\n```text\n// V8 runtime\nvar incompatibleXml1 = <container><item/></container>; // Don't use\nvar incompatibleXml2 = new XML('<container><item/></container>'); // Don't use\n\nvar xml3 = XmlService.parse('<container><item/></container>'); // OK\n```\n\nExample:\n```text\n// Create a sample array\nvar myArray = ['a', 'b', 'c'];\n// Add a property to the array\nmyArray.foo = 'bar';\n\n// The default behavior for an array is to return keys of all properties,\n// including 'foo'.\nLogger.log(\"Normal for...in loop:\");\nfor (var item in myArray) {\n Logger.log(item); // Logs 0, 1, 2, foo\n}\n\n// To only log the array values with `for..in`, a custom iterator can be used.\n```\n\nExample:\n```text\n// Rhino runtime custom iterator\nfunction ArrayIterator(array) {\n this.array = array;\n this.currentIndex = 0;\n}\n\nArrayIterator.prototype.next = function() {\n if (this.currentIndex\n >= this.array.length) {\n throw StopIteration;\n }\n return \"[\" + this.currentIndex\n + \"]=\" + this.array[this.currentIndex++];\n};\n\n// Direct myArray to use the custom iterator\nmyArray.__iterator__ = function() {\n return new ArrayIterator(this);\n}\n\n\nLogger.log(\"With custom Rhino iterator:\");\nfor (var item in myArray) {\n // Logs [0]=a, [1]=b, [2]=c\n Logger.log(item);\n}\n```\n\nExample:\n```text\n// V8 runtime (ECMAScript 6) custom iterator\nmyArray[Symbol.iterator] = function() {\n var currentIndex = 0;\n var array = this;\n\n return {\n next: function() {\n if (currentIndex < array.length) {\n return {\n value: \"[${currentIndex}]=\"\n + array[currentIndex++],\n done: false};\n } else {\n return {done: true};\n }\n }\n };\n}\n\nLogger.log(\"With V8 custom iterator:\");\n// Must use for...of since\n// for...in doesn't expect an iterable.\nfor (var item of myArray) {\n // Logs [0]=a, [1]=b, [2]=c\n Logger.log(item);\n}\n```\n\nExample:\n```text\n// Rhino runtime\n\ntry {\n doSomething();\n} catch (e if e instanceof TypeError) { // Don't use\n // Handle exception\n}\n```\n\nExample:\n```text\n// V8 runtime\ntry {\n doSomething();\n} catch (e) {\n if (e instanceof TypeError) {\n // Handle exception\n }\n}\n```\n\nExample:\n```text\n// Rhino runtime\nvar event = new Date(\n Date.UTC(2012, 11, 21, 12));\n\n// Outputs \"December 21, 2012\" in Rhino\nconsole.log(event.toLocaleDateString());\n\n// Also outputs \"December 21, 2012\",\n// ignoring the parameters passed in.\nconsole.log(event.toLocaleDateString(\n 'de-DE',\n { year: 'numeric',\n month: 'long',\n day: 'numeric' }));\n```\n\nExample:\n```text\n// V8 runtime\nvar event = new Date(\n Date.UTC(2012, 11, 21, 12));\n\n// Outputs \"12/21/2012\" in V8\nconsole.log(event.toLocaleDateString());\n\n// Outputs \"21. Dezember 2012\"\nconsole.log(event.toLocaleDateString(\n 'de-DE',\n { year: 'numeric',\n month: 'long',\n day: 'numeric' }));\n```\n\nExample:\n```text\n// Rhino runtime Error.prototype.stack\n// stack trace format\nat filename:92 (innerFunction)\nat filename:97 (outerFunction)\n```\n\nExample:\n```text\n// V8 runtime Error.prototype.stack\n// stack trace format\nError: error message\nat innerFunction (filename:92:11)\nat outerFunction (filename:97:5)\n```\n\nExample:\n```text\n// Rhino runtime\nvar enumName =\n JSON.stringify(Charts.ChartType.BUBBLE);\n\n// enumName evaluates to {}\n```\n\nExample:\n```text\n// V8 runtime\nvar enumName =\n JSON.stringify(Charts.ChartType.BUBBLE);\n\n// enumName evaluates to \"BUBBLE\"\n```\n\nExample:\n```text\n// Rhino runtime\nSpreadsheetApp.getActiveRange()\n .setValue(undefined);\n\n// The active range now has the string\n// \"undefined\" as its value.\n```\n\nExample:\n```text\n// V8 runtime\nSpreadsheetApp.getActiveRange()\n .setValue(undefined);\n\n// The active range now has no content, as\n// setValue(null) removes content from\n// ranges.\n```\n\nExample:\n```text\n// Rhino runtime\n\n// Apps Script built-in services defined here, in the actual global context.\nvar SpreadsheetApp = {\n openById: function() { ... }\n getActive: function() { ... }\n // etc.\n};\n\nfunction() {\n // Implicit special context; all your code goes here. If the global this\n // is referenced in your code, it only contains elements from this context.\n\n // Any global variables you defined.\n var x = 42;\n\n // Your script functions.\n function myFunction() {\n ...\n }\n // End of your code.\n}();\n```\n\nExample:\n```text\n// Rhino runtime\nvar myGlobal = 5;\n\nfunction myFunction() {\n\n // Only logs [myFunction, myGlobal];\n console.log(Object.keys(this));\n\n // Only logs [myFunction, myGlobal];\n console.log(\n Object.getOwnPropertyNames(this));\n}\n```\n\nExample:\n```text\n// V8 runtime\nvar myGlobal = 5;\n\nfunction myFunction() {\n\n // Logs an array that includes the names\n // of Apps Script services\n // (CalendarApp, GmailApp, etc.) in\n // addition to myFunction and myGlobal.\n console.log(Object.keys(this));\n\n // Logs an array that includes the same\n // values as above, and also includes\n // ECMAScript built-ins like Math, Date,\n // and Object.\n console.log(\n Object.getOwnPropertyNames(this));\n}\n```\n\nExample:\n```text\n//Rhino runtime\n\n//Project A\n\nfunction caller() {\n var date = new Date();\n // Returns true\n return B.callee(date);\n}\n\n//Project B\n\nfunction callee(date) {\n // Returns true\n return(date instanceof Date);\n}\n```\n\nExample:\n```text\n//V8 runtime\n\n//Project A\n\nfunction caller() {\n var date = new Date();\n // Returns false\n return B.callee(date);\n}\n\n//Project B\n\nfunction callee(date) {\n // Incorrectly returns false\n return(date instanceof Date);\n // Consider using return (date.constructor.name ==\n // “Date”) instead.\n // return (date.constructor.name == “Date”) -> Returns\n // true\n}\n```\n\nExample:\n```text\n//V8 runtime\n\n//Project A\n\nfunction caller() {\n var date = new Date();\n // Returns True\n return B.callee(date, date => date instanceof Date);\n}\n\n//Project B\n\nfunction callee(date, checkInstanceOf) {\n // Returns True\n return checkInstanceOf(date);\n}\n```\n\nExample:\n```text\n// Rhino runtime\n// Project A\nfunction testPassingNonSharedProperties() {\n PropertiesService.getScriptProperties()\n .setProperty('project', 'Project-A');\n B.setScriptProperties();\n // Prints: Project-B\n Logger.log(B.getScriptProperties(\n PropertiesService, 'project'));\n}\n\n//Project B\nfunction setScriptProperties() {\n PropertiesService.getScriptProperties()\n .setProperty('project', 'Project-B');\n}\nfunction getScriptProperties(\n propertiesService, key) {\n return propertiesService.getScriptProperties()\n .getProperty(key);\n}\n```\n\nExample:\n```text\n// V8 runtime\n// Project A\nfunction testPassingNonSharedProperties() {\n PropertiesService.getScriptProperties()\n .setProperty('project', 'Project-A');\n B.setScriptProperties();\n // Prints: Project-A\n Logger.log(B.getScriptProperties(\n PropertiesService, 'project'));\n}\n\n// Project B\nfunction setProperties() {\n PropertiesService.getScriptProperties()\n .setProperty('project', 'Project-B');\n}\nfunction getScriptProperties(\n propertiesService, key) {\n return propertiesService.getScriptProperties()\n .getProperty(key);\n}\n```\n\nExample:\n```text\nvar conn = Jdbc.getCloudSqlConnection(\"jdbc:google:mysql://...\");\nvar stmt = conn.prepareStatement(\"INSERT INTO employees (name, age) VALUES (?, ?)\");\nvar params = [[\"John Doe\", 30], [\"John Smith\", 25]];\nfor (var i = 0; i < params.length; i++) {\n stmt.setString(1, params[i][0]);\n stmt.setInt(2, params[i][1]);\n stmt.execute();\n}\n```\n\nExample:\n```text\nvar conn = Jdbc.getCloudSqlConnection(\"jdbc:google:mysql://...\");\nvar stmt = conn.prepareStatement(\"INSERT INTO employees (name, age) VALUES (?, ?)\");\nvar params = [[\"John Doe\", 30], [\"John Smith\", 25]];\nstmt.executeBatch(params);\n```\n\nExample:\n```text\nvar conn = Jdbc.getCloudSqlConnection(\"jdbc:google:mysql://...\");\nvar stmt = conn.createStatement();\nvar rs = stmt.executeQuery(\"SELECT name, age FROM employees\");\nwhile (rs.next()) {\n Logger.log(rs.getString('name') + \", \" + rs.getInt('age'));\n}\n```\n\nExample:\n```text\nvar conn = Jdbc.getCloudSqlConnection(\"jdbc:google:mysql://...\");\nvar stmt = conn.createStatement();\nvar rs = stmt.executeQuery(\"SELECT name, age FROM employees\");\nvar rows = rs.getRows(\"getString('name'), getInt('age')\");\nfor (var i = 0; i < rows.length; i++) {\n Logger.log(rows[i][0] + \", \" + rows[i][1]);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.838Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":31,"totalLines":464,"estimatedTokens":2241}}905{"id":"doc-respond_to_google_chat_app_commands_google_for_d-470337bc","source":"documentation","title":"Respond to Google Chat app commands | Google for Developers","url":"https://developers.google.com/workspace/chat/quick-commands","text":"Example:\n```text\n/**\n * Handles slash and quick commands.\n *\n * @param {Object} event - The Google Chat event.\n * @param {Object} res - The HTTP response object.\n */\nfunction handleAppCommands(event, res) {\n const {appCommandId, appCommandType} = event.appCommandMetadata;\n\n switch (appCommandId) {\n case ABOUT_COMMAND_ID:\n return res.send({\n privateMessageViewer: event.user,\n text: 'The Avatar app replies to Google Chat messages.'\n });\n case HELP_COMMAND_ID:\n return res.send({\n privateMessageViewer: event.user,\n text: 'The Avatar app replies to Google Chat messages.'\n });\n }\n}\n```\n\nExample:\n```text\n// Checks for the presence of a slash command in the message.\nif (event.message.slashCommand) {\n // Executes the slash command logic based on its ID.\n // Slash command IDs are set in the Google Chat API configuration.\n switch (event.message.slashCommand.commandId) {\n case ABOUT_COMMAND_ID:\n return {\n privateMessageViewer: event.user,\n text: 'The Avatar app replies to Google Chat messages.'\n };\n }\n}\n```\n\nExample:\n```text\ndef handle_app_commands(event: Mapping[str, Any]) -> Mapping[str, Any]:\n \"\"\"Handles slash and quick commands.\n\n Args:\n Mapping[str, Any] event: The Google Chat event.\n\n Returns:\n Mapping[str, Any]: the response\n \"\"\"\n app_command_id = event[\"appCommandMetadata\"][\"appCommandId\"]\n\n if app_command_id == ABOUT_COMMAND_ID:\n return {\n \"privateMessageViewer\": event[\"user\"],\n \"text\": \"The Avatar app replies to Google Chat messages.\",\n }\n elif app_command_id == HELP_COMMAND_ID:\n return {\n \"privateMessageViewer\": event[\"user\"],\n \"text\": \"The Avatar app replies to Google Chat messages.\",\n }\n return {}\n```\n\nExample:\n```text\n/**\n * Handles slash and quick commands.\n *\n * @param event The Google Chat event.\n * @param response The HTTP response object.\n */\nprivate void handleAppCommands(JsonObject event, HttpResponse response) throws Exception {\n int appCommandId = event.getAsJsonObject(\"appCommandMetadata\").get(\"appCommandId\").getAsInt();\n\n switch (appCommandId) {\n case ABOUT_COMMAND_ID:\n Message aboutMessage = new Message();\n aboutMessage.setText(\"The Avatar app replies to Google Chat messages.\");\n aboutMessage.setPrivateMessageViewer(new User()\n .setName(event.getAsJsonObject(\"user\").get(\"name\").getAsString()));\n response.getWriter().write(gson.toJson(aboutMessage));\n return;\n case HELP_COMMAND_ID:\n Message helpMessage = new Message();\n helpMessage.setText(\"The Avatar app replies to Google Chat messages.\");\n helpMessage.setPrivateMessageViewer(new User()\n .setName(event.getAsJsonObject(\"user\").get(\"name\").getAsString()));\n response.getWriter().write(gson.toJson(helpMessage));\n return;\n }\n}\n```\n\nExample:\n```text\n/**\n * Handles the APP_COMMAND event type. This function is triggered when a user\n * interacts with a quick command within the Google Chat app. It responds\n * based on the command ID.\n *\n * @param {Object} event The event object from Google Chat, containing details\n * about the app command interaction. It includes information like the\n * command ID and the user who triggered it.\n */\nfunction onAppCommand(event) {\n // Executes the quick command logic based on its ID.\n // Command IDs are set in the Google Chat API configuration.\n switch (event.appCommandMetadata.appCommandId) {\n case HELP_COMMAND_ID:\n return {\n privateMessageViewer: event.user,\n text: 'The Avatar app replies to Google Chat messages.'\n };\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event from Google Chat.\n *\n * @param {Object} event The interaction event from Google Chat.\n * @param {Object} res The HTTP response object.\n * @return {Object} The JSON response message with a confirmation.\n */\nfunction handleAppCommand(event, res) {\n // Collect the command ID and type from the event metadata.\n const {appCommandId, appCommandType} = event.appCommandMetadata;\n\n // Use appCommandType to detect message actions.\n if (appCommandType === 'MESSAGE_ACTION' &&\n appCommandId === REMIND_ME_COMMAND_ID) {\n\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n const messageText = event.message.text;\n\n // Return a response that includes details from the original message.\n return res.send({\n text: `Setting a reminder for this message: \"${messageText}\"`\n });\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event in Google Chat.\n *\n * @param {Object} event The interaction event from Google Chat.\n * @return {Object} The JSON response message with a confirmation.\n */\nfunction onAppCommand(event) {\n // Collect the command ID and type from the event metadata.\n const {appCommandId, appCommandType} = event.appCommandMetadata;\n\n if (appCommandType === 'MESSAGE_ACTION' &&\n appCommandId === REMIND_ME_COMMAND_ID) {\n\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n const messageText = event.message.text;\n\n // Return a response that includes details from the original message.\n return { \"text\": \"Setting a reminder for message: \" + messageText };\n }\n}\n```\n\nExample:\n```text\ndef handle_app_command(event):\n \"\"\"Responds to an APP_COMMAND interaction event from Google Chat.\n\n Args:\n event (dict): The interaction event from Google Chat.\n\n Returns:\n dict: The JSON response message with a confirmation.\n \"\"\"\n # Collect the command ID and type from the event metadata.\n metadata = event.get('appCommandMetadata', {})\n if metadata.get('appCommandType') == 'MESSAGE_ACTION' and \\\n metadata.get('appCommandId') == REMIND_ME_COMMAND_ID:\n\n # Message actions can access the context of the message they were\n # invoked on, such as the text or sender of that message.\n message_text = event.get('message', {}).get('text')\n\n # Return a response that includes details from the original message.\n return {\n \"text\": f'Setting a reminder for message: \"{message_text}\"'\n }\n```\n\nExample:\n```text\n/**\n * Responds to an APP_COMMAND interaction event from Google Chat.\n *\n * @param event The interaction event from Google Chat.\n * @param response The HTTP response object.\n */\nvoid handleAppCommand(JsonObject event, HttpResponse response) throws Exception {\n // Collect the command ID and type from the event metadata.\n JsonObject metadata = event.getAsJsonObject(\"appCommandMetadata\");\n String appCommandType = metadata.get(\"appCommandType\").getAsString();\n\n if (appCommandType.equals(\"MESSAGE_ACTION\")) {\n int commandId = metadata.get(\"appCommandId\").getAsInt();\n if (commandId == REMIND_ME_COMMAND_ID) {\n // Message actions can access the context of the message they were\n // invoked on, such as the text or sender of that message.\n String messageText = event.getAsJsonObject(\"message\").get(\"text\").getAsString();\n\n // Return a response that includes details from the original message.\n JsonObject responseMessage = new JsonObject();\n responseMessage.addProperty(\"text\", \"Setting a reminder for message: \" + messageText);\n response.getWriter().write(responseMessage.toString());\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.840Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":232,"estimatedTokens":1884}}906{"id":"doc-class_chiplist_apps_script_google_for_developers-644ff734","source":"documentation","title":"Class ChipList | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/chip-list","text":"Example:\n```text\nconst chip = CardService.newChip();\n// Finish building the text chip...\n\nconst chipList = CardService.newChipList()\n .setLayout(CardService.ChipListLayout.WRAPPED)\n .addChip(chip);\n```\n\nExample:\n```text\nconst chip = CardService.newChip();\n// Finish building the text chip...\n\nconst chipList =\n CardService.newChipList()\n .setLayout(CardService.ChipListLayout.HORIZONTAL_SCROLLABLE)\n .addChip(chip);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.842Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":22,"estimatedTokens":123}}907{"id":"doc-class_textbutton_apps_script_google_for_develope-6c32d748","source":"documentation","title":"Class TextButton | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/text-button","text":"Example:\n```text\nconst textButton = CardService.newTextButton()\n .setText('Open Link')\n .setOpenLink(CardService.newOpenLink().setUrl(\n 'https://www.google.com'));\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAuthorizationAction().setAuthorizationUrl('url');\nCardService.newTextButton().setText('Authorize').setAuthorizationAction(action);\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('composeEmailCallback');\nCardService.newTextButton()\n .setText('Compose Email')\n .setComposeAction(action, CardService.ComposedEmailType.REPLY_AS_DRAFT);\n\n// ...\n\nfunction composeEmailCallback(e) {\n const thread = GmailApp.getThreadById(e.threadId);\n const draft = thread.createDraftReply('This is a reply');\n return CardService.newComposeActionResponseBuilder()\n .setGmailDraft(draft)\n .build();\n}\n```\n\nExample:\n```text\nconst textButton = CardService.newTextButton().setMaterialIcon(\n CardService.newMaterialIcon().setName('search'),\n);\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('notificationCallback');\nCardService.newTextButton()\n .setText('Create notification')\n .setOnClickAction(action);\n\n// ...\n\nfunction notificationCallback() {\n return CardService.newActionResponseBuilder()\n .setNotification(\n CardService.newNotification().setText('Some info to display to user'),\n )\n .build();\n}\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('openLinkCallback');\nCardService.newTextButton()\n .setText('Open Link')\n .setOnClickOpenLinkAction(action);\n\n// ...\n\nfunction openLinkCallback() {\n return CardService.newActionResponseBuilder()\n .setOpenLink(CardService.newOpenLink().setUrl('https://www.google.com'))\n .build();\n}\n```\n\nExample:\n```text\nconst overflowMenuItem =\n CardService.newOverflowMenuItem()\n .setStartIcon(\n CardService.newIconImage().setIconUrl(\n 'https://www.google.com/images/branding/googleg/1x/googleg_standard_color_64dp.png',\n ),\n )\n .setText('Open Link')\n .setOpenLink(\n CardService.newOpenLink().setUrl('https://www.google.com'));\n\nconst overflowMenu =\n CardService.newOverflowMenu().addMenuItem(overflowMenuItem).build();\n```\n\nExample:\n```text\nconst button =\n CardService.newTextButton()\n .setText('Filled')\n .setTextButtonStyle(CardService.TextButtonStyle.FILLED)\n .setOpenLink(CardService.newOpenLink().setUrl('www.google.com'));\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.844Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":108,"estimatedTokens":662}}908{"id":"doc-class_scriptapp_apps_script_google_for_developer-37819a5c","source":"documentation","title":"Class ScriptApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/script/script-app","text":"Example:\n```text\n// Deletes all triggers in the current project.\nconst triggers = ScriptApp.getProjectTriggers();\nfor (let i = 0; i < triggers.length; i++) {\n ScriptApp.deleteTrigger(triggers[i]);\n}\n```\n\nExample:\n```text\nconst authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);\nconst status = authInfo.getAuthorizationStatus();\nconst url = authInfo.getAuthorizationUrl();\n```\n\nExample:\n```text\nconst authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL, [\n 'https://www.googleapis.com/auth/documents',\n 'https://www.googleapis.com/auth/presentations',\n]);\nconst status = authInfo.getAuthorizationStatus();\nconst url = authInfo.getAuthorizationUrl();\n```\n\nExample:\n```text\nconst idToken = ScriptApp.getIdentityToken();\nconst body = idToken.split('.')[1];\nconst decoded = Utilities\n .newBlob(\n Utilities.base64Decode(body),\n )\n .getDataAsString();\nconst payload = JSON.parse(decoded);\n\nLogger.log(`Profile ID: ${payload.sub}`);\n```\n\nExample:\n```text\nconst url = 'https://www.googleapis.com/drive/v3/files';\nconst method = 'GET';\nconst headers = {\n Authorization: 'Bearer ' + ScriptApp.getOAuthToken(),\n};\nconst response = UrlFetchApp.fetch(url, {\n method,\n headers,\n});\n```\n\nExample:\n```text\nLogger.log(\n `Current project has ${ScriptApp.getProjectTriggers().length} triggers.`,\n);\n```\n\nExample:\n```text\n// Get the URL of the published web app.\nconst url = ScriptApp.getService().getUrl();\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst triggers = ScriptApp.getUserTriggers(doc);\n// Log the handler function for the first trigger in the array.\nLogger.log(triggers[0].getHandlerFunction());\n```\n\nExample:\n```text\nconst form = FormApp.getActiveForm();\nconst triggers = ScriptApp.getUserTriggers(form);\n// Log the trigger source for the first trigger in the array.\nLogger.log(triggers[0].getTriggerSource());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst triggers = ScriptApp.getUserTriggers(ss);\n// Log the event type for the first trigger in the array.\nLogger.log(triggers[0].getEventType());\n```\n\nExample:\n```text\nScriptApp.invalidateAuth();\n```\n\nExample:\n```text\n// Generate a callback URL, given the name of a callback function. The script\n// does not need to be published as a web app; the /usercallback URL suffix\n// replaces /edit in any script's URL.\nfunction getCallbackURL(callbackFunction) {\n // IMPORTANT: Replace string below with the URL from your script, minus the\n // /edit at the end.\n const scriptUrl =\n 'https://script.google.com/macros/d/1234567890abcdefghijklmonpqrstuvwxyz';\n const urlSuffix = '/usercallback?state=';\n const stateToken = ScriptApp.newStateToken()\n .withMethod(callbackFunction)\n .withTimeout(120)\n .createToken();\n return scriptUrl + urlSuffix + stateToken;\n}\n```\n\nExample:\n```text\n// Creates an edit trigger for a spreadsheet identified by ID.\nScriptApp.newTrigger('myFunction')\n .forSpreadsheet('1234567890abcdefghijklmnopqrstuvwxyz_a1b2c3')\n .onEdit()\n .create();\n```\n\nExample:\n```text\nScriptApp.requireAllScopes(ScriptApp.AuthMode.FULL);\n```\n\nExample:\n```text\nScriptApp.requireScopes(ScriptApp.AuthMode.FULL, [\n 'https://www.googleapis.com/auth/documents',\n 'https://www.googleapis.com/auth/presentations',\n]);\n```\n\nExample:\n```text\nLogger.log(\n `Current script has ${ScriptApp.getScriptTriggers().length} triggers.`,\n);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.846Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":144,"estimatedTokens":889}}909{"id":"doc-class_textinput_apps_script_google_for_developer-32d23ed4","source":"documentation","title":"Class TextInput | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/text-input","text":"Example:\n```text\nconst textInput = CardService.newTextInput()\n .setFieldName('text_input_form_input_key')\n .setTitle('Text input title')\n .setHint('Text input hint');\n```\n\nExample:\n```text\nconst workflowDataSource =\n CardService.newWorkflowDataSource().setIncludeVariables(true);\n\nconst hostAppDataSource =\n CardService.newHostAppDataSource().setWorkflowDataSource(workflowDataSource);\n\nconst textInput = CardService.newTextInput()\n .setFieldName('text_input_form_input_key')\n .setTitle('Text input title')\n .setHint('Text input hint')\n .setHostAppDataSource(hostAppDataSource);\n```\n\nExample:\n```text\nconst textInput = CardService.newTextInput()\n .setFieldName('text_input_form_input_key')\n .setTitle('Text input title')\n .setInputMode(CardService.TextInputMode.PLAIN_TEXT);\n```\n\nExample:\n```text\nconst action = CardService.newAction()\n .setFunctionName('suggestionCallback')\n .setParameters({numSuggestions: 3});\n\nCardService.newTextInput()\n .setFieldName('option-field')\n .setTitle('Option Selected')\n .setSuggestionsAction(action);\n\n// ...\n\nfunction suggestionCallback(e) {\n const suggestions = CardService.newSuggestions();\n const numSuggestions = Number.parseInt(e.parameter.numSuggestions);\n for (let i = 1; i <= numSuggestions; i++) {\n suggestions.addSuggestion(`Suggestion ${i}`);\n }\n return CardService.newSuggestionsResponseBuilder()\n .setSuggestions(suggestions)\n .build();\n}\n```\n\nExample:\n```text\nconst validation = CardService.newValidation().setCharacterLimit('10').setType(\n CardService.InputType.TEXT);\n\nconst input = CardService.newTextInput()\n .setFieldName('text_name_xxx1')\n .setTitle('Max 10 characters')\n .setValidation(validation);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.847Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":68,"estimatedTokens":502}}910{"id":"doc-class_cardsection_apps_script_google_for_develop-3d6b2794","source":"documentation","title":"Class CardSection | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/card-section","text":"Example:\n```text\nconst image = CardService.newImage();\n// Build image ...\nconst textParagraph = CardService.newTextParagraph();\n// Build text paragraph ...\n\nconst cardSection = CardService.newCardSection()\n .setHeader('Section header')\n .addWidget(image)\n .addWidget(textParagraph);\n```\n\nExample:\n```text\nconst collapseButton =\n CardService.newTextButton()\n .setTextButtonStyle(CardService.TextButtonStyle.BORDERLESS)\n .setText('show less');\n\nconst expandButton =\n CardService.newImageButton()\n .setImageButtonStyle(CardService.ImageButtonStyle.FILLED)\n .setMaterialIcon(CardService.newMaterialIcon().setName('bug_report'));\n\nconst collapsibleSection =\n CardService.newCardSection()\n .setCollapsible(true)\n .setNumUncollapsibleWidgets(1)\n .setCollapseControl(\n CardService.newCollapseControl()\n .setHorizontalAlign(CardService.HorizontalAlignment.CENTER)\n .setCollapseButton(collapseButton)\n .setExpandButton(expandButton),\n );\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.848Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":38,"estimatedTokens":284}}911{"id":"doc-class_overflowmenu_apps_script_google_for_develo-8eec4271","source":"documentation","title":"Class OverflowMenu | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/overflow-menu","text":"Example:\n```text\nconst overflowMenuItem = CardService.newOverflowMenuItem();\n// Finish building the overflow menu item...\n\nconst overflowMenu =\n CardService.newOverflowMenu().addMenuItem(overflowMenuItem);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.849Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":57}}912{"id":"doc-work_with_conferences_google_meet_google_for_dev-72e4ee32","source":"documentation","title":"Work with conferences | Google Meet | Google for Developers","url":"https://developers.google.com/meet/api/guides/conferences","text":"Example:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecord;\nimport com.google.apps.meet.v2.ConferenceRecordName;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.GetConferenceRecordRequest;\n\npublic class AsyncGetConferenceRecord {\n\n public static void main(String[] args) throws Exception {\n asyncGetConferenceRecord();\n }\n\n public static void asyncGetConferenceRecord() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n GetConferenceRecordRequest request =\n GetConferenceRecordRequest.newBuilder()\n .setName(ConferenceRecordName.of(\"[CONFERENCE_RECORD]\").toString())\n .build();\n ApiFuture<ConferenceRecord> future =\n conferenceRecordsServiceClient.getConferenceRecordCallable().futureCall(request);\n // Do something.\n ConferenceRecord response = future.get();\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Resource name of the conference.\n */\n// const name = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callGetConferenceRecord() {\n // Construct request\n const request = {\n name,\n };\n\n // Run request\n const response = await meetClient.getConferenceRecord(request);\n console.log(response);\n}\n\ncallGetConferenceRecord();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_get_conference_record():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.GetConferenceRecordRequest(\n name=\"name_value\",\n )\n\n # Make the request\n response = await client.get_conference_record(request=request)\n\n # Handle the response\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/CONFERENCE_RECORD_NAME\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecord;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.ListConferenceRecordsRequest;\n\npublic class AsyncListConferenceRecords {\n\n public static void main(String[] args) throws Exception {\n asyncListConferenceRecords();\n }\n\n public static void asyncListConferenceRecords() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n ListConferenceRecordsRequest request =\n ListConferenceRecordsRequest.newBuilder()\n .setPageSize(883849137)\n .setPageToken(\"pageToken873572522\")\n .setFilter(\"filter-1274492040\")\n .build();\n ApiFuture<ConferenceRecord> future =\n conferenceRecordsServiceClient.listConferenceRecordsPagedCallable().futureCall(request);\n // Do something.\n for (ConferenceRecord element : future.get().iterateAll()) {\n // doThingsWith(element);\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Optional. Maximum number of conference records to return. The service might\n * return fewer than this value. If unspecified, at most 25 conference records\n * are returned. The maximum value is 100; values above 100 are coerced to\n * 100. Maximum might change in the future.\n */\n// const pageSize = 1234\n/**\n * Optional. Page token returned from previous List Call.\n */\n// const pageToken = 'abc123'\n/**\n * Optional. User specified filtering condition in EBNF\n * format (https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form).\n * The following are the filterable fields:\n * * `space.meeting_code`\n * * `space.name`\n * * `start_time`\n * * `end_time`\n * For example, consider the following filters:\n * * `space.name = \"spaces/NAME\"`\n * * `space.meeting_code = \"abc-mnop-xyz\"`\n * * `start_time>=\"2024-01-01T00:00:00.000Z\" AND\n * start_time<=\"2024-01-02T00:00:00.000Z\"`\n * * `end_time IS NULL`\n */\n// const filter = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callListConferenceRecords() {\n // Construct request\n const request = {\n };\n\n // Run request\n const iterable = meetClient.listConferenceRecordsAsync(request);\n for await (const response of iterable) {\n console.log(response);\n }\n}\n\ncallListConferenceRecords();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_list_conference_records():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.ListConferenceRecordsRequest()\n\n # Make the request\n page_result = client.list_conference_records(request=request)\n\n # Handle the response\n async for response in page_result:\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.850Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":231,"estimatedTokens":1870}}913{"id":"doc-work_with_participants_google_meet_google_for_de-09977d18","source":"documentation","title":"Work with participants | Google Meet | Google for Developers","url":"https://developers.google.com/meet/api/guides/participants","text":"Example:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.GetParticipantRequest;\nimport com.google.apps.meet.v2.Participant;\nimport com.google.apps.meet.v2.ParticipantName;\n\npublic class AsyncGetParticipant {\n\n public static void main(String[] args) throws Exception {\n asyncGetParticipant();\n }\n\n public static void asyncGetParticipant() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n GetParticipantRequest request =\n GetParticipantRequest.newBuilder()\n .setName(ParticipantName.of(\"[CONFERENCE_RECORD]\", \"[PARTICIPANT]\").toString())\n .build();\n ApiFuture<Participant> future =\n conferenceRecordsServiceClient.getParticipantCallable().futureCall(request);\n // Do something.\n Participant response = future.get();\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Resource name of the participant.\n */\n// const name = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callGetParticipant() {\n // Construct request\n const request = {\n name,\n };\n\n // Run request\n const response = await meetClient.getParticipant(request);\n console.log(response);\n}\n\ncallGetParticipant();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_get_participant():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.GetParticipantRequest(\n name=\"name_value\",\n )\n\n # Make the request\n response = await client.get_participant(request=request)\n\n # Handle the response\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/CONFERENCE_RECORD_NAME/participants/PARTICIPANT_NAME\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordName;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.ListParticipantsRequest;\nimport com.google.apps.meet.v2.Participant;\n\npublic class AsyncListParticipants {\n\n public static void main(String[] args) throws Exception {\n asyncListParticipants();\n }\n\n public static void asyncListParticipants() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n ListParticipantsRequest request =\n ListParticipantsRequest.newBuilder()\n .setParent(ConferenceRecordName.of(\"[CONFERENCE_RECORD]\").toString())\n .setPageSize(883849137)\n .setPageToken(\"pageToken873572522\")\n .setFilter(\"filter-1274492040\")\n .build();\n ApiFuture<Participant> future =\n conferenceRecordsServiceClient.listParticipantsPagedCallable().futureCall(request);\n // Do something.\n for (Participant element : future.get().iterateAll()) {\n // doThingsWith(element);\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Format: `conferenceRecords/{conference_record}`\n */\n// const parent = 'abc123'\n/**\n * Maximum number of participants to return. The service might return fewer\n * than this value.\n * If unspecified, at most 100 participants are returned.\n * The maximum value is 250; values above 250 are coerced to 250.\n * Maximum might change in the future.\n */\n// const pageSize = 1234\n/**\n * Page token returned from previous List Call.\n */\n// const pageToken = 'abc123'\n/**\n * Optional. User specified filtering condition in EBNF\n * format (https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form).\n * The following are the filterable fields:\n * * `earliest_start_time`\n * * `latest_end_time`\n * For example, `latest_end_time IS NULL` returns active participants in\n * the conference.\n */\n// const filter = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callListParticipants() {\n // Construct request\n const request = {\n parent,\n };\n\n // Run request\n const iterable = meetClient.listParticipantsAsync(request);\n for await (const response of iterable) {\n console.log(response);\n }\n}\n\ncallListParticipants();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_list_participants():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.ListParticipantsRequest(\n parent=\"parent_value\",\n )\n\n # Make the request\n page_result = client.list_participants(request=request)\n\n # Handle the response\n async for response in page_result:\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/PARENT_NAME/participants\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\ncurl \\\n 'https://people.googleapis.com/v1/people/PERSON_ID?personFields=names%2CemailAddresses&sources=READ_SOURCE_TYPE_OTHER_CONTACT&sources=READ_SOURCE_TYPE_PROFILE&sources=READ_SOURCE_TYPE_CONTACT' \\\n --header 'Authorization: Bearer ACCESS_TOKEN' \\\n --header 'Accept: application/json' \\\n --compressed\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.GetParticipantSessionRequest;\nimport com.google.apps.meet.v2.ParticipantSession;\nimport com.google.apps.meet.v2.ParticipantSessionName;\n\npublic class AsyncGetParticipantSession {\n\n public static void main(String[] args) throws Exception {\n asyncGetParticipantSession();\n }\n\n public static void asyncGetParticipantSession() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n GetParticipantSessionRequest request =\n GetParticipantSessionRequest.newBuilder()\n .setName(\n ParticipantSessionName.of(\n \"[CONFERENCE_RECORD]\", \"[PARTICIPANT]\", \"[PARTICIPANT_SESSION]\")\n .toString())\n .build();\n ApiFuture<ParticipantSession> future =\n conferenceRecordsServiceClient.getParticipantSessionCallable().futureCall(request);\n // Do something.\n ParticipantSession response = future.get();\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Resource name of the participant.\n */\n// const name = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callGetParticipantSession() {\n // Construct request\n const request = {\n name,\n };\n\n // Run request\n const response = await meetClient.getParticipantSession(request);\n console.log(response);\n}\n\ncallGetParticipantSession();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_get_participant_session():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.GetParticipantSessionRequest(\n name=\"name_value\",\n )\n\n # Make the request\n response = await client.get_participant_session(request=request)\n\n # Handle the response\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/CONFERENCE_RECORD_NAME/participants/PARTICIPANT_NAME/participantSessions/PARTICIPANT_SESSION_ID\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.ListParticipantSessionsRequest;\nimport com.google.apps.meet.v2.ParticipantName;\nimport com.google.apps.meet.v2.ParticipantSession;\n\npublic class AsyncListParticipantSessions {\n\n public static void main(String[] args) throws Exception {\n asyncListParticipantSessions();\n }\n\n public static void asyncListParticipantSessions() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n ListParticipantSessionsRequest request =\n ListParticipantSessionsRequest.newBuilder()\n .setParent(ParticipantName.of(\"[CONFERENCE_RECORD]\", \"[PARTICIPANT]\").toString())\n .setPageSize(883849137)\n .setPageToken(\"pageToken873572522\")\n .setFilter(\"filter-1274492040\")\n .build();\n ApiFuture<ParticipantSession> future =\n conferenceRecordsServiceClient.listParticipantSessionsPagedCallable().futureCall(request);\n // Do something.\n for (ParticipantSession element : future.get().iterateAll()) {\n // doThingsWith(element);\n }\n }\n }\n}\n```\n\nExample:\n```text\n// Copyright 2026 Google LLC\n//\n// 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.\n//\n// ** This file is automatically generated by gapic-generator-typescript. **\n// ** https://github.com/googleapis/gapic-generator-typescript **\n// ** All changes to this file may be overwritten. **\n\n\n\n'use strict';\n\nfunction main(parent) {\n /**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n /**\n * Required. Format:\n * `conferenceRecords/{conference_record}/participants/{participant}`\n */\n // const parent = 'abc123'\n /**\n * Optional. Maximum number of participant sessions to return. The service\n * might return fewer than this value. If unspecified, at most 100\n * participants are returned. The maximum value is 250; values above 250 are\n * coerced to 250. Maximum might change in the future.\n */\n // const pageSize = 1234\n /**\n * Optional. Page token returned from previous List Call.\n */\n // const pageToken = 'abc123'\n /**\n * Optional. User specified filtering condition in EBNF\n * format (https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form).\n * The following are the filterable fields:\n * * `start_time`\n * * `end_time`\n * For example, `end_time IS NULL` returns active participant sessions in\n * the conference record.\n */\n // const filter = 'abc123'\n\n // Imports the Meet library\n const {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n // Instantiates a client\n const meetClient = new ConferenceRecordsServiceClient();\n\n async function callListParticipantSessions() {\n // Construct request\n const request = {\n parent,\n };\n\n // Run request\n const iterable = meetClient.listParticipantSessionsAsync(request);\n for await (const response of iterable) {\n console.log(response);\n }\n }\n\n callListParticipantSessions();\n}\n\nprocess.on('unhandledRejection', err => {\n console.error(err.message);\n process.exitCode = 1;\n});\nmain(...process.argv.slice(2));\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_list_participant_sessions():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.ListParticipantSessionsRequest(\n parent=\"parent_value\",\n )\n\n # Make the request\n page_result = client.list_participant_sessions(request=request)\n\n # Handle the response\n async for response in page_result:\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/CONFERENCE_RECORD_NAME/participants/PARENT_NAME/participantSessions\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.851Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":17,"totalLines":511,"estimatedTokens":4189}}914{"id":"doc-work_with_artifacts_google_meet_google_for_devel-2d65760d","source":"documentation","title":"Work with artifacts | Google Meet | Google for Developers","url":"https://developers.google.com/meet/api/guides/artifacts","text":"Example:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.GetRecordingRequest;\nimport com.google.apps.meet.v2.Recording;\nimport com.google.apps.meet.v2.RecordingName;\n\npublic class AsyncGetRecording {\n\n public static void main(String[] args) throws Exception {\n asyncGetRecording();\n }\n\n public static void asyncGetRecording() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n GetRecordingRequest request =\n GetRecordingRequest.newBuilder()\n .setName(RecordingName.of(\"[CONFERENCE_RECORD]\", \"[RECORDING]\").toString())\n .build();\n ApiFuture<Recording> future =\n conferenceRecordsServiceClient.getRecordingCallable().futureCall(request);\n // Do something.\n Recording response = future.get();\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Resource name of the recording.\n */\n// const name = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callGetRecording() {\n // Construct request\n const request = {\n name,\n };\n\n // Run request\n const response = await meetClient.getRecording(request);\n console.log(response);\n}\n\ncallGetRecording();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_get_recording():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.GetRecordingRequest(\n name=\"name_value\",\n )\n\n # Make the request\n response = await client.get_recording(request=request)\n\n # Handle the response\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/CONFERENCE_RECORD_NAME/recordings/RECORDING_NAME\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordName;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.ListRecordingsRequest;\nimport com.google.apps.meet.v2.Recording;\n\npublic class AsyncListRecordings {\n\n public static void main(String[] args) throws Exception {\n asyncListRecordings();\n }\n\n public static void asyncListRecordings() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n ListRecordingsRequest request =\n ListRecordingsRequest.newBuilder()\n .setParent(ConferenceRecordName.of(\"[CONFERENCE_RECORD]\").toString())\n .setPageSize(883849137)\n .setPageToken(\"pageToken873572522\")\n .build();\n ApiFuture<Recording> future =\n conferenceRecordsServiceClient.listRecordingsPagedCallable().futureCall(request);\n // Do something.\n for (Recording element : future.get().iterateAll()) {\n // doThingsWith(element);\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Format: `conferenceRecords/{conference_record}`\n */\n// const parent = 'abc123'\n/**\n * Maximum number of recordings to return. The service might return fewer\n * than this value.\n * If unspecified, at most 10 recordings are returned.\n * The maximum value is 100; values above 100 are coerced to 100.\n * Maximum might change in the future.\n */\n// const pageSize = 1234\n/**\n * Page token returned from previous List Call.\n */\n// const pageToken = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callListRecordings() {\n // Construct request\n const request = {\n parent,\n };\n\n // Run request\n const iterable = meetClient.listRecordingsAsync(request);\n for await (const response of iterable) {\n console.log(response);\n }\n}\n\ncallListRecordings();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_list_recordings():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.ListRecordingsRequest(\n parent=\"parent_value\",\n )\n\n # Make the request\n page_result = client.list_recordings(request=request)\n\n # Handle the response\n async for response in page_result:\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/PARENT_NAME/recordings\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.GetTranscriptRequest;\nimport com.google.apps.meet.v2.Transcript;\nimport com.google.apps.meet.v2.TranscriptName;\n\npublic class AsyncGetTranscript {\n\n public static void main(String[] args) throws Exception {\n asyncGetTranscript();\n }\n\n public static void asyncGetTranscript() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n GetTranscriptRequest request =\n GetTranscriptRequest.newBuilder()\n .setName(TranscriptName.of(\"[CONFERENCE_RECORD]\", \"[TRANSCRIPT]\").toString())\n .build();\n ApiFuture<Transcript> future =\n conferenceRecordsServiceClient.getTranscriptCallable().futureCall(request);\n // Do something.\n Transcript response = future.get();\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Resource name of the transcript.\n */\n// const name = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callGetTranscript() {\n // Construct request\n const request = {\n name,\n };\n\n // Run request\n const response = await meetClient.getTranscript(request);\n console.log(response);\n}\n\ncallGetTranscript();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_get_transcript():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.GetTranscriptRequest(\n name=\"name_value\",\n )\n\n # Make the request\n response = await client.get_transcript(request=request)\n\n # Handle the response\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/CONFERENCE_RECORD_NAME/transcripts/TRANSCRIPT_NAME\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordName;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.ListTranscriptsRequest;\nimport com.google.apps.meet.v2.Transcript;\n\npublic class AsyncListTranscripts {\n\n public static void main(String[] args) throws Exception {\n asyncListTranscripts();\n }\n\n public static void asyncListTranscripts() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n ListTranscriptsRequest request =\n ListTranscriptsRequest.newBuilder()\n .setParent(ConferenceRecordName.of(\"[CONFERENCE_RECORD]\").toString())\n .setPageSize(883849137)\n .setPageToken(\"pageToken873572522\")\n .build();\n ApiFuture<Transcript> future =\n conferenceRecordsServiceClient.listTranscriptsPagedCallable().futureCall(request);\n // Do something.\n for (Transcript element : future.get().iterateAll()) {\n // doThingsWith(element);\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Format: `conferenceRecords/{conference_record}`\n */\n// const parent = 'abc123'\n/**\n * Maximum number of transcripts to return. The service might return fewer\n * than this value.\n * If unspecified, at most 10 transcripts are returned.\n * The maximum value is 100; values above 100 are coerced to 100.\n * Maximum might change in the future.\n */\n// const pageSize = 1234\n/**\n * Page token returned from previous List Call.\n */\n// const pageToken = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callListTranscripts() {\n // Construct request\n const request = {\n parent,\n };\n\n // Run request\n const iterable = meetClient.listTranscriptsAsync(request);\n for await (const response of iterable) {\n console.log(response);\n }\n}\n\ncallListTranscripts();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_list_transcripts():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.ListTranscriptsRequest(\n parent=\"parent_value\",\n )\n\n # Make the request\n page_result = client.list_transcripts(request=request)\n\n # Handle the response\n async for response in page_result:\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/PARENT_NAME/transcripts\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.GetTranscriptEntryRequest;\nimport com.google.apps.meet.v2.TranscriptEntry;\nimport com.google.apps.meet.v2.TranscriptEntryName;\n\npublic class AsyncGetTranscriptEntry {\n\n public static void main(String[] args) throws Exception {\n asyncGetTranscriptEntry();\n }\n\n public static void asyncGetTranscriptEntry() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n GetTranscriptEntryRequest request =\n GetTranscriptEntryRequest.newBuilder()\n .setName(\n TranscriptEntryName.of(\"[CONFERENCE_RECORD]\", \"[TRANSCRIPT]\", \"[ENTRY]\")\n .toString())\n .build();\n ApiFuture<TranscriptEntry> future =\n conferenceRecordsServiceClient.getTranscriptEntryCallable().futureCall(request);\n // Do something.\n TranscriptEntry response = future.get();\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Resource name of the `TranscriptEntry`.\n */\n// const name = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callGetTranscriptEntry() {\n // Construct request\n const request = {\n name,\n };\n\n // Run request\n const response = await meetClient.getTranscriptEntry(request);\n console.log(response);\n}\n\ncallGetTranscriptEntry();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_get_transcript_entry():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.GetTranscriptEntryRequest(\n name=\"name_value\",\n )\n\n # Make the request\n response = await client.get_transcript_entry(request=request)\n\n # Handle the response\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/CONFERENCE_RECORD_NAME/transcripts/TRANSCRIPT_NAME/entries/TRANSCRIPT_ENTRY_NAME\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\nimport com.google.api.core.ApiFuture;\nimport com.google.apps.meet.v2.ConferenceRecordsServiceClient;\nimport com.google.apps.meet.v2.ListTranscriptEntriesRequest;\nimport com.google.apps.meet.v2.TranscriptEntry;\nimport com.google.apps.meet.v2.TranscriptName;\n\npublic class AsyncListTranscriptEntries {\n\n public static void main(String[] args) throws Exception {\n asyncListTranscriptEntries();\n }\n\n public static void asyncListTranscriptEntries() throws Exception {\n // This snippet has been automatically generated and should be regarded as a code template only.\n // It will require modifications to work:\n // - It may require correct/in-range values for request initialization.\n // - It may require specifying regional endpoints when creating the service client as shown in\n // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library\n try (ConferenceRecordsServiceClient conferenceRecordsServiceClient =\n ConferenceRecordsServiceClient.create()) {\n ListTranscriptEntriesRequest request =\n ListTranscriptEntriesRequest.newBuilder()\n .setParent(TranscriptName.of(\"[CONFERENCE_RECORD]\", \"[TRANSCRIPT]\").toString())\n .setPageSize(883849137)\n .setPageToken(\"pageToken873572522\")\n .build();\n ApiFuture<TranscriptEntry> future =\n conferenceRecordsServiceClient.listTranscriptEntriesPagedCallable().futureCall(request);\n // Do something.\n for (TranscriptEntry element : future.get().iterateAll()) {\n // doThingsWith(element);\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This snippet has been automatically generated and should be regarded as a code template only.\n * It will require modifications to work.\n * It may require correct/in-range values for request initialization.\n * TODO(developer): Uncomment these variables before running the sample.\n */\n/**\n * Required. Format:\n * `conferenceRecords/{conference_record}/transcripts/{transcript}`\n */\n// const parent = 'abc123'\n/**\n * Maximum number of entries to return. The service might return fewer than\n * this value.\n * If unspecified, at most 10 entries are returned.\n * The maximum value is 100; values above 100 are coerced to 100.\n * Maximum might change in the future.\n */\n// const pageSize = 1234\n/**\n * Page token returned from previous List Call.\n */\n// const pageToken = 'abc123'\n\n// Imports the Meet library\nconst {ConferenceRecordsServiceClient} = require('@google-apps/meet').v2;\n\n// Instantiates a client\nconst meetClient = new ConferenceRecordsServiceClient();\n\nasync function callListTranscriptEntries() {\n // Construct request\n const request = {\n parent,\n };\n\n // Run request\n const iterable = meetClient.listTranscriptEntriesAsync(request);\n for await (const response of iterable) {\n console.log(response);\n }\n}\n\ncallListTranscriptEntries();\n```\n\nExample:\n```text\n# This snippet has been automatically generated and should be regarded as a\n# code template only.\n# It will require modifications to work:\n# - It may require correct/in-range values for request initialization.\n# - It may require specifying regional endpoints when creating the service\n# client as shown in:\n# https://googleapis.dev/python/google-api-core/latest/client_options.html\nfrom google.apps import meet_v2\n\n\nasync def sample_list_transcript_entries():\n # Create a client\n client = meet_v2.ConferenceRecordsServiceAsyncClient()\n\n # Initialize request argument(s)\n request = meet_v2.ListTranscriptEntriesRequest(\n parent=\"parent_value\",\n )\n\n # Make the request\n page_result = client.list_transcript_entries(request=request)\n\n # Handle the response\n async for response in page_result:\n print(response)\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2/conferenceRecords/PARENT_NAME/transcripts/TRANSCRIPT_NAME/entries\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2beta/conferenceRecords/CONFERENCE_RECORD_NAME/smartNotes/SMART_NOTES_NAME\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\nExample:\n```text\ncurl -X GET \"https://meet.googleapis.com/v2beta/conferenceRecords/PARENT_NAME/smartNotes\" \\\n-H \"Authorization: Bearer ACCESS_TOKEN\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.854Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":26,"totalLines":685,"estimatedTokens":5399}}915{"id":"doc-list_members_in_a_space_google_chat_google_for_d-f61852d9","source":"documentation","title":"List members in a space | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/list-members","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.memberships.readonly',\n];\n\n// This sample shows how to list memberships with user credential\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here\n parent: 'spaces/SPACE_NAME',\n // Filter membership by type (HUMAN or BOT) or role (ROLE_MEMBER or\n // ROLE_MANAGER)\n filter: 'member.type = \"HUMAN\"',\n };\n\n // Make the request\n const pageResult = chatClient.listMembershipsAsync(request);\n\n // Handle the response. Iterating over pageResult will yield results and\n // resolve additional pages automatically.\n for await (const response of pageResult) {\n console.log(response);\n }\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_user_credentials\nfrom google.apps import chat_v1 as google_chat\n\nSCOPES = [\"https://www.googleapis.com/auth/chat.memberships.readonly\"]\n\n# This sample shows how to list memberships with user credential\ndef list_memberships_user_cred():\n # Create a client\n client = create_client_with_user_credentials(SCOPES)\n\n # Initialize request argument(s)\n request = google_chat.ListMembershipsRequest(\n # Replace SPACE_NAME here\n parent = 'spaces/SPACE_NAME',\n # Filter membership by type (HUMAN or BOT) or role (ROLE_MEMBER or\n # ROLE_MANAGER)\n filter = 'member.type = \"HUMAN\"',\n # Number of results that will be returned at once\n page_size = 100\n )\n\n # Make the request\n page_result = client.list_memberships(request)\n\n # Handle the response. Iterating over page_result will yield results and\n # resolve additional pages automatically.\n for response in page_result:\n print(response)\n\nlist_memberships_user_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.ListMembershipsRequest;\nimport com.google.chat.v1.ListMembershipsResponse;\nimport com.google.chat.v1.Membership;\n\n// This sample shows how to list memberships with user credential.\npublic class ListMembershipsUserCred {\n\n private static final String SCOPE =\n \"https://www.googleapis.com/auth/chat.memberships.readonly\";\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithUserCredentials(\n ImmutableList.of(SCOPE))) {\n ListMembershipsRequest.Builder request = ListMembershipsRequest.newBuilder()\n // Replace SPACE_NAME here.\n .setParent(\"spaces/SPACE_NAME\")\n // Filter membership by type (HUMAN or BOT) or role\n // (ROLE_MEMBER or ROLE_MANAGER).\n .setFilter(\"member.type = \\\"HUMAN\\\"\")\n // Number of results that will be returned at once.\n .setPageSize(10);\n\n // Iterating over results and resolve additional pages automatically.\n for (Membership response :\n chatServiceClient.listMemberships(request.build()).iterateAll()) {\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to list memberships with user credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.memberships.readonly'\n * referenced in the manifest file (appsscript.json).\n */\nfunction listMembershipsUserCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here\n const parent = \"spaces/SPACE_NAME\";\n // Filter membership by type (HUMAN or BOT) or role (ROLE_MEMBER or\n // ROLE_MANAGER)\n const filter = 'member.type = \"HUMAN\"';\n\n // Iterate through the response pages using page tokens\n let responsePage;\n let pageToken = null;\n do {\n // Request response pages\n responsePage = Chat.Spaces.Members.list(parent, {\n filter: filter,\n pageSize: 10,\n pageToken: pageToken,\n });\n // Handle response pages\n if (responsePage.memberships) {\n for (const membership of responsePage.memberships) {\n console.log(membership);\n }\n }\n // Update the page token to the next one\n pageToken = responsePage.nextPageToken;\n } while (pageToken);\n}\n```\n\nExample:\n```text\nimport {createClientWithAppCredentials} from './authentication-utils.js';\n\n// This sample shows how to list memberships with app credential\nasync function main() {\n // Create a client\n const chatClient = createClientWithAppCredentials();\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here\n parent: 'spaces/SPACE_NAME',\n // Filter membership by type (HUMAN or BOT) or role (ROLE_MEMBER or\n // ROLE_MANAGER)\n filter: 'member.type = \"HUMAN\"',\n };\n\n // Make the request\n const pageResult = chatClient.listMembershipsAsync(request);\n\n // Handle the response. Iterating over pageResult will yield results and\n // resolve additional pages automatically.\n for await (const response of pageResult) {\n console.log(response);\n }\n}\n\nawait main();\n```\n\nExample:\n```text\nfrom authentication_utils import create_client_with_app_credentials\nfrom google.apps import chat_v1 as google_chat\n\n# This sample shows how to list memberships with app credential\ndef list_memberships_app_cred():\n # Create a client\n client = create_client_with_app_credentials()\n\n # Initialize request argument(s)\n request = google_chat.ListMembershipsRequest(\n # Replace SPACE_NAME here\n parent = 'spaces/SPACE_NAME',\n # Filter membership by type (HUMAN or BOT) or role (ROLE_MEMBER or\n # ROLE_MANAGER)\n filter = 'member.type = \"HUMAN\"',\n # Number of results that will be returned at once\n page_size = 100\n )\n\n # Make the request\n page_result = client.list_memberships(request)\n\n # Handle the response. Iterating over page_result will yield results and\n # resolve additional pages automatically.\n for response in page_result:\n print(response)\n\nlist_memberships_app_cred()\n```\n\nExample:\n```text\nimport com.google.chat.v1.ChatServiceClient;\nimport com.google.chat.v1.ListMembershipsRequest;\nimport com.google.chat.v1.ListMembershipsResponse;\nimport com.google.chat.v1.Membership;\n\n// This sample shows how to list memberships with app credential.\npublic class ListMembershipsAppCred {\n\n public static void main(String[] args) throws Exception {\n try (ChatServiceClient chatServiceClient =\n AuthenticationUtils.createClientWithAppCredentials()) {\n ListMembershipsRequest.Builder request = ListMembershipsRequest.newBuilder()\n // Replace SPACE_NAME here.\n .setParent(\"spaces/SPACE_NAME\")\n // Filter membership by type (HUMAN or BOT) or role\n // (ROLE_MEMBER or ROLE_MANAGER).\n .setFilter(\"member.type = \\\"HUMAN\\\"\")\n // Number of results that will be returned at once.\n .setPageSize(10);\n\n // Iterate over results and resolve additional pages automatically.\n for (Membership response :\n chatServiceClient.listMemberships(request.build()).iterateAll()) {\n System.out.println(JsonFormat.printer().print(response));\n }\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * This sample shows how to list memberships with app credential\n *\n * It relies on the OAuth2 scope 'https://www.googleapis.com/auth/chat.bot'\n * used by service accounts.\n */\nfunction listMembershipsAppCred() {\n // Initialize request argument(s)\n // TODO(developer): Replace SPACE_NAME here\n const parent = \"spaces/SPACE_NAME\";\n // Filter membership by type (HUMAN or BOT) or role (ROLE_MEMBER or\n // ROLE_MANAGER)\n const filter = 'member.type = \"HUMAN\"';\n\n // Iterate through the response pages using page tokens\n let responsePage;\n let pageToken = null;\n do {\n // Request response pages\n responsePage = Chat.Spaces.Members.list(\n parent,\n {\n filter: filter,\n pageSize: 10,\n pageToken: pageToken,\n },\n getHeaderWithAppCredentials(),\n );\n // Handle response pages\n if (responsePage.memberships) {\n for (const membership of responsePage.memberships) {\n console.log(membership);\n }\n }\n // Update the page token to the next one\n pageToken = responsePage.nextPageToken;\n } while (pageToken);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.855Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":283,"estimatedTokens":2118}}916{"id":"doc-class_spreadsheet_apps_script_google_for_develop-565e51da","source":"documentation","title":"Class Spreadsheet | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet","text":"Example:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Adds the key 'NAME' in the developer metadata for the spreadsheet.\nss.addDeveloperMetadata('NAME');\n\n// Gets the first developer metadata object and logs its key.\nconst developerMetaData = ss.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Adds the key 'NAME' in the developer metadata for the spreadsheet and sets\n// the visibility to the developer project that created the metadata.\nss.addDeveloperMetadata(\n 'NAME',\n SpreadsheetApp.DeveloperMetadataVisibility.PROJECT,\n);\n\n// Gets the first developer metadata object and logs its key and visibility\n// setting.\nconst developerMetaData = ss.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(`Key: ${developerMetaData.getKey()},\n. Visibility: ${developerMetaData.getVisibility()}`);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Adds the key 'NAME' and sets the value to 'GOOGLE' in the developer metadata\n// for the spreadsheet.\nss.addDeveloperMetadata('NAME', 'GOOGLE');\n\n// Gets the first developer metadata object and logs its key and value.\nconst developerMetaData = ss.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(\n `Key: ${developerMetaData.getKey()}, Value: ${\n developerMetaData.getValue()}`,\n);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Adds the key 'NAME', sets the value to 'GOOGLE', and sets the visibility\n// to any developer project with document access.\nss.addDeveloperMetadata(\n 'NAME',\n 'GOOGLE',\n SpreadsheetApp.DeveloperMetadataVisibility.DOCUMENT,\n);\n\n// Gets the first developer metadata object and logs its key, value, and\n// visibility setting.\nconst developerMetaData = ss.getDeveloperMetadata()[0];\nconsole.log(`Key: ${developerMetaData.getKey()},\n Value: ${developerMetaData.getValue()},\n Visibility: ${developerMetaData.getVisibility()}`);\n```\n\nExample:\n```text\n// The onOpen function is executed automatically every time a Spreadsheet is\n// loaded\nfunction onOpen() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const menuEntries = [];\n // When the user clicks on \"addMenuExample\" then \"Menu Entry 1\", the function\n // function1 is executed.\n menuEntries.push({name: 'Menu Entry 1', functionName: 'function1'});\n menuEntries.push(null); // line separator\n menuEntries.push({name: 'Menu Entry 2', functionName: 'function2'});\n\n ss.addMenu('addMenuExample', menuEntries);\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Appends a new row with 3 columns to the bottom of the current\n// data region in the sheet containing the values in the array.\nsheet.appendRow(['a man', 'a plan', 'panama']);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nsheet.getRange('a1').setValue(\n 'Whenever it is a damp, drizzly November in my soul...');\n\n// Sets the first column to a width which fits the text\nsheet.autoResizeColumn(1);\n```\n\nExample:\n```text\n// This code makes a copy of the current spreadsheet and names it appropriately\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nss.copy(`Copy of ${ss.getName()}`);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Adds developer metadata to the spreadsheet.\nss.addDeveloperMetadata('NAME', 'CHARLIE');\nss.addDeveloperMetadata('COMPANY', 'EXAMPLE ORGANIZATION');\nss.addDeveloperMetadata('TECHNOLOGY', 'JAVASCRIPT');\n\n// Creates a developer metadata finder.\nconst developerMetadataFinder = ss.createDeveloperMetadataFinder();\n\n// Finds the developer metadata objects with 'COMPANY' as the key.\nconst googleMetadataFromSpreadsheet =\n developerMetadataFinder.withKey('COMPANY').find();\n\n// Gets the first result of developer metadata that has the key 'COMPANY' and\n// logs its value.\nconsole.log(googleMetadataFromSpreadsheet[0].getValue());\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\n\n// Creates a text finder.\nconst textFinder = spreadsheet.createTextFinder('dog');\n\n// Returns the first occurrence of 'dog' in the spreadsheet.\nconst firstOccurrence = textFinder.findNext();\n\n// Replaces the last found occurrence of 'dog' with 'cat' and returns the number\n// of occurrences replaced.\nconst numOccurrencesReplaced = textFinder.replaceWith('cat');\n```\n\nExample:\n```text\n// The code below deletes the currently active sheet and stores the new active\n// sheet in a variable\nconst newSheet = SpreadsheetApp.getActiveSpreadsheet().deleteActiveSheet();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Columns start at \"1\" - this deletes the first column\nsheet.deleteColumn(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Columns start at \"1\" - this deletes the first two columns\nsheet.deleteColumns(1, 2);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Rows start at \"1\" - this deletes the first row\nsheet.deleteRow(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Rows start at \"1\" - this deletes the first two rows\nsheet.deleteRows(1, 2);\n```\n\nExample:\n```text\n// The code below deletes the specified sheet.\nconst ss = SpreadsheetApp.getActive();\nconst sheet = ss.getSheetByName('My Sheet');\nss.deleteSheet(sheet);\n```\n\nExample:\n```text\n// The code below makes a duplicate of the active sheet\nSpreadsheetApp.getActiveSpreadsheet().duplicateActiveSheet();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Returns the active cell\nconst cell = sheet.getActiveCell();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\nconst activeRange = sheet.getActiveRange();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\n// Returns the list of active ranges.\nconst activeRangeList = sheet.getActiveRangeList();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets an array of the bandings in the spreadsheet.\nconst bandings = ss.getBandings();\n\n// Logs the range of the first banding in the spreadsheet to the console.\nconsole.log(bandings[0].getRange().getA1Notation());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Columns start at 1\nLogger.log(sheet.getColumnWidth(1));\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\n// Returns the current highlighted cell in the one of the active ranges.\nconst currentCell = sheet.getCurrentCell();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This represents ALL the data\nconst range = sheet.getDataRange();\nconst values = range.getValues();\n\n// This logs the spreadsheet in CSV format with a trailing comma\nfor (let i = 0; i < values.length; i++) {\n let row = '';\n for (let j = 0; j < values[i].length; j++) {\n if (values[i][j]) {\n row = row + values[i][j];\n }\n row = `${row},`;\n }\n Logger.log(row);\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet by its ID. If you created your script from within a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets an array of the data source formulas on Sheet1.\n// To get an array of data source formulas for the entire spreadsheet,\n// replace 'sheet' with 'ss'.\nconst dataSourceFormulas = sheet.getDataSourceFormulas();\n\n// Logs the first data source formula in the array.\nconsole.log(dataSourceFormulas[0].getFormula());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets an array of the data source pivot tables on Sheet1.\n// To get an array of data source pivot tables for the entire\n// spreadsheet, replace 'sheet' with 'ss'.\nconst dataSourcePivotTables = sheet.getDataSourcePivotTables();\n\n// Logs the last time that the first pivot table in the array was refreshed.\nconsole.log(dataSourcePivotTables[0].getStatus().getLastRefreshedTime());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Activates BigQuery operations for the connected spreadsheet.\nSpreadsheetApp.enableBigQueryExecution();\n\n// Gets the frequency type of the first referesh schedule in the array.\nconst frequencyType = ss.getDataSourceRefreshSchedules()[0]\n .getFrequency()\n .getFrequencyType()\n .toString();\n\n// Logs the frequency type to the console.\nconsole.log(frequencyType);\n```\n\nExample:\n```text\n// Turns data execution on for BigQuery data sources.\nSpreadsheetApp.enableBigQueryExecution();\n\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets the first data source sheet in the spreadsheet.\nconst dataSource = ss.getDataSourceSheets()[0];\n\n// Gets the name of the data source sheet.\nconsole.log(dataSource.asSheet().getName());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets an array of data source tables on Sheet1.\n// To get an array of data source tables for the entire spreadsheet,\n// replace 'sheet' with 'ss'.\nconst dataSourceTables = sheet.getDataSourceTables();\n\n// Logs the last completed data execution time on the first data source table.\nconsole.log(dataSourceTables[0].getStatus().getLastExecutionTime());\n```\n\nExample:\n```text\n// Turns data execution on for BigQuery data sources.\nSpreadsheetApp.enableBigQueryExecution();\n\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets the data sources on the spreadsheet.\nconst dataSources = ss.getDataSources();\n\n// Logs the name of the first column on the first data source.\nconsole.log(dataSources[0].getColumns()[0].getName());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Adds 'Google' as a key to the spreadsheet metadata.\nss.addDeveloperMetadata('Google');\n\n// Gets the spreadsheet's metadata.\nconst ssMetadata = ss.getDeveloperMetadata();\n\n// Gets the first set of the spreadsheet's metadata and logs the key to the\n// console.\nconsole.log(ssMetadata[0].getKey());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets the form URL from the spreadsheet.\nconst formUrl = ss.getFormUrl();\n\n// Logs the form URL to the console.\nconsole.log(formUrl);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nLogger.log('Number of frozen columns: %s', sheet.getFrozenColumns());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nLogger.log('Number of frozen rows: %s', sheet.getFrozenRows());\n```\n\nExample:\n```text\n// The code below logs the ID for the active spreadsheet.\nLogger.log(SpreadsheetApp.getActiveSpreadsheet().getId());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the over-the-grid images from Sheet1.\n// To get the over-the-grid images from the entire spreadsheet, use\n// ss.getImages() instead.\nconst images = sheet.getImages();\n\n// For each image, logs the anchor cell in A1 notation.\nfor (const image of images) {\n console.log(image.getAnchorCell().getA1Notation());\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Sets the iterative calculation convergence threshold for the spreadsheet.\nss.setIterativeCalculationConvergenceThreshold(2);\n\n// Logs the threshold to the console.\nconsole.log(ss.getIterativeCalculationConvergenceThreshold());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This logs the value in the very last cell of this sheet\nconst lastRow = sheet.getLastRow();\nconst lastColumn = sheet.getLastColumn();\nconst lastCell = sheet.getRange(lastRow, lastColumn);\nLogger.log(lastCell.getValue());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Sets the max iterative calculation cycles for the spreadsheet.\nss.setMaxIterativeCalculationCycles(10);\n\n// Logs the max iterative calculation cycles to the console.\nconsole.log(ss.getMaxIterativeCalculationCycles());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nLogger.log(ss.getName());\n```\n\nExample:\n```text\n// The code below logs the name of the first named range.\nconst namedRanges = SpreadsheetApp.getActiveSpreadsheet().getNamedRanges();\nfor (let i = 0; i < namedRanges.length; i++) {\n Logger.log(namedRanges[i].getName());\n}\n```\n\nExample:\n```text\n// The code below logs the number of sheets in the active spreadsheet.\nLogger.log(SpreadsheetApp.getActiveSpreadsheet().getNumSheets());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst owner = ss.getOwner();\nLogger.log(owner.getEmail());\n```\n\nExample:\n```text\n// The code below returns the list of predefined themes.\nconst predefinedThemesList =\n SpreadsheetApp.getActiveSpreadsheet().getPredefinedSpreadsheetThemes();\n```\n\nExample:\n```text\n// Remove all range protections in the spreadsheet that the user has permission\n// to edit.\nconst ss = SpreadsheetApp.getActive();\nconst protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);\nfor (let i = 0; i < protections.length; i++) {\n const protection = protections[i];\n if (protection.canEdit()) {\n protection.remove();\n }\n}\n```\n\nExample:\n```text\n// Remove all sheet protections in the spreadsheet that the user has permission\n// to edit.\nconst ss = SpreadsheetApp.getActive();\nconst protections = ss.getProtections(SpreadsheetApp.ProtectionType.SHEET);\nfor (let i = 0; i < protections.length; i++) {\n const protection = protections[i];\n if (protection.canEdit()) {\n protection.remove();\n }\n}\n```\n\nExample:\n```text\n// Get a range A1:D4 on sheet titled \"Invoices\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst range = ss.getRange('Invoices!A1:D4');\n\n// Get cell A1 on the first sheet\nconst sheet = ss.getSheets()[0];\nconst cell = sheet.getRange('A1');\n```\n\nExample:\n```text\n// Log the number of columns for the range named 'TaxRates' in the active\n// spreadsheet.\nconst range = SpreadsheetApp.getActiveSpreadsheet().getRangeByName('TaxRates');\nif (range != null) {\n Logger.log(range.getNumColumns());\n}\n```\n\nExample:\n```text\n// Get a list of ranges A1:D4, F1:H4.\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\nconst rangeList = sheet.getRangeList(['A1:D4', 'F1:H4']);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Logs the calculation interval for the spreadsheet to the console.\nconsole.log(ss.getRecalculationInterval().toString());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Rows start at 1\nLogger.log(sheet.getRowHeight(1));\n```\n\nExample:\n```text\nconst selection = SpreadsheetApp.getActiveSpreadsheet().getSelection();\nconst currentCell = selection.getCurrentCell();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetById(12345);\n```\n\nExample:\n```text\n// The code below logs the index of a sheet named \"Expenses\"\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Expenses');\nif (sheet != null) {\n Logger.log(sheet.getIndex());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nLogger.log(sheet.getSheetId());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nLogger.log(sheet.getSheetName());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The two samples below produce the same output\nlet values = sheet.getSheetValues(1, 1, 3, 3);\nLogger.log(values);\n\nconst range = sheet.getRange(1, 1, 3, 3);\nvalues = range.getValues();\nLogger.log(values);\n```\n\nExample:\n```text\n// The code below logs the name of the second sheet\nconst sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();\n// Iterates through the sheets and logs the name and ID of each sheet.\nfor (const sheet of sheets) {\n Logger.log(`name: ${sheet.getName()}, ID: ${sheet.getSheetId()}`);\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets the spreadsheet locale.\nconst ssLocale = ss.getSpreadsheetLocale();\n\n// Logs the locale to the console.\nconsole.log(ssLocale);\n```\n\nExample:\n```text\n// The code below returns the current theme of the spreadsheet.\nconst currentTheme =\n SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTheme();\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Sets the time zone of the spreadsheet.\nss.setSpreadsheetTimeZone('America/New_York');\n\n// Gets the time zone of the spreadsheet.\nconst ssTimeZone = ss.getSpreadsheetTimeZone();\n\n// Logs the time zone to the console.\nconsole.log(ssTimeZone);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nLogger.log(ss.getUrl());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This hides the first column\nlet range = sheet.getRange('A1');\nsheet.hideColumn(range);\n\n// This hides the first 3 columns\nrange = sheet.getRange('A:C');\nsheet.hideColumn(range);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This hides the first row\nconst range = sheet.getRange('A1');\nsheet.hideRow(range);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts a column after the first column position\nsheet.insertColumnAfter(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts a column in the first column position\nsheet.insertColumnBefore(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Inserts two columns after the first column on the first sheet of the\n// spreadsheet.\nsheet.insertColumnsAfter(1, 2);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts five columns before the first column\nsheet.insertColumnsBefore(1, 5);\n```\n\nExample:\n```text\n// Activates BigQuery operations.\nSpreadsheetApp.enableBigQueryExecution();\n\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Builds a data source specification.\n// TODO (developer): Update the project ID to your own Google Cloud project ID.\nconst dataSourceSpec = SpreadsheetApp.newDataSourceSpec()\n .asBigQuery()\n .setProjectId('project-id-1')\n .setTableProjectId('bigquery-public-data')\n .setDatasetId('ncaa_basketball')\n .setTableId('mbb_historical_teams_games')\n .build();\n\n// Adds the data source and its data to the spreadsheet.\nss.insertDataSourceSheet(dataSourceSpec);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst binaryData = []; // TODO(developer): Replace with your binary data.\nconst blob = Utilities.newBlob(binaryData, 'image/png', 'MyImageName');\nsheet.insertImage(blob, 1, 1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst binaryData = []; // TODO(developer): Replace with your binary data.\nconst blob = Utilities.newBlob(binaryData, 'image/png', 'MyImageName');\nsheet.insertImage(blob, 1, 1, 10, 10);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nsheet.insertImage('https://www.google.com/images/srpr/logo3w.png', 1, 1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nsheet.insertImage(\n 'https://www.google.com/images/srpr/logo3w.png',\n 1,\n 1,\n 10,\n 10,\n);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts a row after the first row position\nsheet.insertRowAfter(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts a row before the first row position\nsheet.insertRowBefore(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts five rows after the first row\nsheet.insertRowsAfter(1, 5);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts five rows before the first row\nsheet.insertRowsBefore(1, 5);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nss.insertSheet();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nss.insertSheet(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst templateSheet = ss.getSheetByName('Sales');\nss.insertSheet(1, {template: templateSheet});\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst templateSheet = ss.getSheetByName('Sales');\nss.insertSheet({template: templateSheet});\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nss.insertSheet('My New Sheet');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nss.insertSheet('My New Sheet', 1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst templateSheet = ss.getSheetByName('Sales');\nss.insertSheet('My New Sheet', 1, {template: templateSheet});\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst templateSheet = ss.getSheetByName('Sales');\nss.insertSheet('My New Sheet', {template: templateSheet});\n```\n\nExample:\n```text\n// Activates BigQuery operations.\nSpreadsheetApp.enableBigQueryExecution();\n\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Adds a sheet and sets cell A1 as the parameter cell.\nconst parameterCell = ss.insertSheet('parameterSheet').getRange('A1');\n\n// Sets the value of the parameter cell to 'Duke'.\nparameterCell.setValue('Duke');\n\nconst query = 'select * from `bigquery-public-data`.`ncaa_basketball`.' +\n '`mbb_historical_tournament_games` WHERE win_school_ncaa = @SCHOOL';\n\n// Adds a data source with a query parameter.\n// TODO(developer): Update the project ID to your own Google Cloud project ID.\nconst dataSourceSpec = SpreadsheetApp.newDataSourceSpec()\n .asBigQuery()\n .setProjectId('project-id-1')\n .setRawQuery(query)\n .setParameterFromCell('SCHOOL', 'parameterSheet!A1')\n .build();\n\n// Adds sheets for the data source and data source table to the spreadsheet.\nss.insertSheetWithDataSourceTable(dataSourceSpec);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Columns start at 1\nLogger.log(sheet.isColumnHiddenByUser(1));\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Activates iterative calculation on the spreadsheet.\nss.setIterativeCalculationEnabled(true);\n\n// Logs whether iterative calculation is activated for the spreadsheet.\nconsole.log(ss.isIterativeCalculationEnabled());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Rows start at 1\nLogger.log(sheet.isRowHiddenByFilter(1));\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Rows start at 1\nLogger.log(sheet.isRowHiddenByUser(1));\n```\n\nExample:\n```text\n// This example assumes that there are 2 sheets in the current\n// active spreadsheet: one named \"first\" in position 1 and another named\n// \"second\" in position 2.\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\n// Gets the \"first\" sheet and activates it.\nconst sheet = spreadsheet.getSheetByName('first').activate();\n\n// Logs 'Current index of sheet: 1'\nconsole.log('Current index of sheet: %s', sheet.getIndex());\n\nspreadsheet.moveActiveSheet(2);\n\n// Logs 'New index of sheet: 2'\nconsole.log('New index of sheet: %s', sheet.getIndex());\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst chart = sheet.newChart().setPosition(1, 1, 0, 0).build();\nsheet.insertChart(chart);\nconst objectSheet = SpreadsheetApp.getActive().moveChartToObjectSheet(chart);\n```\n\nExample:\n```text\n// Activates BigQuery operations.\nSpreadsheetApp.enableBigQueryExecution();\n\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets the first data source sheet on the spreadsheet.\nconst dataSheet = ss.getDataSourceSheets()[0];\n\n// Refreshes all data sources on the spreadsheet.\nss.refreshAllDataSources();\n\n// Logs the last refreshed time of the first data source sheet.\nconsole.log(\n `Last refresh time: ${dataSheet.getStatus().getLastRefreshedTime()}`,\n);\n```\n\nExample:\n```text\n// The onOpen function is executed automatically every time a Spreadsheet is\n// loaded\nfunction onOpen() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n ss.addMenu('badMenu', [\n {name: 'remove bad menu', functionName: 'removeBadMenu'},\n {name: 'foo', functionName: 'foo'},\n ]);\n}\nfunction removeBadMenu() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n ss.removeMenu(\n 'badMenu'); // name must match the name used when added the menu\n}\nfunction foo() {\n // Do nothing\n}\n```\n\nExample:\n```text\n// The code below creates a new named range \"foo\", and then remove it.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nss.setNamedRange('foo', ss.getActiveRange());\nss.removeNamedRange('foo');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nss.rename('This is the new name');\n```\n\nExample:\n```text\n// The code below renames the active sheet to \"Hello world\"\nSpreadsheetApp.getActiveSpreadsheet().renameActiveSheet('Hello world');\n```\n\nExample:\n```text\n// The code below applies default theme on the spreadsheet.\nSpreadsheetApp.getActiveSpreadsheet().resetSpreadsheetTheme();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\nconst range = sheet.getRange('A1:D4');\nsheet.setActiveRange(range);\n\nconst selection = sheet.getSelection();\n// Current cell: A1\nconst currentCell = selection.getCurrentCell();\n// Active Range: A1:D4\nconst activeRange = selection.getActiveRange();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['D4', 'B2:C4']);\nsheet.setActiveRangeList(rangeList);\n\nconst selection = sheet.getSelection();\n// Current cell: B2\nconst currentCell = selection.getCurrentCell();\n// Active range: B2:C4\nconst activeRange = selection.getActiveRange();\n// Active range list: [D4, B2:C4]\nconst activeRangeList = selection.getActiveRangeList();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D4');\nsheet.setActiveSelection(range);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nsheet.setActiveSelection('A1:D4');\n```\n\nExample:\n```text\n// The code below makes the first sheet active in the active spreadsheet.\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nspreadsheet.setActiveSheet(spreadsheet.getSheets()[0]);\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nconst firstSheet = spreadsheet.getSheets()[0];\nconst secondSheet = spreadsheet.getSheets()[1];\n// Set the first sheet as the active sheet and select the range D4:F4.\nspreadsheet.setActiveSheet(firstSheet).getRange('D4:F4').activate();\n\n// Switch to the second sheet to do some work.\nspreadsheet.setActiveSheet(secondSheet);\n// Switch back to first sheet, and restore its selection.\nspreadsheet.setActiveSheet(firstSheet, true);\n\n// The selection of first sheet is restored, and it logs D4:F4\nconst range = spreadsheet.getActiveSheet().getSelection().getActiveRange();\nLogger.log(range.getA1Notation());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sets the first column to a width of 200 pixels\nsheet.setColumnWidth(1, 200);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\nconst cell = sheet.getRange('B5');\nsheet.setCurrentCell(cell);\n\nconst selection = sheet.getSelection();\n// Current cell: B5\nconst currentCell = selection.getCurrentCell();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Freezes the first column\nsheet.setFrozenColumns(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Freezes the first row\nsheet.setFrozenRows(1);\n```\n\nExample:\n```text\n// The code below creates a new named range \"TaxRates\" in the active spreadsheet\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nss.setNamedRange('TaxRates', SpreadsheetApp.getActiveRange());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Sets the calculation interval for the spreadsheet to 'ON_CHANGE'.\nconst interval = ss.setRecalculationInterval(\n SpreadsheetApp.RecalculationInterval.ON_CHANGE,\n);\n\n// Logs the calculation interval to the console.\nconsole.log(interval);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sets the first row to a height of 200 pixels\nsheet.setRowHeight(1, 200);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Sets the spreadsheet locale.\nss.setSpreadsheetLocale('fr');\n\n// Gets the spreadsheet locale.\nconst ssLocale = ss.getSpreadsheetLocale();\n\n// Logs the locale to the console.\nconsole.log(ssLocale);\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\n// The code below sets the second predefined theme as the current theme of the\n// spreadsheet.\nconst predefinedThemesList = spreadsheet.getPredefinedSpreadsheetThemes();\nspreadsheet.setSpreadsheetTheme(predefinedThemesList[1]);\n```\n\nExample:\n```text\nconst htmlApp = HtmlService\n .createHtmlOutput(\n '<p>A change of speed, a change of style...</p>',\n )\n .setTitle('My HtmlService Application')\n .setWidth(250)\n .setHeight(300);\n\nSpreadsheetApp.getActiveSpreadsheet().show(htmlApp);\n\n// The script resumes execution immediately after showing the dialog.\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sorts the sheet by the first column, ascending\nsheet.sort(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sorts the sheet by the first column, descending\nsheet.sort(1, false);\n```\n\nExample:\n```text\n// Show a popup with the message \"Task started\".\nSpreadsheetApp.getActiveSpreadsheet().toast('Task started');\n```\n\nExample:\n```text\n// Show a popup with the title \"Status\" and the message \"Task started\".\nSpreadsheetApp.getActiveSpreadsheet().toast('Task started', 'Status');\n```\n\nExample:\n```text\n// Show a 3-second popup with the title \"Status\" and the message \"Task started\".\nSpreadsheetApp.getActiveSpreadsheet().toast('Task started', 'Status', 3);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This unhides the first column if it was previously hidden\nconst range = sheet.getRange('A1');\nsheet.unhideColumn(range);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This unhides the first row if it was previously hidden\nconst range = sheet.getRange('A1');\nsheet.unhideRow(range);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst menuEntries = [];\nmenuEntries.push({name: 'Lone Menu Entry', functionName: 'function1'});\nss.updateMenu('addMenuExample', menuEntries);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst permissions = sheet.getSheetProtection();\n\npermissions.setProtected(true);\npermissions.addUser('user@example.com');\n\n// Logs the users that have access to edit this sheet. Note that this\n// is different from access to the entire spreadsheet - getUsers()\n// only returns users if permissions.isProtected() is set to true.\nconst users = permissions.getUsers();\nLogger.log(users);\n```\n\nExample:\n```text\n// Determine if the document allows anonymous viewing via the Drive API.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst file = DriveApp.getFileById(ss.getId());\nconst access = file.getSharingAccess();\nconst permission = file.getSharingPermission();\nconst isAnonymousAccess = access === DriveApp.Access.ANYONE ||\n access === DriveApp.Access.ANYONE_WITH_LINK;\nconst isAnonymousEdit =\n isAnonymousAccess && permission !== DriveApp.Permission.NONE;\n```\n\nExample:\n```text\n// Determine if the document allow anonymous edits via the Drive API.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst file = DriveApp.getFileById(ss.getId());\nconst access = file.getSharingAccess();\nconst permission = file.getSharingPermission();\nconst isAnonymousAccess = access === DriveApp.Access.ANYONE ||\n access === DriveApp.Access.ANYONE_WITH_LINK;\nconst isAnonymousEdit =\n isAnonymousAccess && permission === DriveApp.Permission.EDIT;\n```\n\nExample:\n```text\n// Set the document's policy on anonymous reading and writing via the Drive API.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst file = DriveApp.getFileById(ss.getId());\n\n// Set anonymous read.\nfile.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);\n\n// Set anonymous write.\nfile.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);\n\n// Disable anonymous access.\nfile.setSharing(DriveApp.Access.PRIVATE, file.getSharingPermission());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst permissions = sheet.getSheetProtection();\n\n// This copies the permissions on the first sheet to the second sheet\nconst sheetToClonePermissionsTo = ss.getSheets()[1];\nsheetToClonePermissionsTo.setSheetProtection(permissions);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.863Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":128,"totalLines":1506,"estimatedTokens":10437}}917{"id":"doc-choose_google_drive_api_scopes_google_for_develo-8b8e8b8b","source":"documentation","title":"Choose Google Drive API scopes | Google for Developers","url":"https://developers.google.com/drive/api/guides/api-specific-auth","text":"Example:\n```text\nList<String> SCOPES = Arrays.asList(\n DriveScopes.DRIVE_FILE,\n DriveScopes.DRIVE_METADATA_READONLY\n);\n```\n\nExample:\n```text\nSCOPES = [\n \"https://www.googleapis.com/auth/drive.file\",\n \"https://www.googleapis.com/auth/drive.metadata.readonly\",\n]\n```\n\nExample:\n```text\nconst SCOPES = [\n 'https://www.googleapis.com/auth/drive.file',\n 'https://www.googleapis.com/auth/drive.metadata.readonly'\n];\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.881Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":25,"estimatedTokens":108}}918{"id":"doc-handle_api_errors_google_calendar_google_for_dev-fbc9c8f0","source":"documentation","title":"Handle API errors | Google Calendar | Google for Developers","url":"https://developers.google.com/calendar/api/guides/errors","text":"Example:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"calendar\",\n \"reason\": \"timeRangeEmpty\",\n \"message\": \"The specified time range is empty.\",\n \"locationType\": \"parameter\",\n \"location\": \"timeMax\",\n }\n ],\n \"code\": 400,\n \"message\": \"The specified time range is empty.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"authError\",\n \"message\": \"Invalid Credentials\",\n \"locationType\": \"header\",\n \"location\": \"Authorization\",\n }\n ],\n \"code\": 401,\n \"message\": \"Invalid Credentials\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"usageLimits\",\n \"reason\": \"userRateLimitExceeded\",\n \"message\": \"User Rate Limit Exceeded\"\n }\n ],\n \"code\": 403,\n \"message\": \"User Rate Limit Exceeded\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"usageLimits\",\n \"reason\": \"rateLimitExceeded\",\n \"message\": \"Rate Limit Exceeded\"\n }\n ],\n \"code\": 403,\n \"message\": \"Rate Limit Exceeded\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"usageLimits\",\n \"message\": \"Calendar usage limits exceeded.\",\n \"reason\": \"quotaExceeded\"\n }\n ],\n \"code\": 403,\n \"message\": \"Calendar usage limits exceeded.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"calendar\",\n \"reason\": \"forbiddenForNonOrganizer\",\n \"message\": \"Shared properties can only be changed by the organizer of the event.\"\n }\n ],\n \"code\": 403,\n \"message\": \"Shared properties can only be changed by the organizer of the event.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"duplicate\",\n \"message\": \"The requested identifier already exists.\"\n }\n ],\n \"code\": 409,\n \"message\": \"The requested identifier already exists.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"conflict\",\n \"message\": \"Conflict\"\n }\n ],\n \"code\": 409,\n \"message\": \"Conflict\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"calendar\",\n \"reason\": \"fullSyncRequired\",\n \"message\": \"Sync token is no longer valid, a full sync is required.\",\n \"locationType\": \"parameter\",\n \"location\": \"syncToken\",\n }\n ],\n \"code\": 410,\n \"message\": \"Sync token is no longer valid, a full sync is required.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"calendar\",\n \"reason\": \"updatedMinTooLongAgo\",\n \"message\": \"The requested minimum modification time lies too far in the past.\",\n \"locationType\": \"parameter\",\n \"location\": \"updatedMin\",\n }\n ],\n \"code\": 410,\n \"message\": \"The requested minimum modification time lies too far in the past.\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"deleted\",\n \"message\": \"Resource has been deleted\"\n }\n ],\n \"code\": 410,\n \"message\": \"Resource has been deleted\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"conditionNotMet\",\n \"message\": \"Precondition Failed\",\n \"locationType\": \"header\",\n \"location\": \"If-Match\",\n }\n ],\n \"code\": 412,\n \"message\": \"Precondition Failed\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"usageLimits\",\n \"reason\": \"rateLimitExceeded\",\n \"message\": \"Rate Limit Exceeded\"\n }\n ],\n \"code\": 429,\n \"message\": \"Rate Limit Exceeded\"\n }\n}\n```\n\nExample:\n```text\n{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"global\",\n \"reason\": \"backendError\",\n \"message\": \"Backend Error\",\n }\n ],\n \"code\": 500,\n \"message\": \"Backend Error\"\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.881Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":249,"estimatedTokens":929}}919{"id":"doc-verify_requests_from_google_chat_google_for_deve-b2db3cc1","source":"documentation","title":"Verify requests from Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/verify-requests-from-chat","text":"Example:\n```text\nPOST\nHost: yourappurl.com\nAuthorization: Bearer AbCdEf123456\nContent-Type: application/json\nUser-Agent: Google-Dynamite\n```\n\nExample:\n```text\ngcloud functions add-invoker-policy-binding RECEIVING_FUNCTION \\\n --member='serviceAccount:chat@system.gserviceaccount.com'\n```\n\nExample:\n```text\nString CHAT_ISSUER = \"chat@system.gserviceaccount.com\";\nJsonFactory factory = JacksonFactory.getDefaultInstance();\n\nGoogleIdTokenVerifier verifier =\n new GoogleIdTokenVerifier.Builder(new ApacheHttpTransport(), factory)\n .setAudience(Collections.singletonList(AUDIENCE))\n .build();\n\nGoogleIdToken idToken = GoogleIdToken.parse(factory, bearer);\nreturn idToken != null\n && verifier.verify(idToken)\n && idToken.getPayload().getEmailVerified()\n && idToken.getPayload().getEmail().equals(CHAT_ISSUER);\n```\n\nExample:\n```text\n# Bearer Tokens received by apps will always specify this issuer.\nCHAT_ISSUER = 'chat@system.gserviceaccount.com'\n\ntry:\n # Verify valid token, signed by CHAT_ISSUER, intended for a third party.\n request = requests.Request()\n token = id_token.verify_oauth2_token(bearer, request, AUDIENCE)\n return token['email'] == CHAT_ISSUER\n\nexcept:\n return False\n```\n\nExample:\n```text\n// Bearer Tokens received by apps will always specify this issuer.\nconst chatIssuer = 'chat@system.gserviceaccount.com';\n\n// Verify valid token, signed by chatIssuer, intended for a third party.\ntry {\n const ticket = await client.verifyIdToken({\n idToken: bearer,\n audience: audience\n });\n return ticket.getPayload().email_verified\n && ticket.getPayload().email === chatIssuer;\n} catch (unused) {\n return false;\n}\n```\n\nExample:\n```text\nString CHAT_ISSUER = \"chat@system.gserviceaccount.com\";\nJsonFactory factory = JacksonFactory.getDefaultInstance();\n\nGooglePublicKeysManager keyManagerBuilder =\n new GooglePublicKeysManager.Builder(new ApacheHttpTransport(), factory)\n .setPublicCertsEncodedUrl(\n \"https://www.googleapis.com/service_accounts/v1/metadata/x509/\" + CHAT_ISSUER)\n .build();\n\nGoogleIdTokenVerifier verifier =\n new GoogleIdTokenVerifier.Builder(keyManagerBuilder).setIssuer(CHAT_ISSUER).build();\n\nGoogleIdToken idToken = GoogleIdToken.parse(factory, bearer);\nreturn idToken != null\n && verifier.verify(idToken)\n && idToken.verifyAudience(Collections.singletonList(AUDIENCE))\n && idToken.verifyIssuer(CHAT_ISSUER);\n```\n\nExample:\n```text\n# Bearer Tokens received by apps will always specify this issuer.\nCHAT_ISSUER = 'chat@system.gserviceaccount.com'\n\ntry:\n # Verify valid token, signed by CHAT_ISSUER, intended for a third party.\n request = requests.Request()\n certs_url = 'https://www.googleapis.com/service_accounts/v1/metadata/x509/' + CHAT_ISSUER\n token = id_token.verify_token(bearer, request, AUDIENCE, certs_url)\n return token['iss'] == CHAT_ISSUER\n\nexcept:\n return False\n```\n\nExample:\n```text\n// Bearer Tokens received by apps will always specify this issuer.\nconst chatIssuer = 'chat@system.gserviceaccount.com';\n\n// Verify valid token, signed by CHAT_ISSUER, intended for a third party.\ntry {\n const response = await fetch('https://www.googleapis.com/service_accounts/v1/metadata/x509/' + chatIssuer);\n const certs = await response.json();\n await client.verifySignedJwtWithCertsAsync(\n bearer, certs, audience, [chatIssuer]);\n return true;\n} catch (unused) {\n return false;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.882Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":120,"estimatedTokens":859}}920{"id":"doc-send_batch_requests_google_calendar_google_for_d-405f98d1","source":"documentation","title":"Send batch requests | Google Calendar | Google for Developers","url":"https://developers.google.com/calendar/api/guides/batch","text":"Example:\n```text\nPOST /batch/farm/v1 HTTP/1.1\nAuthorization: Bearer your_auth_token\nHost: www.googleapis.com\nContent-Type: multipart/mixed; boundary=batch_foobarbaz\nContent-Length: total_content_length\n\n--batch_foobarbaz\nContent-Type: application/http\nContent-ID: <item1:12930812@barnyard.example.com>\n\nGET /farm/v1/animals/pony\n\n--batch_foobarbaz\nContent-Type: application/http\nContent-ID: <item2:12930812@barnyard.example.com>\n\nPUT /farm/v1/animals/sheep\nContent-Type: application/json\nContent-Length: part_content_length\nIf-Match: \"etag/sheep\"\n\n{\n \"animalName\": \"sheep\",\n \"animalAge\": \"5\"\n \"peltColor\": \"green\",\n}\n\n--batch_foobarbaz\nContent-Type: application/http\nContent-ID: <item3:12930812@barnyard.example.com>\n\nGET /farm/v1/animals\nIf-None-Match: \"etag/animals\"\n\n--batch_foobarbaz--\n```\n\nExample:\n```text\nHTTP/1.1 200\nContent-Length: response_total_content_length\nContent-Type: multipart/mixed; boundary=batch_foobarbaz\n\n--batch_foobarbaz\nContent-Type: application/http\nContent-ID: <response-item1:12930812@barnyard.example.com>\n\nHTTP/1.1 200 OK\nContent-Type application/json\nContent-Length: response_part_1_content_length\nETag: \"etag/pony\"\n\n{\n \"kind\": \"farm#animal\",\n \"etag\": \"etag/pony\",\n \"selfLink\": \"/farm/v1/animals/pony\",\n \"animalName\": \"pony\",\n \"animalAge\": 34,\n \"peltColor\": \"white\"\n}\n\n--batch_foobarbaz\nContent-Type: application/http\nContent-ID: <response-item2:12930812@barnyard.example.com>\n\nHTTP/1.1 200 OK\nContent-Type: application/json\nContent-Length: response_part_2_content_length\nETag: \"etag/sheep\"\n\n{\n \"kind\": \"farm#animal\",\n \"etag\": \"etag/sheep\",\n \"selfLink\": \"/farm/v1/animals/sheep\",\n \"animalName\": \"sheep\",\n \"animalAge\": 5,\n \"peltColor\": \"green\"\n}\n\n--batch_foobarbaz\nContent-Type: application/http\nContent-ID: <response-item3:12930812@barnyard.example.com>\n\nHTTP/1.1 304 Not Modified\nETag: \"etag/animals\"\n\n--batch_foobarbaz--\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.883Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":92,"estimatedTokens":472}}921{"id":"doc-update_a_user_s_space_read_state_google_chat_goo-5210e946","source":"documentation","title":"Update a user's space read state | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/update-space-read-state","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.users.readstate',\n];\n\n// This sample shows how to update a space read state for the calling user\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const timestamp = new Date('2000-01-01').getTime();\n const request = {\n spaceReadState: {\n // Replace SPACE_NAME here\n name: 'users/me/spaces/SPACE_NAME/spaceReadState',\n lastReadTime: {\n seconds: Math.floor(timestamp / 1000),\n nanos: (timestamp % 1000) * 1000000,\n },\n },\n updateMask: {\n // The field paths to update.\n paths: ['last_read_time'],\n },\n };\n\n // Make the request\n const response = await chatClient.updateSpaceReadState(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.883Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":43,"estimatedTokens":257}}922{"id":"doc-get_details_about_a_user_s_thread_read_state_goo-cb4675d4","source":"documentation","title":"Get details about a user's thread read state | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/get-thread-read-state","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.users.readstate.readonly',\n];\n\n// This sample shows how to get the thread read state for a space and calling\n// user\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME and THREAD_NAME here\n name: 'users/me/spaces/SPACE_NAME/threads/THREAD_NAME/threadReadState',\n };\n\n // Make the request\n const response = await chatClient.getThreadReadState(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.884Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":33,"estimatedTokens":195}}923{"id":"doc-class_columns_apps_script_google_for_developers-67566ff8","source":"documentation","title":"Class Columns | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/columns","text":"Example:\n```text\n// Build a column that is aligned in the center and fills the space:\nconst column =\n CardService.newColumn()\n .setHorizontalSizeStyle(\n CardService.HorizontalSizeStyle.FILL_AVAILABLE_SPACE)\n .setHorizontalAlignment(CardService.HorizontalAlignment.CENTER)\n .setVerticalAlignment(CardService.VerticalAlignment.CENTER);\nconst columns = CardService.newColumns().addColumn(column).setWrapStyle(\n CardService.WrapStyle.WRAP);\n```\n\nExample:\n```text\nconst columns = CardService.newColumns().addColumn(CardService.newColumn());\n```\n\nExample:\n```text\nconst columns = CardService.newColumns()\n .addColumn(CardService.newColumn())\n .setWrapStyle(CardService.WrapStyle.WRAP);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.885Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":26,"estimatedTokens":194}}924{"id":"doc-class_datasourcechart_apps_script_google_for_dev-bb955502","source":"documentation","title":"Class DataSourceChart | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/data-source-chart","text":"Example:\n```text\nconst spreadsheet = SpreadsheetApp.getActive();\nconst formula = spreadsheet.getDataSourceFormulas()[0];\n// Cancel the ongoing refresh on the formula.\nformula.cancelDataRefresh();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.886Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":9,"estimatedTokens":54}}925{"id":"doc-class_datasourcepivottable_apps_script_google_fo-8175d1de","source":"documentation","title":"Class DataSourcePivotTable | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/data-source-pivot-table","text":"Example:\n```text\n// TODO(developer): Replace with your spreadsheet ID which has a Looker data\n// source.\nconst spreadsheet = SpreadsheetApp.openById('abcd1234');\nconst datasource = spreadsheet.getDataSources()[0];\nconst pivotTable = datasource.createDataSourcePivotTableOnNewSheet();\n\npivotTable.addPivotValue('columnName');\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActive();\nconst formula = spreadsheet.getDataSourceFormulas()[0];\n// Cancel the ongoing refresh on the formula.\nformula.cancelDataRefresh();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.888Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":20,"estimatedTokens":136}}926{"id":"doc-class_datasourcesheet_apps_script_google_for_dev-36696177","source":"documentation","title":"Class DataSourceSheet | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/data-source-sheet","text":"Example:\n```text\nconst spreadsheet = SpreadsheetApp.getActive();\nconst formula = spreadsheet.getDataSourceFormulas()[0];\n// Cancel the ongoing refresh on the formula.\nformula.cancelDataRefresh();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.889Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":9,"estimatedTokens":54}}927{"id":"doc-package_google_apps_card_v1_google_workspace_add-a9a0ef57","source":"documentation","title":"Package google.apps.card.v1 | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/reference/rpc/google.apps.card.v1","text":"Example:\n```text\n\"color\": {\n \"red\": 1,\n \"green\": 0,\n \"blue\": 0,\n}\n```\n\nExample:\n```text\n{\n \"cardsV2\": [\n {\n \"cardId\": \"unique-card-id\",\n \"card\": {\n \"header\": {\n \"title\": \"Sasha\",\n \"subtitle\": \"Software Engineer\",\n \"imageUrl\":\n \"https://developers.google.com/workspace/chat/images/quickstart-app-avatar.png\",\n \"imageType\": \"CIRCLE\",\n \"imageAltText\": \"Avatar for Sasha\"\n },\n \"sections\": [\n {\n \"header\": \"Contact Info\",\n \"collapsible\": true,\n \"uncollapsibleWidgetsCount\": 1,\n \"widgets\": [\n {\n \"decoratedText\": {\n \"startIcon\": {\n \"knownIcon\": \"EMAIL\"\n },\n \"text\": \"sasha@example.com\"\n }\n },\n {\n \"decoratedText\": {\n \"startIcon\": {\n \"knownIcon\": \"PERSON\"\n },\n \"text\": \"<font color=\\\"#80e27e\\\">Online</font>\"\n }\n },\n {\n \"decoratedText\": {\n \"startIcon\": {\n \"knownIcon\": \"PHONE\"\n },\n \"text\": \"+1 (555) 555-1234\"\n }\n },\n {\n \"buttonList\": {\n \"buttons\": [\n {\n \"text\": \"Share\",\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://example.com/share\"\n }\n }\n },\n {\n \"text\": \"Edit\",\n \"onClick\": {\n \"action\": {\n \"function\": \"goToView\",\n \"parameters\": [\n {\n \"key\": \"viewType\",\n \"value\": \"EDIT\"\n }\n ]\n }\n }\n }\n ]\n }\n }\n ]\n }\n ]\n }\n }\n ]\n}\n```\n\nExample:\n```text\n\"cardActions\": [\n {\n \"actionLabel\": \"Settings\",\n \"onClick\": {\n \"action\": {\n \"functionName\": \"goToView\",\n \"parameters\": [\n {\n \"key\": \"viewType\",\n \"value\": \"SETTING\"\n }\n ],\n \"loadIndicator\": \"LoadIndicator.SPINNER\"\n }\n }\n },\n {\n \"actionLabel\": \"Send Feedback\",\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://example.com/feedback\"\n }\n }\n }\n]\n```\n\nExample:\n```text\n{\n \"carouselCards\": [\n {\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"First text paragraph in carousel\",\n }\n }\n ]\n },\n {\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"Second text paragraph in carousel\",\n }\n }\n ]\n },\n {\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"Third text paragraph in carousel\",\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n\"divider\": {}\n```\n\nExample:\n```text\n{\n \"autoComplete\": {\n \"items\": [\n {\n \"text\": \"C++\"\n },\n {\n \"text\": \"Java\"\n },\n {\n \"text\": \"JavaScript\"\n },\n {\n \"text\": \"Python\"\n }\n ]\n }\n}\n```\n\nExample:\n```text\n\"grid\": {\n \"title\": \"A fine collection of items\",\n \"columnCount\": 2,\n \"borderStyle\": {\n \"type\": \"STROKE\",\n \"cornerRadius\": 4\n },\n \"items\": [\n {\n \"image\": {\n \"imageUri\": \"https://www.example.com/image.png\",\n \"cropStyle\": {\n \"type\": \"SQUARE\"\n },\n \"borderStyle\": {\n \"type\": \"STROKE\"\n }\n },\n \"title\": \"An item\",\n \"textAlignment\": \"CENTER\"\n }\n ],\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://www.example.com\"\n }\n }\n}\n```\n\nExample:\n```text\n\"iconUrl\":\n\"https://developers.google.com/workspace/chat/images/quickstart-app-avatar.png\"\n```\n\nExample:\n```text\n\"materialIcon\": {\n \"name\": \"check_box\"\n}\n```\n\nExample:\n```text\nhttps://developers.google.com/workspace/chat/images/quickstart-app-avatar.png\n```\n\nExample:\n```text\ncropStyle {\n \"type\": \"RECTANGLE_CUSTOM\",\n \"aspectRatio\": 16/9\n}\n```\n\nExample:\n```text\n{\n \"action\": {\n \"linkPreview\": {\n \"title\": \"Smart chip title\",\n \"linkPreviewTitle\": \"Link preview title\",\n \"previewCard\": {\n \"header\": {\n \"title\": \"Preview card header\",\n },\n \"sections\": [\n {\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"Description of the link.\"\n }\n }\n ]\n }\n ]\n }\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"name\": \"check_box\",\n \"fill\": true,\n \"weight\": 300,\n \"grade\": -25\n}\n```\n\nExample:\n```text\nnavigations : {\n pushCard : CARD\n }\n```\n\nExample:\n```text\nnavigations : {\n popCard : true,\n }, {\n pushCard : CARD\n }\n```\n\nExample:\n```text\nnavigations : {\n popCard : true,\n }\n```\n\nExample:\n```text\nnavigations : {\n popCard : true,\n }, ... {\n pushCard : CARD\n }\n```\n\nExample:\n```text\nnavigations : {\n popToCardName : CARD_NAME,\n }, {\n pushCard : CARD\n }\n```\n\nExample:\n```text\nnavigations : {\n popToRoot : true\n }, {\n pushCard : CARD\n }\n```\n\nExample:\n```text\nnavigations : {\n updateCard : CARD\n }\n```\n\nExample:\n```text\n{\n \"renderActions\": {\n \"action\": {\n \"notification\": {\n \"text\": \"Email address is added: salam.heba@example.com\"\n }\n },\n \"hostAppAction\": {\n \"gmailAction\": {\n \"openCreatedDraftAction\": {\n \"draftId\": \"msg-a:r-79766936926021702\",\n \"threadServerPermId\": \"thread-f:15700999851086004\"\n }\n }\n }\n }\n}\n```\n\nExample:\n```text\n\"textParagraph\": {\n \"text\": \" <b>bold text</b>\"\n}\n```\n\nExample:\n```text\n\"image\": {\n \"imageUrl\":\n \"https://developers.google.com/workspace/chat/images/quickstart-app-avatar.png\",\n \"altText\": \"Chat app avatar\"\n}\n```\n\nExample:\n```text\n\"decoratedText\": {\n \"icon\": {\n \"knownIcon\": \"EMAIL\"\n },\n \"topLabel\": \"Email Address\",\n \"text\": \"sasha@example.com\",\n \"bottomLabel\": \"This is a new Email address!\",\n \"switchControl\": {\n \"name\": \"has_send_welcome_email_to_sasha\",\n \"selected\": false,\n \"controlType\": \"CHECKBOX\"\n }\n}\n```\n\nExample:\n```text\n\"buttonList\": {\n \"buttons\": [\n {\n \"text\": \"Edit\",\n \"color\": {\n \"red\": 0,\n \"green\": 0,\n \"blue\": 1,\n },\n \"disabled\": true,\n },\n {\n \"icon\": {\n \"knownIcon\": \"INVITE\",\n \"altText\": \"check calendar\"\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://example.com/calendar\"\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\n\"textInput\": {\n \"name\": \"mailing_address\",\n \"label\": \"Mailing Address\"\n}\n```\n\nExample:\n```text\n\"textInput\": {\n \"name\": \"preferred_programing_language\",\n \"label\": \"Preferred Language\",\n \"initialSuggestions\": {\n \"items\": [\n {\n \"text\": \"C++\"\n },\n {\n \"text\": \"Java\"\n },\n {\n \"text\": \"JavaScript\"\n },\n {\n \"text\": \"Python\"\n }\n ]\n }\n}\n```\n\nExample:\n```text\n\"selectionInput\": {\n \"name\": \"size\",\n \"label\": \"Size\"\n \"type\": \"DROPDOWN\",\n \"items\": [\n {\n \"text\": \"S\",\n \"value\": \"small\",\n \"selected\": false\n },\n {\n \"text\": \"M\",\n \"value\": \"medium\",\n \"selected\": true\n },\n {\n \"text\": \"L\",\n \"value\": \"large\",\n \"selected\": false\n },\n {\n \"text\": \"XL\",\n \"value\": \"extra_large\",\n \"selected\": false\n }\n ]\n}\n```\n\nExample:\n```text\n\"dateTimePicker\": {\n \"name\": \"appointment_time\",\n \"label\": \"Book your appointment at:\",\n \"type\": \"DATE_AND_TIME\",\n \"valueMsEpoch\": 796435200000\n}\n```\n\nExample:\n```text\n\"divider\": {\n}\n```\n\nExample:\n```text\n\"columns\": {\n \"columnItems\": [\n {\n \"horizontalSizeStyle\": \"FILL_AVAILABLE_SPACE\",\n \"horizontalAlignment\": \"CENTER\",\n \"verticalAlignment\": \"CENTER\",\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"First column text paragraph\"\n }\n }\n ]\n },\n {\n \"horizontalSizeStyle\": \"FILL_AVAILABLE_SPACE\",\n \"horizontalAlignment\": \"CENTER\",\n \"verticalAlignment\": \"CENTER\",\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"Second column text paragraph\"\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"First text paragraph in the carousel.\"\n }\n },\n {\n \"textParagraph\": {\n \"text\": \"Second text paragraph in the carousel.\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\n\"chipList\": {\n \"chips\": [\n {\n \"text\": \"Edit\",\n \"disabled\": true,\n },\n {\n \"icon\": {\n \"knownIcon\": \"INVITE\",\n \"altText\": \"check calendar\"\n },\n \"onClick\": {\n \"openLink\": {\n \"url\": \"https://example.com/calendar\"\n }\n }\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.894Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":33,"totalLines":570,"estimatedTokens":2328}}928{"id":"doc-class_datasourcetable_apps_script_google_for_dev-88fbef71","source":"documentation","title":"Class DataSourceTable | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/data-source-table","text":"Example:\n```text\nSpreadsheetApp.enableBigQueryExecution();\nconst spreadsheet = SpreadsheetApp.getActive();\nconst spec = SpreadsheetApp.newDataSourceSpec()\n .asBigQuery()\n .setProjectId('big_query_project')\n .setRawQuery('select @FIELD from table limit @LIMIT')\n .setParameterFromCell('FIELD', 'Sheet1!A1')\n .setParameterFromCell('LIMIT', 'namedRangeCell')\n .build();\n// Starts data execution asynchronously.\nconst dataSheet = spreadsheet.insertSheetWithDataSourceTable(spec);\nconst dataSourceTable = dataSheet.getDataSourceTables()[0];\n// waitForCompletion() blocks script execution until data execution completes.\ndataSourceTable.waitForCompletion(60);\n// Check status after execution.\nLogger.log(\n 'Data execution state: %s.',\n dataSourceTable.getStatus().getExecutionState(),\n);\n```\n\nExample:\n```text\nSpreadsheetApp.enableBigQueryExecution();\nconst dataSheet = SpreadsheetApp.getActive().getSheetByName('Data Sheet 1');\nconst dataSourceTable = dataSheet.getDataSourceTables()[0];\nconst dataSource = dataSourceTable.getDataSource();\nconst newSpec = dataSource.getSpec()\n .copy()\n .asBigQuery()\n .setRawQuery('select name from table limit 2')\n .removeAllParameters()\n .build();\n// Updates data source specification and starts data execution asynchronously.\ndataSource.updateSpec(newSpec);\n// Check status during execution.\nLogger.log(\n 'Data execution state: %s.',\n dataSourceTable.getStatus().getExecutionState(),\n);\n// waitForCompletion() blocks script execution until data execution completes.\ndataSourceTable.waitForCompletion(60);\n// Check status after execution.\nLogger.log(\n 'Data execution state: %s.',\n dataSourceTable.getStatus().getExecutionState(),\n);\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActive();\nconst formula = spreadsheet.getDataSourceFormulas()[0];\n// Cancel the ongoing refresh on the formula.\nformula.cancelDataRefresh();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.895Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":60,"estimatedTokens":523}}929{"id":"doc-class_datasourceformula_apps_script_google_for_d-c7b8ba45","source":"documentation","title":"Class DataSourceFormula | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/data-source-formula","text":"Example:\n```text\nconst spreadsheet = SpreadsheetApp.getActive();\nconst formula = spreadsheet.getDataSourceFormulas()[0];\n// Cancel the ongoing refresh on the formula.\nformula.cancelDataRefresh();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.896Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":9,"estimatedTokens":54}}930{"id":"doc-get_details_about_a_user_s_space_read_state_goog-a78e5a0b","source":"documentation","title":"Get details about a user's space read state | Google Chat | Google for Developers","url":"https://developers.google.com/workspace/chat/get-space-read-state","text":"Example:\n```text\nimport {createClientWithUserCredentials} from './authentication-utils.js';\n\nconst USER_AUTH_OAUTH_SCOPES = [\n 'https://www.googleapis.com/auth/chat.users.readstate.readonly',\n];\n\n// This sample shows how to get the space read state for the calling user\nasync function main() {\n // Create a client\n const chatClient = await createClientWithUserCredentials(\n USER_AUTH_OAUTH_SCOPES,\n );\n\n // Initialize request argument(s)\n const request = {\n // Replace SPACE_NAME here\n name: 'users/me/spaces/SPACE_NAME/spaceReadState',\n };\n\n // Make the request\n const response = await chatClient.getSpaceReadState(request);\n\n // Handle the response\n console.log(response);\n}\n\nawait main();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.897Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":32,"estimatedTokens":183}}931{"id":"doc-class_linkpreview_apps_script_google_for_develop-584a3ce0","source":"documentation","title":"Class LinkPreview | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/link-preview","text":"Example:\n```text\nconst decoratedText =\n CardService.newDecoratedText().setTopLabel('Hello').setText('Hi!');\n\nconst cardSection = CardService.newCardSection().addWidget(decoratedText);\n\nconst card = CardService.newCardBuilder().addSection(cardSection).build();\n\nreturn CardService.newLinkPreview().setPreviewCard(card).setTitle(\n 'Smart chip title');\n```\n\nExample:\n```text\nreturn CardService.newLinkPreview().setLinkPreviewTitle('Link preview title');\n```\n\nExample:\n```text\nreturn CardService.newLinkPreview().setTitle('Smart chip title');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.900Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":24,"estimatedTokens":141}}932{"id":"doc-class_datasource_apps_script_google_for_develope-19a9fe8c","source":"documentation","title":"Class DataSource | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/data-source","text":"Example:\n```text\nSpreadsheetApp.enableBigQueryExecution();\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nconst dataSource = spreadsheet.getDataSources()[0];\ndataSource.cancelAllLinkedDataSourceObjectRefreshes();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.902Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":9,"estimatedTokens":61}}933{"id":"doc-class_cardservice_apps_script_google_for_develop-73c27d81","source":"documentation","title":"Class CardService | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/card-service","text":"Example:\n```text\nfunction createCard() {\n return CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader().setTitle('CardTitle'))\n .build();\n}\n```\n\nExample:\n```text\nfunction createCards() {\n return [\n CardService.newCardBuilder().build(),\n CardService.newCardBuilder().build(),\n CardService.newCardBuilder().build(),\n ];\n}\n```\n\nExample:\n```text\nfunction createWidgetDemoCard() {\n return CardService.newCardBuilder()\n .setHeader(\n CardService.newCardHeader()\n .setTitle('Widget demonstration')\n .setSubtitle('Check out these widgets')\n .setImageStyle(CardService.ImageStyle.SQUARE)\n .setImageUrl('https://www.example.com/images/headerImage.png'),\n )\n .addSection(\n CardService.newCardSection()\n .setHeader('Simple widgets') // optional\n .addWidget(\n CardService.newTextParagraph().setText(\n 'These widgets are display-only. ' +\n 'A text paragraph can have multiple lines and ' +\n 'formatting.',\n ),\n )\n .addWidget(\n CardService.newImage().setImageUrl(\n 'https://www.example.com/images/mapsImage.png',\n ),\n ),\n )\n .addCardAction(\n CardService.newCardAction().setText('Gmail').setOpenLink(\n CardService.newOpenLink().setUrl('https://mail.google.com/mail'),\n ),\n )\n .build();\n}\n```\n\nExample:\n```text\nconst cardHeader =\n CardService.newCardHeader()\n .setTitle('Sasha')\n .setSubtitle('Software Engineer')\n .setImageUrl(\n 'https://developers.google.com/chat/images/quickstart-app-avatar.png',\n )\n .setImageStyle(CardService.ImageStyle.CIRCLE)\n .setImageAltText('Avatar for Sasha');\n\nconst cardSection =\n CardService.newCardSection()\n .setHeader('Contact Info')\n .setCollapsible(true)\n .setNumUncollapsibleWidgets(1)\n .addWidget(\n CardService.newDecoratedText()\n .setStartIcon(\n CardService.newIconImage().setIcon(CardService.Icon.EMAIL))\n .setText('sasha@example.com'),\n )\n .addWidget(\n CardService.newDecoratedText()\n .setStartIcon(\n CardService.newIconImage().setIcon(CardService.Icon.PERSON))\n .setText('<font color=\"#80e27e\">Online</font>'),\n )\n .addWidget(\n CardService.newDecoratedText()\n .setStartIcon(\n CardService.newIconImage().setIcon(CardService.Icon.PHONE))\n .setText('+1 (555) 555-1234'),\n )\n .addWidget(\n CardService.newButtonSet()\n .addButton(\n CardService.newTextButton().setText('Share').setOpenLink(\n CardService.newOpenLink().setUrl(\n 'https://example.com/share'),\n ),\n )\n .addButton(\n CardService.newTextButton()\n .setText('Edit')\n .setOnClickAction(\n CardService.newAction()\n .setFunctionName('goToView')\n .setParameters({viewType: 'EDIT'}),\n ),\n ),\n );\n\nconst card = CardService.newCardBuilder()\n .setHeader(cardHeader)\n .addSection(cardSection)\n .build();\n```\n\nExample:\n```text\nconst actionStatus = CardService.newActionStatus()\n .setStatusCode(CardService.Status.OK)\n .setUserFacingMessage('Success');\n```\n\nExample:\n```text\nconst cardSection = CardService.newCardSection();\ncardSection.addWidget(\n CardService.newTextParagraph().setText('This is a text paragraph widget.'),\n);\n\nconst card = CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader().setTitle('Card title'))\n .addSection(cardSection)\n .build();\n\nconst cardWithId =\n CardService.newCardWithId().setCardId('card_id').setCard(card);\n```\n\nExample:\n```text\nconst carousel =\n CardService.newCarousel()\n .addCarouselCard(CardService.newCarouselCard().addWidget(\n CardService.newTextParagraph().setText('The first text paragraph in carousel')))\n .addCarouselCard(CardService.newCarouselCard().addWidget(\n CardService.newTextParagraph().setText('The second text paragraph in carousel')))\n .addCarouselCard(CardService.newCarouselCard().addWidget(\n CardService.newTextParagraph().setText('The third text paragraph in carousel')))\n```\n\nExample:\n```text\nconst carouselCard = CardService.newCarouselCard().addWidget(\n CardService.newTextParagraph().setText('Text paragraph in carousel'));\n```\n\nExample:\n```text\nconst card = CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader().setTitle('Card title'))\n .build();\nconst dialog = CardService.newDialog().setBody(card);\n\nconst dialogAction = CardService.newDialogAction().setDialog(dialog);\n\nconst chatActionResponse = CardService.newChatActionResponse()\n .setResponseType(CardService.ResponseType.DIALOG)\n .setDialogAction(dialogAction);\n```\n\nExample:\n```text\nconst cardSection = CardService.newCardSection();\ncardSection.addWidget(\n CardService.newTextParagraph().setText('This is a text paragraph widget.'),\n);\n\nconst card = CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader().setTitle('Card title'))\n .addSection(cardSection)\n .build();\n\nconst cardWithId =\n CardService.newCardWithId().setCardId('card_id').setCard(card);\n\nconst chatResponse =\n CardService.newChatResponseBuilder().addCardsV2(cardWithId).build();\n```\n\nExample:\n```text\nconst chip = CardService.newChip()\n .setLabel('Open Link')\n .setOpenLink(CardService.newOpenLink().setUrl(\n 'https://www.google.com'));\n```\n\nExample:\n```text\nconst chip = CardService.newChip();\n// Finish building the text chip...\n\nconst chipList = CardService.newChipList()\n .setLayout(CardService.ChipListLayout.WRAPPED)\n .addChip(chip);\n```\n\nExample:\n```text\nconst collapseControl =\n CardService.newCollapseControl()\n .setHorizontalAlign(CardService.HorizontalAlignment.START)\n .setExpandButton(CardService.newTextButton().setText('Expand'))\n .setCollapseButton(CardService.newTextButton().setText('Collapse'));\n```\n\nExample:\n```text\nconst columnWidget = CardService.newTextParagraph();\nconst column =\n CardService.newColumn()\n .setHorizontalSizeStyle(\n CardService.HorizontalSizeStyle.FILL_AVAILABLE_SPACE)\n .setHorizontalAlignment(CardService.HorizontalAlignment.CENTER)\n .setVerticalAlignment(CardService.VerticalAlignment.CENTER)\n .addWidget(columnWidget);\n```\n\nExample:\n```text\nconst firstColumn =\n CardService.newColumn()\n .setHorizontalSizeStyle(\n CardService.HorizontalSizeStyle.FILL_AVAILABLE_SPACE)\n .setHorizontalAlignment(CardService.HorizontalAlignment.CENTER)\n .setVerticalAlignment(CardService.VerticalAlignment.CENTER);\nconst secondColumn =\n CardService.newColumn()\n .setHorizontalSizeStyle(\n CardService.HorizontalSizeStyle.FILL_AVAILABLE_SPACE)\n .setHorizontalAlignment(CardService.HorizontalAlignment.CENTER)\n .setVerticalAlignment(CardService.VerticalAlignment.CENTER);\nconst columns = CardService.newColumns()\n .addColumn(firstColumn)\n .addColumn(secondColumn)\n .setWrapStyle(CardService.WrapStyle.WRAP);\n```\n\nExample:\n```text\nconst card = CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader().setTitle('Card title'))\n .build();\n\n// Sets the card of the dialog.\nconst dialog = CardService.newDialog().setBody(card);\n```\n\nExample:\n```text\nconst card = CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader().setTitle('Card title'))\n .build();\nconst dialog = CardService.newDialog().setBody(card);\n\nconst dialogAction = CardService.newDialogAction().setDialog(dialog);\n```\n\nExample:\n```text\nfunction buildCard() {\n const cardSection1TextParagraph1 =\n CardService.newTextParagraph().setText('Hello world!');\n\n const cardSection1Divider1 = CardService.newDivider();\n\n const cardSection1TextParagraph2 =\n CardService.newTextParagraph().setText('Hello world!');\n\n const cardSection1 = CardService.newCardSection()\n .addWidget(cardSection1TextParagraph1)\n .addWidget(cardSection1Divider1)\n .addWidget(cardSection1TextParagraph2);\n\n const card = CardService.newCardBuilder().addSection(cardSection1).build();\n\n return card;\n}\n```\n\nExample:\n```text\nconst decoratedText =\n CardService.newDecoratedText().setTopLabel('Hello').setText('Hi!');\n\nconst cardSection = CardService.newCardSection().addWidget(decoratedText);\n\nconst card = CardService.newCardBuilder().addSection(cardSection).build();\n\nreturn CardService.newLinkPreview().setPreviewCard(card).setTitle(\n 'Smart chip title');\n```\n\nExample:\n```text\nconst materialIcon =\n CardService.newMaterialIcon().setName('check_box').setFill(true);\n\nconst cardSection = CardService.newCardSection();\ncardSection.addWidget(\n CardService.newDecoratedText()\n .setStartIcon(CardService.newIconImage().setMaterialIcon(materialIcon))\n .setText('sasha@example.com'),\n);\n\nconst card = CardService.newCardBuilder()\n .setHeader(CardService.newCardHeader().setTitle('Card Title'))\n .addSection(cardSection)\n .build();\n```\n\nExample:\n```text\nconst overflowMenuItem = CardService.newOverflowMenuItem();\n// Finish building the overflow menu item...\n\nconst overflowMenu =\n CardService.newOverflowMenu().addMenuItem(overflowMenuItem);\n```\n\nExample:\n```text\nconst overflowMenuItem =\n CardService.newOverflowMenuItem()\n .setStartIcon(\n CardService.newIconImage().setIconUrl(\n 'https://www.google.com/images/branding/googleg/1x/googleg_standard_color_64dp.png',\n ),\n )\n .setText('Open Link')\n .setOpenLink(\n CardService.newOpenLink().setUrl('https://www.google.com'));\n```\n\nExample:\n```text\nconst validation =\n CardService.newValidation().setCharacterLimit(5).setInputType(\n CardService.InputType.EMAIL);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.905Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":23,"totalLines":353,"estimatedTokens":2744}}934{"id":"doc-preview_links_with_smart_chips_google_workspace_-9d60d24e","source":"documentation","title":"Preview links with smart chips | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/apps-script/add-ons/editors/gsao/preview-links","text":"Example:\n```text\n{\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Preview support cases\",\n \"logoUrl\": \"https://www.example.com/images/company-logo.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"caseLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"logoUrl\": \"https://www.example.com/images/support-icon.png\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n }\n }\n ]\n },\n \"sheets\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"caseLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"logoUrl\": \"https://www.example.com/images/support-icon.png\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n }\n }\n ]\n },\n \"slides\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"caseLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"logoUrl\": \"https://www.example.com/images/support-icon.png\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"docs\": {\n \"matchedUrl\": {\n \"url\": \"https://www.example.com/support/cases/123456\"\n }\n }\n}\n```\n\nExample:\n```text\n/**\n* Entry point for a support case link preview.\n*\n* @param {!Object} event The event object.\n* @return {!Card} The resulting preview link card.\n*/\nfunction caseLinkPreview(event) {\n\n // If the event object URL matches a specified pattern for support case links.\n if (event.docs.matchedUrl.url) {\n\n // Uses the event object to parse the URL and identify the case details.\n const caseDetails = parseQuery(event.docs.matchedUrl.url);\n\n // Builds a preview card with the case name, and description\n const caseHeader = CardService.newCardHeader()\n .setTitle(`Case ${caseDetails[\"name\"][0]}`);\n const caseDescription = CardService.newTextParagraph()\n .setText(caseDetails[\"description\"][0]);\n\n // Returns the card.\n // Uses the text from the card's header for the title of the smart chip.\n return CardService.newCardBuilder()\n .setHeader(caseHeader)\n .addSection(CardService.newCardSection().addWidget(caseDescription))\n .build();\n }\n}\n\n/**\n* Extracts the URL parameters from the given URL.\n*\n* @param {!string} url The URL to parse.\n* @return {!Map} A map with the extracted URL parameters.\n*/\nfunction parseQuery(url) {\n const query = url.split(\"?\")[1];\n if (query) {\n return query.split(\"&\")\n .reduce(function(o, e) {\n var temp = e.split(\"=\");\n var key = temp[0].trim();\n var value = temp[1].trim();\n value = isNaN(value) ? value : Number(value);\n if (o[key]) {\n o[key].push(value);\n } else {\n o[key] = [value];\n }\n return o;\n }, {});\n }\n return null;\n}\n```\n\nExample:\n```text\n/**\n * \n * A support case link preview.\n *\n * @param {!URL} url The event object.\n * @return {!Card} The resulting preview link card.\n */\nfunction caseLinkPreview(url) {\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n // Parses the URL and identify the case details.\n const name = `Case ${url.searchParams.get(\"name\")}`;\n return {\n action: {\n linkPreview: {\n title: name,\n previewCard: {\n header: {\n title: name\n },\n sections: [{\n widgets: [{\n textParagraph: {\n text: url.searchParams.get(\"description\")\n }\n }]\n }]\n }\n }\n }\n };\n}\n```\n\nExample:\n```text\ndef case_link_preview(url):\n \"\"\"A support case link preview.\n Args:\n url: A matching URL.\n Returns:\n The resulting preview link card.\n \"\"\"\n\n # Parses the URL and identify the case details.\n query_string = parse_qs(url.query)\n name = f'Case {query_string[\"name\"][0]}'\n # Uses the text from the card's header for the title of the smart chip.\n return {\n \"action\": {\n \"linkPreview\": {\n \"title\": name,\n \"previewCard\": {\n \"header\": {\n \"title\": name\n },\n \"sections\": [{\n \"widgets\": [{\n \"textParagraph\": {\n \"text\": query_string[\"description\"][0]\n }\n }]\n }],\n }\n }\n }\n }\n```\n\nExample:\n```text\n/**\n * A support case link preview.\n *\n * @param url A matching URL.\n * @return The resulting preview link card.\n */\nJsonObject caseLinkPreview(URL url) throws UnsupportedEncodingException {\n // Parses the URL and identify the case details.\n Map<String, String> caseDetails = new HashMap<String, String>();\n for (String pair : url.getQuery().split(\"&\")) {\n caseDetails.put(URLDecoder.decode(pair.split(\"=\")[0], \"UTF-8\"), URLDecoder.decode(pair.split(\"=\")[1], \"UTF-8\"));\n }\n\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n JsonObject cardHeader = new JsonObject();\n String caseName = String.format(\"Case %s\", caseDetails.get(\"name\"));\n cardHeader.add(\"title\", new JsonPrimitive(caseName));\n\n JsonObject textParagraph = new JsonObject();\n textParagraph.add(\"text\", new JsonPrimitive(caseDetails.get(\"description\")));\n\n JsonObject widget = new JsonObject();\n widget.add(\"textParagraph\", textParagraph);\n\n JsonArray widgets = new JsonArray();\n widgets.add(widget);\n\n JsonObject section = new JsonObject();\n section.add(\"widgets\", widgets);\n\n JsonArray sections = new JsonArray();\n sections.add(section);\n\n JsonObject previewCard = new JsonObject();\n previewCard.add(\"header\", cardHeader);\n previewCard.add(\"sections\", sections);\n\n JsonObject linkPreview = new JsonObject();\n linkPreview.add(\"title\", new JsonPrimitive(caseName));\n linkPreview.add(\"previewCard\", previewCard);\n\n JsonObject action = new JsonObject();\n action.add(\"linkPreview\", linkPreview);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n return renderActions;\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/New_York\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\",\n \"https://www.googleapis.com/auth/workspace.linkcreate\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Manage support cases\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"caseLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n },\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ],\n \"createActionTriggers\": [\n {\n \"id\": \"createCase\",\n \"labelText\": \"Create support case\",\n \"localizedLabelText\": {\n \"es\": \"Crear caso de soporte\"\n },\n \"runFunction\": \"createCaseInputCard\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Preview support cases\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"URL\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n },\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Responds to any HTTP request related to link previews.\n *\n * @param {Object} req An HTTP request context.\n * @param {Object} res An HTTP response context.\n */\nexports.createLinkPreview = (req, res) => {\n const event = req.body;\n if (event.docs.matchedUrl.url) {\n const url = event.docs.matchedUrl.url;\n const parsedUrl = new URL(url);\n // If the event object URL matches a specified pattern for preview links.\n if (parsedUrl.hostname === 'example.com') {\n if (parsedUrl.pathname.startsWith('/support/cases/')) {\n return res.json(caseLinkPreview(parsedUrl));\n }\n }\n }\n};\n\n\n/**\n * \n * A support case link preview.\n *\n * @param {!URL} url The event object.\n * @return {!Card} The resulting preview link card.\n */\nfunction caseLinkPreview(url) {\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n // Parses the URL and identify the case details.\n const name = `Case ${url.searchParams.get(\"name\")}`;\n return {\n action: {\n linkPreview: {\n title: name,\n previewCard: {\n header: {\n title: name\n },\n sections: [{\n widgets: [{\n textParagraph: {\n text: url.searchParams.get(\"description\")\n }\n }]\n }]\n }\n }\n }\n };\n}\n```\n\nExample:\n```text\nfrom typing import Any, Mapping\nfrom urllib.parse import urlparse, parse_qs\n\nimport flask\nimport functions_framework\n\n\n@functions_framework.http\ndef create_link_preview(req: flask.Request):\n \"\"\"Responds to any HTTP request related to link previews.\n Args:\n req: An HTTP request context.\n Returns:\n An HTTP response context.\n \"\"\"\n event = req.get_json(silent=True)\n if event[\"docs\"][\"matchedUrl\"][\"url\"]:\n url = event[\"docs\"][\"matchedUrl\"][\"url\"]\n parsed_url = urlparse(url)\n # If the event object URL matches a specified pattern for preview links.\n if parsed_url.hostname == \"example.com\":\n if parsed_url.path.startswith(\"/support/cases/\"):\n return case_link_preview(parsed_url)\n\n return {}\n\n\n\n\ndef case_link_preview(url):\n \"\"\"A support case link preview.\n Args:\n url: A matching URL.\n Returns:\n The resulting preview link card.\n \"\"\"\n\n # Parses the URL and identify the case details.\n query_string = parse_qs(url.query)\n name = f'Case {query_string[\"name\"][0]}'\n # Uses the text from the card's header for the title of the smart chip.\n return {\n \"action\": {\n \"linkPreview\": {\n \"title\": name,\n \"previewCard\": {\n \"header\": {\n \"title\": name\n },\n \"sections\": [{\n \"widgets\": [{\n \"textParagraph\": {\n \"text\": query_string[\"description\"][0]\n }\n }]\n }],\n }\n }\n }\n }\n```\n\nExample:\n```text\nimport com.google.cloud.functions.HttpFunction;\nimport com.google.cloud.functions.HttpRequest;\nimport com.google.cloud.functions.HttpResponse;\nimport com.google.gson.Gson;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URL;\nimport java.net.URLDecoder;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class CreateLinkPreview implements HttpFunction {\n private static final Gson gson = new Gson();\n\n /**\n * Responds to any HTTP request related to link previews.\n *\n * @param request An HTTP request context.\n * @param response An HTTP response context.\n */\n @Override\n public void service(HttpRequest request, HttpResponse response) throws Exception {\n JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);\n String url = event.getAsJsonObject(\"docs\")\n .getAsJsonObject(\"matchedUrl\")\n .get(\"url\")\n .getAsString();\n URL parsedURL = new URL(url);\n // If the event object URL matches a specified pattern for preview links.\n if (\"example.com\".equals(parsedURL.getHost())) {\n if (parsedURL.getPath().startsWith(\"/support/cases/\")) {\n response.getWriter().write(gson.toJson(caseLinkPreview(parsedURL)));\n return;\n }\n }\n\n response.getWriter().write(\"{}\");\n }\n\n\n /**\n * A support case link preview.\n *\n * @param url A matching URL.\n * @return The resulting preview link card.\n */\n JsonObject caseLinkPreview(URL url) throws UnsupportedEncodingException {\n // Parses the URL and identify the case details.\n Map<String, String> caseDetails = new HashMap<String, String>();\n for (String pair : url.getQuery().split(\"&\")) {\n caseDetails.put(URLDecoder.decode(pair.split(\"=\")[0], \"UTF-8\"), URLDecoder.decode(pair.split(\"=\")[1], \"UTF-8\"));\n }\n\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n JsonObject cardHeader = new JsonObject();\n String caseName = String.format(\"Case %s\", caseDetails.get(\"name\"));\n cardHeader.add(\"title\", new JsonPrimitive(caseName));\n\n JsonObject textParagraph = new JsonObject();\n textParagraph.add(\"text\", new JsonPrimitive(caseDetails.get(\"description\")));\n\n JsonObject widget = new JsonObject();\n widget.add(\"textParagraph\", textParagraph);\n\n JsonArray widgets = new JsonArray();\n widgets.add(widget);\n\n JsonObject section = new JsonObject();\n section.add(\"widgets\", widgets);\n\n JsonArray sections = new JsonArray();\n sections.add(section);\n\n JsonObject previewCard = new JsonObject();\n previewCard.add(\"header\", cardHeader);\n previewCard.add(\"sections\", sections);\n\n JsonObject linkPreview = new JsonObject();\n linkPreview.add(\"title\", new JsonPrimitive(caseName));\n linkPreview.add(\"previewCard\", previewCard);\n\n JsonObject action = new JsonObject();\n action.add(\"linkPreview\", linkPreview);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n return renderActions;\n }\n\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.914Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":609,"estimatedTokens":4090}}935{"id":"doc-create_and_populate_folders_google_drive_google_-1c8ebe70","source":"documentation","title":"Create and populate folders | Google Drive | Google for Developers","url":"https://developers.google.com/drive/api/guides/folder","text":"Example:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\n\n/* Class to demonstrate use of Drive's create folder API */\npublic class CreateFolder {\n\n\n /**\n * Create new folder.\n *\n * @return Inserted folder id if successful, {@code null} otherwise.\n * @throws IOException if service account credentials file not found.\n */\n public static String createFolder() throws IOException {\n // Load pre-authorized user credentials from the environment.\n // TODO(developer) - See https://developers.google.com/identity for\n // guides on implementing OAuth2 for your application.\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n // File's metadata.\n File fileMetadata = new File();\n fileMetadata.setName(\"Test\");\n fileMetadata.setMimeType(\"application/vnd.google-apps.folder\");\n try {\n File file = service.files().create(fileMetadata)\n .setFields(\"id\")\n .execute();\n System.out.println(\"Folder ID: \" + file.getId());\n return file.getId();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to create folder: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef create_folder():\n \"\"\"Create a folder and prints the folder ID\n Returns : Folder Id\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n file_metadata = {\n \"name\": \"Invoices\",\n \"mimeType\": \"application/vnd.google-apps.folder\",\n }\n\n # pylint: disable=maybe-no-member\n file = service.files().create(body=file_metadata, fields=\"id\").execute()\n print(f'Folder ID: \"{file.get(\"id\")}\".')\n return file.get(\"id\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return None\n\n\nif __name__ == \"__main__\":\n create_folder()\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Creates a new folder in Google Drive.\n * @return {Promise<string|null|undefined>} The ID of the created folder.\n */\nasync function createFolder() {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // The metadata for the new folder.\n const fileMetadata = {\n name: 'Invoices',\n mimeType: 'application/vnd.google-apps.folder',\n };\n\n // Create the new folder.\n const file = await service.files.create({\n requestBody: fileMetadata,\n fields: 'id',\n });\n\n // Print the ID of the new folder.\n console.log('Folder Id:', file.data.id);\n return file.data.id;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nfunction createFolder()\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $fileMetadata = new Drive\\DriveFile(array(\n 'name' => 'Invoices',\n 'mimeType' => 'application/vnd.google-apps.folder'));\n $file = $driveService->files->create($fileMetadata, array(\n 'fields' => 'id'));\n printf(\"Folder ID: %s\\n\", $file->id);\n return $file->id;\n\n }catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n}\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use of Drive create folder API.\n public class CreateFolder\n {\n /// <summary>\n /// Creates a new folder.\n /// </summary>\n /// <returns>created folder id, null otherwise</returns>\n public static string DriveCreateFolder()\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n // File metadata\n var fileMetadata = new Google.Apis.Drive.v3.Data.File()\n {\n Name = \"Invoices\",\n MimeType = \"application/vnd.google-apps.folder\"\n };\n\n // Create a new folder on drive.\n var request = service.Files.Create(fileMetadata);\n request.Fields = \"id\";\n var file = request.Execute();\n // Prints the created folder id.\n Console.WriteLine(\"Folder ID: \" + file.Id);\n return file.Id;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"name\": \"FOLDER_NAME\",\n \"mimeType\": \"application/vnd.google-apps.folder\"\n }'\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.FileContent;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\n\n/* Class to demonstrate Drive's upload to folder use-case. */\npublic class UploadToFolder {\n\n /**\n * Upload a file to the specified folder.\n *\n * @param realFolderId Id of the folder.\n * @return Inserted file metadata if successful, {@code null} otherwise.\n * @throws IOException if service account credentials file not found.\n */\n public static File uploadToFolder(String realFolderId) throws IOException {\n // Load pre-authorized user credentials from the environment.\n // TODO(developer) - See https://developers.google.com/identity for\n // guides on implementing OAuth2 for your application.\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n\n // File's metadata.\n File fileMetadata = new File();\n fileMetadata.setName(\"photo.jpg\");\n fileMetadata.setParents(Collections.singletonList(realFolderId));\n java.io.File filePath = new java.io.File(\"files/photo.jpg\");\n FileContent mediaContent = new FileContent(\"image/jpeg\", filePath);\n try {\n File file = service.files().create(fileMetadata, mediaContent)\n .setFields(\"id, parents\")\n .execute();\n System.out.println(\"File ID: \" + file.getId());\n return file;\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to upload file: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\nfrom googleapiclient.http import MediaFileUpload\n\n\ndef upload_to_folder(folder_id):\n \"\"\"Upload a file to the specified folder and prints file ID, folder ID\n Args: Id of the folder\n Returns: ID of the file uploaded\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n file_metadata = {\"name\": \"photo.jpg\", \"parents\": [folder_id]}\n media = MediaFileUpload(\n \"download.jpeg\", mimetype=\"image/jpeg\", resumable=True\n )\n # pylint: disable=maybe-no-member\n file = (\n service.files()\n .create(body=file_metadata, media_body=media, fields=\"id\")\n .execute()\n )\n print(f'File ID: \"{file.get(\"id\")}\".')\n return file.get(\"id\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return None\n\n\nif __name__ == \"__main__\":\n upload_to_folder(folder_id=\"1s0oKEZZXjImNngxHGnY0xed6Mw-tvspu\")\n```\n\nExample:\n```text\nimport fs from 'node:fs';\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Uploads a file to the specified folder.\n * @param {string} folderId The ID of the folder to upload the file to.\n * @return {Promise<string>} The ID of the uploaded file.\n */\nasync function uploadToFolder(folderId) {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // The request body for the file to be uploaded.\n const requestBody = {\n name: 'photo.jpg',\n parents: [folderId],\n };\n\n // The media content to be uploaded.\n const media = {\n mimeType: 'image/jpeg',\n body: fs.createReadStream('files/photo.jpg'),\n };\n\n // Upload the file to the specified folder.\n const file = await service.files.create({\n requestBody,\n media,\n fields: 'id',\n });\n\n // Print the ID of the uploaded file.\n console.log('File Id:', file.data.id);\n if (!file.data.id) {\n throw new Error('File ID not found.');\n }\n return file.data.id;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nfunction uploadToFolder($folderId)\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $fileMetadata = new Drive\\DriveFile(array(\n 'name' => 'photo.jpg',\n 'parents' => array($folderId)\n ));\n $content = file_get_contents('../files/photo.jpg');\n $file = $driveService->files->create($fileMetadata, array(\n 'data' => $content,\n 'mimeType' => 'image/jpeg',\n 'uploadType' => 'multipart',\n 'fields' => 'id'));\n printf(\"File ID: %s\\n\", $file->id);\n return $file->id;\n } catch (Exception $e) {\n echo \"Error Message: \" . $e;\n }\n}\nrequire_once 'vendor/autoload.php';\nuploadToFolder();\n```\n\nExample:\n```text\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use of Drive upload to folder.\n public class UploadToFolder\n {\n /// <summary>\n /// Upload a file to the specified folder.\n /// </summary>\n /// <param name=\"filePath\">Image path to upload.</param>\n /// <param name=\"folderId\">Id of the folder.</param>\n /// <returns>Inserted file metadata if successful, null otherwise</returns>\n public static Google.Apis.Drive.v3.Data.File DriveUploadToFolder\n (string filePath, string folderId)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n // Upload file photo.jpg in specified folder on drive.\n var fileMetadata = new Google.Apis.Drive.v3.Data.File()\n {\n Name = \"photo.jpg\",\n Parents = new List<string>\n {\n folderId\n }\n };\n FilesResource.CreateMediaUpload request;\n // Create a new file on drive.\n using (var stream = new FileStream(filePath,\n FileMode.Open))\n {\n // Create a new file, with metadata and stream.\n request = service.Files.Create(\n fileMetadata, stream, \"image/jpeg\");\n request.Fields = \"id\";\n request.Upload();\n }\n var file = request.ResponseBody;\n // Prints the uploaded file id.\n Console.WriteLine(\"File ID: \" + file.Id);\n return file;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is FileNotFoundException)\n {\n Console.WriteLine(\"File not found\");\n }\n else if (e is DirectoryNotFoundException)\n {\n Console.WriteLine(\"Directory Not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\ncurl -X POST 'https://www.googleapis.com/drive/v3/files' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Content-Type: application/json' \\\n -d '{\n \"name\": \"photo.jpg\",\n \"parents\": [\n \"FOLDER_ID\"\n ]\n }'\n```\n\nExample:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.drive.Drive;\nimport com.google.api.services.drive.DriveScopes;\nimport com.google.api.services.drive.model.File;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.List;\n\n/* Class to demonstrate use case for moving file to folder.*/\npublic class MoveFileToFolder {\n\n\n /**\n * @param fileId Id of file to be moved.\n * @param folderId Id of folder where the fill will be moved.\n * @return list of parent ids for the file.\n */\n public static List<String> moveFileToFolder(String fileId, String folderId)\n throws IOException {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application.*/\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(Arrays.asList(DriveScopes.DRIVE_FILE));\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(\n credentials);\n // Build a new authorized API client service.\n Drive service = new Drive.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Drive samples\")\n .build();\n\n // Retrieve the existing parents to remove\n File file = service.files().get(fileId)\n .setFields(\"parents\")\n .execute();\n StringBuilder previousParents = new StringBuilder();\n for (String parent : file.getParents()) {\n previousParents.append(parent);\n previousParents.append(',');\n }\n try {\n // Move the file to the new folder\n file = service.files().update(fileId, null)\n .setAddParents(folderId)\n .setRemoveParents(previousParents.toString())\n .setFields(\"id, parents\")\n .execute();\n\n return file.getParents();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n System.err.println(\"Unable to move file: \" + e.getDetails());\n throw e;\n }\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef move_file_to_folder(file_id, folder_id):\n \"\"\"Move specified file to the specified folder.\n Args:\n file_id: Id of the file to move.\n folder_id: Id of the folder\n Print: An object containing the new parent folder and other meta data\n Returns : Parent Ids for the file\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # call drive api client\n service = build(\"drive\", \"v3\", credentials=creds)\n\n # pylint: disable=maybe-no-member\n # Retrieve the existing parents to remove\n file = service.files().get(fileId=file_id, fields=\"parents\").execute()\n previous_parents = \",\".join(file.get(\"parents\"))\n # Move the file to the new folder\n file = (\n service.files()\n .update(\n fileId=file_id,\n addParents=folder_id,\n removeParents=previous_parents,\n fields=\"id, parents\",\n )\n .execute()\n )\n return file.get(\"parents\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n return None\n\n\nif __name__ == \"__main__\":\n move_file_to_folder(\n file_id=\"1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9\",\n folder_id=\"1jvTFoyBhUspwDncOTB25kb9k0Fl0EqeN\",\n )\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Moves a file to a new folder in Google Drive.\n * @param {string} fileId The ID of the file to move.\n * @param {string} folderId The ID of the folder to move the file to.\n * @return {Promise<number>} The status of the move operation.\n */\nasync function moveFileToFolder(fileId, folderId) {\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Drive API client (v3).\n const service = google.drive({version: 'v3', auth});\n\n // Get the file's metadata to retrieve its current parents.\n const file = await service.files.get({\n fileId,\n fields: 'parents',\n });\n\n // Get the current parents as a comma-separated string.\n const previousParents = (file.data.parents ?? []).join(',');\n\n // Move the file to the new folder.\n const result = await service.files.update({\n fileId,\n addParents: folderId,\n removeParents: previousParents,\n fields: 'id, parents',\n });\n\n // Print the status of the move operation.\n console.log(result.status);\n return result.status;\n}\n```\n\nExample:\n```text\n<?php\nuse Google\\Client;\nuse Google\\Service\\Drive;\nuse Google\\Service\\Drive\\DriveFile;\nfunction moveFileToFolder($fileId,$folderId)\n{\n try {\n $client = new Client();\n $client->useApplicationDefaultCredentials();\n $client->addScope(Drive::DRIVE);\n $driveService = new Drive($client);\n $emptyFileMetadata = new DriveFile();\n // Retrieve the existing parents to remove\n $file = $driveService->files->get($fileId, array('fields' => 'parents'));\n $previousParents = join(',', $file->parents);\n // Move the file to the new folder\n $file = $driveService->files->update($fileId, $emptyFileMetadata, array(\n 'addParents' => $folderId,\n 'removeParents' => $previousParents,\n 'fields' => 'id, parents'));\n return $file->parents;\n } catch(Exception $e) {\n echo \"Error Message: \".$e;\n }\n}\n```\n\nExample:\n```text\nusing Google;\nusing Google.Apis.Auth.OAuth2;\nusing Google.Apis.Drive.v3;\nusing Google.Apis.Services;\n\nnamespace DriveV3Snippets\n{\n // Class to demonstrate use-case of Drive move file to folder.\n public class MoveFileToFolder\n {\n /// <summary>\n /// Move specified file to the specified folder.\n /// </summary>\n /// <param name=\"fileId\">Id of file to be moved.</param>\n /// <param name=\"folderId\">Id of folder where the fill will be moved.</param>\n /// <returns>list of parent ids for the file, null otherwise.</returns>\n public static IList<string> DriveMoveFileToFolder(string fileId,\n string folderId)\n {\n try\n {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for \n guides on implementing OAuth2 for your application. */\n GoogleCredential credential = GoogleCredential.GetApplicationDefault()\n .CreateScoped(DriveService.Scope.Drive);\n\n // Create Drive API service.\n var service = new DriveService(new BaseClientService.Initializer\n {\n HttpClientInitializer = credential,\n ApplicationName = \"Drive API Snippets\"\n });\n\n // Retrieve the existing parents to remove\n var getRequest = service.Files.Get(fileId);\n getRequest.Fields = \"parents\";\n var file = getRequest.Execute();\n var previousParents = String.Join(\",\", file.Parents);\n // Move the file to the new folder\n var updateRequest =\n service.Files.Update(new Google.Apis.Drive.v3.Data.File(),\n fileId);\n updateRequest.Fields = \"id, parents\";\n updateRequest.AddParents = folderId;\n updateRequest.RemoveParents = previousParents;\n file = updateRequest.Execute();\n\n return file.Parents;\n }\n catch (Exception e)\n {\n // TODO(developer) - handle error appropriately\n if (e is AggregateException)\n {\n Console.WriteLine(\"Credential Not found\");\n }\n else if (e is GoogleApiException)\n {\n Console.WriteLine(\"File or Folder not found\");\n }\n else\n {\n throw;\n }\n }\n return null;\n }\n }\n}\n```\n\nExample:\n```text\ncurl -X PATCH 'https://www.googleapis.com/drive/v3/files/FILE_ID?addParents=NEW_PARENT_ID&removeParents=PREVIOUS_PARENT_ID' \\\n -H 'Authorization: Bearer ACCESS_TOKEN' \\\n -H 'Accept: application/json'\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.916Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":800,"estimatedTokens":6349}}936{"id":"doc-rest_resource_spaces_google_meet_google_for_deve-81eabc4a","source":"documentation","title":"REST Resource: spaces | Google Meet | Google for Developers","url":"https://developers.google.com/meet/api/reference/rest/v2beta/spaces","text":"Example:\n```text\nSpaceConfigActiveConferencePhoneAccessGatewaySipAccess\n```\n\nExample:\n```text\nAccessTypeEntryPointAccessModerationModerationRestrictionsAttendanceReportGenerationTypeArtifactConfig\n```\n\nExample:\n```text\nRestrictionTypeRestrictionTypeRestrictionTypeDefaultJoinAsViewerType\n```\n\nExample:\n```text\nRecordingConfigTranscriptionConfigSmartNotesConfig\n```\n\nExample:\n```text\nAutoGenerationType\n```\n\nExample:\n```text\n{\n \"conferenceRecord\": string\n}\n```\n\nExample:\n```text\n{\n \"phoneNumber\": string,\n \"pin\": string,\n \"regionCode\": string,\n \"languageCode\": string\n}\n```\n\nExample:\n```text\n{\n \"uri\": string,\n \"sipAccessCode\": string\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.918Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":51,"estimatedTokens":165}}937{"id":"doc-manage_focus_time_out_of_office_and_working_loca-c4e10ec5","source":"documentation","title":"Manage focus time, out of office, and working location events | Google Calendar | Google for Developers","url":"https://developers.google.com/calendar/api/guides/calendar-status","text":"Example:\n```text\n/** Creates a focus time event. */\nfunction createFocusTime() {\n const event = {\n start: { dateTime: '2023-11-14T10:00:00+01:00' },\n end: { dateTime: '2023-11-14T12:00:00+01:00' },\n eventType: 'focusTime',\n focusTimeProperties: {\n chatStatus: 'doNotDisturb',\n autoDeclineMode: 'declineOnlyNewConflictingInvitations',\n declineMessage: 'Declined because I am in focus time.',\n }\n }\n createEvent(event);\n}\n\n/** Creates an out of office event. */\nfunction createOutOfOffice() {\n const event = {\n start: { dateTime: '2023-11-15T10:00:00+01:00' },\n end: { dateTime: '2023-11-15T18:00:00+01:00' },\n eventType: 'outOfOffice',\n outOfOfficeProperties: {\n autoDeclineMode: 'declineOnlyNewConflictingInvitations',\n declineMessage: 'Declined because I am on vacation.',\n }\n }\n createEvent(event);\n}\n\n/** Creates a working location event. */\nfunction createWorkingLocation() {\n const event = {\n start: { date: \"2023-06-01\" },\n end: { date: \"2023-06-02\" },\n eventType: \"workingLocation\",\n visibility: \"public\",\n transparency: \"transparent\",\n workingLocationProperties: {\n type: 'customLocation',\n customLocation: { label: \"a custom location\" },\n }\n }\n createEvent(event);\n}\n\n/**\n * Creates a Calendar event.\n * See https://developers.google.com/workspace/calendar/api/v3/reference/events/insert\n */\nfunction createEvent(event) {\n const calendarId = 'primary';\n\n try {\n var response = Calendar.Events.insert(event, calendarId);\n var event = (response.eventType === 'workingLocation') ? parseWorkingLocation(response) : response;\n console.log(event);\n } catch (exception) {\n console.log(exception.message);\n }\n}\n\n/**\n * Reads the event with the given eventId.\n * See https://developers.google.com/workspace/calendar/api/v3/reference/events/get\n */\nfunction readEvent() {\n const calendarId = 'primary';\n\n // Replace with a valid eventId.\n const eventId = \"sample-event-id\";\n\n try {\n var response = Calendar.Events.get(calendarId, eventId);\n var event = (response.eventType === 'workingLocation') ? parseWorkingLocation(response) : response;\n console.log(event);\n } catch (exception) {\n console.log(exception.message);\n }\n}\n\n/** Lists focus time events. */\nfunction listFocusTimes() {\n listEvents('focusTime');\n}\n\n/** Lists out of office events. */\nfunction listOutOfOffices() {\n listEvents('outOfOffice');\n}\n\n/** Lists working location events. */\nfunction listWorkingLocations() {\n listEvents('workingLocation');\n}\n\n/**\n * Lists events with the given event type.\n * See https://developers.google.com/workspace/calendar/api/v3/reference/events/list\n */\nfunction listEvents(eventType = 'default') {\n const calendarId = 'primary'\n\n // Query parameters for the list request.\n const optionalArgs = {\n eventTypes: [eventType],\n showDeleted: false,\n singleEvents: true,\n timeMax: '2023-04-01T00:00:00+01:00',\n timeMin: '2023-03-27T00:00:00+01:00',\n }\n try {\n var response = Calendar.Events.list(calendarId, optionalArgs);\n response.items.forEach(event =>\n console.log(eventType === 'workingLocation' ? parseWorkingLocation(event) : event));\n } catch (exception) {\n console.log(exception.message);\n }\n}\n\n/**\n * Parses working location properties of an event into a string.\n * See https://developers.google.com/workspace/calendar/api/v3/reference/events#resource\n */\nfunction parseWorkingLocation(event) {\n if (event.eventType != \"workingLocation\") {\n throw new Error(\"'\" + event.summary + \"' is not a working location event.\");\n }\n\n var location = 'No Location';\n const workingLocation = event.workingLocationProperties;\n if (workingLocation) {\n if (workingLocation.type === 'homeOffice') {\n location = 'Home';\n }\n if (workingLocation.type === 'officeLocation') {\n location = workingLocation.officeLocation.label;\n }\n if (workingLocation.type === 'customLocation') {\n location = workingLocation.customLocation.label;\n }\n }\n return `${event.start.date}: ${location}`;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.919Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":148,"estimatedTokens":1025}}938{"id":"doc-manage_projects_with_google_chat_vertex_ai_and_f-6c060c69","source":"documentation","title":"Manage projects with Google Chat, Vertex AI, and Firestore | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/chat/tutorial-project-management","text":"Example:\n```text\ngcloud projects create PROJECT_ID\n```\n\nExample:\n```text\ngcloud billing accounts list\n```\n\nExample:\n```text\ngcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID\n```\n\nExample:\n```text\ngcloud config set project PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable chat.googleapis.com \\\naiplatform.googleapis.com \\\ncloudfunctions.googleapis.com \\\nfirestore.googleapis.com \\\ncloudbuild.googleapis.com \\\npubsub.googleapis.com \\\nrun.googleapis.com\n```\n\nExample:\n```text\ngcloud firestore databases create \\\n--location=LOCATION \\\n--type=firestore-native\n```\n\nExample:\n```text\ngit clone https://github.com/googleworkspace/add-ons-samples.git\n```\n\nExample:\n```text\ncd add-ons-samples/node/chat/project-management-app\n```\n\nExample:\n```text\ngcloud functions deploy project-management-tutorial \\\n--gen2 \\\n--region=REGION \\\n--runtime=nodejs20 \\\n--source=. \\\n--entry-point=projectManagementChatApp \\\n--trigger-http \\\n--allow-unauthenticated\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.931Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":61,"estimatedTokens":247}}939{"id":"doc-rest_resource_conferencerecords_transcripts_goog-88383cfc","source":"documentation","title":"REST Resource: conferenceRecords.transcripts | Google Meet | Google for Developers","url":"https://developers.google.com/meet/api/reference/rest/v2beta/conferenceRecords.transcripts","text":"Example:\n```text\nStateDocsDestination\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.932Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":6,"estimatedTokens":14}}940{"id":"doc-advanced_drive_service_apps_script_google_for_de-b16ec40d","source":"documentation","title":"Advanced Drive Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/advanced/drive","text":"Example:\n```text\n/**\n * Uploads a new file to the user's Drive.\n */\nfunction uploadFile() {\n try {\n // Makes a request to fetch a URL.\n const image = UrlFetchApp.fetch(\"http://goo.gl/nd7zjB\").getBlob();\n let file = {\n name: \"google_logo.png\",\n mimeType: \"image/png\",\n };\n // Create a file in the user's Drive.\n file = Drive.Files.create(file, image, { fields: \"id,size\" });\n console.log(\"ID: %s, File size (bytes): %s\", file.id, file.size);\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed to upload file with error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Creates a new folder.\n */\nfunction createFolder() {\n var folderMetadata = {\n 'name': 'New Folder',\n 'mimeType': 'application/vnd.google-apps.folder'\n };\n var folder = Drive.Files.create(folderMetadata);\n Logger.log('Folder ID: ' + folder.id);\n}\n```\n\nExample:\n```text\n/**\n * Searches for files with a specific name.\n */\nfunction searchFiles() {\n var query = 'name contains \"Project Plan\" and trashed = false';\n var files = Drive.Files.list({\n 'q': query,\n 'fields': 'files(id, name, mimeType)'\n });\n if (files.files && files.files.length > 0) {\n for (var i = 0; i < files.files.length; i++) {\n var file = files.files[i];\n Logger.log('%s (ID: %s)', file.name, file.id);\n }\n } else {\n Logger.log('No files found.');\n }\n}\n```\n\nExample:\n```text\n/**\n * Lists the top-level folders in the user's Drive.\n */\nfunction listRootFolders() {\n const query =\n '\"root\" in parents and trashed = false and ' +\n 'mimeType = \"application/vnd.google-apps.folder\"';\n let folders;\n let pageToken = null;\n do {\n try {\n folders = Drive.Files.list({\n q: query,\n pageSize: 100,\n pageToken: pageToken,\n });\n if (!folders.files || folders.files.length === 0) {\n console.log(\"All folders found.\");\n return;\n }\n for (let i = 0; i < folders.files.length; i++) {\n const folder = folders.files[i];\n console.log(\"%s (ID: %s)\", folder.name, folder.id);\n }\n pageToken = folders.nextPageToken;\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n }\n } while (pageToken);\n}\n```\n\nExample:\n```text\n/**\n * Lists the revisions of a given file.\n * @param {string} fileId The ID of the file to list revisions for.\n */\nfunction listRevisions(fileId) {\n let revisions;\n let pageToken = null;\n do {\n try {\n revisions = Drive.Revisions.list(fileId, {\n fields: \"revisions(modifiedTime,size),nextPageToken\",\n });\n if (!revisions.revisions || revisions.revisions.length === 0) {\n console.log(\"All revisions found.\");\n return;\n }\n for (let i = 0; i < revisions.revisions.length; i++) {\n const revision = revisions.revisions[i];\n const date = new Date(revision.modifiedTime);\n console.log(\n \"Date: %s, File size (bytes): %s\",\n date.toLocaleString(),\n revision.size,\n );\n }\n pageToken = revisions.nextPageToken;\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n }\n } while (pageToken);\n}\n```\n\nExample:\n```text\n/**\n * Adds a custom app property to a file. Unlike Apps Script's DocumentProperties,\n * Drive's custom file properties can be accessed outside of Apps Script and\n * by other applications; however, appProperties are only visible to the script.\n * @param {string} fileId The ID of the file to add the app property to.\n */\nfunction addAppProperty(fileId) {\n try {\n let file = {\n appProperties: {\n department: \"Sales\",\n },\n };\n // Updates a file to add an app property.\n file = Drive.Files.update(file, fileId, null, {\n fields: \"id,appProperties\",\n });\n console.log(\n \"ID: %s, appProperties: %s\",\n file.id,\n JSON.stringify(file.appProperties, null, 2),\n );\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Adds a user to a file as an editor without sending an email notification.\n */\nfunction addEditor() {\n var fileId = '1234567890abcdefghijklmnopqrstuvwxyz';\n var userEmail = 'bob@example.com';\n var request = {\n 'role': 'writer',\n 'type': 'user',\n 'emailAddress': userEmail\n };\n Drive.Permissions.create(request, fileId, {\n 'sendNotificationEmail': false\n });\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.934Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":182,"estimatedTokens":1139}}941{"id":"doc-populate_a_team_vacation_calendar_apps_script_go-b5d73634","source":"documentation","title":"Populate a team vacation calendar | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/vacation-calendar","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/vacation-calendar\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Set the ID of the team calendar to add events to. You can find the calendar's\n// ID on the settings page.\nconst TEAM_CALENDAR_ID = \"ENTER_TEAM_CALENDAR_ID_HERE\";\n// Set the email address of the Google Group that contains everyone in the team.\n// Ensure the group has less than 500 members to avoid timeouts.\n// Change to an array in order to add indirect members frrm multiple groups, for example:\n// let GROUP_EMAIL = ['ENTER_GOOGLE_GROUP_EMAIL_HERE', 'ENTER_ANOTHER_GOOGLE_GROUP_EMAIL_HERE'];\nconst GROUP_EMAIL = \"ENTER_GOOGLE_GROUP_EMAIL_HERE\";\n\nconst ONLY_DIRECT_MEMBERS = false;\n\nconst KEYWORDS = [\"vacation\", \"ooo\", \"out of office\", \"offline\"];\nconst MONTHS_IN_ADVANCE = 3;\n\n/**\n * Sets up the script to run automatically every hour.\n */\nfunction setup() {\n const triggers = ScriptApp.getProjectTriggers();\n if (triggers.length > 0) {\n throw new Error(\"Triggers are already setup.\");\n }\n ScriptApp.newTrigger(\"sync\").timeBased().everyHours(1).create();\n // Runs the first sync immediately.\n sync();\n}\n\n/**\n * Looks through the group members' public calendars and adds any\n * 'vacation' or 'out of office' events to the team calendar.\n */\nfunction sync() {\n // Defines the calendar event date range to search.\n const today = new Date();\n const maxDate = new Date();\n maxDate.setMonth(maxDate.getMonth() + MONTHS_IN_ADVANCE);\n\n // Determines the time the the script was last run.\n let lastRun = PropertiesService.getScriptProperties().getProperty(\"lastRun\");\n lastRun = lastRun ? new Date(lastRun) : null;\n\n // Gets the list of users in the Google Group.\n let users = getAllMembers(GROUP_EMAIL);\n if (ONLY_DIRECT_MEMBERS) {\n users = GroupsApp.getGroupByEmail(GROUP_EMAIL).getUsers();\n } else if (Array.isArray(GROUP_EMAIL)) {\n users = getUsersFromGroups(GROUP_EMAIL);\n }\n\n // For each user, finds events having one or more of the keywords in the event\n // summary in the specified date range. Imports each of those to the team\n // calendar.\n let count = 0;\n for (const user of users) {\n const username = user.getEmail().split(\"@\")[0];\n const events = findEvents(user, today, maxDate, lastRun);\n for (const event of events) {\n importEvent(username, event);\n count++;\n }\n }\n\n PropertiesService.getScriptProperties().setProperty(\"lastRun\", today);\n console.log(`Imported ${count} events`);\n}\n\n/**\n * Imports the given event from the user's calendar into the shared team\n * calendar.\n * @param {string} username The team member that is attending the event.\n * @param {Calendar.Event} event The event to import.\n */\nfunction importEvent(username, event) {\n event.summary = `[${username}] ${event.summary}`;\n event.organizer = {\n id: TEAM_CALENDAR_ID,\n };\n event.attendees = [];\n\n // If the event is not of type 'default', it can't be imported, so it needs\n // to be changed.\n if (event.eventType !== \"default\") {\n event.eventType = \"default\";\n event.outOfOfficeProperties = undefined;\n event.focusTimeProperties = undefined;\n }\n\n console.log(\"Importing: %s\", event.summary);\n try {\n Calendar.Events.import(event, TEAM_CALENDAR_ID);\n } catch (e) {\n console.error(\n \"Error attempting to import event: %s. Skipping.\",\n e.toString(),\n );\n }\n}\n\n/**\n * In a given user's calendar, looks for occurrences of the given keyword\n * in events within the specified date range and returns any such events\n * found.\n * @param {Session.User} user The user to retrieve events for.\n * @param {string} keyword The keyword to look for.\n * @param {Date} start The starting date of the range to examine.\n * @param {Date} end The ending date of the range to examine.\n * @param {Date} optSince A date indicating the last time this script was run.\n * @return {Calendar.Event[]} An array of calendar events.\n */\nfunction findEvents(user, start, end, optSince) {\n const params = {\n eventTypes: \"outOfOffice\",\n timeMin: formatDateAsRFC3339(start),\n timeMax: formatDateAsRFC3339(end),\n showDeleted: true,\n };\n if (optSince) {\n // This prevents the script from examining events that have not been\n // modified since the specified date (that is, the last time the\n // script was run).\n params.updatedMin = formatDateAsRFC3339(optSince);\n }\n let pageToken = null;\n let events = [];\n do {\n params.pageToken = pageToken;\n let response;\n try {\n response = Calendar.Events.list(user.getEmail(), params);\n } catch (e) {\n console.error(\n \"Error retriving events for %s, %s: %s; skipping\",\n user,\n keyword,\n e.toString(),\n );\n continue;\n }\n events = events.concat(response.items);\n pageToken = response.nextPageToken;\n } while (pageToken);\n return events;\n}\n\n/**\n * Returns an RFC3339 formated date String corresponding to the given\n * Date object.\n * @param {Date} date a Date.\n * @return {string} a formatted date string.\n */\nfunction formatDateAsRFC3339(date) {\n return Utilities.formatDate(date, \"UTC\", \"yyyy-MM-dd'T'HH:mm:ssZ\");\n}\n\n/**\n * Get both direct and indirect members (and delete duplicates).\n * @param {string} the e-mail address of the group.\n * @return {object} direct and indirect members.\n */\nfunction getAllMembers(groupEmail) {\n const group = GroupsApp.getGroupByEmail(groupEmail);\n let users = group.getUsers();\n const childGroups = group.getGroups();\n for (let i = 0; i < childGroups.length; i++) {\n const childGroup = childGroups[i];\n users = users.concat(getAllMembers(childGroup.getEmail()));\n }\n // Remove duplicate members\n const uniqueUsers = [];\n const userEmails = {};\n for (let i = 0; i < users.length; i++) {\n const user = users[i];\n if (!userEmails[user.getEmail()]) {\n uniqueUsers.push(user);\n userEmails[user.getEmail()] = true;\n }\n }\n return uniqueUsers;\n}\n\n/**\n * Get indirect members from multiple groups (and delete duplicates).\n * @param {array} the e-mail addresses of multiple groups.\n * @return {object} indirect members of multiple groups.\n */\nfunction getUsersFromGroups(groupEmails) {\n const users = [];\n for (const groupEmail of groupEmails) {\n const groupUsers = GroupsApp.getGroupByEmail(groupEmail).getUsers();\n for (const user of groupUsers) {\n if (!users.some((u) => u.getEmail() === user.getEmail())) {\n users.push(user);\n }\n }\n }\n return users;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.935Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":221,"estimatedTokens":1769}}942{"id":"doc-manage_focus_time_out_of_office_and_working_loca-fd596405","source":"documentation","title":"Manage focus time, out of office, and working location events | Google Calendar | Google for Developers","url":"https://developers.google.com/calendar/api/guides/working-hours-and-location","text":"Example:\n```text\n/** Creates a focus time event. */\nfunction createFocusTime() {\n const event = {\n start: { dateTime: '2023-11-14T10:00:00+01:00' },\n end: { dateTime: '2023-11-14T12:00:00+01:00' },\n eventType: 'focusTime',\n focusTimeProperties: {\n chatStatus: 'doNotDisturb',\n autoDeclineMode: 'declineOnlyNewConflictingInvitations',\n declineMessage: 'Declined because I am in focus time.',\n }\n }\n createEvent(event);\n}\n\n/** Creates an out of office event. */\nfunction createOutOfOffice() {\n const event = {\n start: { dateTime: '2023-11-15T10:00:00+01:00' },\n end: { dateTime: '2023-11-15T18:00:00+01:00' },\n eventType: 'outOfOffice',\n outOfOfficeProperties: {\n autoDeclineMode: 'declineOnlyNewConflictingInvitations',\n declineMessage: 'Declined because I am on vacation.',\n }\n }\n createEvent(event);\n}\n\n/** Creates a working location event. */\nfunction createWorkingLocation() {\n const event = {\n start: { date: \"2023-06-01\" },\n end: { date: \"2023-06-02\" },\n eventType: \"workingLocation\",\n visibility: \"public\",\n transparency: \"transparent\",\n workingLocationProperties: {\n type: 'customLocation',\n customLocation: { label: \"a custom location\" },\n }\n }\n createEvent(event);\n}\n\n/**\n * Creates a Calendar event.\n * See https://developers.google.com/workspace/calendar/api/v3/reference/events/insert\n */\nfunction createEvent(event) {\n const calendarId = 'primary';\n\n try {\n var response = Calendar.Events.insert(event, calendarId);\n var event = (response.eventType === 'workingLocation') ? parseWorkingLocation(response) : response;\n console.log(event);\n } catch (exception) {\n console.log(exception.message);\n }\n}\n\n/**\n * Reads the event with the given eventId.\n * See https://developers.google.com/workspace/calendar/api/v3/reference/events/get\n */\nfunction readEvent() {\n const calendarId = 'primary';\n\n // Replace with a valid eventId.\n const eventId = \"sample-event-id\";\n\n try {\n var response = Calendar.Events.get(calendarId, eventId);\n var event = (response.eventType === 'workingLocation') ? parseWorkingLocation(response) : response;\n console.log(event);\n } catch (exception) {\n console.log(exception.message);\n }\n}\n\n/** Lists focus time events. */\nfunction listFocusTimes() {\n listEvents('focusTime');\n}\n\n/** Lists out of office events. */\nfunction listOutOfOffices() {\n listEvents('outOfOffice');\n}\n\n/** Lists working location events. */\nfunction listWorkingLocations() {\n listEvents('workingLocation');\n}\n\n/**\n * Lists events with the given event type.\n * See https://developers.google.com/workspace/calendar/api/v3/reference/events/list\n */\nfunction listEvents(eventType = 'default') {\n const calendarId = 'primary'\n\n // Query parameters for the list request.\n const optionalArgs = {\n eventTypes: [eventType],\n showDeleted: false,\n singleEvents: true,\n timeMax: '2023-04-01T00:00:00+01:00',\n timeMin: '2023-03-27T00:00:00+01:00',\n }\n try {\n var response = Calendar.Events.list(calendarId, optionalArgs);\n response.items.forEach(event =>\n console.log(eventType === 'workingLocation' ? parseWorkingLocation(event) : event));\n } catch (exception) {\n console.log(exception.message);\n }\n}\n\n/**\n * Parses working location properties of an event into a string.\n * See https://developers.google.com/workspace/calendar/api/v3/reference/events#resource\n */\nfunction parseWorkingLocation(event) {\n if (event.eventType != \"workingLocation\") {\n throw new Error(\"'\" + event.summary + \"' is not a working location event.\");\n }\n\n var location = 'No Location';\n const workingLocation = event.workingLocationProperties;\n if (workingLocation) {\n if (workingLocation.type === 'homeOffice') {\n location = 'Home';\n }\n if (workingLocation.type === 'officeLocation') {\n location = workingLocation.officeLocation.label;\n }\n if (workingLocation.type === 'customLocation') {\n location = workingLocation.customLocation.label;\n }\n }\n return `${event.start.date}: ${location}`;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.937Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":148,"estimatedTokens":1025}}943{"id":"doc-installable_triggers_apps_script_google_for_deve-c99cfd1c","source":"documentation","title":"Installable Triggers | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/triggers/installable","text":"Example:\n```text\n/**\n * Creates two time-driven triggers.\n * @see https://developers.google.com/apps-script/guides/triggers/installable#time-driven_triggers\n */\nfunction createTimeDrivenTriggers() {\n // Trigger every 6 hours.\n ScriptApp.newTrigger(\"myFunction\").timeBased().everyHours(6).create();\n // Trigger every Monday at 09:00.\n ScriptApp.newTrigger(\"myFunction\")\n .timeBased()\n .onWeekDay(ScriptApp.WeekDay.MONDAY)\n .atHour(9)\n .create();\n}\n```\n\nExample:\n```text\n/**\n * Creates a trigger for when a spreadsheet opens.\n * @see https://developers.google.com/apps-script/guides/triggers/installable\n */\nfunction createSpreadsheetOpenTrigger() {\n const ss = SpreadsheetApp.getActive();\n ScriptApp.newTrigger(\"myFunction\").forSpreadsheet(ss).onOpen().create();\n}\n```\n\nExample:\n```text\n/**\n * Deletes a trigger.\n * @param {string} triggerId The Trigger ID.\n * @see https://developers.google.com/apps-script/guides/triggers/installable\n */\nfunction deleteTrigger(triggerId) {\n // Loop over all triggers.\n const allTriggers = ScriptApp.getProjectTriggers();\n for (let index = 0; index < allTriggers.length; index++) {\n // If the current trigger is the correct one, delete it.\n if (allTriggers[index].getUniqueId() === triggerId) {\n ScriptApp.deleteTrigger(allTriggers[index]);\n break;\n }\n }\n}\n```\n\nExample:\n```text\nFrom: noreply-apps-scripts-notifications@google.com\nSubject: Summary of failures for Apps Script\nYour script has recently failed to finish successfully.\nA summary of the failure(s) is shown below.\n```\n\nExample:\n```text\nSimple triggers like `onOpen()` can't be deactivated from this\n page; instead, edit the appropriate script and remove or rename\n the `onOpen()` function.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.938Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":66,"estimatedTokens":436}}944{"id":"doc-rest_resource_conferencerecords_recordings_googl-43c7d7c3","source":"documentation","title":"REST Resource: conferenceRecords.recordings | Google Meet | Google for Developers","url":"https://developers.google.com/meet/api/reference/rest/v2beta/conferenceRecords.recordings","text":"Example:\n```text\nStateDriveDestination\n```\n\nExample:\n```text\n{\n \"file\": string,\n \"exportUri\": string\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.939Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":14,"estimatedTokens":31}}945{"id":"doc-build_a_homepage_for_a_google_chat_app_google_fo-113af00c","source":"documentation","title":"Build a homepage for a Google Chat app | Google for Developers","url":"https://developers.google.com/chat/send-app-home-card-message","text":"Example:\n```text\napp.post('/', async (req, res) => {\n let event = req.body.chat;\n\n let body = {};\n if (event.type === 'APP_HOME') {\n // App home is requested\n body = { action: { navigations: [{\n pushCard: getHomeCard()\n }]}}\n } else if (event.type === 'SUBMIT_FORM') {\n // The update button from app home is clicked\n commonEvent = req.body.commonEventObject;\n if (commonEvent && commonEvent.invokedFunction === 'updateAppHome') {\n body = updateAppHome()\n }\n }\n\n return res.json(body);\n});\n\n// Create the app home card\nfunction getHomeCard() {\n return { sections: [{ widgets: [\n { textParagraph: {\n text: \"Here is the app home 🏠 It's \" + new Date().toTimeString()\n }},\n { buttonList: { buttons: [{\n text: \"Update app home\",\n onClick: { action: {\n function: \"updateAppHome\"\n }}\n }]}}\n ]}]};\n}\n```\n\nExample:\n```text\n@app.route('/', methods=['POST'])\ndef post() -> Mapping[str, Any]:\n \"\"\"Handle requests from Google Chat\n\n Returns:\n Mapping[str, Any]: the response\n \"\"\"\n event = request.get_json()\n match event['chat'].get('type'):\n\n case 'APP_HOME':\n # App home is requested\n body = { \"action\": { \"navigations\": [{\n \"pushCard\": get_home_card()\n }]}}\n\n case 'SUBMIT_FORM':\n # The update button from app home is clicked\n event_object = event.get('commonEventObject')\n if event_object is not None:\n if 'update_app_home' == event_object.get('invokedFunction'):\n body = update_app_home()\n\n case _:\n # Other response types are not supported\n body = {}\n\n return json.jsonify(body)\n\n\ndef get_home_card() -> Mapping[str, Any]:\n \"\"\"Create the app home card\n\n Returns:\n Mapping[str, Any]: the card\n \"\"\"\n return { \"sections\": [{ \"widgets\": [\n { \"textParagraph\": {\n \"text\": \"Here is the app home 🏠 It's \" +\n datetime.datetime.now().isoformat()\n }},\n { \"buttonList\": { \"buttons\": [{\n \"text\": \"Update app home\",\n \"onClick\": { \"action\": {\n \"function\": \"update_app_home\"\n }}\n }]}}\n ]}]}\n```\n\nExample:\n```text\n// Process Google Chat events\n@PostMapping(\"/\")\n@ResponseBody\npublic GenericJson onEvent(@RequestBody JsonNode event) throws Exception {\n switch (event.at(\"/chat/type\").asText()) {\n case \"APP_HOME\":\n // App home is requested\n GenericJson navigation = new GenericJson();\n navigation.set(\"pushCard\", getHomeCard());\n\n GenericJson action = new GenericJson();\n action.set(\"navigations\", List.of(navigation));\n\n GenericJson response = new GenericJson();\n response.set(\"action\", action);\n return response;\n case \"SUBMIT_FORM\":\n // The update button from app home is clicked\n if (event.at(\"/commonEventObject/invokedFunction\").asText().equals(\"updateAppHome\")) {\n return updateAppHome();\n }\n }\n\n return new GenericJson();\n}\n\n// Create the app home card\nGoogleAppsCardV1Card getHomeCard() {\n return new GoogleAppsCardV1Card()\n .setSections(List.of(new GoogleAppsCardV1Section()\n .setWidgets(List.of(\n new GoogleAppsCardV1Widget()\n .setTextParagraph(new GoogleAppsCardV1TextParagraph()\n .setText(\"Here is the app home 🏠 It's \" + new Date())),\n new GoogleAppsCardV1Widget()\n .setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()\n .setText(\"Update app home\")\n .setOnClick(new GoogleAppsCardV1OnClick()\n .setAction(new GoogleAppsCardV1Action()\n .setFunction(\"updateAppHome\"))))))))));\n}\n```\n\nExample:\n```text\n/**\n * Responds to a APP_HOME event in Google Chat.\n */\nfunction onAppHome() {\n return { action: { navigations: [{\n pushCard: getHomeCard()\n }]}};\n}\n\n/**\n * Returns the app home card.\n */\nfunction getHomeCard() {\n return { sections: [{ widgets: [\n { textParagraph: {\n text: \"Here is the app home 🏠 It's \" + new Date().toTimeString()\n }},\n { buttonList: { buttons: [{\n text: \"Update app home\",\n onClick: { action: {\n function: \"updateAppHome\"\n }}\n }]}}\n ]}]};\n}\n```\n\nExample:\n```text\n// Update the app home\nfunction updateAppHome() {\n return { renderActions: { action: { navigations: [{\n updateCard: getHomeCard()\n }]}}}\n};\n```\n\nExample:\n```text\ndef update_app_home() -> Mapping[str, Any]:\n \"\"\"Update the app home\n\n Returns:\n Mapping[str, Any]: the update card render action\n \"\"\"\n return { \"renderActions\": { \"action\": { \"navigations\": [{\n \"updateCard\": get_home_card()\n }]}}}\n```\n\nExample:\n```text\n// Update the app home\nGenericJson updateAppHome() {\n GenericJson navigation = new GenericJson();\n navigation.set(\"updateCard\", getHomeCard());\n\n GenericJson action = new GenericJson();\n action.set(\"navigations\", List.of(navigation));\n\n GenericJson renderActions = new GenericJson();\n renderActions.set(\"action\", action);\n\n GenericJson response = new GenericJson();\n response.set(\"renderActions\", renderActions);\n return response;\n}\n```\n\nExample:\n```text\n/**\n * Updates the home app.\n */\nfunction updateAppHome() {\n return { renderActions: { action: { navigations: [{\n updateCard: getHomeCard()\n }]}}};\n}\n```\n\nExample:\n```text\n{ renderActions: { action: { navigations: [{ updateCard: { sections: [{\n header: \"Add new contact\",\n widgets: [{ \"textInput\": {\n label: \"Name\",\n type: \"SINGLE_LINE\",\n name: \"contactName\"\n }}, { textInput: {\n label: \"Address\",\n type: \"MULTIPLE_LINE\",\n name: \"address\"\n }}, { decoratedText: {\n text: \"Add to favorites\",\n switchControl: {\n controlType: \"SWITCH\",\n name: \"saveFavorite\"\n }\n }}, { decoratedText: {\n text: \"Merge with existing contacts\",\n switchControl: {\n controlType: \"SWITCH\",\n name: \"mergeContact\",\n selected: true\n }\n }}, { buttonList: { buttons: [{\n text: \"Next\",\n onClick: { action: { function: \"openSequentialDialog\" }}\n }]}}]\n}]}}]}}}\n```\n\nExample:\n```text\n{ renderActions: { action: {\n navigations: [{ endNavigation: { action: \"CLOSE_DIALOG\" }}]\n}}}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.940Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":258,"estimatedTokens":1522}}946{"id":"doc-advanced_chat_service_apps_script_google_for_dev-19914751","source":"documentation","title":"Advanced Chat Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/advanced/chat","text":"Example:\n```text\n\"oauthScopes\": [\n \"https://www.googleapis.com/auth/chat.messages.create\"\n]\n```\n\nExample:\n```text\n/**\n * Posts a new message to the specified space on behalf of the user.\n * @param {string} spaceName The resource name of the space.\n */\nfunction postMessageWithUserCredentials(spaceName) {\n try {\n const message = { text: \"Hello world!\" };\n Chat.Spaces.Messages.create(message, spaceName);\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed to create message with error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Posts a new message to the specified space on behalf of the app.\n * @param {string} spaceName The resource name of the space.\n */\nfunction postMessageWithAppCredentials(spaceName) {\n try {\n // See https://developers.google.com/chat/api/guides/auth/service-accounts\n // for details on how to obtain a service account OAuth token.\n const appToken = getToken_();\n const message = { text: \"Hello world!\" };\n Chat.Spaces.Messages.create(\n message,\n spaceName,\n {},\n // Authenticate with the service account token.\n { Authorization: `Bearer ${appToken}` },\n );\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed to create message with error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n\"oauthScopes\": [\n \"https://www.googleapis.com/auth/chat.spaces.readonly\"\n]\n```\n\nExample:\n```text\n/**\n * Gets information about a Chat space.\n * @param {string} spaceName The resource name of the space.\n */\nfunction getSpace(spaceName) {\n try {\n const space = Chat.Spaces.get(spaceName);\n console.log(\"Space display name: %s\", space.displayName);\n console.log(\"Space type: %s\", space.spaceType);\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed to get space with error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n\"oauthScopes\": [\n \"https://www.googleapis.com/auth/chat.spaces.create\"\n]\n```\n\nExample:\n```text\n/**\n * Creates a new Chat space.\n */\nfunction createSpace() {\n try {\n const space = { displayName: \"New Space\", spaceType: \"SPACE\" };\n Chat.Spaces.create(space);\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed to create space with error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n\"oauthScopes\": [\n \"https://www.googleapis.com/auth/chat.memberships.readonly\"\n]\n```\n\nExample:\n```text\n/**\n * Lists all the members of a Chat space.\n * @param {string} spaceName The resource name of the space.\n */\nfunction listMemberships(spaceName) {\n let response;\n let pageToken = null;\n try {\n do {\n response = Chat.Spaces.Members.list(spaceName, {\n pageSize: 10,\n pageToken: pageToken,\n });\n if (!response.memberships || response.memberships.length === 0) {\n pageToken = response.nextPageToken;\n continue;\n }\n for (const membership of response.memberships) {\n console.log(\n \"Member: %s, Role: %s\",\n membership.member.displayName,\n membership.role,\n );\n }\n pageToken = response.nextPageToken;\n } while (pageToken);\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n\"oauthScopes\": [\n \"https://www.googleapis.com/auth/chat.messages.readonly\",\n \"https://www.googleapis.com/auth/chat.users.readstate.readonly\"\n]\n```\n\nExample:\n```text\n/**\n * Searches for unread messages for the caller user in all Chat spaces.\n */\nfunction searchMessages() {\n let response;\n let pageToken = null;\n try {\n do {\n response = Chat.Spaces.Messages.search(\n {\n filter: 'is_unread()'\n },\n 'spaces/-',\n {\n pageSize: 10,\n pageToken: pageToken,\n }\n );\n if (!response.results || response.results.length === 0) {\n pageToken = response.nextPageToken;\n continue;\n }\n for (const result of response.results) {\n const message = result.message;\n console.log(\n \"**%s at %s in %s:**\\n%s\",\n message.sender.name,\n message.createTime,\n message.space.name,\n message.text,\n );\n }\n pageToken = response.nextPageToken;\n } while (pageToken);\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.941Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":192,"estimatedTokens":1114}}947{"id":"doc-add_interactive_ui_elements_to_cards_google_chat-4407287c","source":"documentation","title":"Add interactive UI elements to cards | Google Chat | Google for Developers","url":"https://developers.google.com/chat/ui/widgets/selection-input","text":"Example:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Section Header\",\n \"widgets\": [\n {\n \"selectionInput\": {\n \"name\": \"contacts\",\n \"type\": \"DROPDOWN\",\n \"label\": \"Select contact from organization\",\n \"data_source_configs\": [\n {\n \"platformDataSource\": {\n \"commonDataSource\": \"USER\"\n },\n \"min_characters_trigger\": 1\n }\n ]\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Section Header\",\n \"widgets\": [\n {\n \"selectionInput\": {\n \"name\": \"crm_leads\",\n \"type\": \"DROPDOWN\",\n \"label\": \"Select CRM Lead\",\n \"data_source_configs\": [\n {\n \"remoteDataSource\": {\n \"function\": \"getCrmLeads\"\n },\n \"min_characters_trigger\": 2\n }\n ],\n \"items\": [\n {\n \"text\": \"Suggested Lead 1\",\n \"value\": \"lead-1\"\n }\n ]\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"selectionInput\": {\n \"name\": \"contacts\",\n \"type\": \"MULTI_SELECT\",\n \"label\": \"Selected contacts\",\n \"multiSelectMaxSelectedItems\": 5,\n \"multiSelectMinQueryLength\": 1,\n \"platformDataSource\": {\n \"commonDataSource\": \"USER\"\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"selectionInput\": {\n \"name\": \"spaces\",\n \"type\": \"MULTI_SELECT\",\n \"label\": \"Selected contacts\",\n \"multiSelectMaxSelectedItems\": 3,\n \"multiSelectMinQueryLength\": 1,\n \"platformDataSource\": {\n \"hostAppDataSource\": {\n \"chatDataSource\": {\n \"spaceDataSource\": {\n \"defaultToCurrentSpace\": true\n }\n }\n }\n }\n }\n}\n```\n\nExample:\n```text\nselectionInput: {\n name: \"contacts\",\n type: \"MULTI_SELECT\",\n label: \"Selected contacts\",\n multiSelectMaxSelectedItems: 3,\n multiSelectMinQueryLength: 1,\n externalDataSource: { function: \"getContacts\" },\n // Suggested items loaded by default.\n // The list is static here but it could be dynamic.\n items: [getContact(\"3\")]\n}\n```\n\nExample:\n```text\n'selectionInput': {\n 'name': \"contacts\",\n 'type': \"MULTI_SELECT\",\n 'label': \"Selected contacts\",\n 'multiSelectMaxSelectedItems': 3,\n 'multiSelectMinQueryLength': 1,\n 'externalDataSource': { 'function': \"getContacts\" },\n # Suggested items loaded by default.\n # The list is static here but it could be dynamic.\n 'items': [get_contact(\"3\")]\n}\n```\n\nExample:\n```text\n.setSelectionInput(new GoogleAppsCardV1SelectionInput()\n .setName(\"contacts\")\n .setType(\"MULTI_SELECT\")\n .setLabel(\"Selected contacts\")\n .setMultiSelectMaxSelectedItems(3)\n .setMultiSelectMinQueryLength(1)\n .setExternalDataSource(new GoogleAppsCardV1Action().setFunction(\"getContacts\"))\n .setItems(List.of(getContact(\"3\")))))))))));\n```\n\nExample:\n```text\n/**\n * Responds to a WIDGET_UPDATE event in Google Chat.\n *\n * @param {Object} event The event object from Chat API.\n * @return {Object} Response from the Chat app.\n */\nfunction onWidgetUpdate(event) {\n if (event.common[\"invokedFunction\"] === \"getContacts\") {\n const query = event.common.parameters[\"autocomplete_widget_query\"];\n return { actionResponse: {\n type: \"UPDATE_WIDGET\",\n updatedWidget: { suggestions: { items: [\n // The list is static here but it could be dynamic.\n getContact(\"1\"), getContact(\"2\"), getContact(\"3\"), getContact(\"4\"), getContact(\"5\")\n // Only return items based on the query from the user\n ].filter(e => !query || e.text.includes(query))}}\n }};\n }\n}\n\n/**\n * Generate a suggested contact given an ID.\n *\n * @param {String} id The ID of the contact to return.\n * @return {Object} The contact formatted as a suggested item for selectors.\n */\nfunction getContact(id) {\n return {\n value: id,\n startIconUri: \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n text: \"Contact \" + id\n };\n}\n```\n\nExample:\n```text\ndef on_widget_update(event: dict) -> dict:\n \"\"\"Responds to a WIDGET_UPDATE event in Google Chat.\"\"\"\n if \"getContacts\" == event.get(\"common\").get(\"invokedFunction\"):\n query = event.get(\"common\").get(\"parameters\").get(\"autocomplete_widget_query\")\n return { 'actionResponse': {\n 'type': \"UPDATE_WIDGET\",\n 'updatedWidget': { 'suggestions': { 'items': list(filter(lambda e: query is None or query in e[\"text\"], [\n # The list is static here but it could be dynamic.\n get_contact(\"1\"), get_contact(\"2\"), get_contact(\"3\"), get_contact(\"4\"), get_contact(\"5\")\n # Only return items based on the query from the user\n ]))}}\n }}\n\n\ndef get_contact(id: str) -> dict:\n \"\"\"Generate a suggested contact given an ID.\"\"\"\n return {\n 'value': id,\n 'startIconUri': \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n 'text': \"Contact \" + id\n }\n```\n\nExample:\n```text\n// Responds to a WIDGET_UPDATE event in Google Chat.\nMessage onWidgetUpdate(JsonNode event) {\n if (\"getContacts\".equals(event.at(\"/invokedFunction\").asText())) {\n String query = event.at(\"/common/parameters/autocomplete_widget_query\").asText();\n return new Message().setActionResponse(new ActionResponse()\n .setType(\"UPDATE_WIDGET\")\n .setUpdatedWidget(new UpdatedWidget()\n .setSuggestions(new SelectionItems().setItems(List.of(\n // The list is static here but it could be dynamic.\n getContact(\"1\"), getContact(\"2\"), getContact(\"3\"), getContact(\"4\"), getContact(\"5\")\n // Only return items based on the query from the user\n ).stream().filter(e -> query == null || e.getText().indexOf(query) > -1).toList()))));\n }\n return null;\n}\n\n// Generate a suggested contact given an ID.\nGoogleAppsCardV1SelectionItem getContact(String id) {\n return new GoogleAppsCardV1SelectionItem()\n .setValue(id)\n .setStartIconUri(\"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\")\n .setText(\"Contact \" + id);\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Select contacts\",\n \"widgets\": [\n {\n \"selectionInput\": {\n \"type\": \"MULTI_SELECT\",\n \"label\": \"Selected contacts\",\n \"name\": \"contacts\",\n \"multiSelectMaxSelectedItems\": 3,\n \"multiSelectMinQueryLength\": 1,\n \"onChangeAction\": {\n \"all_widgets_are_required\": true\n },\n \"items\": [\n {\n \"value\": \"contact-1\",\n \"startIconUri\": \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n \"text\": \"Contact 1\",\n \"bottomText\": \"Contact one description\",\n \"selected\": false\n },\n {\n \"value\": \"contact-2\",\n \"startIconUri\": \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n \"text\": \"Contact 2\",\n \"bottomText\": \"Contact two description\",\n \"selected\": false\n },\n {\n \"value\": \"contact-3\",\n \"startIconUri\": \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n \"text\": \"Contact 3\",\n \"bottomText\": \"Contact three description\",\n \"selected\": false\n },\n {\n \"value\": \"contact-4\",\n \"startIconUri\": \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n \"text\": \"Contact 4\",\n \"bottomText\": \"Contact four description\",\n \"selected\": false\n },\n {\n \"value\": \"contact-5\",\n \"startIconUri\": \"https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png\",\n \"text\": \"Contact 5\",\n \"bottomText\": \"Contact five description\",\n \"selected\": false\n }\n ]\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"widgets\": [\n {\n \"textParagraph\": {\n \"text\": \"A datetime picker widget with both date and time:\"\n }\n },\n {\n \"divider\": {}\n },\n {\n \"dateTimePicker\": {\n \"name\": \"date_time_picker_date_and_time\",\n \"label\": \"meeting\",\n \"type\": \"DATE_AND_TIME\"\n }\n },\n {\n \"textParagraph\": {\n \"text\": \"A datetime picker widget with just date:\"\n }\n },\n {\n \"divider\": {}\n },\n {\n \"dateTimePicker\": {\n \"name\": \"date_time_picker_date_only\",\n \"label\": \"Choose a date\",\n \"type\": \"DATE_ONLY\",\n \"onChangeAction\":{\n \"all_widgets_are_required\": true\n }\n }\n },\n {\n \"textParagraph\": {\n \"text\": \"A datetime picker widget with just time:\"\n }\n },\n {\n \"divider\": {}\n },\n {\n \"dateTimePicker\": {\n \"name\": \"date_time_picker_time_only\",\n \"label\": \"Select a time\",\n \"type\": \"TIME_ONLY\"\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Section Header\",\n \"collapsible\": true,\n \"uncollapsibleWidgetsCount\": 1,\n \"widgets\": [\n {\n \"selectionInput\": {\n \"name\": \"location\",\n \"label\": \"Select Color\",\n \"type\": \"DROPDOWN\",\n \"onChangeAction\": {\n \"all_widgets_are_required\": true\n },\n \"items\": [\n {\n \"text\": \"Red\",\n \"value\": \"red\",\n \"selected\": false\n },\n {\n \"text\": \"Green\",\n \"value\": \"green\",\n \"selected\": false\n },\n {\n \"text\": \"White\",\n \"value\": \"white\",\n \"selected\": false\n },\n {\n \"text\": \"Blue\",\n \"value\": \"blue\",\n \"selected\": false\n },\n {\n \"text\": \"Black\",\n \"value\": \"black\",\n \"selected\": false\n }\n ]\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Tell us about yourself\",\n \"collapsible\": true,\n \"uncollapsibleWidgetsCount\": 2,\n \"widgets\": [\n {\n \"textInput\": {\n \"name\": \"favoriteColor\",\n \"label\": \"Favorite color\",\n \"type\": \"SINGLE_LINE\",\n \"validation\": {\"character_limit\":15},\n \"onChangeAction\":{\n \"all_widgets_are_required\": true\n }\n }\n }\n ]\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"sections\": [\n {\n \"header\": \"Validate text inputs by input types\",\n \"collapsible\": true,\n \"uncollapsibleWidgetsCount\": 2,\n \"widgets\": [\n {\n \"textInput\": {\n \"name\": \"mailing_address\",\n \"label\": \"Please enter a valid email address\",\n \"type\": \"SINGLE_LINE\",\n \"validation\": {\n \"input_type\": \"EMAIL\"\n },\n \"onChangeAction\": {\n \"all_widgets_are_required\": true\n }\n }\n },\n {\n \"textInput\": {\n \"name\": \"validate_integer\",\n \"label\": \"Please enter a number\",\n \"type\": \"SINGLE_LINE\",\n \"validation\": {\n \"input_type\": \"INTEGER\"\n }\n }\n },\n {\n \"textInput\": {\n \"name\": \"validate_float\",\n \"label\": \"Please enter a number with a decimal\",\n \"type\": \"SINGLE_LINE\",\n \"validation\": {\n \"input_type\": \"FLOAT\"\n }\n }\n }\n ]\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.943Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":480,"estimatedTokens":3031}}948{"id":"doc-format_messages_google_chat_google_for_developer-85527dbf","source":"documentation","title":"Format messages | Google Chat | Google for Developers","url":"https://developers.google.com/chat/format-messages","text":"Example:\n```text\n{\n \"text\": \"Your pizza delivery *has arrived*!\\nThank you for using _Cymbal Pizza!_\"\n }\n```\n\nExample:\n```text\n{\n \"text\": \"I can meet there at:\\nNoon\\n3 pm\\n5 pm\\nWhat time works for you?\",\n \"formattedText\": \"I can meet <http://example.com|there> at:\\n* Noon\\n* 3 pm\\n* 5 pm\\nWhat time works for *you*?\",\n }\n```\n\nExample:\n```text\n{\n \"text\": \"Hello <customEmojis/CUSTOM_EMOJI_ID>.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Hello <chat-emoji data-custom-emoji=\\\"customEmojis/CUSTOM_EMOJI_ID\\\">.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Hello <chat-emoji data-emoji-name=\\\"CUSTOM_EMOJI_NAME\\\">.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"A customer has reported an issue. Assigning ticket #942 to <users/123456789012345678901>.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Assigning ticket #942 to <chat-user data-user=\\\"users/123456789012345678901\\\">.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Assigning ticket #942 to <chat-user data-email=\\\"mahan@example.com\\\">.\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Important message for <users/all>: Code freeze starts at midnight tonight!\"\n}\n```\n\nExample:\n```text\n{\n \"text\": \"Important message for <chat-user data-user=\\\"users/all\\\">: Code freeze starts at midnight tonight!\"\n}\n```\n\nExample:\n```text\nThis is a code block.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.948Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":77,"estimatedTokens":325}}949{"id":"doc-respond_to_incidents_with_google_chat_vertex_ai_-e4f596eb","source":"documentation","title":"Respond to incidents with Google Chat, Vertex AI, Apps Script, and user authentication | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/chat/tutorial-incident-response","text":"Example:\n```text\ngcloud projects create PROJECT_ID\n```\n\nExample:\n```text\ngcloud billing accounts list\n```\n\nExample:\n```text\ngcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID\n```\n\nExample:\n```text\ngcloud config set project PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable chat.googleapis.com docs.googleapis.com admin.googleapis.com aiplatform.googleapis.com\n```\n\nExample:\n```text\nconst PROJECT_ID = 'replace-with-your-project-id';\nconst VERTEX_AI_LOCATION_ID = 'us-central1';\nconst CLOSE_INCIDENT_COMMAND_ID = 1;\nconst MODEL_ID = 'gemini-2.5-flash-lite';\n```\n\nExample:\n```text\n/**\n * Responds to a MESSAGE event in Google Chat.\n * \n * It always responds with a simple \"Hello\" text message.\n *\n * @param {Object} event the event object from Google Chat\n */\nfunction onMessage(event) {\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: \"Hello from Incident Response app!\"\n }}}}};\n}\n\n/**\n * Responds to an APP_COMMAND event in Google Chat.\n *\n * @param {Object} event the event object from Google Chat\n */\nfunction onAppCommand(event) {\n if (event.chat.appCommandPayload.appCommandMetadata.appCommandId != CLOSE_INCIDENT_COMMAND_ID) {\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: \"Command not recognized. Use the quick command `Close incident` to close the incident managed by this space.\"\n }}}}};\n }\n return { action: { navigations: [{ pushCard: { sections: [{\n header: \"Close Incident\",\n widgets: [{\n textInput: {\n label: \"Please describe the incident resolution\",\n type: \"MULTIPLE_LINE\",\n name: \"description\"\n }\n }, {\n buttonList: { buttons: [{\n text: \"Close Incident\",\n onClick: { action: { function: \"closeIncident\" }}\n }]}\n }]\n }]}}]}};\n}\n\n/**\n * Responds to a BUTTON_CLICKED event in Google Chat from Close Incident dialog.\n *\n * @param {Object} event the event object from Google Chat\n */\nfunction closeIncident(event) {\n if (event.chat.buttonClickedPayload.isDialogEvent) {\n if (event.chat.buttonClickedPayload.dialogEventType == 'SUBMIT_DIALOG') {\n return processSubmitDialog_(event);\n }\n return { action: { navigations: [{ endNavigation: {\n action: \"CLOSE_DIALOG\" }\n }]}};\n }\n}\n\n/**\n * Responds to a BUTTON_CLICKED event in Google Chat from Close Incident dialog submission.\n *\n * It creates a Doc with a summary of the incident information and posts a message\n * to the space with a link to the Doc.\n *\n * @param {Object} event the event object from Google Chat\n */\nfunction processSubmitDialog_(event) {\n const resolution = event.commonEventObject.formInputs.description.stringInputs.value[0];\n const space = event.chat.buttonClickedPayload.space;\n const chatHistory = concatenateAllSpaceMessages_(space.name);\n const chatSummary = summarizeChatHistory_(chatHistory);\n const docUrl = createDoc_(space.displayName, resolution, chatHistory, chatSummary);\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: `Incident closed with the following resolution: ${resolution}\\n\\nHere is the automatically generated post-mortem:\\n${docUrl}`\n }}}}};\n}\n\n/**\n * Lists all the messages in the Chat space, then concatenate all of them into\n * a single text containing the full Chat history.\n *\n * For simplicity for this demo, it only fetches the first 100 messages.\n *\n * @return {string} a text containing all the messages in the space in the format:\n * Sender's name: Message\n */\nfunction concatenateAllSpaceMessages_(spaceName) {\n // Call Chat API method spaces.messages.list\n const response = Chat.Spaces.Messages.list(spaceName, { 'pageSize': 100 });\n const messages = response.messages;\n // Fetch the display names of the message senders and returns a text\n // concatenating all the messages.\n let userMap = new Map();\n return messages\n .map(message => `${getUserDisplayName_(userMap, message.sender.name)}: ${message.text}`)\n .join('\\n');\n}\n\n/**\n * Obtains the display name of a user by using the Admin Directory API.\n *\n * The fetched display name is cached in the provided map, so we only call the API\n * once per user.\n *\n * If the user does not have a display name, then the full name is used.\n *\n * @param {Map} userMap a map containing the display names previously fetched\n * @param {string} userName the resource name of the user\n * @return {string} the user's display name\n */\nfunction getUserDisplayName_(userMap, userName) {\n if (userMap.has(userName)) {\n return userMap.get(userName);\n }\n let displayName = 'Unknown User';\n try {\n const user = AdminDirectory.Users.get(\n userName.replace(\"users/\", \"\"),\n { projection: 'BASIC', viewType: 'domain_public' });\n displayName = user.name.displayName ? user.name.displayName : user.name.fullName;\n } catch (e) {\n // Ignore error if the API call fails (for example, because it's an\n // out-of-domain user or Chat app) and just use 'Unknown User'.\n }\n userMap.set(userName, displayName);\n return displayName;\n}\n```\n\nExample:\n```text\n/**\n * Handles an incident by creating a chat space with the provided title and members, and posting a message.\n * All the actions are done using user credentials.\n *\n * @param {Object} formData - The data submitted by the user. It should contain the fields:\n * - title: The display name of the chat space.\n * - description: The description of the incident.\n * - users: A comma-separated string of user emails to be added to the space.\n * @return {string} The resource name of the new space.\n */\nfunction handleIncident(formData) {\n const users = formData.users.trim().length > 0 ? formData.users.split(',') : [];\n const spaceName = setUpSpace_(formData.title, users);\n addAppToSpace_(spaceName);\n createMessage_(spaceName, formData.description);\n return spaceName;\n}\n\n/**\n * Creates a chat space.\n *\n * @return {string} the resource name of the new space.\n */\nfunction setUpSpace_(displayName, users) {\n const memberships = users.map(email => ({\n member: {\n name: `users/${email}`,\n type: \"HUMAN\"\n }\n }));\n const request = {\n space: {\n displayName: displayName,\n spaceType: \"SPACE\"\n },\n memberships: memberships\n };\n // Call Chat API method spaces.setup\n const space = Chat.Spaces.setup(request);\n return space.name;\n}\n\n/**\n * Adds this Chat app to the space.\n *\n * @return {string} the resource name of the new membership.\n */\nfunction addAppToSpace_(spaceName) {\n const request = {\n member: {\n name: \"users/app\",\n type: \"BOT\"\n }\n };\n // Call Chat API method spaces.members.create\n const membership = Chat.Spaces.Members.create(request, spaceName);\n return membership.name;\n}\n\n/**\n * Creates a chat message.\n *\n * @param {string} spaceName - The resource name of the space.\n * @param {string} message - The text to be posted.\n * @return {string} the resource name of the new message.\n */\nfunction createMessage_(spaceName, message) {\n const request = {\n text: message\n };\n // Call Chat API method spaces.messages.create\n const result = Chat.Spaces.Messages.create(request, spaceName);\n return result.name;\n}\n```\n\nExample:\n```text\n/**\n * Creates a Doc in the user's Google Drive and writes a summary of the incident information to it.\n *\n * @param {string} title The title of the incident\n * @param {string} resolution Incident resolution described by the user\n * @param {string} chatHistory The whole Chat history be included in the document\n * @param {string} chatSummary A summary of the Chat conversation to be included in the document\n * @return {string} the URL of the created Doc\n */\nfunction createDoc_(title, resolution, chatHistory, chatSummary) {\n let doc = DocumentApp.create(title);\n let body = doc.getBody();\n body.appendParagraph(`Post-Mortem: ${title}`).setHeading(DocumentApp.ParagraphHeading.TITLE);\n body.appendParagraph(\"Resolution\").setHeading(DocumentApp.ParagraphHeading.HEADING1);\n body.appendParagraph(resolution);\n body.appendParagraph(\"Summary of the conversation\").setHeading(DocumentApp.ParagraphHeading.HEADING1);\n body.appendParagraph(chatSummary);\n body.appendParagraph(\"Full Chat history\").setHeading(DocumentApp.ParagraphHeading.HEADING1);\n body.appendParagraph(chatHistory);\n return doc.getUrl();\n}\n```\n\nExample:\n```text\n/**\n * Summarizes a Chat conversation using the Vertex AI text prediction API.\n *\n * @param {string} chatHistory The Chat history that will be summarized.\n * @return {string} The content from the text prediction response.\n */\nfunction summarizeChatHistory_(chatHistory) {\n const API_ENDPOINT = `https://${VERTEX_AI_LOCATION_ID}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${VERTEX_AI_LOCATION_ID}/publishers/google/models/${MODEL_ID}:generateContent`;\n const prompt =\n \"Summarize the following conversation between Engineers resolving an incident\"\n + \" in a few sentences. Use only the information from the conversation.\\n\\n\"\n + chatHistory;\n // Get the access token.\n const accessToken = ScriptApp.getOAuthToken();\n\n const headers = {\n 'Authorization': 'Bearer ' + accessToken,\n 'Content-Type': 'application/json',\n };\n const payload = {\n 'contents': {\n 'role': 'user',\n 'parts' : [\n {\n 'text': prompt\n }\n ]\n }\n }\n const options = {\n 'method': 'post',\n 'headers': headers,\n 'payload': JSON.stringify(payload),\n 'muteHttpExceptions': true,\n };\n try {\n const response = UrlFetchApp.fetch(API_ENDPOINT, options);\n const responseCode = response.getResponseCode();\n const responseText = response.getContentText();\n\n if (responseCode === 200) {\n const jsonResponse = JSON.parse(responseText);\n console.log(jsonResponse)\n if (jsonResponse.candidates && jsonResponse.candidates.length > 0) {\n return jsonResponse.candidates[0].content.parts[0].text; // Access the summarized text\n } else {\n return \"No summary found in response.\";\n }\n\n } else {\n console.error(\"Vertex AI API Error:\", responseCode, responseText);\n return `Error: ${responseCode} - ${responseText}`;\n }\n } catch (e) {\n console.error(\"UrlFetchApp Error:\", e);\n return \"Error: \" + e.toString();\n }\n}\n```\n\nExample:\n```text\n/**\n * Serves the web page from Index.html.\n */\nfunction doGet() {\n return HtmlService\n .createTemplateFromFile('Index')\n .evaluate();\n}\n\n/**\n * Serves the web content from the specified filename.\n */\nfunction include(filename) {\n return HtmlService\n .createHtmlOutputFromFile(filename)\n .getContent();\n}\n\n/**\n * Returns the email address of the user running the script.\n */\nfunction getUserEmail() {\n return Session.getActiveUser().getEmail();\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet'>\n <?!= include('Stylesheet'); ?>\n </head>\n <body>\n <div class=\"container\">\n <div class=\"content\">\n <h1>Incident Manager</h1>\n <form id=\"incident-form\" onsubmit=\"handleFormSubmit(this)\">\n <div id=\"form\">\n <p>\n <label for=\"title\">Incident title</label><br/>\n <input type=\"text\" name=\"title\" id=\"title\" />\n </p>\n <p>\n <label for=\"users\">Incident responders</label><br/>\n <small>\n Please enter a comma-separated list of email addresses of the users\n that should be added to the space.\n Do not include <?= getUserEmail() ?> as it will be added automatically.\n </small><br/>\n <input type=\"text\" name=\"users\" id=\"users\" />\n </p>\n <p>\n <label for=\"description\">Initial message</label></br>\n <small>This message will be posted after the space is created.</small><br/>\n <textarea name=\"description\" id=\"description\"></textarea>\n </p>\n <p class=\"text-center\">\n <input type=\"submit\" value=\"CREATE CHAT SPACE\" />\n </p>\n </div>\n <div id=\"output\" class=\"hidden\"></div>\n <div id=\"clear\" class=\"hidden\">\n <input type=\"reset\" value=\"CREATE ANOTHER INCIDENT\" onclick=\"onReset()\" />\n </div>\n </form>\n </div>\n </div>\n <?!= include('JavaScript'); ?>\n </body>\n</html>\n```\n\nExample:\n```text\n<script>\n var formDiv = document.getElementById('form');\n var outputDiv = document.getElementById('output');\n var clearDiv = document.getElementById('clear');\n\n function handleFormSubmit(formObject) {\n event.preventDefault();\n outputDiv.innerHTML = 'Please wait while we create the space...';\n hide(formDiv);\n show(outputDiv);\n google.script.run\n .withSuccessHandler(updateOutput)\n .withFailureHandler(onFailure)\n .handleIncident(formObject);\n }\n\n function updateOutput(response) {\n var spaceId = response.replace('spaces/', '');\n outputDiv.innerHTML =\n '<p>Space created!</p><p><a href=\"https://mail.google.com/chat/#chat/space/'\n + spaceId\n + '\" target=\"_blank\">Open space</a></p>';\n show(outputDiv);\n show(clearDiv);\n }\n\n function onFailure(error) {\n outputDiv.innerHTML = 'ERROR: ' + error.message;\n outputDiv.classList.add('error');\n show(outputDiv);\n show(clearDiv);\n }\n\n function onReset() {\n outputDiv.innerHTML = '';\n outputDiv.classList.remove('error');\n show(formDiv);\n hide(outputDiv);\n hide(clearDiv);\n }\n\n function hide(element) {\n element.classList.add('hidden');\n }\n\n function show(element) {\n element.classList.remove('hidden');\n }\n</script>\n```\n\nExample:\n```text\n<style>\n * {\n box-sizing: border-box;\n }\n body {\n font-family: Roboto, Arial, Helvetica, sans-serif;\n }\n div.container {\n display: flex;\n justify-content: center;\n align-items: center;\n position: absolute;\n top: 0; bottom: 0; left: 0; right: 0;\n }\n div.content {\n width: 80%;\n max-width: 1000px;\n padding: 1rem;\n border: 1px solid #999;\n border-radius: 0.25rem;\n box-shadow: 0 2px 2px 0 rgba(66, 66, 66, 0.08), 0 2px 4px 2px rgba(66, 66, 66, 0.16);\n }\n h1 {\n text-align: center;\n padding-bottom: 1rem;\n margin: 0 -1rem 1rem -1rem;\n border-bottom: 1px solid #999;\n }\n #output {\n text-align: center;\n min-height: 250px;\n }\n div#clear {\n text-align: center;\n padding-top: 1rem;\n margin: 1rem -1rem 0 -1rem;\n border-top: 1px solid #999;\n }\n input[type=text], textarea {\n width: 100%;\n padding: 1rem 0.5rem;\n margin: 0.5rem 0;\n border: 0;\n border-bottom: 1px solid #999;\n background-color: #f0f0f0;\n }\n textarea {\n height: 5rem;\n }\n small {\n color: #999;\n }\n input[type=submit], input[type=reset] {\n padding: 1rem;\n border: none;\n background-color: #6200ee;\n color: #fff;\n border-radius: 0.25rem;\n width: 25%;\n }\n .hidden {\n display: none;\n }\n .text-center {\n text-align: center;\n }\n .error {\n color: red;\n }\n</style>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.950Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":533,"estimatedTokens":3805}}950{"id":"doc-import_data_to_google_chat_google_for_developers-e0ba3e0a","source":"documentation","title":"Import data to Google Chat | Google for Developers","url":"https://developers.google.com/chat/api/guides/import-data","text":"Example:\n```text\nfunction createSpaceInImportMode() {\n const space = Chat.Spaces.create({\n spaceType: 'SPACE',\n displayName: 'DISPLAY_NAME',\n importMode: true,\n createTime: (new Date('January 1, 2000')).toJSON()\n });\n console.log(space.name);\n}\n```\n\nExample:\n```text\n\"\"\"Create a space in import mode.\"\"\"\n\nimport datetime\n\nfrom google.oauth2 import service_account\nfrom googleapiclient.discovery import build\n\n# Specify required scopes.\nSCOPES = [\n 'https://www.googleapis.com/auth/chat.import',\n]\n\nCREDENTIALS = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n .with_subject('EMAIL')\n)\n\n# Build a service endpoint for Chat API.\nservice = build('chat', 'v1', credentials=CREDENTIALS)\n\nresult = (\n service.spaces()\n .create(\n body={\n 'spaceType': 'SPACE',\n 'displayName': 'DISPLAY_NAME',\n 'importMode': True,\n 'createTime': f'{datetime.datetime(2000, 1, 1).isoformat()}Z',\n }\n )\n .execute()\n)\n\nprint(result)\n```\n\nExample:\n```text\n\"\"\"Create a message in import mode space.\"\"\"\n\nimport datetime\n\nfrom google.oauth2 import service_account\nfrom googleapiclient.discovery import build\n\n# Specify required scopes.\nSCOPES = [\n 'https://www.googleapis.com/auth/chat.import',\n]\n\nCREDENTIALS = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n .with_subject('EMAIL')\n)\n\n# Build a service endpoint for Chat API.\nservice = build('chat', 'v1', credentials=CREDENTIALS)\n\nNAME = 'spaces/SPACE_NAME'\nresult = (\n service.spaces()\n .messages()\n .create(\n parent=NAME,\n body={\n 'text': 'Hello, world!',\n 'createTime': f'{datetime.datetime(2000, 1, 2).isoformat()}Z',\n },\n )\n .execute()\n)\n\nprint(result)\n```\n\nExample:\n```text\n\"\"\"Create a historical membership in import mode space.\"\"\"\n\nimport datetime\n\nfrom google.oauth2 import service_account\nfrom googleapiclient.discovery import build\n\n# Specify required scopes.\nSCOPES = [\n 'https://www.googleapis.com/auth/chat.import',\n]\n\nCREDENTIALS = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n .with_subject('EMAIL')\n)\n\n# Build a service endpoint for Chat API.\nservice = build('chat', 'v1', credentials=CREDENTIALS)\n\nNAME = 'spaces/SPACE_NAME'\nUSER = 'users/USER_ID'\nresult = (\n service.spaces()\n .members()\n .create(\n parent=NAME,\n body={\n 'createTime': f'{datetime.datetime(2000, 1, 3).isoformat()}Z',\n 'deleteTime': f'{datetime.datetime(2000, 1, 4).isoformat()}Z',\n 'member': {'name': USER, 'type': 'HUMAN'},\n },\n )\n .execute()\n)\n\nprint(result)\n```\n\nExample:\n```text\n\"\"\"Complete import.\"\"\"\n\nfrom google.oauth2 import service_account\nfrom googleapiclient.discovery import build\n\n# Specify required scopes.\nSCOPES = [\n 'https://www.googleapis.com/auth/chat.import',\n]\n\nCREDENTIALS = (\n service_account.Credentials.from_service_account_file('credentials.json')\n .with_scopes(SCOPES)\n .with_subject('EMAIL')\n)\n\n# Build a service endpoint for Chat API.\nservice = build('chat', 'v1', credentials=CREDENTIALS)\n\nNAME = 'spaces/SPACE_NAME'\nresult = service.spaces().completeImport(name=NAME).execute()\n\nprint(result)\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.952Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":162,"estimatedTokens":844}}951{"id":"doc-deploy_a_meet_add_on_google_meet_google_for_deve-13812c30","source":"documentation","title":"Deploy a Meet add-on | Google Meet | Google for Developers","url":"https://developers.google.com/meet/add-ons/guides/build-add-on","text":"Example:\n```text\n{\n \"addOns\": {\n \"common\": {\n \"name\": \"NAME\",\n \"logoUrl\": \"LOGO_URL\"\n },\n \"meet\": {\n \"web\": {\n \"sidePanelUrl\": \"SIDE_PANEL_URL\",\n \"supportsScreenSharing\": SUPPORTS_SCREENSHARING,\n \"addOnOrigins\": [\"ADD_ON_ORIGINS\"],\n \"logoUrl\": \"MEET_WEB_LOGO_URL\",\n \"darkModeLogoUrl\": \"DARK_MODE_LOGO_URL\"\n }\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.952Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":22,"estimatedTokens":101}}952{"id":"doc-protect_file_content_google_drive_google_for_dev-5385fb45","source":"documentation","title":"Protect file content | Google Drive | Google for Developers","url":"https://developers.google.com/drive/api/guides/content-restrictions","text":"Example:\n```text\nFile updatedFile =\n new File()\n .setContentRestrictions(\n ImmutableList.of(new ContentRestriction().setReadOnly(true).setReason(\"Finalized contract.\"));\n\nFile response = driveService.files().update(\"FILE_ID\", updatedFile).setFields(\"contentRestrictions\").execute();\n```\n\nExample:\n```text\ncontent_restriction = {'readOnly': True, 'reason':'Finalized contract.'}\n\nresponse = drive_service.files().update(fileId=\"FILE_ID\", body = {'contentRestrictions' : [content_restriction]}, fields = \"contentRestrictions\").execute();\n```\n\nExample:\n```text\n/**\n* Set a content restriction on a file.\n* @return{obj} updated file\n**/\nasync function addContentRestriction() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});\n const service = google.drive({version: 'v3', auth});\n const contentRestriction = {\n 'readOnly': True,\n 'reason': 'Finalized contract.',\n };\n const updatedFile = {\n 'contentRestrictions': [contentRestriction],\n };\n try {\n const response = await service.files.update({\n fileId: 'FILE_ID',\n resource: updatedFile,\n fields: 'contentRestrictions',\n });\n return response;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\nExample:\n```text\nFile updatedFile =\nnew File()\n .setContentRestrictions(\n ImmutableList.of(new ContentRestriction().setReadOnly(false));\n\nFile response = driveService.files().update(\"FILE_ID\", updatedFile).setFields(\"contentRestrictions\").execute();\n```\n\nExample:\n```text\ncontent_restriction = {'readOnly': False}\n\nresponse = drive_service.files().update(fileId=\"FILE_ID\", body = {'contentRestrictions' : [content_restriction]}, fields = \"contentRestrictions\").execute();\n```\n\nExample:\n```text\n/**\n* Remove a content restriction on a file.\n* @return{obj} updated file\n**/\nasync function removeContentRestriction() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});\n const service = google.drive({version: 'v3', auth});\n const contentRestriction = {\n 'readOnly': False,\n };\n const updatedFile = {\n 'contentRestrictions': [contentRestriction],\n };\n try {\n const response = await service.files.update({\n fileId: 'FILE_ID',\n resource: updatedFile,\n fields: 'contentRestrictions',\n });\n return response;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\nExample:\n```text\nFile response = driveService.files().get(\"FILE_ID\").setFields(\"contentRestrictions\").execute();\n```\n\nExample:\n```text\nresponse = drive_service.files().get(fileId=\"FILE_ID\", fields = \"contentRestrictions\").execute();\n```\n\nExample:\n```text\n/**\n* Get content restrictions on a file.\n* @return{obj} updated file\n**/\nasync function fetchContentRestrictions() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});\n const service = google.drive({version: 'v3', auth});\n try {\n const response = await service.files.get({\n fileId: 'FILE_ID',\n fields: 'contentRestrictions',\n });\n return response;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\nExample:\n```text\nFile updatedFile =\n new File()\n .setContentRestrictions(\n ImmutableList.of(new ContentRestriction().setReadOnly(true).setOwnerRestricted(true).setReason(\"Finalized contract.\"));\n\nFile response = driveService.files().update(\"FILE_ID\", updatedFile).setFields(\"contentRestrictions\").execute();\n```\n\nExample:\n```text\ncontent_restriction = {'readOnly': True, 'ownerRestricted': True, 'reason':'Finalized contract.'}\n\nresponse = drive_service.files().update(fileId=\"FILE_ID\", body = {'contentRestrictions' : [content_restriction]}, fields = \"contentRestrictions\").execute();\n```\n\nExample:\n```text\n/**\n* Set an owner restricted content restriction on a file.\n* @return{obj} updated file\n**/\nasync function addOwnerRestrictedContentRestriction() {\n // Get credentials and build service\n // TODO (developer) - Use appropriate auth mechanism for your app\n\n const {GoogleAuth} = require('google-auth-library');\n const {google} = require('googleapis');\n\n const auth = new GoogleAuth({scopes: 'https://www.googleapis.com/auth/drive'});\n const service = google.drive({version: 'v3', auth});\n const contentRestriction = {\n 'readOnly': True,\n 'ownerRestricted': True,\n 'reason': 'Finalized contract.',\n };\n const updatedFile = {\n 'contentRestrictions': [contentRestriction],\n };\n try {\n const response = await service.files.update({\n fileId: 'FILE_ID',\n resource: updatedFile,\n fields: 'contentRestrictions',\n });\n return response;\n } catch (err) {\n // TODO (developer) - Handle error\n throw err;\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.954Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":198,"estimatedTokens":1342}}953{"id":"doc-script_projects_apps_script_google_for_developer-92e290fc","source":"documentation","title":"Script Projects | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/projects","text":"Example:\n```text\nfunction createEvent(){\n// Creates an event in the script project's time zone and logs the ID\nvar event = CalendarApp.getDefaultCalendar().createEvent('New test event',\n new Date('December 20, 2022 17:00:00'),\n new Date('December 20, 2022 18:00:00'));\nconsole.log('Event ID: ' + event.getId());\n}\nfunction createEventPacific(){\n// Creates an event with a specified time zone and logs the event ID.\nvar event = CalendarApp.getDefaultCalendar().createEvent('New sample event',\n new Date('December 20, 2022 17:00:00 PDT'),\n new Date('December 20, 2022 18:00:00 PDT'));\nconsole.log('Event ID: ' + event.getId());\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.955Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":19,"estimatedTokens":164}}954{"id":"doc-properties_service_apps_script_google_for_develo-ecf8e888","source":"documentation","title":"Properties Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/properties","text":"Example:\n```text\ntry {\n // Set a property in each of the three property stores.\n const scriptProperties = PropertiesService.getScriptProperties();\n const userProperties = PropertiesService.getUserProperties();\n const documentProperties = PropertiesService.getDocumentProperties();\n\n scriptProperties.setProperty(\"SERVER_URL\", \"http://www.example.com/\");\n userProperties.setProperty(\"DISPLAY_UNITS\", \"metric\");\n documentProperties.setProperty(\n \"SOURCE_DATA_ID\",\n \"1j3GgabZvXUF177W0Zs_2v--H6SPCQb4pmZ6HsTZYT5k\",\n );\n} catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n}\n```\n\nExample:\n```text\ntry {\n // Set multiple script properties in one call.\n const scriptProperties = PropertiesService.getScriptProperties();\n scriptProperties.setProperties({\n cow: \"moo\",\n sheep: \"baa\",\n chicken: \"cluck\",\n });\n} catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n}\n```\n\nExample:\n```text\ntry {\n // Get the value for the user property 'DISPLAY_UNITS'.\n const userProperties = PropertiesService.getUserProperties();\n const units = userProperties.getProperty(\"DISPLAY_UNITS\");\n console.log(\"values of units %s\", units);\n} catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n}\n```\n\nExample:\n```text\ntry {\n // Get multiple script properties in one call, then log them all.\n const scriptProperties = PropertiesService.getScriptProperties();\n const data = scriptProperties.getProperties();\n for (const key in data) {\n console.log(\"Key: %s, Value: %s\", key, data[key]);\n }\n} catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n}\n```\n\nExample:\n```text\ntry {\n // Change the unit type in the user property 'DISPLAY_UNITS'.\n const userProperties = PropertiesService.getUserProperties();\n let units = userProperties.getProperty(\"DISPLAY_UNITS\");\n units = \"imperial\"; // Only changes local value, not stored value.\n userProperties.setProperty(\"DISPLAY_UNITS\", units); // Updates stored value.\n} catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n}\n```\n\nExample:\n```text\ntry {\n // Delete the user property 'DISPLAY_UNITS'.\n const userProperties = PropertiesService.getUserProperties();\n userProperties.deleteProperty(\"DISPLAY_UNITS\");\n} catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n}\n```\n\nExample:\n```text\ntry {\n // Get user properties in the current script.\n const userProperties = PropertiesService.getUserProperties();\n // Delete all user properties in the current script.\n userProperties.deleteAllProperties();\n} catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.956Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":104,"estimatedTokens":727}}955{"id":"doc-class_attachment_apps_script_google_for_develope-332a12e7","source":"documentation","title":"Class Attachment | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/attachment","text":"Example:\n```text\nconst attachment = CardService.newAttachment()\n .setResourceUrl('https://fakeresourceurl.com')\n .setTitle('Attachment title')\n .setMimeType('text/html')\n .setIconUrl('https://fakeresourceurl.com/iconurl.png');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.957Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":83}}956{"id":"doc-calendar_manifest_resource_apps_script_google_fo-7fa824fd","source":"documentation","title":"Calendar manifest resource | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/manifest/calendar-addons","text":"Example:\n```text\n{\n \"createSettingsUrlFunction\": string,\n \"conferenceSolution\": [\n {\n object (ConferenceSolution)\n }\n ],\n \"currentEventAccess\": string,\n \"eventOpenTrigger\": {\n object (EventOpenTrigger)\n },\n \"eventUpdateTrigger\": {\n object (EventUpdateTrigger)\n },\n \"eventAttachmentTrigger\": {\n object (EventAttachmentTrigger)\n },\n \"homepageTrigger\": {\n object (HomepageTrigger)\n }\n}\n```\n\nExample:\n```text\n{\n \"id\": string,\n \"logoUrl\": string,\n \"name\": string,\n \"onCreateFunction\": string\n}\n```\n\nExample:\n```text\n{\n \"runFunction\": string\n}\n```\n\nExample:\n```text\n{\n \"runFunction\": string,\n \"label\": string,\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.958Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":51,"estimatedTokens":167}}957{"id":"doc-advanced_people_service_apps_script_google_for_d-6c2e2a84","source":"documentation","title":"Advanced People Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/advanced/people","text":"Example:\n```text\n/**\n * Gets a list of people in the user's contacts.\n * @see https://developers.google.com/people/api/rest/v1/people.connections/list\n */\nfunction getConnections() {\n try {\n // Get the list of connections/contacts of user's profile\n const people = People.People.Connections.list(\"people/me\", {\n personFields: \"names,emailAddresses\",\n });\n // Print the connections/contacts\n console.log(\"Connections: %s\", JSON.stringify(people, null, 2));\n } catch (err) {\n // TODO (developers) - Handle exception here\n console.log(\"Failed to get the connection with an error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Gets the own user's profile.\n * @see https://developers.google.com/people/api/rest/v1/people/getBatchGet\n */\nfunction getSelf() {\n try {\n // Get own user's profile using People.getBatchGet() method\n const people = People.People.getBatchGet({\n resourceNames: [\"people/me\"],\n personFields: \"names,emailAddresses\",\n // Use other query parameter here if needed\n });\n console.log(\"Myself: %s\", JSON.stringify(people, null, 2));\n } catch (err) {\n // TODO (developer) -Handle exception\n console.log(\"Failed to get own profile with an error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Gets the person information for any Google Account.\n * @param {string} accountId The account ID.\n * @see https://developers.google.com/people/api/rest/v1/people/get\n */\nfunction getAccount(accountId) {\n try {\n // Get the Account details using account ID.\n const people = People.People.get(`people/${accountId}`, {\n personFields: \"names,emailAddresses\",\n });\n // Print the profile details of Account.\n console.log(\"Public Profile: %s\", JSON.stringify(people, null, 2));\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed to get account with an error %s\", err.message);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.959Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":66,"estimatedTokens":482}}958{"id":"doc-class_utilities_apps_script_google_for_developer-87a9da8b","source":"documentation","title":"Class Utilities | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/utilities/utilities","text":"Example:\n```text\n// This is the base64 encoded form of \"Google グループ\"\nconst base64data = 'R29vZ2xlIOOCsOODq+ODvOODlw==';\n\n// This logs:\n// [71, 111, 111, 103, 108, 101, 32, -29, -126, -80,\n// -29, -125, -85, -29, -125, -68, -29, -125, -105]\nconst decoded = Utilities.base64Decode(base64data);\nLogger.log(decoded);\n\n// If you want a String instead of a byte array:\n// This logs the original \"Google グループ\"\nLogger.log(Utilities.newBlob(decoded).getDataAsString());\n```\n\nExample:\n```text\n// This is the base64 encoded form of \"Google グループ\"\nconst base64data = 'R29vZ2xlIOOCsOODq+ODvOODlw==';\n\nconst decoded = Utilities.base64Decode(base64data, Utilities.Charset.UTF_8);\n\n// This logs:\n// [71, 111, 111, 103, 108, 101, 32, -29, -126, -80,\n// -29, -125, -85, -29, -125, -68, -29, -125, -105]\nLogger.log(decoded);\n\n// If you want a String instead of a byte array:\n// This logs the original \"Google グループ\"\nLogger.log(Utilities.newBlob(decoded).getDataAsString());\n```\n\nExample:\n```text\n// This is the base64 web-safe encoded form of \"Google グループ\"\nconst base64data = 'R29vZ2xlIOOCsOODq-ODvOODlw==';\n\nconst decoded = Utilities.base64DecodeWebSafe(base64data);\n\n// This logs:\n// [71, 111, 111, 103, 108, 101, 32, -29, -126, -80,\n// -29, -125, -85, -29, -125, -68, -29, -125, -105]\nLogger.log(decoded);\n\n// If you want a String instead of a byte array:\n// This logs the original \"Google グループ\"\nLogger.log(Utilities.newBlob(decoded).getDataAsString());\n```\n\nExample:\n```text\n// This is the base64 web-safe encoded form of \"Google グループ\"\nconst base64data = 'R29vZ2xlIOOCsOODq-ODvOODlw==';\n\nconst decoded = Utilities.base64DecodeWebSafe(\n base64data,\n Utilities.Charset.UTF_8,\n);\n\n// This logs:\n// [71, 111, 111, 103, 108, 101, 32, -29, -126, -80,\n// -29, -125, -85, -29, -125, -68, -29, -125, -105]\nLogger.log(decoded);\n\n// If you want a String instead of a byte array:\n// This logs the original \"Google グループ\"\nLogger.log(Utilities.newBlob(decoded).getDataAsString());\n```\n\nExample:\n```text\n// Instantiates a blob here for clarity\nconst blob = Utilities.newBlob('A string here');\n\n// Writes 'QSBzdHJpbmcgaGVyZQ==' to the log.\nconst encoded = Utilities.base64Encode(blob.getBytes());\nLogger.log(encoded);\n```\n\nExample:\n```text\n// Writes 'QSBzdHJpbmcgaGVyZQ==' to the log.\nconst encoded = Utilities.base64Encode('A string here');\nLogger.log(encoded);\n```\n\nExample:\n```text\n// \"Google Groups\" in Katakana (Japanese)\nconst input = 'Google グループ';\n\n// Writes \"R29vZ2xlIOOCsOODq+ODvOODlw==\" to the log\nconst encoded = Utilities.base64Encode(input, Utilities.Charset.UTF_8);\nLogger.log(encoded);\n```\n\nExample:\n```text\n// Instantiates a blob here for clarity\nconst blob = Utilities.newBlob('A string here');\n\n// Writes 'QSBzdHJpbmcgaGVyZQ==' to the log.\nconst encoded = Utilities.base64EncodeWebSafe(blob.getBytes());\nLogger.log(encoded);\n```\n\nExample:\n```text\n// Writes 'QSBzdHJpbmcgaGVyZQ==' to the log.\nconst encoded = Utilities.base64EncodeWebSafe('A string here');\nLogger.log(encoded);\n```\n\nExample:\n```text\n// \"Google Groups\" in Katakana (Japanese)\nconst input = 'Google グループ';\n\n// Writes \"R29vZ2xlIOOCsOODq-ODvOODlw==\" to the log\nconst encoded = Utilities.base64EncodeWebSafe(input, Utilities.Charset.UTF_8);\nLogger.log(encoded);\n```\n\nExample:\n```text\nconst input = Utilities.base64Decode(\n 'aW5wdXQgdG8gaGFzaA0K'); // == base64encode(\"input to hash\")\nconst digest = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, input);\nLogger.log(digest);\n```\n\nExample:\n```text\nconst digest = Utilities.computeDigest(\n Utilities.DigestAlgorithm.MD5,\n 'input to hash',\n);\nLogger.log(digest);\n```\n\nExample:\n```text\nconst digest = Utilities.computeDigest(\n Utilities.DigestAlgorithm.MD5,\n 'input to hash',\n Utilities.Charset.US_ASCII,\n);\nLogger.log(digest);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst input = Utilities.base64Decode(\n 'aW5wdXQgdG8gaGFzaA0K'); // == base64encode(\"input to hash\")\nconst key = Utilities.base64Decode('a2V5'); // == base64encode(\"key\")\nconst signature = Utilities.computeHmacSha256Signature(input, key);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeHmacSha256Signature(\n 'this is my input',\n 'my key - use a stronger one',\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeHmacSha256Signature(\n 'this is my input',\n 'my key - use a stronger one',\n Utilities.Charset.US_ASCII,\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst input = Utilities.base64Decode(\n 'aW5wdXQgdG8gaGFzaA0K'); // == base64encode(\"input to hash\")\nconst key = Utilities.base64Decode('a2V5'); // == base64encode(\"key\")\nconst signature = Utilities.computeHmacSignature(\n Utilities.MacAlgorithm.HMAC_MD5,\n input,\n key,\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeHmacSignature(\n Utilities.MacAlgorithm.HMAC_MD5,\n 'input to hash',\n 'key',\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeHmacSignature(\n Utilities.MacAlgorithm.HMAC_MD5,\n 'input to hash',\n 'key',\n Utilities.Charset.US_ASCII,\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeRsaSha1Signature(\n 'this is my input',\n PropertiesService.getScriptProperties().getProperty('YOUR_PRIVATE_KEY'),\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeRsaSha1Signature(\n 'this is my input',\n PropertiesService.getScriptProperties().getProperty('YOUR_PRIVATE_KEY'),\n Utilities.Charset.US_ASCII,\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeRsaSha256Signature(\n 'this is my input',\n PropertiesService.getScriptProperties().getProperty('YOUR_PRIVATE_KEY'),\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeRsaSignature(\n Utilities.RsaAlgorithm.RSA_SHA_256,\n 'this is my input',\n PropertiesService.getScriptProperties().getProperty('YOUR_PRIVATE_KEY'),\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeRsaSignature(\n Utilities.RsaAlgorithm.RSA_SHA_256,\n 'this is my input',\n PropertiesService.getScriptProperties().getProperty('YOUR_PRIVATE_KEY'),\n Utilities.Charset.US_ASCII,\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This formats the date as Greenwich Mean Time in the format\n// year-month-dateThour-minute-second.\nconst formattedDate = Utilities.formatDate(\n new Date(),\n 'GMT',\n 'yyyy-MM-dd\\'T\\'HH:mm:ss\\'Z\\'',\n);\nLogger.log(formattedDate);\n```\n\nExample:\n```text\n// \" 123.456000\"\nUtilities.formatString('%11.6f', 123.456);\n\n// \" abc\"\nUtilities.formatString('%6s', 'abc');\n```\n\nExample:\n```text\n// This assigns a UUID as a temporary ID for a data object you are creating in\n// your script.\nconst myDataObject = {\n tempId: Utilities.getUuid(),\n};\n```\n\nExample:\n```text\nconst textBlob = Utilities.newBlob(\n 'Some text to compress using gzip compression',\n);\n\n// Create the compressed blob.\nconst gzipBlob = Utilities.gzip(textBlob);\n```\n\nExample:\n```text\nconst textBlob = Utilities.newBlob(\n 'Some text to compress using gzip compression',\n);\n\n// Create the compressed blob.\nconst gzipBlob = Utilities.gzip(textBlob, 'text.gz');\n```\n\nExample:\n```text\n// Creates a blob object from a byte array.\nconst data = [71, 79, 79, 71, 76, 69];\nconst blob = Utilities.newBlob(data);\n\n// Logs the blob data as a string to the console.\nconsole.log(blob.getDataAsString());\n```\n\nExample:\n```text\n// Declares a byte array.\nconst data = [71, 79, 79, 71, 76, 69];\n\n// Declares the content type of the blob.\nconst contentType = 'application/json';\n\n// Creates a blob object from the byte array and content type.\nconst blob = Utilities.newBlob(data, contentType);\n\n// Logs the blob data as a string to the console.\nconsole.log(blob.getDataAsString());\n\n// Logs the content type of the blob to the console.\nconsole.log(blob.getContentType());\n```\n\nExample:\n```text\n// Declares a byte array.\nconst data = [71, 79, 79, 71, 76, 69];\n\n// Declares the content type of the blob.\nconst contentType = 'application/json';\n\n// Declares the name of the blob.\nconst name = 'Example blob';\n\n// Creates a blob object from the byte array, content type, and name.\nconst blob = Utilities.newBlob(data, contentType, name);\n\n// Logs the blob data as a string to the console.\nconsole.log('Blob data:', blob.getDataAsString());\n\n// Logs the content type of the blob to the console.\nconsole.log('Blob content type:', blob.getContentType());\n\n// Logs the name of the blob to the console.\nconsole.log('Blob name:', blob.getName());\n```\n\nExample:\n```text\n// Declares a string for the blob.\nconst data = 'GOOGLE';\n\n// Creates a blob object from a string.\nconst blob = Utilities.newBlob(data);\n\n// Logs the blob data in byte array to the console.\nconsole.log('Blob Data:', blob.getBytes());\n```\n\nExample:\n```text\n// Declares a string for the blob.\nconst data = 'GOOGLE';\n\n// Declares the content type of blob.\nconst contentType = 'application/json';\n\n// Creates a blob object from the string and content type.\nconst blob = Utilities.newBlob(data, contentType);\n\n// Logs the blob data in byte array to the console.\nconsole.log('Blob data:', blob.getBytes());\n\n// Logs the content type of the blob to the console.\nconsole.log(blob.getContentType());\n```\n\nExample:\n```text\n// Declares a string for the blob.\nconst data = 'GOOGLE';\n\n// Declares the content type of the blob.\nconst contentType = 'application/json';\n\n// Declares the name of the blob.\nconst name = 'Example blob';\n\n// Create a blob object from the string, content type, and name.\nconst blob = Utilities.newBlob(data, contentType, name);\n\n// Logs the blob data in byte array to the console.\nconsole.log('Blob data:', blob.getBytes());\n\n// Logs the content type of the blob to the console.\nconsole.log('Blob content type:', blob.getContentType());\n\n// Logs the name of the blob to the console.\nconsole.log('Blob name:', blob.getName());\n```\n\nExample:\n```text\n// This creates a two-dimensional array of the format [[a, b, c], [d, e, f]]\nconst csvString = 'a,b,c\\nd,e,f';\nconst data = Utilities.parseCsv(csvString);\n```\n\nExample:\n```text\n// This creates a two-dimensional array of the format [[a, b, c], [d, e, f]]\nconst csvString = 'a\\tb\\tc\\nd\\te\\tf';\nconst data = Utilities.parseCsv(csvString, '\\t');\n```\n\nExample:\n```text\n// This set of parameters parses the given string as a date in Greenwich Mean\n// Time, formatted as year-month-dateThour-minute-second.\nconst date = Utilities.parseDate(\n '1970-01-01 00:00:00',\n 'GMT',\n 'yyyy-MM-dd\\' \\'HH:mm:ss',\n);\nLogger.log(date);\n```\n\nExample:\n```text\n// Creates a blob object from a string.\nconst data = 'GOOGLE';\nconst blob = Utilities.newBlob(data);\n\n// Puts the script to sleep for 10,000 milliseconds (10 seconds).\nUtilities.sleep(10000);\n\n// Logs the blob data in byte array to the console.\nconsole.log(blob.getBytes());\n```\n\nExample:\n```text\nconst textBlob = Utilities.newBlob(\n 'Some text to compress using gzip compression',\n);\n\n// Create the compressed blob.\nconst gzipBlob = Utilities.gzip(textBlob, 'text.gz');\n\n// Uncompress the data.\nconst uncompressedBlob = Utilities.ungzip(gzipBlob);\n```\n\nExample:\n```text\nconst googleFavIconUrl = 'https://www.google.com/favicon.ico';\nconst googleLogoUrl = 'https://www.google.com/images/srpr/logo3w.png';\n\n// Fetch the Google favicon.ico file and get the Blob data\nconst faviconBlob = UrlFetchApp.fetch(googleFavIconUrl).getBlob();\nconst logoBlob = UrlFetchApp.fetch(googleLogoUrl).getBlob();\n\n// zip now references a blob containing an archive of both faviconBlob and\n// logoBlob\nconst zip = Utilities.zip([faviconBlob, logoBlob], 'google_images.zip');\n\n// This now unzips the blobs\nconst files = Utilities.unzip(zip);\n```\n\nExample:\n```text\nconst googleFavIconUrl = 'https://www.google.com/favicon.ico';\nconst googleLogoUrl = 'https://www.google.com/images/srpr/logo3w.png';\n\n// Fetch the Google favicon.ico file and get the Blob data\nconst faviconBlob = UrlFetchApp.fetch(googleFavIconUrl).getBlob();\nconst logoBlob = UrlFetchApp.fetch(googleLogoUrl).getBlob();\n\n// zip now references a blob containing an archive of both faviconBlob and\n// logoBlob\nconst zip = Utilities.zip([faviconBlob, logoBlob]);\n```\n\nExample:\n```text\nconst googleFavIconUrl = 'https://www.google.com/favicon.ico';\nconst googleLogoUrl = 'https://www.google.com/images/srpr/logo3w.png';\n\n// Fetch the Google favicon.ico file and get the Blob data\nconst faviconBlob = UrlFetchApp.fetch(googleFavIconUrl).getBlob();\nconst logoBlob = UrlFetchApp.fetch(googleLogoUrl).getBlob();\n\n// zip now references a blob containing an archive of both faviconBlob and\n// logoBlob\nconst zip = Utilities.zip([faviconBlob, logoBlob], 'google_images.zip');\n```\n\nExample:\n```text\n// Returns the object { name: \"John Smith\", company: \"Virginia Company\"}\nconst obj = Utilities.jsonParse(\n '{\"name\":\"John Smith\",\"company\":\"Virginia Company\"}',\n);\n```\n\nExample:\n```text\n// Logs: {\"name\":\"John Smith\",\"company\":\"Virginia Company\"}\nconst person = {\n name: 'John Smith',\n company: 'Virginia Company',\n};\nconst json = Utilities.jsonStringify(person);\nLogger.log(json);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.962Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":45,"totalLines":546,"estimatedTokens":3421}}959{"id":"doc-create_third_party_resources_from_the_menu_googl-a9fe00a5","source":"documentation","title":"Create third-party resources from the @ menu | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/apps-script/add-ons/editors/gsao/create-insert-resource-smart-chip","text":"Example:\n```text\n{\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\",\n \"https://www.googleapis.com/auth/workspace.linkcreate\"\n ],\n \"addOns\": {\n \"docs\": {\n \"linkPreviewTriggers\": [\n ...\n ],\n \"createActionTriggers\": [\n {\n \"id\": \"createCase\",\n \"labelText\": \"Create support case\",\n \"localizedLabelText\": {\n \"es\": \"Crear caso de soporte\"\n },\n \"runFunction\": \"createCaseInputCard\",\n \"logoUrl\": \"https://www.example.com/images/case.png\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Produces a support case creation form card.\n * \n * @param {!Object} event The event object.\n * @param {!Object=} errors An optional map of per-field error messages.\n * @param {boolean} isUpdate Whether to return the form as an update card navigation.\n * @return {!Card|!ActionResponse} The resulting card or action response.\n */\nfunction createCaseInputCard(event, errors, isUpdate) {\n\n const cardHeader = CardService.newCardHeader()\n .setTitle('Create a support case')\n\n const cardSectionTextInput1 = CardService.newTextInput()\n .setFieldName('name')\n .setTitle('Name')\n .setMultiline(false);\n\n const cardSectionTextInput2 = CardService.newTextInput()\n .setFieldName('description')\n .setTitle('Description')\n .setMultiline(true);\n\n const cardSectionSelectionInput1 = CardService.newSelectionInput()\n .setFieldName('priority')\n .setTitle('Priority')\n .setType(CardService.SelectionInputType.DROPDOWN)\n .addItem('P0', 'P0', false)\n .addItem('P1', 'P1', false)\n .addItem('P2', 'P2', false)\n .addItem('P3', 'P3', false);\n\n const cardSectionSelectionInput2 = CardService.newSelectionInput()\n .setFieldName('impact')\n .setTitle('Impact')\n .setType(CardService.SelectionInputType.CHECK_BOX)\n .addItem('Blocks a critical customer operation', 'Blocks a critical customer operation', false);\n\n const cardSectionButtonListButtonAction = CardService.newAction()\n .setPersistValues(true)\n .setFunctionName('submitCaseCreationForm')\n .setParameters({});\n\n const cardSectionButtonListButton = CardService.newTextButton()\n .setText('Create')\n .setTextButtonStyle(CardService.TextButtonStyle.TEXT)\n .setOnClickAction(cardSectionButtonListButtonAction);\n\n const cardSectionButtonList = CardService.newButtonSet()\n .addButton(cardSectionButtonListButton);\n\n // Builds the form inputs with error texts for invalid values.\n const cardSection = CardService.newCardSection();\n if (errors?.name) {\n cardSection.addWidget(createErrorTextParagraph(errors.name));\n }\n cardSection.addWidget(cardSectionTextInput1);\n if (errors?.description) {\n cardSection.addWidget(createErrorTextParagraph(errors.description));\n }\n cardSection.addWidget(cardSectionTextInput2);\n if (errors?.priority) {\n cardSection.addWidget(createErrorTextParagraph(errors.priority));\n }\n cardSection.addWidget(cardSectionSelectionInput1);\n if (errors?.impact) {\n cardSection.addWidget(createErrorTextParagraph(errors.impact));\n }\n\n cardSection.addWidget(cardSectionSelectionInput2);\n cardSection.addWidget(cardSectionButtonList);\n\n const card = CardService.newCardBuilder()\n .setHeader(cardHeader)\n .addSection(cardSection)\n .build();\n\n if (isUpdate) {\n return CardService.newActionResponseBuilder()\n .setNavigation(CardService.newNavigation().updateCard(card))\n .build();\n } else {\n return card;\n }\n}\n```\n\nExample:\n```text\n/**\n * Produces a support case creation form card.\n * \n * @param {!Object} event The event object.\n * @param {!Object=} errors An optional map of per-field error messages.\n * @param {boolean} isUpdate Whether to return the form as an update card navigation.\n * @return {!Card|!ActionResponse} The resulting card or action response.\n */\nfunction createCaseInputCard(event, errors, isUpdate) {\n\n const cardHeader1 = {\n title: \"Create a support case\"\n };\n\n const cardSection1TextInput1 = {\n textInput: {\n name: \"name\",\n label: \"Name\"\n }\n };\n\n const cardSection1TextInput2 = {\n textInput: {\n name: \"description\",\n label: \"Description\",\n type: \"MULTIPLE_LINE\"\n }\n };\n\n const cardSection1SelectionInput1 = {\n selectionInput: {\n name: \"priority\",\n label: \"Priority\",\n type: \"DROPDOWN\",\n items: [{\n text: \"P0\",\n value: \"P0\"\n }, {\n text: \"P1\",\n value: \"P1\"\n }, {\n text: \"P2\",\n value: \"P2\"\n }, {\n text: \"P3\",\n value: \"P3\"\n }]\n }\n };\n\n const cardSection1SelectionInput2 = {\n selectionInput: {\n name: \"impact\",\n label: \"Impact\",\n items: [{\n text: \"Blocks a critical customer operation\",\n value: \"Blocks a critical customer operation\"\n }]\n }\n };\n\n const cardSection1ButtonList1Button1Action1 = {\n function: process.env.URL,\n parameters: [\n {\n key: \"submitCaseCreationForm\",\n value: true\n }\n ],\n persistValues: true\n };\n\n const cardSection1ButtonList1Button1 = {\n text: \"Create\",\n onClick: {\n action: cardSection1ButtonList1Button1Action1\n }\n };\n\n const cardSection1ButtonList1 = {\n buttonList: {\n buttons: [cardSection1ButtonList1Button1]\n }\n };\n\n // Builds the creation form and adds error text for invalid inputs.\n const cardSection1 = [];\n if (errors?.name) {\n cardSection1.push(createErrorTextParagraph(errors.name));\n }\n cardSection1.push(cardSection1TextInput1);\n if (errors?.description) {\n cardSection1.push(createErrorTextParagraph(errors.description));\n }\n cardSection1.push(cardSection1TextInput2);\n if (errors?.priority) {\n cardSection1.push(createErrorTextParagraph(errors.priority));\n }\n cardSection1.push(cardSection1SelectionInput1);\n if (errors?.impact) {\n cardSection1.push(createErrorTextParagraph(errors.impact));\n }\n\n cardSection1.push(cardSection1SelectionInput2);\n cardSection1.push(cardSection1ButtonList1);\n\n const card = {\n header: cardHeader1,\n sections: [{\n widgets: cardSection1\n }]\n };\n\n if (isUpdate) {\n return {\n renderActions: {\n action: {\n navigations: [{\n updateCard: card\n }]\n }\n }\n };\n } else {\n return {\n action: {\n navigations: [{\n pushCard: card\n }]\n }\n };\n }\n}\n```\n\nExample:\n```text\ndef create_case_input_card(event, errors = {}, isUpdate = False):\n \"\"\"Produces a support case creation form card.\n Args:\n event: The event object.\n errors: An optional dict of per-field error messages.\n isUpdate: Whether to return the form as an update card navigation.\n Returns:\n The resulting card or action response.\n \"\"\"\n card_header1 = {\n \"title\": \"Create a support case\"\n }\n\n card_section1_text_input1 = {\n \"textInput\": {\n \"name\": \"name\",\n \"label\": \"Name\"\n }\n }\n\n card_section1_text_input2 = {\n \"textInput\": {\n \"name\": \"description\",\n \"label\": \"Description\",\n \"type\": \"MULTIPLE_LINE\"\n }\n }\n\n card_section1_selection_input1 = {\n \"selectionInput\": {\n \"name\": \"priority\",\n \"label\": \"Priority\",\n \"type\": \"DROPDOWN\",\n \"items\": [{\n \"text\": \"P0\",\n \"value\": \"P0\"\n }, {\n \"text\": \"P1\",\n \"value\": \"P1\"\n }, {\n \"text\": \"P2\",\n \"value\": \"P2\"\n }, {\n \"text\": \"P3\",\n \"value\": \"P3\"\n }]\n }\n }\n\n card_section1_selection_input2 = {\n \"selectionInput\": {\n \"name\": \"impact\",\n \"label\": \"Impact\",\n \"items\": [{\n \"text\": \"Blocks a critical customer operation\",\n \"value\": \"Blocks a critical customer operation\"\n }]\n }\n }\n\n card_section1_button_list1_button1_action1 = {\n \"function\": os.environ[\"URL\"],\n \"parameters\": [\n {\n \"key\": \"submitCaseCreationForm\",\n \"value\": True\n }\n ],\n \"persistValues\": True\n }\n\n card_section1_button_list1_button1 = {\n \"text\": \"Create\",\n \"onClick\": {\n \"action\": card_section1_button_list1_button1_action1\n }\n }\n\n card_section1_button_list1 = {\n \"buttonList\": {\n \"buttons\": [card_section1_button_list1_button1]\n }\n }\n\n # Builds the creation form and adds error text for invalid inputs.\n card_section1 = []\n if \"name\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"name\"]))\n card_section1.append(card_section1_text_input1)\n if \"description\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"description\"]))\n card_section1.append(card_section1_text_input2)\n if \"priority\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"priority\"]))\n card_section1.append(card_section1_selection_input1)\n if \"impact\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"impact\"]))\n\n card_section1.append(card_section1_selection_input2)\n card_section1.append(card_section1_button_list1)\n\n card = {\n \"header\": card_header1,\n \"sections\": [{\n \"widgets\": card_section1\n }]\n }\n\n if isUpdate:\n return {\n \"renderActions\": {\n \"action\": {\n \"navigations\": [{\n \"updateCard\": card\n }]\n }\n }\n }\n else:\n return {\n \"action\": {\n \"navigations\": [{\n \"pushCard\": card\n }]\n }\n }\n```\n\nExample:\n```text\n/**\n * Produces a support case creation form.\n * \n * @param event The event object.\n * @param errors A map of per-field error messages.\n * @param isUpdate Whether to return the form as an update card navigation.\n * @return The resulting card or action response.\n */\nJsonObject createCaseInputCard(JsonObject event, Map<String, String> errors, boolean isUpdate) {\n JsonObject cardHeader = new JsonObject();\n cardHeader.add(\"title\", new JsonPrimitive(\"Create a support case\"));\n\n JsonObject cardSectionTextInput1 = new JsonObject();\n cardSectionTextInput1.add(\"name\", new JsonPrimitive(\"name\"));\n cardSectionTextInput1.add(\"label\", new JsonPrimitive(\"Name\"));\n\n JsonObject cardSectionTextInput1Widget = new JsonObject();\n cardSectionTextInput1Widget.add(\"textInput\", cardSectionTextInput1);\n\n JsonObject cardSectionTextInput2 = new JsonObject();\n cardSectionTextInput2.add(\"name\", new JsonPrimitive(\"description\"));\n cardSectionTextInput2.add(\"label\", new JsonPrimitive(\"Description\"));\n cardSectionTextInput2.add(\"type\", new JsonPrimitive(\"MULTIPLE_LINE\"));\n\n JsonObject cardSectionTextInput2Widget = new JsonObject();\n cardSectionTextInput2Widget.add(\"textInput\", cardSectionTextInput2);\n\n JsonObject cardSectionSelectionInput1ItemsItem1 = new JsonObject();\n cardSectionSelectionInput1ItemsItem1.add(\"text\", new JsonPrimitive(\"P0\"));\n cardSectionSelectionInput1ItemsItem1.add(\"value\", new JsonPrimitive(\"P0\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem2 = new JsonObject();\n cardSectionSelectionInput1ItemsItem2.add(\"text\", new JsonPrimitive(\"P1\"));\n cardSectionSelectionInput1ItemsItem2.add(\"value\", new JsonPrimitive(\"P1\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem3 = new JsonObject();\n cardSectionSelectionInput1ItemsItem3.add(\"text\", new JsonPrimitive(\"P2\"));\n cardSectionSelectionInput1ItemsItem3.add(\"value\", new JsonPrimitive(\"P2\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem4 = new JsonObject();\n cardSectionSelectionInput1ItemsItem4.add(\"text\", new JsonPrimitive(\"P3\"));\n cardSectionSelectionInput1ItemsItem4.add(\"value\", new JsonPrimitive(\"P3\"));\n\n JsonArray cardSectionSelectionInput1Items = new JsonArray();\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem1);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem2);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem3);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem4);\n\n JsonObject cardSectionSelectionInput1 = new JsonObject();\n cardSectionSelectionInput1.add(\"name\", new JsonPrimitive(\"priority\"));\n cardSectionSelectionInput1.add(\"label\", new JsonPrimitive(\"Priority\"));\n cardSectionSelectionInput1.add(\"type\", new JsonPrimitive(\"DROPDOWN\"));\n cardSectionSelectionInput1.add(\"items\", cardSectionSelectionInput1Items);\n\n JsonObject cardSectionSelectionInput1Widget = new JsonObject();\n cardSectionSelectionInput1Widget.add(\"selectionInput\", cardSectionSelectionInput1);\n\n JsonObject cardSectionSelectionInput2ItemsItem = new JsonObject();\n cardSectionSelectionInput2ItemsItem.add(\"text\", new JsonPrimitive(\"Blocks a critical customer operation\"));\n cardSectionSelectionInput2ItemsItem.add(\"value\", new JsonPrimitive(\"Blocks a critical customer operation\"));\n\n JsonArray cardSectionSelectionInput2Items = new JsonArray();\n cardSectionSelectionInput2Items.add(cardSectionSelectionInput2ItemsItem);\n\n JsonObject cardSectionSelectionInput2 = new JsonObject();\n cardSectionSelectionInput2.add(\"name\", new JsonPrimitive(\"impact\"));\n cardSectionSelectionInput2.add(\"label\", new JsonPrimitive(\"Impact\"));\n cardSectionSelectionInput2.add(\"items\", cardSectionSelectionInput2Items);\n\n JsonObject cardSectionSelectionInput2Widget = new JsonObject();\n cardSectionSelectionInput2Widget.add(\"selectionInput\", cardSectionSelectionInput2);\n\n JsonObject cardSectionButtonListButtonActionParametersParameter = new JsonObject();\n cardSectionButtonListButtonActionParametersParameter.add(\"key\", new JsonPrimitive(\"submitCaseCreationForm\"));\n cardSectionButtonListButtonActionParametersParameter.add(\"value\", new JsonPrimitive(true));\n\n JsonArray cardSectionButtonListButtonActionParameters = new JsonArray();\n cardSectionButtonListButtonActionParameters.add(cardSectionButtonListButtonActionParametersParameter);\n\n JsonObject cardSectionButtonListButtonAction = new JsonObject();\n cardSectionButtonListButtonAction.add(\"function\", new JsonPrimitive(System.getenv().get(\"URL\")));\n cardSectionButtonListButtonAction.add(\"parameters\", cardSectionButtonListButtonActionParameters);\n cardSectionButtonListButtonAction.add(\"persistValues\", new JsonPrimitive(true));\n\n JsonObject cardSectionButtonListButtonOnCLick = new JsonObject();\n cardSectionButtonListButtonOnCLick.add(\"action\", cardSectionButtonListButtonAction);\n\n JsonObject cardSectionButtonListButton = new JsonObject();\n cardSectionButtonListButton.add(\"text\", new JsonPrimitive(\"Create\"));\n cardSectionButtonListButton.add(\"onClick\", cardSectionButtonListButtonOnCLick);\n\n JsonArray cardSectionButtonListButtons = new JsonArray();\n cardSectionButtonListButtons.add(cardSectionButtonListButton);\n\n JsonObject cardSectionButtonList = new JsonObject();\n cardSectionButtonList.add(\"buttons\", cardSectionButtonListButtons);\n\n JsonObject cardSectionButtonListWidget = new JsonObject();\n cardSectionButtonListWidget.add(\"buttonList\", cardSectionButtonList);\n\n // Builds the form inputs with error texts for invalid values.\n JsonArray cardSection = new JsonArray();\n if (errors.containsKey(\"name\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"name\").toString()));\n }\n cardSection.add(cardSectionTextInput1Widget);\n if (errors.containsKey(\"description\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"description\").toString()));\n }\n cardSection.add(cardSectionTextInput2Widget);\n if (errors.containsKey(\"priority\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"priority\").toString()));\n }\n cardSection.add(cardSectionSelectionInput1Widget);\n if (errors.containsKey(\"impact\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"impact\").toString()));\n }\n\n cardSection.add(cardSectionSelectionInput2Widget);\n cardSection.add(cardSectionButtonListWidget);\n\n JsonObject cardSectionWidgets = new JsonObject();\n cardSectionWidgets.add(\"widgets\", cardSection);\n\n JsonArray sections = new JsonArray();\n sections.add(cardSectionWidgets);\n\n JsonObject card = new JsonObject();\n card.add(\"header\", cardHeader);\n card.add(\"sections\", sections);\n\n JsonObject navigation = new JsonObject();\n if (isUpdate) {\n navigation.add(\"updateCard\", card);\n } else {\n navigation.add(\"pushCard\", card);\n }\n\n JsonArray navigations = new JsonArray();\n navigations.add(navigation);\n\n JsonObject action = new JsonObject();\n action.add(\"navigations\", navigations);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n if (!isUpdate) {\n return renderActions;\n }\n\n JsonObject update = new JsonObject();\n update.add(\"renderActions\", renderActions);\n\n return update;\n}\n```\n\nExample:\n```text\n/**\n * Returns a submit form response that inserts a link into the document.\n * \n * @param {string} title The title of the link to insert.\n * @param {string} url The URL of the link to insert.\n * @return {!SubmitFormResponse} The resulting submit form response.\n */\nfunction createLinkRenderAction(title, url) {\n return {\n renderActions: {\n action: {\n links: [{\n title: title,\n url: url\n }]\n }\n }\n };\n}\n```\n\nExample:\n```text\ndef create_link_render_action(title, url):\n \"\"\"Returns a submit form response that inserts a link into the document.\n Args:\n title: The title of the link to insert.\n url: The URL of the link to insert.\n Returns:\n The resulting submit form response.\n \"\"\"\n return {\n \"renderActions\": {\n \"action\": {\n \"links\": [{\n \"title\": title,\n \"url\": url\n }]\n }\n }\n }\n```\n\nExample:\n```text\n/**\n * Returns a submit form response that inserts a link into the document.\n * \n * @param title The title of the link to insert.\n * @param url The URL of the link to insert.\n * @return The resulting submit form response.\n */\nJsonObject createLinkRenderAction(String title, String url) {\n JsonObject link = new JsonObject();\n link.add(\"title\", new JsonPrimitive(title));\n link.add(\"url\", new JsonPrimitive(url));\n\n JsonArray links = new JsonArray();\n links.add(link);\n\n JsonObject action = new JsonObject();\n action.add(\"links\", links);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n JsonObject linkRenderAction = new JsonObject();\n linkRenderAction.add(\"renderActions\", renderActions);\n\n return linkRenderAction;\n}\n```\n\nExample:\n```text\n/**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param {!Object} event The event object with form input values.\n * @return {!ActionResponse|!SubmitFormResponse} The resulting response.\n */\nfunction submitCaseCreationForm(event) {\n const caseDetails = {\n name: event.formInput.name,\n description: event.formInput.description,\n priority: event.formInput.priority,\n impact: !!event.formInput.impact,\n };\n\n const errors = validateFormInputs(caseDetails);\n if (Object.keys(errors).length > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n const title = `Case ${caseDetails.name}`;\n // Adds the case details as parameters to the generated link URL.\n const url = 'https://example.com/support/cases/?' + generateQuery(caseDetails);\n return createLinkRenderAction(title, url);\n }\n}\n\n/**\n* Build a query path with URL parameters.\n*\n* @param {!Map} parameters A map with the URL parameters.\n* @return {!string} The resulting query path.\n*/\nfunction generateQuery(parameters) {\n return Object.entries(parameters).flatMap(([k, v]) =>\n Array.isArray(v) ? v.map(e => `${k}=${encodeURIComponent(e)}`) : `${k}=${encodeURIComponent(v)}`\n ).join(\"&\");\n}\n```\n\nExample:\n```text\n/**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param {!Object} event The event object with form input values.\n * @return {!ActionResponse|!SubmitFormResponse} The resulting response.\n */\nfunction submitCaseCreationForm(event) {\n const caseDetails = {\n name: event.commonEventObject.formInputs?.name?.stringInputs?.value[0],\n description: event.commonEventObject.formInputs?.description?.stringInputs?.value[0],\n priority: event.commonEventObject.formInputs?.priority?.stringInputs?.value[0],\n impact: !!event.commonEventObject.formInputs?.impact?.stringInputs?.value[0],\n };\n\n const errors = validateFormInputs(caseDetails);\n if (Object.keys(errors).length > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n const title = `Case ${caseDetails.name}`;\n // Adds the case details as parameters to the generated link URL.\n const url = new URL('https://example.com/support/cases/');\n for (const [key, value] of Object.entries(caseDetails)) {\n url.searchParams.append(key, value);\n }\n return createLinkRenderAction(title, url.href);\n }\n}\n```\n\nExample:\n```text\ndef submit_case_creation_form(event):\n \"\"\"Submits the creation form.\n\n If valid, returns a render action that inserts a new link\n into the document. If invalid, returns an update card navigation that\n re-renders the creation form with error messages.\n Args:\n event: The event object with form input values.\n Returns:\n The resulting response.\n \"\"\"\n formInputs = event[\"commonEventObject\"][\"formInputs\"] if \"formInputs\" in event[\"commonEventObject\"] else None\n case_details = {\n \"name\": None,\n \"description\": None,\n \"priority\": None,\n \"impact\": None,\n }\n if formInputs is not None:\n case_details[\"name\"] = formInputs[\"name\"][\"stringInputs\"][\"value\"][0] if \"name\" in formInputs else None\n case_details[\"description\"] = formInputs[\"description\"][\"stringInputs\"][\"value\"][0] if \"description\" in formInputs else None\n case_details[\"priority\"] = formInputs[\"priority\"][\"stringInputs\"][\"value\"][0] if \"priority\" in formInputs else None\n case_details[\"impact\"] = formInputs[\"impact\"][\"stringInputs\"][\"value\"][0] if \"impact\" in formInputs else False\n\n errors = validate_form_inputs(case_details)\n if len(errors) > 0:\n return create_case_input_card(event, errors, True) # Update mode\n else:\n title = f'Case {case_details[\"name\"]}'\n # Adds the case details as parameters to the generated link URL.\n url = \"https://example.com/support/cases/?\" + urlencode(case_details)\n return create_link_render_action(title, url)\n```\n\nExample:\n```text\n/**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param event The event object with form input values.\n * @return The resulting response.\n */\nJsonObject submitCaseCreationForm(JsonObject event) throws Exception {\n JsonObject formInputs = event.getAsJsonObject(\"commonEventObject\").getAsJsonObject(\"formInputs\");\n Map<String, String> caseDetails = new HashMap<String, String>();\n if (formInputs != null) {\n if (formInputs.has(\"name\")) {\n caseDetails.put(\"name\", formInputs.getAsJsonObject(\"name\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"description\")) {\n caseDetails.put(\"description\", formInputs.getAsJsonObject(\"description\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"priority\")) {\n caseDetails.put(\"priority\", formInputs.getAsJsonObject(\"priority\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"impact\")) {\n caseDetails.put(\"impact\", formInputs.getAsJsonObject(\"impact\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n }\n\n Map<String, String> errors = validateFormInputs(caseDetails);\n if (errors.size() > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n String title = String.format(\"Case %s\", caseDetails.get(\"name\"));\n // Adds the case details as parameters to the generated link URL.\n URIBuilder uriBuilder = new URIBuilder(\"https://example.com/support/cases/\");\n for (String caseDetailKey : caseDetails.keySet()) {\n uriBuilder.addParameter(caseDetailKey, caseDetails.get(caseDetailKey));\n }\n return createLinkRenderAction(title, uriBuilder.build().toURL().toString());\n }\n}\n```\n\nExample:\n```text\n/**\n * Validates case creation form input values.\n * \n * @param {!Object} caseDetails The values of each form input submitted by the user.\n * @return {!Object} A map from field name to error message. An empty object\n * represents a valid form submission.\n */\nfunction validateFormInputs(caseDetails) {\n const errors = {};\n if (!caseDetails.name) {\n errors.name = 'You must provide a name';\n }\n if (!caseDetails.description) {\n errors.description = 'You must provide a description';\n }\n if (!caseDetails.priority) {\n errors.priority = 'You must provide a priority';\n }\n if (caseDetails.impact && caseDetails.priority !== 'P0' && caseDetails.priority !== 'P1') {\n errors.impact = 'If an issue blocks a critical customer operation, priority must be P0 or P1';\n }\n\n return errors;\n}\n\n/**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param {string} errorMessage A description of input value error.\n * @return {!TextParagraph} The resulting text paragraph.\n */\nfunction createErrorTextParagraph(errorMessage) {\n return CardService.newTextParagraph()\n .setText('<font color=\\\"#BA0300\\\"><b>Error:</b> ' + errorMessage + '</font>');\n}\n```\n\nExample:\n```text\n/**\n * Validates case creation form input values.\n * \n * @param {!Object} caseDetails The values of each form input submitted by the user.\n * @return {!Object} A map from field name to error message. An empty object\n * represents a valid form submission.\n */\nfunction validateFormInputs(caseDetails) {\n const errors = {};\n if (caseDetails.name === undefined) {\n errors.name = 'You must provide a name';\n }\n if (caseDetails.description === undefined) {\n errors.description = 'You must provide a description';\n }\n if (caseDetails.priority === undefined) {\n errors.priority = 'You must provide a priority';\n }\n if (caseDetails.impact && !(['P0', 'P1']).includes(caseDetails.priority)) {\n errors.impact = 'If an issue blocks a critical customer operation, priority must be P0 or P1';\n }\n\n return errors;\n}\n\n/**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param {string} errorMessage A description of input value error.\n * @return {!TextParagraph} The resulting text paragraph.\n */\nfunction createErrorTextParagraph(errorMessage) {\n return {\n textParagraph: {\n text: '<font color=\\\"#BA0300\\\"><b>Error:</b> ' + errorMessage + '</font>'\n }\n }\n}\n```\n\nExample:\n```text\ndef validate_form_inputs(case_details):\n \"\"\"Validates case creation form input values.\n Args:\n case_details: The values of each form input submitted by the user.\n Returns:\n A dict from field name to error message. An empty object represents a valid form submission.\n \"\"\"\n errors = {}\n if case_details[\"name\"] is None:\n errors[\"name\"] = \"You must provide a name\"\n if case_details[\"description\"] is None:\n errors[\"description\"] = \"You must provide a description\"\n if case_details[\"priority\"] is None:\n errors[\"priority\"] = \"You must provide a priority\"\n if case_details[\"impact\"] is not None and case_details[\"priority\"] not in ['P0', 'P1']:\n errors[\"impact\"] = \"If an issue blocks a critical customer operation, priority must be P0 or P1\"\n return errors\n\n\ndef create_error_text_paragraph(error_message):\n \"\"\"Returns a text paragraph with red text indicating a form field validation error.\n Args:\n error_essage: A description of input value error.\n Returns:\n The resulting text paragraph.\n \"\"\"\n return {\n \"textParagraph\": {\n \"text\": '<font color=\\\"#BA0300\\\"><b>Error:</b> ' + error_message + '</font>'\n }\n }\n```\n\nExample:\n```text\n/**\n * Validates case creation form input values.\n * \n * @param caseDetails The values of each form input submitted by the user.\n * @return A map from field name to error message. An empty object\n * represents a valid form submission.\n */\nMap<String, String> validateFormInputs(Map<String, String> caseDetails) {\n Map<String, String> errors = new HashMap<String, String>();\n if (!caseDetails.containsKey(\"name\")) {\n errors.put(\"name\", \"You must provide a name\");\n }\n if (!caseDetails.containsKey(\"description\")) {\n errors.put(\"description\", \"You must provide a description\");\n }\n if (!caseDetails.containsKey(\"priority\")) {\n errors.put(\"priority\", \"You must provide a priority\");\n }\n if (caseDetails.containsKey(\"impact\") && !Arrays.asList(new String[]{\"P0\", \"P1\"}).contains(caseDetails.get(\"priority\"))) {\n errors.put(\"impact\", \"If an issue blocks a critical customer operation, priority must be P0 or P1\");\n }\n\n return errors;\n}\n\n/**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param errorMessage A description of input value error.\n * @return The resulting text paragraph.\n */\nJsonObject createErrorTextParagraph(String errorMessage) {\n JsonObject textParagraph = new JsonObject();\n textParagraph.add(\"text\", new JsonPrimitive(\"<font color=\\\"#BA0300\\\"><b>Error:</b> \" + errorMessage + \"</font>\"));\n\n JsonObject textParagraphWidget = new JsonObject();\n textParagraphWidget.add(\"textParagraph\", textParagraph);\n\n return textParagraphWidget;\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/New_York\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\",\n \"https://www.googleapis.com/auth/workspace.linkcreate\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Manage support cases\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"caseLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n },\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ],\n \"createActionTriggers\": [\n {\n \"id\": \"createCase\",\n \"labelText\": \"Create support case\",\n \"localizedLabelText\": {\n \"es\": \"Crear caso de soporte\"\n },\n \"runFunction\": \"createCaseInputCard\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n{\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\",\n \"https://www.googleapis.com/auth/workspace.linkcreate\"\n ],\n \"addOns\": {\n \"common\": {\n \"name\": \"Manage support cases\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"$URL1\",\n \"patterns\": [\n {\n \"hostPattern\": \"example.com\",\n \"pathPrefix\": \"support/cases\"\n },\n {\n \"hostPattern\": \"*.example.com\",\n \"pathPrefix\": \"cases\"\n },\n {\n \"hostPattern\": \"cases.example.com\"\n }\n ],\n \"labelText\": \"Support case\",\n \"localizedLabelText\": {\n \"es\": \"Caso de soporte\"\n },\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ],\n \"createActionTriggers\": [\n {\n \"id\": \"createCase\",\n \"labelText\": \"Create support case\",\n \"localizedLabelText\": {\n \"es\": \"Crear caso de soporte\"\n },\n \"runFunction\": \"$URL2\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/support-icon.png\"\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2024 Google LLC\n * \n * 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.\n */\n\n/**\n* Entry point for a support case link preview.\n*\n* @param {!Object} event The event object.\n* @return {!Card} The resulting preview link card.\n*/\nfunction caseLinkPreview(event) {\n\n // If the event object URL matches a specified pattern for support case links.\n if (event.docs.matchedUrl.url) {\n\n // Uses the event object to parse the URL and identify the case details.\n const caseDetails = parseQuery(event.docs.matchedUrl.url);\n\n // Builds a preview card with the case name, and description\n const caseHeader = CardService.newCardHeader()\n .setTitle(`Case ${caseDetails[\"name\"][0]}`);\n const caseDescription = CardService.newTextParagraph()\n .setText(caseDetails[\"description\"][0]);\n\n // Returns the card.\n // Uses the text from the card's header for the title of the smart chip.\n return CardService.newCardBuilder()\n .setHeader(caseHeader)\n .addSection(CardService.newCardSection().addWidget(caseDescription))\n .build();\n }\n}\n\n/**\n* Extracts the URL parameters from the given URL.\n*\n* @param {!string} url The URL to parse.\n* @return {!Map} A map with the extracted URL parameters.\n*/\nfunction parseQuery(url) {\n const query = url.split(\"?\")[1];\n if (query) {\n return query.split(\"&\")\n .reduce(function(o, e) {\n var temp = e.split(\"=\");\n var key = temp[0].trim();\n var value = temp[1].trim();\n value = isNaN(value) ? value : Number(value);\n if (o[key]) {\n o[key].push(value);\n } else {\n o[key] = [value];\n }\n return o;\n }, {});\n }\n return null;\n}\n\n\n\n/**\n * Produces a support case creation form card.\n * \n * @param {!Object} event The event object.\n * @param {!Object=} errors An optional map of per-field error messages.\n * @param {boolean} isUpdate Whether to return the form as an update card navigation.\n * @return {!Card|!ActionResponse} The resulting card or action response.\n */\nfunction createCaseInputCard(event, errors, isUpdate) {\n\n const cardHeader = CardService.newCardHeader()\n .setTitle('Create a support case')\n\n const cardSectionTextInput1 = CardService.newTextInput()\n .setFieldName('name')\n .setTitle('Name')\n .setMultiline(false);\n\n const cardSectionTextInput2 = CardService.newTextInput()\n .setFieldName('description')\n .setTitle('Description')\n .setMultiline(true);\n\n const cardSectionSelectionInput1 = CardService.newSelectionInput()\n .setFieldName('priority')\n .setTitle('Priority')\n .setType(CardService.SelectionInputType.DROPDOWN)\n .addItem('P0', 'P0', false)\n .addItem('P1', 'P1', false)\n .addItem('P2', 'P2', false)\n .addItem('P3', 'P3', false);\n\n const cardSectionSelectionInput2 = CardService.newSelectionInput()\n .setFieldName('impact')\n .setTitle('Impact')\n .setType(CardService.SelectionInputType.CHECK_BOX)\n .addItem('Blocks a critical customer operation', 'Blocks a critical customer operation', false);\n\n const cardSectionButtonListButtonAction = CardService.newAction()\n .setPersistValues(true)\n .setFunctionName('submitCaseCreationForm')\n .setParameters({});\n\n const cardSectionButtonListButton = CardService.newTextButton()\n .setText('Create')\n .setTextButtonStyle(CardService.TextButtonStyle.TEXT)\n .setOnClickAction(cardSectionButtonListButtonAction);\n\n const cardSectionButtonList = CardService.newButtonSet()\n .addButton(cardSectionButtonListButton);\n\n // Builds the form inputs with error texts for invalid values.\n const cardSection = CardService.newCardSection();\n if (errors?.name) {\n cardSection.addWidget(createErrorTextParagraph(errors.name));\n }\n cardSection.addWidget(cardSectionTextInput1);\n if (errors?.description) {\n cardSection.addWidget(createErrorTextParagraph(errors.description));\n }\n cardSection.addWidget(cardSectionTextInput2);\n if (errors?.priority) {\n cardSection.addWidget(createErrorTextParagraph(errors.priority));\n }\n cardSection.addWidget(cardSectionSelectionInput1);\n if (errors?.impact) {\n cardSection.addWidget(createErrorTextParagraph(errors.impact));\n }\n\n cardSection.addWidget(cardSectionSelectionInput2);\n cardSection.addWidget(cardSectionButtonList);\n\n const card = CardService.newCardBuilder()\n .setHeader(cardHeader)\n .addSection(cardSection)\n .build();\n\n if (isUpdate) {\n return CardService.newActionResponseBuilder()\n .setNavigation(CardService.newNavigation().updateCard(card))\n .build();\n } else {\n return card;\n }\n}\n\n\n/**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param {!Object} event The event object with form input values.\n * @return {!ActionResponse|!SubmitFormResponse} The resulting response.\n */\nfunction submitCaseCreationForm(event) {\n const caseDetails = {\n name: event.formInput.name,\n description: event.formInput.description,\n priority: event.formInput.priority,\n impact: !!event.formInput.impact,\n };\n\n const errors = validateFormInputs(caseDetails);\n if (Object.keys(errors).length > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n const title = `Case ${caseDetails.name}`;\n // Adds the case details as parameters to the generated link URL.\n const url = 'https://example.com/support/cases/?' + generateQuery(caseDetails);\n return createLinkRenderAction(title, url);\n }\n}\n\n/**\n* Build a query path with URL parameters.\n*\n* @param {!Map} parameters A map with the URL parameters.\n* @return {!string} The resulting query path.\n*/\nfunction generateQuery(parameters) {\n return Object.entries(parameters).flatMap(([k, v]) =>\n Array.isArray(v) ? v.map(e => `${k}=${encodeURIComponent(e)}`) : `${k}=${encodeURIComponent(v)}`\n ).join(\"&\");\n}\n\n\n/**\n * Validates case creation form input values.\n * \n * @param {!Object} caseDetails The values of each form input submitted by the user.\n * @return {!Object} A map from field name to error message. An empty object\n * represents a valid form submission.\n */\nfunction validateFormInputs(caseDetails) {\n const errors = {};\n if (!caseDetails.name) {\n errors.name = 'You must provide a name';\n }\n if (!caseDetails.description) {\n errors.description = 'You must provide a description';\n }\n if (!caseDetails.priority) {\n errors.priority = 'You must provide a priority';\n }\n if (caseDetails.impact && caseDetails.priority !== 'P0' && caseDetails.priority !== 'P1') {\n errors.impact = 'If an issue blocks a critical customer operation, priority must be P0 or P1';\n }\n\n return errors;\n}\n\n/**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param {string} errorMessage A description of input value error.\n * @return {!TextParagraph} The resulting text paragraph.\n */\nfunction createErrorTextParagraph(errorMessage) {\n return CardService.newTextParagraph()\n .setText('<font color=\\\"#BA0300\\\"><b>Error:</b> ' + errorMessage + '</font>');\n}\n\n\n/**\n * Returns a submit form response that inserts a link into the document.\n * \n * @param {string} title The title of the link to insert.\n * @param {string} url The URL of the link to insert.\n * @return {!SubmitFormResponse} The resulting submit form response.\n */\nfunction createLinkRenderAction(title, url) {\n return {\n renderActions: {\n action: {\n links: [{\n title: title,\n url: url\n }]\n }\n }\n };\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2024 Google LLC\n *\n * 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.\n */\n\n/**\n * Responds to any HTTP request related to link previews.\n *\n * @param {Object} req An HTTP request context.\n * @param {Object} res An HTTP response context.\n */\nexports.createLinkPreview = (req, res) => {\n const event = req.body;\n if (event.docs.matchedUrl.url) {\n const url = event.docs.matchedUrl.url;\n const parsedUrl = new URL(url);\n // If the event object URL matches a specified pattern for preview links.\n if (parsedUrl.hostname === 'example.com') {\n if (parsedUrl.pathname.startsWith('/support/cases/')) {\n return res.json(caseLinkPreview(parsedUrl));\n }\n }\n }\n};\n\n\n/**\n * \n * A support case link preview.\n *\n * @param {!URL} url The event object.\n * @return {!Card} The resulting preview link card.\n */\nfunction caseLinkPreview(url) {\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n // Parses the URL and identify the case details.\n const name = `Case ${url.searchParams.get(\"name\")}`;\n return {\n action: {\n linkPreview: {\n title: name,\n previewCard: {\n header: {\n title: name\n },\n sections: [{\n widgets: [{\n textParagraph: {\n text: url.searchParams.get(\"description\")\n }\n }]\n }]\n }\n }\n }\n };\n}\n\n\n\n/**\n * Responds to any HTTP request related to 3P resource creations.\n *\n * @param {Object} req An HTTP request context.\n * @param {Object} res An HTTP response context.\n */\nexports.create3pResources = (req, res) => {\n const event = req.body;\n if (event.commonEventObject.parameters?.submitCaseCreationForm) {\n res.json(submitCaseCreationForm(event));\n } else {\n res.json(createCaseInputCard(event));\n }\n};\n\n\n/**\n * Produces a support case creation form card.\n * \n * @param {!Object} event The event object.\n * @param {!Object=} errors An optional map of per-field error messages.\n * @param {boolean} isUpdate Whether to return the form as an update card navigation.\n * @return {!Card|!ActionResponse} The resulting card or action response.\n */\nfunction createCaseInputCard(event, errors, isUpdate) {\n\n const cardHeader1 = {\n title: \"Create a support case\"\n };\n\n const cardSection1TextInput1 = {\n textInput: {\n name: \"name\",\n label: \"Name\"\n }\n };\n\n const cardSection1TextInput2 = {\n textInput: {\n name: \"description\",\n label: \"Description\",\n type: \"MULTIPLE_LINE\"\n }\n };\n\n const cardSection1SelectionInput1 = {\n selectionInput: {\n name: \"priority\",\n label: \"Priority\",\n type: \"DROPDOWN\",\n items: [{\n text: \"P0\",\n value: \"P0\"\n }, {\n text: \"P1\",\n value: \"P1\"\n }, {\n text: \"P2\",\n value: \"P2\"\n }, {\n text: \"P3\",\n value: \"P3\"\n }]\n }\n };\n\n const cardSection1SelectionInput2 = {\n selectionInput: {\n name: \"impact\",\n label: \"Impact\",\n items: [{\n text: \"Blocks a critical customer operation\",\n value: \"Blocks a critical customer operation\"\n }]\n }\n };\n\n const cardSection1ButtonList1Button1Action1 = {\n function: process.env.URL,\n parameters: [\n {\n key: \"submitCaseCreationForm\",\n value: true\n }\n ],\n persistValues: true\n };\n\n const cardSection1ButtonList1Button1 = {\n text: \"Create\",\n onClick: {\n action: cardSection1ButtonList1Button1Action1\n }\n };\n\n const cardSection1ButtonList1 = {\n buttonList: {\n buttons: [cardSection1ButtonList1Button1]\n }\n };\n\n // Builds the creation form and adds error text for invalid inputs.\n const cardSection1 = [];\n if (errors?.name) {\n cardSection1.push(createErrorTextParagraph(errors.name));\n }\n cardSection1.push(cardSection1TextInput1);\n if (errors?.description) {\n cardSection1.push(createErrorTextParagraph(errors.description));\n }\n cardSection1.push(cardSection1TextInput2);\n if (errors?.priority) {\n cardSection1.push(createErrorTextParagraph(errors.priority));\n }\n cardSection1.push(cardSection1SelectionInput1);\n if (errors?.impact) {\n cardSection1.push(createErrorTextParagraph(errors.impact));\n }\n\n cardSection1.push(cardSection1SelectionInput2);\n cardSection1.push(cardSection1ButtonList1);\n\n const card = {\n header: cardHeader1,\n sections: [{\n widgets: cardSection1\n }]\n };\n\n if (isUpdate) {\n return {\n renderActions: {\n action: {\n navigations: [{\n updateCard: card\n }]\n }\n }\n };\n } else {\n return {\n action: {\n navigations: [{\n pushCard: card\n }]\n }\n };\n }\n}\n\n\n/**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param {!Object} event The event object with form input values.\n * @return {!ActionResponse|!SubmitFormResponse} The resulting response.\n */\nfunction submitCaseCreationForm(event) {\n const caseDetails = {\n name: event.commonEventObject.formInputs?.name?.stringInputs?.value[0],\n description: event.commonEventObject.formInputs?.description?.stringInputs?.value[0],\n priority: event.commonEventObject.formInputs?.priority?.stringInputs?.value[0],\n impact: !!event.commonEventObject.formInputs?.impact?.stringInputs?.value[0],\n };\n\n const errors = validateFormInputs(caseDetails);\n if (Object.keys(errors).length > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n const title = `Case ${caseDetails.name}`;\n // Adds the case details as parameters to the generated link URL.\n const url = new URL('https://example.com/support/cases/');\n for (const [key, value] of Object.entries(caseDetails)) {\n url.searchParams.append(key, value);\n }\n return createLinkRenderAction(title, url.href);\n }\n}\n\n\n/**\n * Validates case creation form input values.\n * \n * @param {!Object} caseDetails The values of each form input submitted by the user.\n * @return {!Object} A map from field name to error message. An empty object\n * represents a valid form submission.\n */\nfunction validateFormInputs(caseDetails) {\n const errors = {};\n if (caseDetails.name === undefined) {\n errors.name = 'You must provide a name';\n }\n if (caseDetails.description === undefined) {\n errors.description = 'You must provide a description';\n }\n if (caseDetails.priority === undefined) {\n errors.priority = 'You must provide a priority';\n }\n if (caseDetails.impact && !(['P0', 'P1']).includes(caseDetails.priority)) {\n errors.impact = 'If an issue blocks a critical customer operation, priority must be P0 or P1';\n }\n\n return errors;\n}\n\n/**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param {string} errorMessage A description of input value error.\n * @return {!TextParagraph} The resulting text paragraph.\n */\nfunction createErrorTextParagraph(errorMessage) {\n return {\n textParagraph: {\n text: '<font color=\\\"#BA0300\\\"><b>Error:</b> ' + errorMessage + '</font>'\n }\n }\n}\n\n\n/**\n * Returns a submit form response that inserts a link into the document.\n * \n * @param {string} title The title of the link to insert.\n * @param {string} url The URL of the link to insert.\n * @return {!SubmitFormResponse} The resulting submit form response.\n */\nfunction createLinkRenderAction(title, url) {\n return {\n renderActions: {\n action: {\n links: [{\n title: title,\n url: url\n }]\n }\n }\n };\n}\n```\n\nExample:\n```text\n# Copyright 2024 Google LLC\n#\n# 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.\n\nfrom typing import Any, Mapping\nfrom urllib.parse import urlencode\n\nimport os\nimport flask\nimport functions_framework\n\n\n@functions_framework.http\ndef create_3p_resources(req: flask.Request):\n \"\"\"Responds to any HTTP request related to 3P resource creations.\n Args:\n req: An HTTP request context.\n Returns:\n An HTTP response context.\n \"\"\"\n event = req.get_json(silent=True)\n parameters = event[\"commonEventObject\"][\"parameters\"] if \"parameters\" in event[\"commonEventObject\"] else None\n if parameters is not None and parameters[\"submitCaseCreationForm\"]:\n return submit_case_creation_form(event)\n else:\n return create_case_input_card(event)\n\n\n\n\ndef create_case_input_card(event, errors = {}, isUpdate = False):\n \"\"\"Produces a support case creation form card.\n Args:\n event: The event object.\n errors: An optional dict of per-field error messages.\n isUpdate: Whether to return the form as an update card navigation.\n Returns:\n The resulting card or action response.\n \"\"\"\n card_header1 = {\n \"title\": \"Create a support case\"\n }\n\n card_section1_text_input1 = {\n \"textInput\": {\n \"name\": \"name\",\n \"label\": \"Name\"\n }\n }\n\n card_section1_text_input2 = {\n \"textInput\": {\n \"name\": \"description\",\n \"label\": \"Description\",\n \"type\": \"MULTIPLE_LINE\"\n }\n }\n\n card_section1_selection_input1 = {\n \"selectionInput\": {\n \"name\": \"priority\",\n \"label\": \"Priority\",\n \"type\": \"DROPDOWN\",\n \"items\": [{\n \"text\": \"P0\",\n \"value\": \"P0\"\n }, {\n \"text\": \"P1\",\n \"value\": \"P1\"\n }, {\n \"text\": \"P2\",\n \"value\": \"P2\"\n }, {\n \"text\": \"P3\",\n \"value\": \"P3\"\n }]\n }\n }\n\n card_section1_selection_input2 = {\n \"selectionInput\": {\n \"name\": \"impact\",\n \"label\": \"Impact\",\n \"items\": [{\n \"text\": \"Blocks a critical customer operation\",\n \"value\": \"Blocks a critical customer operation\"\n }]\n }\n }\n\n card_section1_button_list1_button1_action1 = {\n \"function\": os.environ[\"URL\"],\n \"parameters\": [\n {\n \"key\": \"submitCaseCreationForm\",\n \"value\": True\n }\n ],\n \"persistValues\": True\n }\n\n card_section1_button_list1_button1 = {\n \"text\": \"Create\",\n \"onClick\": {\n \"action\": card_section1_button_list1_button1_action1\n }\n }\n\n card_section1_button_list1 = {\n \"buttonList\": {\n \"buttons\": [card_section1_button_list1_button1]\n }\n }\n\n # Builds the creation form and adds error text for invalid inputs.\n card_section1 = []\n if \"name\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"name\"]))\n card_section1.append(card_section1_text_input1)\n if \"description\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"description\"]))\n card_section1.append(card_section1_text_input2)\n if \"priority\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"priority\"]))\n card_section1.append(card_section1_selection_input1)\n if \"impact\" in errors:\n card_section1.append(create_error_text_paragraph(errors[\"impact\"]))\n\n card_section1.append(card_section1_selection_input2)\n card_section1.append(card_section1_button_list1)\n\n card = {\n \"header\": card_header1,\n \"sections\": [{\n \"widgets\": card_section1\n }]\n }\n\n if isUpdate:\n return {\n \"renderActions\": {\n \"action\": {\n \"navigations\": [{\n \"updateCard\": card\n }]\n }\n }\n }\n else:\n return {\n \"action\": {\n \"navigations\": [{\n \"pushCard\": card\n }]\n }\n }\n\n\n\n\ndef submit_case_creation_form(event):\n \"\"\"Submits the creation form.\n\n If valid, returns a render action that inserts a new link\n into the document. If invalid, returns an update card navigation that\n re-renders the creation form with error messages.\n Args:\n event: The event object with form input values.\n Returns:\n The resulting response.\n \"\"\"\n formInputs = event[\"commonEventObject\"][\"formInputs\"] if \"formInputs\" in event[\"commonEventObject\"] else None\n case_details = {\n \"name\": None,\n \"description\": None,\n \"priority\": None,\n \"impact\": None,\n }\n if formInputs is not None:\n case_details[\"name\"] = formInputs[\"name\"][\"stringInputs\"][\"value\"][0] if \"name\" in formInputs else None\n case_details[\"description\"] = formInputs[\"description\"][\"stringInputs\"][\"value\"][0] if \"description\" in formInputs else None\n case_details[\"priority\"] = formInputs[\"priority\"][\"stringInputs\"][\"value\"][0] if \"priority\" in formInputs else None\n case_details[\"impact\"] = formInputs[\"impact\"][\"stringInputs\"][\"value\"][0] if \"impact\" in formInputs else False\n\n errors = validate_form_inputs(case_details)\n if len(errors) > 0:\n return create_case_input_card(event, errors, True) # Update mode\n else:\n title = f'Case {case_details[\"name\"]}'\n # Adds the case details as parameters to the generated link URL.\n url = \"https://example.com/support/cases/?\" + urlencode(case_details)\n return create_link_render_action(title, url)\n\n\n\n\ndef validate_form_inputs(case_details):\n \"\"\"Validates case creation form input values.\n Args:\n case_details: The values of each form input submitted by the user.\n Returns:\n A dict from field name to error message. An empty object represents a valid form submission.\n \"\"\"\n errors = {}\n if case_details[\"name\"] is None:\n errors[\"name\"] = \"You must provide a name\"\n if case_details[\"description\"] is None:\n errors[\"description\"] = \"You must provide a description\"\n if case_details[\"priority\"] is None:\n errors[\"priority\"] = \"You must provide a priority\"\n if case_details[\"impact\"] is not None and case_details[\"priority\"] not in ['P0', 'P1']:\n errors[\"impact\"] = \"If an issue blocks a critical customer operation, priority must be P0 or P1\"\n return errors\n\n\ndef create_error_text_paragraph(error_message):\n \"\"\"Returns a text paragraph with red text indicating a form field validation error.\n Args:\n error_essage: A description of input value error.\n Returns:\n The resulting text paragraph.\n \"\"\"\n return {\n \"textParagraph\": {\n \"text\": '<font color=\\\"#BA0300\\\"><b>Error:</b> ' + error_message + '</font>'\n }\n }\n\n\n\n\ndef create_link_render_action(title, url):\n \"\"\"Returns a submit form response that inserts a link into the document.\n Args:\n title: The title of the link to insert.\n url: The URL of the link to insert.\n Returns:\n The resulting submit form response.\n \"\"\"\n return {\n \"renderActions\": {\n \"action\": {\n \"links\": [{\n \"title\": title,\n \"url\": url\n }]\n }\n }\n }\n```\n\nExample:\n```text\n# Copyright 2023 Google LLC\n#\n# 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.\n\nfrom typing import Any, Mapping\nfrom urllib.parse import urlparse, parse_qs\n\nimport flask\nimport functions_framework\n\n\n@functions_framework.http\ndef create_link_preview(req: flask.Request):\n \"\"\"Responds to any HTTP request related to link previews.\n Args:\n req: An HTTP request context.\n Returns:\n An HTTP response context.\n \"\"\"\n event = req.get_json(silent=True)\n if event[\"docs\"][\"matchedUrl\"][\"url\"]:\n url = event[\"docs\"][\"matchedUrl\"][\"url\"]\n parsed_url = urlparse(url)\n # If the event object URL matches a specified pattern for preview links.\n if parsed_url.hostname == \"example.com\":\n if parsed_url.path.startswith(\"/support/cases/\"):\n return case_link_preview(parsed_url)\n\n return {}\n\n\n\n\ndef case_link_preview(url):\n \"\"\"A support case link preview.\n Args:\n url: A matching URL.\n Returns:\n The resulting preview link card.\n \"\"\"\n\n # Parses the URL and identify the case details.\n query_string = parse_qs(url.query)\n name = f'Case {query_string[\"name\"][0]}'\n # Uses the text from the card's header for the title of the smart chip.\n return {\n \"action\": {\n \"linkPreview\": {\n \"title\": name,\n \"previewCard\": {\n \"header\": {\n \"title\": name\n },\n \"sections\": [{\n \"widgets\": [{\n \"textParagraph\": {\n \"text\": query_string[\"description\"][0]\n }\n }]\n }],\n }\n }\n }\n }\n```\n\nExample:\n```text\n/**\n * Copyright 2024 Google LLC\n *\n * 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.\n */\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.http.client.utils.URIBuilder;\n\nimport com.google.cloud.functions.HttpFunction;\nimport com.google.cloud.functions.HttpRequest;\nimport com.google.cloud.functions.HttpResponse;\nimport com.google.gson.Gson;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\npublic class Create3pResources implements HttpFunction {\n private static final Gson gson = new Gson();\n\n /**\n * Responds to any HTTP request related to 3p resource creations.\n *\n * @param request An HTTP request context.\n * @param response An HTTP response context.\n */\n @Override\n public void service(HttpRequest request, HttpResponse response) throws Exception {\n JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);\n JsonObject parameters = event.getAsJsonObject(\"commonEventObject\").getAsJsonObject(\"parameters\");\n if (parameters != null && parameters.has(\"submitCaseCreationForm\") && parameters.get(\"submitCaseCreationForm\").getAsBoolean()) {\n response.getWriter().write(gson.toJson(submitCaseCreationForm(event)));\n } else {\n response.getWriter().write(gson.toJson(createCaseInputCard(event, new HashMap<String, String>(), false)));\n }\n }\n\n\n /**\n * Produces a support case creation form.\n * \n * @param event The event object.\n * @param errors A map of per-field error messages.\n * @param isUpdate Whether to return the form as an update card navigation.\n * @return The resulting card or action response.\n */\n JsonObject createCaseInputCard(JsonObject event, Map<String, String> errors, boolean isUpdate) {\n JsonObject cardHeader = new JsonObject();\n cardHeader.add(\"title\", new JsonPrimitive(\"Create a support case\"));\n\n JsonObject cardSectionTextInput1 = new JsonObject();\n cardSectionTextInput1.add(\"name\", new JsonPrimitive(\"name\"));\n cardSectionTextInput1.add(\"label\", new JsonPrimitive(\"Name\"));\n\n JsonObject cardSectionTextInput1Widget = new JsonObject();\n cardSectionTextInput1Widget.add(\"textInput\", cardSectionTextInput1);\n\n JsonObject cardSectionTextInput2 = new JsonObject();\n cardSectionTextInput2.add(\"name\", new JsonPrimitive(\"description\"));\n cardSectionTextInput2.add(\"label\", new JsonPrimitive(\"Description\"));\n cardSectionTextInput2.add(\"type\", new JsonPrimitive(\"MULTIPLE_LINE\"));\n\n JsonObject cardSectionTextInput2Widget = new JsonObject();\n cardSectionTextInput2Widget.add(\"textInput\", cardSectionTextInput2);\n\n JsonObject cardSectionSelectionInput1ItemsItem1 = new JsonObject();\n cardSectionSelectionInput1ItemsItem1.add(\"text\", new JsonPrimitive(\"P0\"));\n cardSectionSelectionInput1ItemsItem1.add(\"value\", new JsonPrimitive(\"P0\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem2 = new JsonObject();\n cardSectionSelectionInput1ItemsItem2.add(\"text\", new JsonPrimitive(\"P1\"));\n cardSectionSelectionInput1ItemsItem2.add(\"value\", new JsonPrimitive(\"P1\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem3 = new JsonObject();\n cardSectionSelectionInput1ItemsItem3.add(\"text\", new JsonPrimitive(\"P2\"));\n cardSectionSelectionInput1ItemsItem3.add(\"value\", new JsonPrimitive(\"P2\"));\n\n JsonObject cardSectionSelectionInput1ItemsItem4 = new JsonObject();\n cardSectionSelectionInput1ItemsItem4.add(\"text\", new JsonPrimitive(\"P3\"));\n cardSectionSelectionInput1ItemsItem4.add(\"value\", new JsonPrimitive(\"P3\"));\n\n JsonArray cardSectionSelectionInput1Items = new JsonArray();\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem1);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem2);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem3);\n cardSectionSelectionInput1Items.add(cardSectionSelectionInput1ItemsItem4);\n\n JsonObject cardSectionSelectionInput1 = new JsonObject();\n cardSectionSelectionInput1.add(\"name\", new JsonPrimitive(\"priority\"));\n cardSectionSelectionInput1.add(\"label\", new JsonPrimitive(\"Priority\"));\n cardSectionSelectionInput1.add(\"type\", new JsonPrimitive(\"DROPDOWN\"));\n cardSectionSelectionInput1.add(\"items\", cardSectionSelectionInput1Items);\n\n JsonObject cardSectionSelectionInput1Widget = new JsonObject();\n cardSectionSelectionInput1Widget.add(\"selectionInput\", cardSectionSelectionInput1);\n\n JsonObject cardSectionSelectionInput2ItemsItem = new JsonObject();\n cardSectionSelectionInput2ItemsItem.add(\"text\", new JsonPrimitive(\"Blocks a critical customer operation\"));\n cardSectionSelectionInput2ItemsItem.add(\"value\", new JsonPrimitive(\"Blocks a critical customer operation\"));\n\n JsonArray cardSectionSelectionInput2Items = new JsonArray();\n cardSectionSelectionInput2Items.add(cardSectionSelectionInput2ItemsItem);\n\n JsonObject cardSectionSelectionInput2 = new JsonObject();\n cardSectionSelectionInput2.add(\"name\", new JsonPrimitive(\"impact\"));\n cardSectionSelectionInput2.add(\"label\", new JsonPrimitive(\"Impact\"));\n cardSectionSelectionInput2.add(\"items\", cardSectionSelectionInput2Items);\n\n JsonObject cardSectionSelectionInput2Widget = new JsonObject();\n cardSectionSelectionInput2Widget.add(\"selectionInput\", cardSectionSelectionInput2);\n\n JsonObject cardSectionButtonListButtonActionParametersParameter = new JsonObject();\n cardSectionButtonListButtonActionParametersParameter.add(\"key\", new JsonPrimitive(\"submitCaseCreationForm\"));\n cardSectionButtonListButtonActionParametersParameter.add(\"value\", new JsonPrimitive(true));\n\n JsonArray cardSectionButtonListButtonActionParameters = new JsonArray();\n cardSectionButtonListButtonActionParameters.add(cardSectionButtonListButtonActionParametersParameter);\n\n JsonObject cardSectionButtonListButtonAction = new JsonObject();\n cardSectionButtonListButtonAction.add(\"function\", new JsonPrimitive(System.getenv().get(\"URL\")));\n cardSectionButtonListButtonAction.add(\"parameters\", cardSectionButtonListButtonActionParameters);\n cardSectionButtonListButtonAction.add(\"persistValues\", new JsonPrimitive(true));\n\n JsonObject cardSectionButtonListButtonOnCLick = new JsonObject();\n cardSectionButtonListButtonOnCLick.add(\"action\", cardSectionButtonListButtonAction);\n\n JsonObject cardSectionButtonListButton = new JsonObject();\n cardSectionButtonListButton.add(\"text\", new JsonPrimitive(\"Create\"));\n cardSectionButtonListButton.add(\"onClick\", cardSectionButtonListButtonOnCLick);\n\n JsonArray cardSectionButtonListButtons = new JsonArray();\n cardSectionButtonListButtons.add(cardSectionButtonListButton);\n\n JsonObject cardSectionButtonList = new JsonObject();\n cardSectionButtonList.add(\"buttons\", cardSectionButtonListButtons);\n\n JsonObject cardSectionButtonListWidget = new JsonObject();\n cardSectionButtonListWidget.add(\"buttonList\", cardSectionButtonList);\n\n // Builds the form inputs with error texts for invalid values.\n JsonArray cardSection = new JsonArray();\n if (errors.containsKey(\"name\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"name\").toString()));\n }\n cardSection.add(cardSectionTextInput1Widget);\n if (errors.containsKey(\"description\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"description\").toString()));\n }\n cardSection.add(cardSectionTextInput2Widget);\n if (errors.containsKey(\"priority\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"priority\").toString()));\n }\n cardSection.add(cardSectionSelectionInput1Widget);\n if (errors.containsKey(\"impact\")) {\n cardSection.add(createErrorTextParagraph(errors.get(\"impact\").toString()));\n }\n\n cardSection.add(cardSectionSelectionInput2Widget);\n cardSection.add(cardSectionButtonListWidget);\n\n JsonObject cardSectionWidgets = new JsonObject();\n cardSectionWidgets.add(\"widgets\", cardSection);\n\n JsonArray sections = new JsonArray();\n sections.add(cardSectionWidgets);\n\n JsonObject card = new JsonObject();\n card.add(\"header\", cardHeader);\n card.add(\"sections\", sections);\n\n JsonObject navigation = new JsonObject();\n if (isUpdate) {\n navigation.add(\"updateCard\", card);\n } else {\n navigation.add(\"pushCard\", card);\n }\n\n JsonArray navigations = new JsonArray();\n navigations.add(navigation);\n\n JsonObject action = new JsonObject();\n action.add(\"navigations\", navigations);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n if (!isUpdate) {\n return renderActions;\n }\n\n JsonObject update = new JsonObject();\n update.add(\"renderActions\", renderActions);\n\n return update;\n }\n\n\n /**\n * Submits the creation form. If valid, returns a render action\n * that inserts a new link into the document. If invalid, returns an\n * update card navigation that re-renders the creation form with error messages.\n * \n * @param event The event object with form input values.\n * @return The resulting response.\n */\n JsonObject submitCaseCreationForm(JsonObject event) throws Exception {\n JsonObject formInputs = event.getAsJsonObject(\"commonEventObject\").getAsJsonObject(\"formInputs\");\n Map<String, String> caseDetails = new HashMap<String, String>();\n if (formInputs != null) {\n if (formInputs.has(\"name\")) {\n caseDetails.put(\"name\", formInputs.getAsJsonObject(\"name\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"description\")) {\n caseDetails.put(\"description\", formInputs.getAsJsonObject(\"description\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"priority\")) {\n caseDetails.put(\"priority\", formInputs.getAsJsonObject(\"priority\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n if (formInputs.has(\"impact\")) {\n caseDetails.put(\"impact\", formInputs.getAsJsonObject(\"impact\").getAsJsonObject(\"stringInputs\").getAsJsonArray(\"value\").get(0).getAsString());\n }\n }\n\n Map<String, String> errors = validateFormInputs(caseDetails);\n if (errors.size() > 0) {\n return createCaseInputCard(event, errors, /* isUpdate= */ true);\n } else {\n String title = String.format(\"Case %s\", caseDetails.get(\"name\"));\n // Adds the case details as parameters to the generated link URL.\n URIBuilder uriBuilder = new URIBuilder(\"https://example.com/support/cases/\");\n for (String caseDetailKey : caseDetails.keySet()) {\n uriBuilder.addParameter(caseDetailKey, caseDetails.get(caseDetailKey));\n }\n return createLinkRenderAction(title, uriBuilder.build().toURL().toString());\n }\n }\n\n\n /**\n * Validates case creation form input values.\n * \n * @param caseDetails The values of each form input submitted by the user.\n * @return A map from field name to error message. An empty object\n * represents a valid form submission.\n */\n Map<String, String> validateFormInputs(Map<String, String> caseDetails) {\n Map<String, String> errors = new HashMap<String, String>();\n if (!caseDetails.containsKey(\"name\")) {\n errors.put(\"name\", \"You must provide a name\");\n }\n if (!caseDetails.containsKey(\"description\")) {\n errors.put(\"description\", \"You must provide a description\");\n }\n if (!caseDetails.containsKey(\"priority\")) {\n errors.put(\"priority\", \"You must provide a priority\");\n }\n if (caseDetails.containsKey(\"impact\") && !Arrays.asList(new String[]{\"P0\", \"P1\"}).contains(caseDetails.get(\"priority\"))) {\n errors.put(\"impact\", \"If an issue blocks a critical customer operation, priority must be P0 or P1\");\n }\n\n return errors;\n }\n\n /**\n * Returns a text paragraph with red text indicating a form field validation error.\n * \n * @param errorMessage A description of input value error.\n * @return The resulting text paragraph.\n */\n JsonObject createErrorTextParagraph(String errorMessage) {\n JsonObject textParagraph = new JsonObject();\n textParagraph.add(\"text\", new JsonPrimitive(\"<font color=\\\"#BA0300\\\"><b>Error:</b> \" + errorMessage + \"</font>\"));\n\n JsonObject textParagraphWidget = new JsonObject();\n textParagraphWidget.add(\"textParagraph\", textParagraph);\n\n return textParagraphWidget;\n }\n\n\n /**\n * Returns a submit form response that inserts a link into the document.\n * \n * @param title The title of the link to insert.\n * @param url The URL of the link to insert.\n * @return The resulting submit form response.\n */\n JsonObject createLinkRenderAction(String title, String url) {\n JsonObject link = new JsonObject();\n link.add(\"title\", new JsonPrimitive(title));\n link.add(\"url\", new JsonPrimitive(url));\n\n JsonArray links = new JsonArray();\n links.add(link);\n\n JsonObject action = new JsonObject();\n action.add(\"links\", links);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n JsonObject linkRenderAction = new JsonObject();\n linkRenderAction.add(\"renderActions\", renderActions);\n\n return linkRenderAction;\n }\n\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2024 Google LLC\n *\n * 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.\n */\n\nimport com.google.cloud.functions.HttpFunction;\nimport com.google.cloud.functions.HttpRequest;\nimport com.google.cloud.functions.HttpResponse;\nimport com.google.gson.Gson;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonPrimitive;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URL;\nimport java.net.URLDecoder;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class CreateLinkPreview implements HttpFunction {\n private static final Gson gson = new Gson();\n\n /**\n * Responds to any HTTP request related to link previews.\n *\n * @param request An HTTP request context.\n * @param response An HTTP response context.\n */\n @Override\n public void service(HttpRequest request, HttpResponse response) throws Exception {\n JsonObject event = gson.fromJson(request.getReader(), JsonObject.class);\n String url = event.getAsJsonObject(\"docs\")\n .getAsJsonObject(\"matchedUrl\")\n .get(\"url\")\n .getAsString();\n URL parsedURL = new URL(url);\n // If the event object URL matches a specified pattern for preview links.\n if (\"example.com\".equals(parsedURL.getHost())) {\n if (parsedURL.getPath().startsWith(\"/support/cases/\")) {\n response.getWriter().write(gson.toJson(caseLinkPreview(parsedURL)));\n return;\n }\n }\n\n response.getWriter().write(\"{}\");\n }\n\n\n /**\n * A support case link preview.\n *\n * @param url A matching URL.\n * @return The resulting preview link card.\n */\n JsonObject caseLinkPreview(URL url) throws UnsupportedEncodingException {\n // Parses the URL and identify the case details.\n Map<String, String> caseDetails = new HashMap<String, String>();\n for (String pair : url.getQuery().split(\"&\")) {\n caseDetails.put(URLDecoder.decode(pair.split(\"=\")[0], \"UTF-8\"), URLDecoder.decode(pair.split(\"=\")[1], \"UTF-8\"));\n }\n\n // Builds a preview card with the case name, and description\n // Uses the text from the card's header for the title of the smart chip.\n JsonObject cardHeader = new JsonObject();\n String caseName = String.format(\"Case %s\", caseDetails.get(\"name\"));\n cardHeader.add(\"title\", new JsonPrimitive(caseName));\n\n JsonObject textParagraph = new JsonObject();\n textParagraph.add(\"text\", new JsonPrimitive(caseDetails.get(\"description\")));\n\n JsonObject widget = new JsonObject();\n widget.add(\"textParagraph\", textParagraph);\n\n JsonArray widgets = new JsonArray();\n widgets.add(widget);\n\n JsonObject section = new JsonObject();\n section.add(\"widgets\", widgets);\n\n JsonArray sections = new JsonArray();\n sections.add(section);\n\n JsonObject previewCard = new JsonObject();\n previewCard.add(\"header\", cardHeader);\n previewCard.add(\"sections\", sections);\n\n JsonObject linkPreview = new JsonObject();\n linkPreview.add(\"title\", new JsonPrimitive(caseName));\n linkPreview.add(\"previewCard\", previewCard);\n\n JsonObject action = new JsonObject();\n action.add(\"linkPreview\", linkPreview);\n\n JsonObject renderActions = new JsonObject();\n renderActions.add(\"action\", action);\n\n return renderActions;\n }\n\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.966Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":24,"totalLines":2406,"estimatedTokens":19415}}960{"id":"doc-google_sheets_macros_apps_script_google_for_deve-fbf791ac","source":"documentation","title":"Google Sheets Macros | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/sheets/macros","text":"Example:\n```text\n{\n ...\n \"sheets\": {\n \"macros\": [{\n \"menuName\": \"QuickRowSum\",\n \"functionName\": \"calculateRowSum\",\n \"defaultShortcut\": \"Ctrl+Alt+Shift+1\"\n }, {\n \"menuName\": \"Headerfy\",\n \"functionName\": \"updateToHeaderStyle\",\n \"defaultShortcut\": \"Ctrl+Alt+Shift+2\"\n }]\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.967Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":19,"estimatedTokens":83}}961{"id":"doc-class_group_apps_script_google_for_developers-d5d80287","source":"documentation","title":"Class Group | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/group","text":"Example:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getRange('2:3');\nrange.shiftRowGroupDepth(1);\nconst group = sheet.getRowGroup(2, 1);\n\n// Collapses this group.\ngroup.collapse();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getRange('2:3');\nrange.shiftRowGroupDepth(1);\nconst group = sheet.getRowGroup(2, 1);\n\n// Expands this group.\ngroup.expand();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nsheet.setRowGroupControlAfter(true);\nconst range = sheet.getRange('2:3');\nrange.shiftRowGroupDepth(1);\nconst group = sheet.getRowGroup(2, 1);\n\n// Returns 4\nconst controlIndex = group.getControlIndex();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getRange('2:3');\nrange.shiftRowGroupDepth(1);\nconst group = sheet.getRowGroup(2, 1);\n\n// Returns 1 if the group is at depth 1.\nconst depth = group.getDepth();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nlet range = sheet.getRange('2:3');\nrange.shiftRowGroupDepth(1);\nconst group = sheet.getRowGroup(1, 1);\n\n// Returns the range 2:3 if the group is over rows 2:3\nrange = group.getRange();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getRange('2:3');\nrange.shiftRowGroupDepth(1);\nconst group = sheet.getRowGroup(2, 1);\n\n// Returns true if the group is collapsed.\nconst isCollapsed = group.isCollapsed();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nlet range = sheet.getRange('2:3');\nrange.shiftRowGroupDepth(1);\nconst group = sheet.getRowGroup(2, 1);\n\n// Removes this group\nrange = group.remove();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.970Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":79,"estimatedTokens":461}}962{"id":"doc-class_console_apps_script_google_for_developers-0fe69e23","source":"documentation","title":"Class console | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/base/console","text":"Example:\n```text\nfunction measuringExecutionTime() {\n const label = \"myFunction() time\"; // Labels the timing log entry.\n console.time(label); // Starts the timer.\n try {\n myFunction(); // Function to time.\n } catch (e) {\n // Logs an ERROR message.\n console.error(\"myFunction() yielded an error: \" + e);\n }\n console.timeEnd(label); // Stops the timer, logs execution duration.\n}\n\nfunction myFunction() {\n // ...\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.972Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":20,"estimatedTokens":112}}963{"id":"doc-class_keyvalue_apps_script_google_for_developers-e9a0f311","source":"documentation","title":"Class KeyValue | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/key-value","text":"Example:\n```text\n// ...\n\nconst action = CardService.newAuthorizationAction().setAuthorizationUrl('url');\nCardService.newTextButton().setText('Authorize').setAuthorizationAction(action);\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('composeEmailCallback');\nCardService.newTextButton()\n .setText('Compose Email')\n .setComposeAction(action, CardService.ComposedEmailType.REPLY_AS_DRAFT);\n\n// ...\n\nfunction composeEmailCallback(e) {\n const thread = GmailApp.getThreadById(e.threadId);\n const draft = thread.createDraftReply('This is a reply');\n return CardService.newComposeActionResponseBuilder()\n .setGmailDraft(draft)\n .build();\n}\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('notificationCallback');\nCardService.newTextButton()\n .setText('Create notification')\n .setOnClickAction(action);\n\n// ...\n\nfunction notificationCallback() {\n return CardService.newActionResponseBuilder()\n .setNotification(\n CardService.newNotification().setText('Some info to display to user'),\n )\n .build();\n}\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('openLinkCallback');\nCardService.newTextButton()\n .setText('Open Link')\n .setOnClickOpenLinkAction(action);\n\n// ...\n\nfunction openLinkCallback() {\n return CardService.newActionResponseBuilder()\n .setOpenLink(CardService.newOpenLink().setUrl('https://www.google.com'))\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.980Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":67,"estimatedTokens":380}}964{"id":"doc-migrate_from_sheets_api_v3_google_sheets_google_-f1d142a7","source":"documentation","title":"Migrate from Sheets API v3 | Google Sheets | Google for Developers","url":"https://developers.google.com/sheets/api/guides/migration","text":"Example:\n```text\nhttps://spreadsheets.google.com/feeds\n```\n\nExample:\n```text\nhttps://www.googleapis.com/auth/spreadsheets\n```\n\nExample:\n```text\nhttps://www.googleapis.com/auth/spreadsheets.readonly\nhttps://www.googleapis.com/auth/spreadsheets\nhttps://www.googleapis.com/auth/drive.readonly\nhttps://www.googleapis.com/auth/drive\n```\n\nExample:\n```text\nhttps://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full\n```\n\nExample:\n```text\nhttps://spreadsheets.google.com/feeds/worksheets/spreadsheetId/public/basic\n```\n\nExample:\n```text\nGET https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId?fields=sheets.properties.title\n```\n\nExample:\n```text\nPOST https://sheets.googleapis.com/v4/spreadsheets\n```\n\nExample:\n```text\n{\n \"properties\": {\"title\": \"NewTitle\"}\n}\n```\n\nExample:\n```text\nGET https://spreadsheets.google.com/feeds/spreadsheets/private/full\n```\n\nExample:\n```text\nGET https://www.googleapis.com/drive/v3/files\n ?q=mimeType='application/vnd.google-apps.spreadsheet'\n```\n\nExample:\n```text\nGET https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full\n```\n\nExample:\n```text\n<feed xmlns=\"http://www.w3.org/2005/Atom\"\n xmlns:openSearch=\"http://a9.com/-/spec/opensearch/1.1/\"\n xmlns:gs=\"http://schemas.google.com/spreadsheets/2006\"\n xmlns:gd=\"http://schemas.google.com/g/2005\"\n gd:etag='W/\"D0cERnk-eip7ImA9WBBXGEg.\"'>\n <id>https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full</id>\n <updated>2006-11-17T18:23:45.173Z</updated>\n <title type=\"text\">Groceries R Us</title>\n <link rel=\"alternate\" type=\"text/html\"\n href=\"https://spreadsheets.google.com/ccc?key=spreadsheetId\"/>\n <link rel=\"http://schemas.google.com/g/2005#feed\"\n type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full\"/>\n <link rel=\"self\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full\"/>\n <link rel=\"http://schemas.google.com/g/2005#post\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full\"/>\n <author>\n <name>Fitzwilliam Darcy</name>\n <email>fitz@example.com</email>\n </author>\n <openSearch:totalResults>1</openSearch:totalResults>\n <openSearch:startIndex>1</openSearch:startIndex>\n <openSearch:itemsPerPage>1</openSearch:itemsPerPage>\n <entry gd:etag='\"YDwqeyI.\"'>\n <id>https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full/sheetId</id>\n <updated>2006-11-17T18:23:45.173Z</updated>\n <title type=\"text\">Sheet1</title>\n <content type=\"text\">Sheet1</content>\n <link rel=\"http://schemas.google.com/spreadsheets/2006#listfeed\"\n type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full\"/>\n <link rel=\"http://schemas.google.com/spreadsheets/2006#cellsfeed\"\n type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full\"/>\n <link rel=\"self\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full/sheetId\"/>\n <link rel=\"edit\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full/sheetId/version\"/>\n <gs:rowCount>100</gs:rowCount>\n <gs:colCount>20</gs:colCount>\n </entry>\n</feed>\n```\n\nExample:\n```text\nGET https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId?includeGridData=false\n```\n\nExample:\n```text\n{\n \"spreadsheetId\": spreadsheetId,\n \"sheets\": [\n {\"properties\": {\n \"sheetId\": sheetId,\n \"title\": \"Sheet1\",\n \"index\": 0,\n \"gridProperties\": {\n \"rowCount\": 100,\n \"columnCount\": 20,\n \"frozenRowCount\": 1,\n \"frozenColumnCount\": 0,\n \"hideGridlines\": false\n },\n ...\n },\n ...\n },\n ...\n ],\n ...\n}\n```\n\nExample:\n```text\nPOST https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full\n```\n\nExample:\n```text\n<entry xmlns=\"http://www.w3.org/2005/Atom\"\n xmlns:gs=\"http://schemas.google.com/spreadsheets/2006\">\n <title>Expenses</title>\n <gs:rowCount>50</gs:rowCount>\n <gs:colCount>10</gs:colCount>\n</entry>\n```\n\nExample:\n```text\nPOST https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId:batchUpdate\n```\n\nExample:\n```text\n{\n \"requests\": [{\n \"addSheet\": {\n \"properties\": {\n \"title\": \"Expenses\",\n \"sheetType\": \"GRID\",\n \"gridProperties\": {\n \"rowCount\": 50,\n \"columnCount\": 10\n }\n }\n }\n }],\n}\n```\n\nExample:\n```text\nPUT https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full/sheetId/version\n```\n\nExample:\n```text\n<entry>\n <id>\n https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full/sheetId\n </id>\n <updated>2007-07-30T18:51:30.666Z</updated>\n <category scheme=\"http://schemas.google.com/spreadsheets/2006\"\n term=\"http://schemas.google.com/spreadsheets/2006#worksheet\"/>\n <title type=\"text\">Expenses</title>\n <content type=\"text\">Expenses</content>\n <link rel=\"http://schemas.google.com/spreadsheets/2006#listfeed\"\n type=\"application/atom+xml\" href=\"https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full\"/>\n <link rel=\"http://schemas.google.com/spreadsheets/2006#cellsfeed\"\n type=\"application/atom+xml\" href=\"https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full\"/>\n <link rel=\"self\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full/sheetId\"/>\n <link rel=\"edit\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full/sheetId/version\"/>\n <gs:rowCount>45</gs:rowCount>\n <gs:colCount>15</gs:colCount>\n</entry>\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"updateSheetProperties\": {\n \"properties\": {\n \"sheetId\": sheetId,\n \"title\": \"Expenses\",\n \"gridProperties\": {\n \"rowCount\": 45,\n \"columnCount\": 15,\n }\n },\n \"fields\": \"title,gridProperties(rowCount,columnCount)\"\n }\n }\n ],\n}\n```\n\nExample:\n```text\nDELETE https://spreadsheets.google.com/feeds/worksheets/spreadsheetId/private/full/sheetId/version\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"deleteSheet\": {\n \"sheetId\": sheetId\n }\n }\n ],\n}\n```\n\nExample:\n```text\nGET https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full\n```\n\nExample:\n```text\n<entry gd:etag='\"S0wCTlpIIip7ImA0X0QI\"'>\n <id>rowId</id>\n <updated>2006-11-17T18:23:45.173Z</updated>\n <category scheme=\"http://schemas.google.com/spreadsheets/2006\"\n term=\"http://schemas.google.com/spreadsheets/2006#list\"/>\n <title type=\"text\">Bingley</title>\n <content type=\"text\">Hours: 10, Items: 2, IPM: 0.0033</content>\n <link rel=\"self\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full/rowId\"/>\n <link rel=\"edit\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full/rowId/version\"/>\n <gsx:name>Bingley</gsx:name>\n <gsx:hours>10</gsx:hours>\n <gsx:items>2</gsx:items>\n <gsx:ipm>0.0033</gsx:ipm>\n</entry>\n```\n\nExample:\n```text\nGET https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full?reverse=true\n```\n\nExample:\n```text\nGET https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full\n ?orderby=column:lastname\n```\n\nExample:\n```text\nGET https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full\n ?sq=age>25%20and%20height<175\n```\n\nExample:\n```text\nGET https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId/values/Sheet1\n```\n\nExample:\n```text\n{\n \"range\": \"Sheet1\",\n \"majorDimension\": \"ROWS\",\n \"values\": [[\"Name\", \"Hours\", \"Items\", \"IPM\"],\n [\"Bingley\", \"10\", \"2\", \"0.0033\"],\n [\"Darcy\", \"14\", \"6\", \"0.0071\"]]\n}\n```\n\nExample:\n```text\nPOST https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full\n```\n\nExample:\n```text\n<entry xmlns=\"http://www.w3.org/2005/Atom\"\n xmlns:gsx=\"http://schemas.google.com/spreadsheets/2006/extended\">\n <gsx:hours>2</gsx:hours>\n <gsx:ipm>0.5</gsx:ipm>\n <gsx:items>60</gsx:items>\n <gsx:name>Elizabeth</gsx:name>\n</entry>\n```\n\nExample:\n```text\nPOST https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId/append/Sheet1\n```\n\nExample:\n```text\n{\n \"values\": [[\"Elizabeth\", \"2\", \"0.5\", \"60\"]]\n}\n```\n\nExample:\n```text\nPUT https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full/rowId/version\n```\n\nExample:\n```text\n<entry gd:etag='\"S0wCTlpIIip7ImA0X0QI\"'>\n <id>rowId</id>\n <updated>2006-11-17T18:23:45.173Z</updated>\n <category scheme=\"http://schemas.google.com/spreadsheets/2006\"\n term=\"http://schemas.google.com/spreadsheets/2006#list\"/>\n <title type=\"text\">Bingley</title>\n <content type=\"text\">Hours: 10, Items: 2, IPM: 0.0033</content>\n <link rel=\"self\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full/rowId\"/>\n <link rel=\"edit\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full/rowId/version\"/>\n <gsx:name>Bingley</gsx:name>\n <gsx:hours>20</gsx:hours>\n <gsx:items>4</gsx:items>\n <gsx:ipm>0.0033</gsx:ipm>\n</entry>\n```\n\nExample:\n```text\nPUT https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId/values/Sheet1!A4\n```\n\nExample:\n```text\nDELETE https://spreadsheets.google.com/feeds/list/spreadsheetId/sheetId/private/full/rowId/version\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"deleteDimension\": {\n \"range\": {\n \"sheetId\": sheetId,\n \"dimension\": \"ROWS\",\n \"startIndex\": 5,\n \"endIndex\": 6\n }\n }\n }\n ],\n}\n```\n\nExample:\n```text\nGET https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full\n```\n\nExample:\n```text\nGET https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full\n ?min-row=2&min-col=4&max-col=4\n```\n\nExample:\n```text\n<entry gd:etag='\"ImB5CBYSRCp7\"'>\n <id>https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full/R9C4</id>\n <updated>2006-11-17T18:27:32.543Z</updated>\n <category scheme=\"http://schemas.google.com/spreadsheets/2006\"\n term=\"http://schemas.google.com/spreadsheets/2006#cell\"/>\n <title type=\"text\">D4</title>\n <content type=\"text\">5</content>\n <link rel=\"self\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full/R9C4\"/>\n <link rel=\"edit\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full/R9C4/srevc\"/>\n <gs:cell row=\"4\" col=\"4\" inputValue=\"=FLOOR(C4/(B4*60),.0001)\"\n numericValue=\"5.0\">5</gs:cell>\n</entry>\n```\n\nExample:\n```text\nGET https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId/values/Sheet2!D2:D?majorDimension=COLUMNS&valueRenderOption=FORMULA\n```\n\nExample:\n```text\n{\n \"spreadsheetId\": spreadsheetId,\n \"valueRanges\": [\n {\"range\": \"Sheet2!D2:D\",\n \"majorDimension\": \"COLUMNS\",\n \"values\": [[\"Widget\", 234, \"=FLOOR(C4/(B4*60),.0001)\", \"=D4\\*1000\"]]\n }]\n}\n```\n\nExample:\n```text\n<entry xmlns=\"http://www.w3.org/2005/Atom\"\n xmlns:gs=\"http://schemas.google.com/spreadsheets/2006\">\n <id>https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full/R2C4</id>\n <link rel=\"edit\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full/R2C4\"/>\n <gs:cell row=\"2\" col=\"4\" inputValue=\"=SUM(A1:B6)\"/>\n</entry>\n```\n\nExample:\n```text\nPUT https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId/values/D2?valueInputOption=USER_ENTERED\n```\n\nExample:\n```text\n{\"values\": [[\"=SUM(A1:B6)\"]]}\n```\n\nExample:\n```text\nPOST https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full/batch\n```\n\nExample:\n```text\n<feed xmlns=\"http://www.w3.org/2005/Atom\"\n xmlns:batch=\"http://schemas.google.com/gdata/batch\"\n xmlns:gs=\"http://schemas.google.com/spreadsheets/2006\">\n <id>https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full</id>\n <entry>\n <batch:id>request1</batch:id>\n <batch:operation type=\"update\"/>\n <id>https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full/R2C4</id>\n <link rel=\"edit\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full/R2C4/version\"/>\n <gs:cell row=\"2\" col=\"4\" inputValue=\"newData\"/>\n </entry>\n ...\n <entry>\n <batch:id>request2</batch:id>\n <batch:operation type=\"update\"/>\n <id>https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full/R2C5</id>\n <link rel=\"edit\" type=\"application/atom+xml\"\n href=\"https://spreadsheets.google.com/feeds/cells/spreadsheetId/sheetId/private/full/R2C5/version\"/>\n <gs:cell row=\"5\" col=\"2\" inputValue=\"moreInfo\"/>\n </entry>\n</feed>\n```\n\nExample:\n```text\nPOST https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId/values:batchUpdate\n```\n\nExample:\n```text\n{\n \"valueInputOption\": \"USER_ENTERED\"\n \"data\": [\n {\"range\": \"D4\",\n \"majorDimension\": \"ROWS\",\n \"values\": [[\"newData\"]]\n },\n {\"range\": \"B5\",\n \"majorDimension\": \"ROWS\",\n \"values\": [[\"moreInfo\"]]\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.982Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":51,"totalLines":505,"estimatedTokens":3435}}965{"id":"doc-class_richtextvaluebuilder_apps_script_google_fo-7e931ea9","source":"documentation","title":"Class RichTextValueBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/rich-text-value-builder","text":"Example:\n```text\n// Creates a Rich Text value for the text \"foo no baz\" with \"foo\" pointing to\n// \"https://bar.foo\" and \"baz\" to \"https://abc.xyz\".\n// \"foo\" is underlined with the default link color, whereas \"baz\" has its text\n// style overridden by a call to `setTextStyle`, and is therefore black and bold\n// with no underlining.\nconst boldStyle = SpreadsheetApp.newTextStyle()\n .setUnderline(false)\n .setBold(true)\n .setForegroundColor('#000000')\n .build();\nconst value = SpreadsheetApp.newRichTextValue()\n .setText('foo no baz')\n .setLinkUrl(0, 3, 'https://bar.foo')\n .setLinkUrl(7, 10, 'https://abc.xyz')\n .setTextStyle(7, 10, boldStyle)\n .build();\n```\n\nExample:\n```text\n// Creates a Rich Text value for the text \"Foo\" which points to\n// \"https://bar.foo\".\nconst value = SpreadsheetApp.newRichTextValue()\n .setText('Foo')\n .setLinkUrl('https://bar.foo')\n .build();\n```\n\nExample:\n```text\n// Creates a Rich Text value for the text \"HelloWorld\", with \"Hello\" bolded, and\n// \"World\" italicized.\nconst bold = SpreadsheetApp.newTextStyle().setBold(true).build();\nconst italic = SpreadsheetApp.newTextStyle().setItalic(true).build();\nconst value = SpreadsheetApp.newRichTextValue()\n .setText('HelloWorld')\n .setTextStyle(0, 5, bold)\n .setTextStyle(5, 10, italic)\n .build();\n```\n\nExample:\n```text\n// Creates a Rich Text value for the text \"HelloWorld\" with \"Hello\" bolded and\n// italicized, and \"World\" only italicized.\nconst bold = SpreadsheetApp.newTextStyle().setBold(true).build();\nconst italic = SpreadsheetApp.newTextStyle().setItalic(true).build();\nconst value = SpreadsheetApp.newRichTextValue()\n .setText('HelloWorld')\n .setTextStyle(0, 5, bold)\n .setTextStyle(italic)\n .build();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.985Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":57,"estimatedTokens":515}}966{"id":"doc-enum_permission_apps_script_google_for_developer-cd4704d4","source":"documentation","title":"Enum Permission | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/drive/permission","text":"Example:\n```text\n// Creates a folder that anyone on the Internet can read from and write to.\n// (Domain administrators can prohibit this setting for Google Workspace users.)\nconst folder = DriveApp.createFolder('Shared Folder');\nfolder.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.986Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":9,"estimatedTokens":79}}967{"id":"doc-class_drawing_apps_script_google_for_developers-0ea40832","source":"documentation","title":"Class Drawing | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/drawing","text":"Example:\n```text\n// Logs the height of all drawings in a sheet\nconst drawings = SpreadsheetApp.getActiveSheet().getDrawings();\nfor (let i = 0; i < drawings.length; i++) {\n Logger.log(drawings[i].getHeight());\n}\n```\n\nExample:\n```text\n// Logs the macro name of all drawings on the active sheet.\nconst drawings = SpreadsheetApp.getActiveSheet().getDrawings();\nfor (let i = 0; i < drawings.length; i++) {\n Logger.log(drawings[i].getOnAction());\n}\n```\n\nExample:\n```text\n// Logs the parent sheet of all drawings on the active sheet.\nconst drawings = SpreadsheetApp.getActiveSheet().getDrawings();\nfor (let i = 0; i < drawings.length; i++) {\n Logger.log(drawings[i].getSheet());\n}\n```\n\nExample:\n```text\n// Logs the width of all drawings in a sheet\nconst drawings = SpreadsheetApp.getActiveSheet().getDrawings();\nfor (let i = 0; i < drawings.length; i++) {\n Logger.log(drawings[i].getWidth());\n}\n```\n\nExample:\n```text\n// Logs the z-index of all drawings on the active sheet.\nconst drawings = SpreadsheetApp.getActiveSheet().getDrawings();\nfor (let i = 0; i < drawings.length; i++) {\n Logger.log(drawings[i].getZIndex());\n}\n```\n\nExample:\n```text\n// Deletes all drawings from the active sheet.\nconst drawings = SpreadsheetApp.getActiveSheet().getDrawings();\nfor (let i = 0; i < drawings.length; i++) {\n drawings[i].remove();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.987Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":55,"estimatedTokens":335}}968{"id":"doc-manifest_structure_apps_script_google_for_develo-76af4aac","source":"documentation","title":"Manifest structure | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/manifest","text":"Example:\n```text\n{\n \"addOns\": {\n object (AddOns)\n },\n \"chat\": {},\n \"dependencies\": {\n object (Dependencies)\n },\n \"exceptionLogging\": string,\n \"executionApi\": {\n object (ExecutionApi)\n },\n \"oauthScopes\": [\n string\n ],\n \"runtimeVersion\": string,\n \"sheets\": {\n object (Sheets)\n },\n \"timeZone\": string,\n \"urlFetchWhitelist\": [\n string\n ],\n \"webapp\": {\n object (Webapp)\n }\n}\n```\n\nExample:\n```text\n\"chat\": {\n \"addToSpaceFallbackMessage\": \"Thank you for adding me!\"\n }\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.990Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":39,"estimatedTokens":134}}969{"id":"doc-v8_runtime_overview_apps_script_google_for_devel-92af7ad4","source":"documentation","title":"V8 runtime overview | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/v8-runtime","text":"Example:\n```text\nfunction normalFunction() {}\n async function asyncFunction() {}\n function* generatorFunction() {}\n\n var varFunction = function() {}\n let letFunction = function() {}\n const constFunction = function() {}\n\n var namedVarFunction = function alternateNameVarFunction() {}\n let namedLetFunction = function alternateNameLetFunction() {}\n const namedConstFunction = function alternateNameConstFunction() {}\n\n var varAsyncFunction = async function() {}\n let letAsyncFunction = async function() {}\n const constAsyncFunction = async function() {}\n\n var namedVarAsyncFunction = async function alternateNameVarAsyncFunction() {}\n let namedLetAsyncFunction = async function alternateNameLetAsyncFunction() {}\n const namedConstAsyncFunction = async function alternateNameConstAsyncFunction() {}\n\n var varGeneratorFunction = function*() {}\n let letGeneratorFunction = function*() {}\n const constGeneratorFunction = function*() {}\n\n var namedVarGeneratorFunction = function* alternateNameVarGeneratorFunction() {}\n let namedLetGeneratorFunction = function* alternateNameLetGeneratorFunction() {}\n const namedConstGeneratorFunction = function* alternateNameConstGeneratorFunction() {}\n\n var varLambda = () => {}\n let letLambda = () => {}\n const constLambda = () => {}\n\n var varAsyncLambda = async () => {}\n let letAsyncLambda = async () => {}\n const constAsyncLambda = async () => {}\n```\n\nExample:\n```text\nfunction onOpen() {\n const ui = SpreadsheetApp.getUi(); // Or DocumentApp, SlidesApp, or FormApp.\n ui.createMenu('Custom Menu')\n .addItem('First item', 'menu.item1')\n .addSeparator()\n .addSubMenu(ui.createMenu('Sub-menu')\n .addItem('Second item', 'menu.item2'))\n .addToUi();\n}\n\nconst menu = {\n item1: function() {\n SpreadsheetApp.getUi().alert('You clicked: First item');\n },\n item2: function() {\n SpreadsheetApp.getUi().alert('You clicked: Second item');\n }\n}\n```\n\nExample:\n```text\n// V8 runtime\nlet s = \"hello\";\nif (s === \"hello\") {\n s = \"world\";\n console.log(s); // Prints \"world\"\n}\nconsole.log(s); // Prints \"hello\"\n\nconst N = 100;\nN = 5; // Results in TypeError\n```\n\nExample:\n```text\n// Rhino runtime\nfunction square(x) {\n return x * x;\n}\n\nconsole.log(square(5)); // Outputs 25\n```\n\nExample:\n```text\n// V8 runtime\nconst square = x => x * x;\nconsole.log(square(5)); // Outputs 25\n\n// Outputs [1, 4, 9]\nconsole.log([1, 2, 3].map(x => x * x));\n```\n\nExample:\n```text\n// V8 runtime\nclass Rectangle {\n constructor(width, height) { // class constructor\n this.width = width;\n this.height = height;\n }\n\n logToConsole() { // class method\n console.log(`Rectangle(width=${this.width}, height=${this.height})`);\n }\n}\n\nconst r = new Rectangle(10, 20);\nr.logToConsole(); // Outputs Rectangle(width=10, height=20)\n```\n\nExample:\n```text\n// Rhino runtime\nvar data = {a: 12, b: false, c: 'blue'};\nvar a = data.a;\nvar c = data.c;\nconsole.log(a, c); // Outputs 12 \"blue\"\n\nvar a = [1, 2, 3];\nvar x = a[0];\nvar y = a[1];\nvar z = a[2];\nconsole.log(x, y, z); // Outputs 1 2 3\n```\n\nExample:\n```text\n// V8 runtime\nconst data = {a: 12, b: false, c: 'blue'};\nconst {a, c} = data;\nconsole.log(a, c); // Outputs 12 \"blue\"\n\n\nconst array = [1, 2, 3];\nconst [x, y, z] = array;\nconsole.log(x, y, z); // Outputs 1 2 3\n```\n\nExample:\n```text\n// Rhino runtime\nvar name =\n 'Hi ' + first + ' ' + last + '.';\nvar url =\n 'http://localhost:3000/api/messages/'\n + id;\n```\n\nExample:\n```text\n// V8 runtime\nconst name = `Hi ${first} ${last}.`;\nconst url =\n `http://localhost:3000/api/messages/${id}`;\n```\n\nExample:\n```text\n// Rhino runtime\nfunction hello(greeting, name) {\n greeting = greeting || \"hello\";\n name = name || \"world\";\n console.log(\n greeting + \" \" + name + \"!\");\n}\n\nhello(); // Outputs \"hello world!\"\n```\n\nExample:\n```text\n// V8 runtime\nconst hello =\n function(greeting=\"hello\", name=\"world\") {\n console.log(\n greeting + \" \" + name + \"!\");\n }\n\nhello(); // Outputs \"hello world!\"\n```\n\nExample:\n```text\n// Rhino runtime\nvar multiline = \"This string is sort of\\n\"\n+ \"like a multi-line string,\\n\"\n+ \"but it's not really one.\";\n```\n\nExample:\n```text\n// V8 runtime\nconst multiline = `This on the other hand,\nactually is a multi-line string,\nthanks to JavaScript ES6`;\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.991Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":201,"estimatedTokens":1097}}970{"id":"doc-class_datetimepicker_apps_script_google_for_deve-95caa436","source":"documentation","title":"Class DateTimePicker | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/date-time-picker","text":"Example:\n```text\nconst dateTimePicker =\n CardService.newDateTimePicker()\n .setTitle('Enter the date and time.')\n .setFieldName('date_time_field')\n // Set default value as Jan 1, 2018, 3:00 AM UTC. Either a number or\n // string is acceptable.\n .setValueInMsSinceEpoch(1514775600)\n // EDT time is 5 hours behind UTC.\n .setTimeZoneOffsetInMins(-5 * 60)\n .setOnChangeAction(\n CardService.newAction().setFunctionName('handleDateTimeChange'),\n );\n```\n\nExample:\n```text\nconst workflowDataSource =\n CardService.newWorkflowDataSource().setIncludeVariables(true);\n\nconst hostAppDataSource =\n CardService.newHostAppDataSource().setWorkflowDataSource(workflowDataSource);\n\nconst dateTimePicker = CardService.newDateTimePicker()\n .setTitle('Enter the date and time.')\n .setFieldName('date_time_field')\n .setHostAppDataSource(hostAppDataSource);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.993Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":31,"estimatedTokens":252}}971{"id":"doc-class_calendareventactionresponse_apps_script_go-9ad4c217","source":"documentation","title":"Class CalendarEventActionResponse | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/calendar-event-action-response","text":"Example:\n```text\n// A CalendarEventActionResponse that adds two attendees to an event.\nconst calendarEventActionResponse =\n CardService.newCalendarEventActionResponseBuilder()\n .addAttendees(['user1@example.com', 'user2@example.com'])\n .build();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.995Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":70}}972{"id":"doc-class_datepicker_apps_script_google_for_develope-a6fac774","source":"documentation","title":"Class DatePicker | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/date-picker","text":"Example:\n```text\nconst dateTimePicker =\n CardService.newDatePicker()\n .setTitle('Enter the date.')\n .setFieldName('date_field')\n // Set default value as Jan 1, 2018 UTC. Either a number or string is\n // acceptable.\n .setValueInMsSinceEpoch(1514775600)\n .setOnChangeAction(\n CardService.newAction().setFunctionName('handleDateTimeChange'),\n );\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.996Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":15,"estimatedTokens":106}}973{"id":"doc-class_fixedfooter_apps_script_google_for_develop-acf7ef66","source":"documentation","title":"Class FixedFooter | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/fixed-footer","text":"Example:\n```text\nconst fixedFooter = CardService.newFixedFooter().setPrimaryButton(\n CardService.newTextButton().setText('help').setOpenLink(\n CardService.newOpenLink().setUrl('http://www.google.com')),\n);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.998Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":9,"estimatedTokens":59}}974{"id":"doc-addons_manifest_resource_apps_script_google_for_-0c10cae6","source":"documentation","title":"AddOns manifest resource | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/manifest/addons","text":"Example:\n```text\n{\n \"common\": {\n object (Common)\n },\n \"calendar\": {\n object (Calendar)\n },\n \"chat\": {\n object (Chat)\n },\n \"drive\": {\n object (Drive)\n },\n \"gmail\": {\n object (Gmail)\n },\n \"docs\": {\n object (Docs)\n },\n \"sheets\": {\n object (Sheets)\n },\n \"slides\": {\n object (Slides)\n },\n \"meet\": {\n object (Meet)\n }\n}\n```\n\nExample:\n```text\n{\n \"homepageTrigger\": {\n object (HomepageTrigger)\n },\n \"layoutProperties\": {\n object (LayoutProperties)\n },\n \"logoUrl\": string,\n \"name\": string,\n \"openLinkUrlPrefixes\": [\n string\n ],\n \"universalActions\": [\n {\n object (UniversalAction)\n }\n ],\n \"useLocaleFromApp\": boolean\n}\n```\n\nExample:\n```text\n{\n \"primaryColor\": string,\n \"secondaryColor\": string\n}\n```\n\nExample:\n```text\n{\n \"label\": string,\n\n // Union field rule can be only one of the following:\n \"openLink\": string,\n \"runFunction\": string,\n // End of list of possible types for union field rule.\n\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:55.999Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":78,"estimatedTokens":247}}975{"id":"doc-class_timepicker_apps_script_google_for_develope-84d347c1","source":"documentation","title":"Class TimePicker | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/time-picker","text":"Example:\n```text\nconst dateTimePicker =\n CardService.newTimePicker()\n .setTitle('Enter the time.')\n .setFieldName('time_field')\n // Set default value as 3:30 AM.\n .setHours(3)\n .setMinutes(30)\n .setOnChangeAction(\n CardService.newAction().setFunctionName('handleDateTimeChange'),\n );\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.007Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":15,"estimatedTokens":91}}976{"id":"doc-class_config_apps_script_google_for_developers-5bac6226","source":"documentation","title":"Class Config | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/config","text":"Example:\n```text\nconst cc = DataStudioApp.createCommunityConnector();\nconst config = cc.getConfig();\n\nconst info_entry = config.newInfo().setId('info_id').setHelpText(\n 'This connector can connect to multiple data endpoints.');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.008Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":62}}977{"id":"doc-class_getschemaresponse_apps_script_google_for_d-bc22c4e3","source":"documentation","title":"Class GetSchemaResponse | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/get-schema-response","text":"Example:\n```text\nfunction getSchema() {\n const cc = DataStudioApp.createCommunityConnector();\n const fields = cc.getFields();\n\n fields.newDimension()\n .setId('Created')\n .setName('Date Created')\n .setDescription('The date that this was created')\n .setType(cc.FieldType.YEAR_MONTH_DAY);\n\n fields.newMetric()\n .setId('Amount')\n .setName('Amount (USD)')\n .setDescription('The cost in US dollars')\n .setType(cc.FieldType.CURRENCY_USD);\n\n return cc.newGetSchemaResponse().setFields(fields).build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.009Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":23,"estimatedTokens":140}}978{"id":"doc-class_conditionalformatrulebuilder_apps_script_g-b8ab55ab","source":"documentation","title":"Class ConditionalFormatRuleBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/conditional-format-rule-builder","text":"Example:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain a number between 1 and 10.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenNumberBetween(1, 10)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Log the boolean criteria type of the first conditional format rules of a\n// sheet.\nconst rule = SpreadsheetApp.getActiveSheet().getConditionalFormatRules()[0];\nconst booleanCondition = rule.getBooleanCondition();\nif (booleanCondition != null) {\n Logger.log(booleanCondition.getCriteriaType());\n}\n```\n\nExample:\n```text\n// Log the gradient minimum color of the first conditional format rule of a\n// sheet.\nconst rule = SpreadsheetApp.getActiveSheet().getConditionalFormatRules()[0];\nconst gradientCondition = rule.getGradientCondition();\nif (gradientCondition != null) {\n // Assume the color has ColorType.RGB.\n Logger.log(gradientCondition.getMinColorObject().asRgbColor().asHexString());\n}\n```\n\nExample:\n```text\n// Log each range of the first conditional format rule of a sheet.\nconst rule = SpreadsheetApp.getActiveSheet().getConditionalFormatRules()[0];\nconst ranges = rule.getRanges();\nfor (let i = 0; i < ranges.length; i++) {\n Logger.log(ranges[i].getA1Notation());\n}\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// set their background color to red if the cell has text equal to \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextEqualTo('hello')\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// set their background color to theme background color if the cell has text\n// equal to \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst color = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.BACKGROUND)\n .build();\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextEqualTo('hello')\n .setBackground(color)\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn their text bold if the cell has text equal to \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextEqualTo('hello')\n .setBold(true)\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// set their font color to red if the cell has text equal to \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextEqualTo('hello')\n .setFontColor('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// set their font color to theme text color if the cell has text equal to\n// \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst color = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.TEXT)\n .build();\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextEqualTo('hello')\n .setFontColor(color)\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// set their background color somewhere between white and red, based on their\n// values in comparison to the ranges minimum and maximum values.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .setGradientMaxpoint('#FF0000')\n .setGradientMinpoint('#FFFFFF')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// set their background color somewhere between theme text and background\n// colors, based on their values in comparison to the ranges minimum and maximum\n// values.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst textColor = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.TEXT)\n .build();\nconst backgroundColor =\n SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.BACKGROUND)\n .build();\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .setGradientMaxpoint(textColor)\n .setGradientMinpoint(backgroundColor)\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// set their background color somewhere from theme accent 1, accent 2 to accent\n// 3 colors, based on their values in comparison to the values 0, 50, and 100.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst color1 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT1)\n .build();\nconst color2 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT2)\n .build();\nconst color3 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT3)\n .build();\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .setGradientMaxpointWithValue(\n color1,\n SpreadsheetApp.InterpolationType.NUMBER,\n '100',\n )\n .setGradientMidpointWithValue(\n color2,\n SpreadsheetApp.InterpolationType.NUMBER,\n '50',\n )\n .setGradientMinpointWithValue(\n color3,\n SpreadsheetApp.InterpolationType.NUMBER,\n '0',\n )\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// set their background color somewhere from red green to blue, based on their\n// values in comparison to the values 0, 50, and 100.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .setGradientMaxpointWithValue(\n '#0000FF',\n SpreadsheetApp.InterpolationType.NUMBER,\n '100',\n )\n .setGradientMidpointWithValue(\n '#00FF00',\n SpreadsheetApp.InterpolationType.NUMBER,\n '50',\n )\n .setGradientMinpointWithValue(\n '#FF0000',\n SpreadsheetApp.InterpolationType.NUMBER,\n '0',\n )\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// set their background color somewhere from theme accent 1 to accent 2 to\n// accent 3 colors, based on their values in comparison to the values 0, 50, and\n// 100.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst color1 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT1)\n .build();\nconst color2 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT2)\n .build();\nconst color3 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT3)\n .build();\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .setGradientMaxpointWithValue(\n color1,\n SpreadsheetApp.InterpolationType.NUMBER,\n '100',\n )\n .setGradientMidpointWithValue(\n color2,\n SpreadsheetApp.InterpolationType.NUMBER,\n '50',\n )\n .setGradientMinpointWithValue(\n color3,\n SpreadsheetApp.InterpolationType.NUMBER,\n '0',\n )\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// set their background color somewhere from red to green to blue, based on\n// their values in comparison to the values 0, 50, and 100.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .setGradientMaxpointWithValue(\n '#0000FF',\n SpreadsheetApp.InterpolationType.NUMBER,\n '100',\n )\n .setGradientMidpointWithValue(\n '#00FF00',\n SpreadsheetApp.InterpolationType.NUMBER,\n '50',\n )\n .setGradientMinpointWithValue(\n '#FF0000',\n SpreadsheetApp.InterpolationType.NUMBER,\n '0',\n )\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn their text italic if the cell has text equal to \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextEqualTo('hello')\n .setItalic(true)\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3\n// and range D4:F6 to turn red if they contain a number between 1 and 10.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeOne = sheet.getRange('A1:B3');\nconst rangeTwo = sheet.getRange('D4:F6');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenNumberBetween(1, 10)\n .setBackground('#FF0000')\n .setRanges([rangeOne, rangeTwo])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// strikethrough their text if the cell has text equal to \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextEqualTo('hello')\n .setStrikethrough(true)\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// underline their text if the cell has text equal to \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextEqualTo('hello')\n .setUnderline(true)\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they are empty.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenCellEmpty()\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they are not empty.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenCellNotEmpty()\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain a date after 11/4/1993.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenDateAfter(new Date('11/4/1993'))\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain a date after today.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenDateAfter(SpreadsheetApp.RelativeDate.TODAY)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain a date before 11/4/1993.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenDateBefore(new Date('11/4/1993'))\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain a date before today.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenDateBefore(SpreadsheetApp.RelativeDate.TODAY)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain the date 11/4/1993.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenDateEqualTo(new Date('11/4/1993'))\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain todays date.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenDateEqualTo(SpreadsheetApp.RelativeDate.TODAY)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they satisfy the condition \"=EQ(B4, C3)\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenFormulaSatisfied('=EQ(B4, C3)')\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain the number 10.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenNumberEqualTo(10)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain a number greater than 10.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenNumberGreaterThan(10)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain a number greater than or equal to 10.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenNumberGreaterThanOrEqualTo(10)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain a number less than 10.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenNumberLessThan(10)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain a number less than or equal to 10.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenNumberLessThanOrEqualTo(10)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain a number not between 1 and 10.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenNumberNotBetween(1, 10)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they don't contain the number 10.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenNumberNotEqualTo(10)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they contain the text \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextContains('hello')\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they don't contain the text \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextDoesNotContain('hello')\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they end with the text \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextEndsWith('hello')\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they have text equal to \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextEqualTo('hello')\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes cells in range A1:B3 to\n// turn red if they start with the text \"hello\".\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenTextStartsWith('hello')\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Adds a new conditional format rule that is a copy of the first active\n// conditional format rule, except it instead sets its cells to have a black\n// background color.\n\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rules = sheet.getConditionalFormatRules();\nconst booleanCondition = rules[0].getBooleanCondition();\nif (booleanCondition != null) {\n const rule = SpreadsheetApp.newConditionalFormatRule()\n .withCriteria(\n booleanCondition.getCriteriaType(),\n booleanCondition.getCriteriaValues(),\n )\n .setBackground('#000000')\n .setRanges(rules[0].getRanges())\n .build();\n rules.push(rule);\n}\nsheet.setConditionalFormatRules(rules);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.013Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":41,"totalLines":776,"estimatedTokens":6650}}979{"id":"doc-class_switch_apps_script_google_for_developers-c3595e44","source":"documentation","title":"Class Switch | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/switch","text":"Example:\n```text\nconst switchDecoratedText =\n CardService.newDecoratedText()\n .setTopLabel('Switch decorated text widget label')\n .setText('This is a decorated text widget with a switch on the right')\n .setWrapText(true)\n .setSwitchControl(\n CardService.newSwitch()\n .setFieldName('form_input_switch_key')\n .setValue('form_input_switch_value')\n .setOnChangeAction(\n CardService.newAction().setFunctionName(\n 'handleSwitchChange'),\n ),\n );\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.014Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":19,"estimatedTokens":153}}980{"id":"doc-class_checkbox_apps_script_google_for_developers-86fbb7a8","source":"documentation","title":"Class Checkbox | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/checkbox","text":"Example:\n```text\nconst config = DataStudioApp.createCommunityConnector().getConfig();\nconst checkbox = config.newCheckbox()\n .setId('use_https')\n .setName('Use Https?')\n .setHelpText('Whether or not https should be used.')\n .setAllowOverride(true);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.017Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":11,"estimatedTokens":87}}981{"id":"doc-class_setcredentialsresponse_apps_script_google_-78d41f47","source":"documentation","title":"Class SetCredentialsResponse | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/set-credentials-response","text":"Example:\n```text\nconst communityConnector = DataStudioApp.createCommunityConnector();\n\nfunction setCredentials(request) {\n const isValid = validateCredentials(request);\n\n if (isValid) {\n // store the credentials somewhere.\n }\n\n return communityConnector.newSetCredentialsResponse().setIsValid(isValid).build();\n}\n\nfunction validateCredentials(request) {\n // ...\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.019Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":20,"estimatedTokens":98}}982{"id":"doc-class_getdataresponse_apps_script_google_for_dev-11b3cf06","source":"documentation","title":"Class GetDataResponse | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/get-data-response","text":"Example:\n```text\nfunction getFields() {\n //...\n}\n\nfunction getData() {\n const cc = DataStudioApp.createCommunityConnector();\n\n return cc.newGetDataResponse()\n .setFields(getFields())\n .addRow(['3', 'Foobar.com'])\n .addRow(['4', 'Foobaz.com'])\n .addRows([\n ['5', 'Fizzbuz.com'],\n ['6', 'Fizzbaz.com'],\n ])\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.021Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":22,"estimatedTokens":96}}983{"id":"doc-class_textarea_apps_script_google_for_developers-fe70c41d","source":"documentation","title":"Class TextArea | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/text-area","text":"Example:\n```text\nconst cc = DataStudioApp.createCommunityConnector();\nconst config = cc.getConfig();\n\nconst textArea1 = config.newTextArea()\n .setId('textArea1')\n .setName('Search')\n .setHelpText('for example, Coldplay')\n .setAllowOverride(true)\n .setPlaceholder('Search for an artist for all songs.');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.022Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":14,"estimatedTokens":107}}984{"id":"doc-groups_service_apps_script_google_for_developers-d24c962c","source":"documentation","title":"Groups Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/groups","text":"Example:\n```text\nvar groups = GroupsApp.getGroups();\nLogger.log('You are a member of %s Google Groups.', groups.length);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.024Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":7,"estimatedTokens":35}}985{"id":"doc-class_selectmultiple_apps_script_google_for_deve-c71ce5f6","source":"documentation","title":"Class SelectMultiple | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/select-multiple","text":"Example:\n```text\nconst cc = DataStudioApp.createCommunityConnector();\nconst config = cc.getConfig();\nconst option1 =\n config.newOptionBuilder().setLabel('option label').setValue('option_value');\n\nconst option2 = config.newOptionBuilder()\n .setLabel('second option label')\n .setValue('option_value_2');\n\nconst info1 = config.newSelectMultiple()\n .setId('api_endpoint')\n .setName('Data Type')\n .setHelpText('Select the data type you\\'re interested in.')\n .setAllowOverride(true)\n .addOption(option1)\n .addOption(option2);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.025Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":21,"estimatedTokens":170}}986{"id":"doc-class_textinput_apps_script_google_for_developer-e91a7e3d","source":"documentation","title":"Class TextInput | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/text-input","text":"Example:\n```text\nconst cc = DataStudioApp.createCommunityConnector();\nconst config = cc.getConfig();\n\nconst info1 = config.newTextInput()\n .setId('info1')\n .setName('Search')\n .setHelpText('for example, Coldplay')\n .setAllowOverride(true)\n .setPlaceholder('Search for an artist for all songs.');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.028Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":14,"estimatedTokens":100}}987{"id":"doc-class_cardheader_apps_script_google_for_develope-98ca35e1","source":"documentation","title":"Class CardHeader | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/card-header","text":"Example:\n```text\nconst cardHeader = CardService.newCardHeader()\n .setTitle('Card header title')\n .setSubtitle('Card header subtitle')\n .setImageStyle(CardService.ImageStyle.CIRCLE)\n .setImageUrl('https://image.png');\n```\n\nExample:\n```text\n// The following assumes you have the image to use in Google Drive and have its\n// ID.\nconst imageBytes = DriveApp.getFileById('123abc').getBlob().getBytes();\nconst encodedImageURL =\n `data:image/jpeg;base64,${Utilities.base64Encode(imageBytes)}`;\n\n// You can store encodeImageURL and use it as a parameter to\n// CardHeader.setImageUrl(imageUrl).\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.029Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":22,"estimatedTokens":174}}988{"id":"doc-class_image_apps_script_google_for_developers-68a0be1d","source":"documentation","title":"Class Image | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/image","text":"Example:\n```text\nconst image = CardService.newImage()\n .setAltText('A nice image')\n .setImageUrl('https://image.png');\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAuthorizationAction().setAuthorizationUrl('url');\nCardService.newTextButton().setText('Authorize').setAuthorizationAction(action);\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('composeEmailCallback');\nCardService.newTextButton()\n .setText('Compose Email')\n .setComposeAction(action, CardService.ComposedEmailType.REPLY_AS_DRAFT);\n\n// ...\n\nfunction composeEmailCallback(e) {\n const thread = GmailApp.getThreadById(e.threadId);\n const draft = thread.createDraftReply('This is a reply');\n return CardService.newComposeActionResponseBuilder()\n .setGmailDraft(draft)\n .build();\n}\n```\n\nExample:\n```text\n// The following assumes you have the image to use in Google Drive and have its\n// ID.\nconst imageBytes = DriveApp.getFileById('123abc').getBlob().getBytes();\nconst encodedImageURL =\n `data:image/jpeg;base64,${Utilities.base64Encode(imageBytes)}`;\n\n// You can store encodeImageURL and use it as a parameter to\n// Image.setImageUrl(url).\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('notificationCallback');\nCardService.newTextButton()\n .setText('Create notification')\n .setOnClickAction(action);\n\n// ...\n\nfunction notificationCallback() {\n return CardService.newActionResponseBuilder()\n .setNotification(\n CardService.newNotification().setText('Some info to display to user'),\n )\n .build();\n}\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('openLinkCallback');\nCardService.newTextButton()\n .setText('Open Link')\n .setOnClickOpenLinkAction(action);\n\n// ...\n\nfunction openLinkCallback() {\n return CardService.newActionResponseBuilder()\n .setOpenLink(CardService.newOpenLink().setUrl('https://www.google.com'))\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.031Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":86,"estimatedTokens":510}}989{"id":"doc-class_selectsingle_apps_script_google_for_develo-510f0c3e","source":"documentation","title":"Class SelectSingle | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/select-single","text":"Example:\n```text\nconst cc = DataStudioApp.createCommunityConnector();\nconst config = cc.getConfig();\nconst option1 =\n config.newOptionBuilder().setLabel('option label').setValue('option_value');\n\nconst option2 = config.newOptionBuilder()\n .setLabel('second option label')\n .setValue('option_value_2');\n\nconst info1 = config.newSelectSingle()\n .setId('api_endpoint')\n .setName('Data Type')\n .setHelpText('Select the data type you\\'re interested in.')\n .setAllowOverride(true)\n .addOption(option1)\n .addOption(option2);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.032Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":21,"estimatedTokens":170}}990{"id":"doc-scopes_google_workspace_add_ons_google_for_devel-5c946274","source":"documentation","title":"Scopes | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/concepts/scopes","text":"Example:\n```text\n{\n ...\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/gmail.addons.current.message.metadata\",\n \"https://www.googleapis.com/auth/userinfo.email\"\n ],\n ...\n }\n```\n\nExample:\n```text\nfunction readSender(e) {\n var accessToken = e.gmail.accessToken;\n var messageId = e.gmail.messageId;\n\n // The following function enables short-lived access to the current\n // message in Gmail. Access to other Gmail messages or data isn't\n // permitted.\n GmailApp.setCurrentMessageAccessToken(accessToken);\n var mailMessage = GmailApp.getMessageById(messageId);\n return mailMessage.getFrom();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.036Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":28,"estimatedTokens":159}}991{"id":"doc-class_rangelist_apps_script_google_for_developer-f4a227bc","source":"documentation","title":"Class RangeList | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/range-list","text":"Example:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['D4', 'B2:C4']);\nrangeList.activate();\n\nconst selection = sheet.getSelection();\n// Current cell: B2\nconst currentCell = selection.getCurrentCell();\n// Active range: B2:C4\nconst activeRange = selection.getActiveRange();\n// Active range list: [D4, B2:C4]\nconst activeRangeList = selection.getActiveRangeList();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.breakApart();\n```\n\nExample:\n```text\n// Changes the state of cells which currently contain either the checked or\n// unchecked value configured in the ranges D4 and E6 to 'checked'.\nconst rangeList = SpreadsheetApp.getActive().getRangeList(['D4', 'E6']);\nrangeList.check();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.clear();\n```\n\nExample:\n```text\n// The code below clears the contents of the following ranges A:A and C:C in the\n// active sheet, but preserves the format, data validation rules, and comments.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.clear({contentsOnly: true});\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.clearContent();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.clearDataValidations();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.clearFormat();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.clearNote();\n```\n\nExample:\n```text\nconst rangeList = SpreadsheetApp.getActive().getRangeList(['D4', 'E6']);\n\n// Inserts checkboxes into each cell in the ranges D4 and E6 configured with\n// 'true' for checked and 'false' for unchecked. Also, sets the value of each\n// cell in the ranges D4 and E6 to 'false'.\nrangeList.insertCheckboxes();\n```\n\nExample:\n```text\nconst rangeList = SpreadsheetApp.getActive().getRangeList(['D4', 'E6']);\n\n// Inserts checkboxes into each cell in the ranges D4 and E6 configured with\n// 'yes' for checked and the empty string for unchecked. Also, sets the value of\n// each cell in the ranges D4 and E6 to the empty string.\nrangeList.insertCheckboxes('yes');\n```\n\nExample:\n```text\nconst rangeList = SpreadsheetApp.getActive().getRangeList(['D4', 'E6']);\n\n// Inserts checkboxes into each cell in the ranges D4 and E6 configured with\n// 'yes' for checked and 'no' for unchecked. Also, sets the value of each cell\n// in the ranges D4 and E6 to 'no'.\nrangeList.insertCheckboxes('yes', 'no');\n```\n\nExample:\n```text\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\n\n// Inserts checkboxes and sets each cell value to 'no' in the range A1:B10.\nrange.insertCheckboxes('yes', 'no');\n\nconst rangeList1 = SpreadsheetApp.getActive().getRangeList(['A1', 'A3']);\nrangeList1.setValue('yes');\n// Removes the checkbox data validation in cells A1 and A3 and clears their\n// value.\nrangeList1.removeCheckboxes();\n\nconst rangeList2 = SpreadsheetApp.getActive().getRangeList(['A5', 'A7']);\nrangeList2.setValue('random');\n// Removes the checkbox data validation in cells A5 and A7 but does not clear\n// their value.\nrangeList2.removeCheckboxes();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.setBackground('red');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\n// Sets the background to red for each range in the range list.\nrangeList.setBackgroundRGB(255, 0, 0);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A2:B4', 'C1:D4']);\n// Sets borders on the top and bottom of the ranges A2:B4 and C1:D4, but leaves\n// the left and right unchanged.\nrangeList.setBorder(true, null, true, null, false, false);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A2:B4', 'C1:D4']);\n// Sets borders on the top and bottom, but leaves the left and right unchanged\n// of the ranges A2:B4 and C1:D4. Also sets the color to 'red', and the border\n// to 'DASHED'.\nrangeList.setBorder(\n true,\n null,\n true,\n null,\n false,\n false,\n 'red',\n SpreadsheetApp.BorderStyle.DASHED,\n);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.setFontColor('red');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.setFontFamily('Roboto');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.setFontLine('line-through');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.setFontSize(20);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.setFontStyle('italic');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.setFontWeight('bold');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A11', 'C11']);\nrangeList.setFormula('=SUM(B1:B10)');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A11', 'C11']);\n// This sets the formula to be the sum of the 3 rows above B5\nrangeList.setFormulaR1C1('=SUM(R[-3]C[0]:R[-1]C[0])');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.setHorizontalAlignment('center');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.setNote('This is a note');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A1:A10', 'C1:C10']);\n// Always show 3 decimal points for the specified ranges.\nrangeList.setNumberFormat('0.000');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A1:A10', 'C1:C10']);\n// Show hyperlinks for all the ranges.\nrangeList.setShowHyperlink(true);\n```\n\nExample:\n```text\n// Sets right-to-left text direction each range in the range list.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A1:A10', 'C1:C10']);\nrangeList.setTextDirection(SpreadsheetApp.TextDirection.RIGHT_TO_LEFT);\n```\n\nExample:\n```text\n// Sets the cells in the ranges A1:A10 and C1:C10 to have text rotated up 45\n// degrees.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['A1:A10', 'C1:C10']);\nrangeList.setTextRotation(45);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\n// Set value of 100 to each range in the range list.\nconst rangeList = sheet.getRangeList(['A:A', 'C:C']);\nrangeList.setValue(100);\n```\n\nExample:\n```text\n// Sets the vertical alignment to middle for the list of ranges.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['D4', 'B2:C4']);\nrangeList.setVerticalAlignment('middle');\n```\n\nExample:\n```text\n// Sets all cell's in ranges D4 and B2:D4 to have vertically stacked text.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['D4', 'B2:C4']);\nrangeList.setVerticalText(true);\n```\n\nExample:\n```text\n// Enable text wrap for the list of ranges.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['D4', 'B2:C4']);\nrangeList.setWrap(true);\n```\n\nExample:\n```text\n// Sets the list of ranges to use the clip wrap strategy.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['D4', 'B2:C4']);\nrangeList.setWrapStrategy(SpreadsheetApp.WrapStrategy.CLIP);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getRange('A1:A4');\nrange.activate();\nrange.setValues([\n ' preceding space',\n 'following space ',\n 'two middle spaces',\n ' =SUM(1,2)',\n]);\n\nconst rangeList = sheet.getRangeList(['A1', 'A2', 'A3', 'A4']);\nrangeList.trimWhitespace();\n\nconst values = range.getValues();\n// Values are ['preceding space', 'following space', 'two middle spaces',\n// '=SUM(1,2)']\n```\n\nExample:\n```text\n// Changes the state of cells which currently contain either the checked or\n// unchecked value configured in the ranges D4 and E6 to 'unchecked'.\nconst rangeList = SpreadsheetApp.getActive().getRangeList(['D4', 'E6']);\nrangeList.uncheck();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.038Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":38,"totalLines":340,"estimatedTokens":2277}}992{"id":"doc-class_group_apps_script_google_for_developers-77ba3413","source":"documentation","title":"Class Group | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/groups/group","text":"Example:\n```text\nfunction listGroupMembers() {\n const group = GroupsApp.getGroupByEmail('example@googlegroups.com');\n console.log(`${group.getEmail()}:`);\n const users = group.getUsers();\n for (let i = 0; i < users.length; i++) {\n const user = users[i];\n console.log(user.getEmail());\n }\n}\n```\n\nExample:\n```text\nfunction listMyGroupEmails() {\n const groups = GroupsApp.getGroups();\n for (let i = 0; i < groups.length; i++) {\n console.log(groups[i].getEmail());\n }\n}\n```\n\nExample:\n```text\nfunction listGroupMembers() {\n const GROUP_EMAIL = 'example@googlegroups.com';\n const group = GroupsApp.getGroupByEmail(GROUP_EMAIL);\n const childGroups = group.getGroups();\n console.log(`Group ${GROUP_EMAIL} has ${childGroups.length} groups:`);\n for (let i = 0; i < childGroups.length; i++) {\n const childGroup = childGroups[i];\n console.log(childGroup.getEmail());\n }\n}\n```\n\nExample:\n```text\nconst group = GroupsApp.getGroupByEmail('example@googlegroups.com');\nconst users = group.getUsers();\nconsole.log('These are the group owners:');\nfor (let i = 0; i < users.length; i++) {\n const user = users[i];\n if (group.getRole(user.getEmail()) === GroupsApp.Role.OWNER) {\n console.log(user.getEmail());\n }\n}\n```\n\nExample:\n```text\nconst group = GroupsApp.getGroupByEmail('example@googlegroups.com');\nconst users = group.getUsers();\nconsole.log('These are the group owners:');\nfor (let i = 0; i < users.length; i++) {\n const user = users[i];\n if (group.getRole(user) === GroupsApp.Role.OWNER) {\n console.log(user.getEmail());\n }\n}\n```\n\nExample:\n```text\nconst group = GroupsApp.getGroupByEmail('example@googlegroups.com');\nconst users = group.getUsers();\nconst roles = group.getRoles(users);\nconsole.log('These are the group owners:');\nfor (let i = 0; i < users.length; i++) {\n if (roles[i] === GroupsApp.Role.OWNER) {\n console.log(users[i].getEmail());\n }\n}\n```\n\nExample:\n```text\nfunction listGroupMembers() {\n const GROUP_EMAIL = 'example@googlegroups.com';\n const group = GroupsApp.getGroupByEmail(GROUP_EMAIL);\n const users = group.getUsers();\n console.log(`Group ${GROUP_EMAIL} has ${users.length} members:`);\n for (let i = 0; i < users.length; i++) {\n const user = users[i];\n console.log(user.getEmail());\n }\n}\n```\n\nExample:\n```text\nconst group = GroupsApp.getGroupByEmail('example@googlegroups.com');\nconst childGroup = GroupsApp.getGroupByEmail('childgroup@googlegroups.com');\nif (group.hasGroup(childGroup)) {\n console.log('childgroup@googlegroups.com is a child group');\n}\n```\n\nExample:\n```text\nconst group = GroupsApp.getGroupByEmail('example@googlegroups.com');\nif (group.hasGroup('childgroup@googlegroups.com')) {\n console.log('childgroup@googlegroups.com is a child group');\n}\n```\n\nExample:\n```text\nconst group = GroupsApp.getGroupByEmail('example@googlegroups.com');\nconst currentUser = Session.getActiveUser();\nif (group.hasUser(currentUser.getEmail())) {\n console.log('You are a member');\n}\n```\n\nExample:\n```text\nconst group = GroupsApp.getGroupByEmail('example@googlegroups.com');\nconst currentUser = Session.getActiveUser();\nif (group.hasUser(currentUser)) {\n console.log('You are a member');\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.040Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":126,"estimatedTokens":794}}993{"id":"doc-class_notification_apps_script_google_for_develo-3c0a5026","source":"documentation","title":"Class Notification | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/notification","text":"Example:\n```text\nconst action = CardService.newAction().setFunctionName('notificationCallback');\nCardService.newTextButton().setText('Save').setOnClickAction(action);\n\n// ...\n\nfunction notificationCallback() {\n return CardService.newActionResponseBuilder()\n .setNotification(\n CardService.newNotification().setText('Some info to display to user'),\n )\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.045Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":17,"estimatedTokens":103}}994{"id":"doc-charts_service_apps_script_google_for_developers-fa6ab78b","source":"documentation","title":"Charts Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/charts","text":"Example:\n```text\nfunction doGet() {\n var data = Charts.newDataTable()\n .addColumn(Charts.ColumnType.STRING, 'Month')\n .addColumn(Charts.ColumnType.NUMBER, 'In Store')\n .addColumn(Charts.ColumnType.NUMBER, 'Online')\n .addRow(['January', 10, 1])\n .addRow(['February', 12, 1])\n .addRow(['March', 20, 2])\n .addRow(['April', 25, 3])\n .addRow(['May', 30, 4])\n .build();\n\n var chart = Charts.newAreaChart()\n .setDataTable(data)\n .setStacked()\n .setRange(0, 40)\n .setTitle('Sales per Month')\n .build();\n\n var htmlOutput = HtmlService.createHtmlOutput().setTitle('My Chart');\n var imageData = Utilities.base64Encode(chart.getAs('image/png').getBytes());\n var imageUrl = \"data:image/png;base64,\" + encodeURI(imageData);\n htmlOutput.append(\"Render chart server side: <br/>\");\n htmlOutput.append(\"<img border=\\\"1\\\" src=\\\"\" + imageUrl + \"\\\">\");\n return htmlOutput;\n\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.047Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":32,"estimatedTokens":238}}995{"id":"doc-class_bigqueryconfig_apps_script_google_for_deve-081c38ce","source":"documentation","title":"Class BigQueryConfig | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/big-query-config","text":"Example:\n```text\nconst cc = DataStudioApp.createCommunityConnector();\n\nconst bqConfig =\n cc.newBigQueryConfig()\n .setBillingProjectId('billingProjectId')\n .setQuery('queryString')\n .setUseStandardSql(true)\n .setAccessToken('accessToken')\n .addQueryParameter('dob', cc.BigQueryParameterType.STRING, '01011990')\n .build();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.049Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":15,"estimatedTokens":96}}996{"id":"doc-class_embeddedchart_apps_script_google_for_devel-d049c61f","source":"documentation","title":"Class EmbeddedChart | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/embedded-chart","text":"Example:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A2:B8');\nlet chart = sheet.getCharts()[0];\nchart = chart.modify()\n .addRange(range)\n .setOption('title', 'Updated!')\n .setOption('animation.duration', 500)\n .setPosition(2, 2, 0, 0)\n .build();\nsheet.updateChart(chart);\n```\n\nExample:\n```text\nfunction newChart(range) {\n const sheet = SpreadsheetApp.getActiveSheet();\n const chartBuilder = sheet.newChart();\n chartBuilder.addRange(range)\n .setChartType(Charts.ChartType.LINE)\n .setOption('title', 'My Line Chart!');\n sheet.insertChart(chartBuilder.build());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B8'))\n .setPosition(5, 5, 0, 0)\n .build();\n\nconst containerInfo = chart.getContainerInfo();\n\n// Logs the values used in setPosition()\nLogger.log(\n 'Anchor Column: %s\\r\\nAnchor Row %s\\r\\nOffset X %s\\r\\nOffset Y %s',\n containerInfo.getAnchorColumn(),\n containerInfo.getAnchorRow(),\n containerInfo.getOffsetX(),\n containerInfo.getOffsetY(),\n);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setHiddenDimensionStrategy(\n Charts.ChartHiddenDimensionStrategy.IGNORE_COLUMNS,\n )\n .setPosition(5, 5, 0, 0)\n .build();\n\n// Logs the strategy to use for hidden rows and columns which is\n// Charts.ChartHiddenDimensionStrategy.IGNORE_COLUMNS in this case.\nLogger.log(chart.getHiddenDimensionStrategy());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B10');\nconst range2 = sheet.getRange('C1:C10');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .addRange(range2)\n .setMergeStrategy(Charts.ChartMergeStrategy.MERGE_ROWS)\n .setPosition(5, 5, 0, 0)\n .build();\n\n// Logs whether rows of multiple ranges are merged, which is MERGE_ROWS in this\n// case.\nLogger.log(chart.getMergeStrategy());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setNumHeaders(1)\n .setPosition(5, 5, 0, 0)\n .build();\n\n// Logs the number of rows or columns to use as headers, which is 1 in this\n// case.\nLogger.log(chart.getHeaders());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B8'))\n .setPosition(5, 5, 0, 0)\n .build();\n\nconst ranges = chart.getRanges();\n\n// There's only one range as a data source for this chart,\n// so this logs \"A1:B8\"\nfor (const i in ranges) {\n const range = ranges[i];\n Logger.log(range.getA1Notation());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .addRange(range)\n .setChartType(Charts.ChartType.BAR)\n .setTransposeRowsAndColumns(true)\n .setPosition(5, 5, 0, 0)\n .build();\n\n// Logs whether rows and columns should be transposed, which is true in this\n// case.\nLogger.log(chart.getTransposeRowsAndColumns());\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nlet chart = sheet.getCharts()[0];\nchart = chart.modify()\n .setOption('width', 800)\n .setOption('height', 640)\n .setPosition(5, 5, 0, 0)\n .build();\nsheet.updateChart(chart);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.052Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":159,"estimatedTokens":1105}}997{"id":"doc-class_linearoptimizationengine_apps_script_googl-b075a13b","source":"documentation","title":"Class LinearOptimizationEngine | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/optimization/linear-optimization-engine","text":"Example:\n```text\nconst engine = LinearOptimizationService.createEngine();\n\n// Add variables, constraints and define the objective with addVariable(),\n// addConstraint(), etc Add two variables, 0 <= x <= 10 and 0 <= y <= 5\nengine.addVariable('x', 0, 10);\nengine.addVariable('y', 0, 5);\n\n// Create the constraint: 0 <= 2 * x + 5 * y <= 10\nlet constraint = engine.addConstraint(0, 10);\nconstraint.setCoefficient('x', 2);\nconstraint.setCoefficient('y', 5);\n\n// Create the constraint: 0 <= 10 * x + 3 * y <= 20\nconstraint = engine.addConstraint(0, 20);\nconstraint.setCoefficient('x', 10);\nconstraint.setCoefficient('y', 3);\n\n// Set the objective to be x + y\nengine.setObjectiveCoefficient('x', 1);\nengine.setObjectiveCoefficient('y', 1);\n\n// Engine should maximize the objective\nengine.setMaximization();\n\n// Solve the linear program\nconst solution = engine.solve();\nif (!solution.isValid()) {\n Logger.log(`No solution ${solution.getStatus()}`);\n} else {\n Logger.log(`Value of x: ${solution.getVariableValue('x')}`);\n Logger.log(`Value of y: ${solution.getVariableValue('y')}`);\n}\n```\n\nExample:\n```text\nconst engine = LinearOptimizationService.createEngine();\n\n// Create a linear constraint with the bounds 0 and 10\nconst constraint = engine.addConstraint(0, 10);\n\n// Create a variable so we can add it to the constraint\nengine.addVariable('x', 0, 5);\n\n// Set the coefficient of the variable in the constraint. The constraint is now:\n// 0 <= 2 * x <= 5\nconstraint.setCoefficient('x', 2);\n```\n\nExample:\n```text\nconst engine = LinearOptimizationService.createEngine();\n\n// Add a boolean variable 'x' (integer >= 0 and <= 1) and a real (continuous >=\n// 0 and <= 100) variable 'y'.\nengine.addVariables(\n ['x', 'y'],\n [0, 0],\n [1, 100],\n [\n LinearOptimizationService.VariableType.INTEGER,\n LinearOptimizationService.VariableType.CONTINUOUS,\n ],\n);\n\n// Adds two constraints:\n// 0 <= x + y <= 3\n// 1 <= 10 * x - y <= 5\nengine.addConstraints(\n [0.0, 1.0],\n [3.0, 5.0],\n [\n ['x', 'y'],\n ['x', 'y'],\n ],\n [\n [1, 1],\n [10, -1],\n ],\n);\n```\n\nExample:\n```text\nconst engine = LinearOptimizationService.createEngine();\nconst constraint = engine.addConstraint(0, 10);\n\n// Add a boolean variable (integer >= 0 and <= 1)\nengine.addVariable('x', 0, 1, LinearOptimizationService.VariableType.INTEGER);\n\n// Add a real (continuous) variable. Notice the lack of type specification.\nengine.addVariable('y', 0, 100);\n```\n\nExample:\n```text\nconst engine = LinearOptimizationService.createEngine();\nconst constraint = engine.addConstraint(0, 10);\n\n// Add a boolean variable (integer >= 0 and <= 1)\nengine.addVariable('x', 0, 1, LinearOptimizationService.VariableType.INTEGER);\n\n// Add a real (continuous) variable\nengine.addVariable(\n 'y',\n 0,\n 100,\n LinearOptimizationService.VariableType.CONTINUOUS,\n);\n```\n\nExample:\n```text\nconst engine = LinearOptimizationService.createEngine();\nconst constraint = engine.addConstraint(0, 10);\n\n// Add a boolean variable (integer >= 0 and <= 1)\nengine.addVariable(\n 'x',\n 0,\n 1,\n LinearOptimizationService.VariableType.INTEGER,\n 2,\n);\n// The objective is now 2 * x.\n\n// Add a real (continuous) variable\nengine.addVariable(\n 'y',\n 0,\n 100,\n LinearOptimizationService.VariableType.CONTINUOUS,\n -5,\n);\n// The objective is now 2 * x - 5 * y.\n```\n\nExample:\n```text\nconst engine = LinearOptimizationService.createEngine();\n\n// Add a boolean variable 'x' (integer >= 0 and <= 1) and a real (continuous >=0\n// and <= 100) variable 'y'.\nengine.addVariables(\n ['x', 'y'],\n [0, 0],\n [1, 100],\n [\n LinearOptimizationService.VariableType.INTEGER,\n LinearOptimizationService.VariableType.CONTINUOUS,\n ],\n);\n```\n\nExample:\n```text\nconst engine = LinearOptimizationService.createEngine();\n\n// Add a real (continuous) variable. Notice the lack of type specification.\nengine.addVariable('y', 0, 100);\n\n// Set the coefficient of 'y' in the objective.\n// The objective is now 5 * y\nengine.setObjectiveCoefficient('y', 5);\n\n// We want to maximize.\nengine.setMaximization();\n```\n\nExample:\n```text\nconst engine = LinearOptimizationService.createEngine();\n\n// Add a real (continuous) variable. Notice the lack of type specification.\nengine.addVariable('y', 0, 100);\n\n// Set the coefficient of 'y' in the objective.\n// The objective is now 5 * y\nengine.setObjectiveCoefficient('y', 5);\n\n// We want to minimize\nengine.setMinimization();\n```\n\nExample:\n```text\nconst engine = LinearOptimizationService.createEngine();\n\n// Add a real (continuous) variable. Notice the lack of type specification.\nengine.addVariable('y', 0, 100);\n\n// Set the coefficient of 'y' in the objective.\n// The objective is now 5 * y\nengine.setObjectiveCoefficient('y', 5);\n```\n\nExample:\n```text\nconst engine = LinearOptimizationService.createEngine();\n\n// Add variables, constraints and define the objective with addVariable(),\n// addConstraint(), etc\nengine.addVariable('x', 0, 10);\n\n// ...\n\n// Solve the linear program\nconst solution = engine.solve();\nif (!solution.isValid()) {\n throw `No solution ${solution.getStatus()}`;\n}\nLogger.log(`Value of x: ${solution.getVariableValue('x')}`);\n```\n\nExample:\n```text\nconst engine = LinearOptimizationService.createEngine();\n\n// Add variables, constraints and define the objective with addVariable(),\n// addConstraint(), etc\nengine.addVariable('x', 0, 10);\n\n// ...\n\n// Solve the linear program\nconst solution = engine.solve(300);\nif (!solution.isValid()) {\n throw `No solution ${solution.getStatus()}`;\n}\nLogger.log(`Value of x: ${solution.getVariableValue('x')}`);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.054Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":235,"estimatedTokens":1404}}998{"id":"doc-class_bigquerydatasourcespec_apps_script_google_-95f505aa","source":"documentation","title":"Class BigQueryDataSourceSpec | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/big-query-data-source-spec","text":"Example:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\n\nconst newSpec = spec.copy();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst parameters = spec.getParameters();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst type = spec.getType();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.058Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":32,"estimatedTokens":194}}999{"id":"doc-class_master_apps_script_google_for_developers-a26e50ee","source":"documentation","title":"Class Master | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/master","text":"Example:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst placeholder = slide.getPlaceholder(\n SlidesApp.PlaceholderType.CENTERED_TITLE,\n);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst placeholder = slide.getPlaceholder(\n SlidesApp.PlaceholderType.CENTERED_TITLE,\n 0,\n);\n```\n\nExample:\n```text\nconst master = SlidesApp.getActivePresentation().getMasters()[0];\nLogger.log(\n `Number of placeholders in the master: ${master.getPlaceholders().length}`,\n);\n```\n\nExample:\n```text\n// Copy a group between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst group = otherPresentationSlide.getGroups()[0];\ncurrentPresentationSlide.insertGroup(\n group); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// Get the Drive image file with the given ID.\nconst image = DriveApp.getFileById('123abc');\nslide.insertImage(image);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// Get the Drive image file with the given ID.\nconst image = DriveApp.getFileById('123abc');\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 300,\n height: 100\n};\nslide.insertImage(image, position.left, position.top, size.width, size.height);\n```\n\nExample:\n```text\n// Copy an image between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst image = otherPresentationSlide.getImages[0];\ncurrentPresentationSlide.insertImage(image);\n```\n\nExample:\n```text\n// Copy a line between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst line = otherPresentationSlide.getLines[0];\ncurrentPresentationSlide.insertLine(line);\n```\n\nExample:\n```text\n// Insert a line in the first slide of the presentation connecting two shapes.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst shape1 = slide.insertShape(SlidesApp.ShapeType.RECTANGLE);\nconst shape2 = slide.insertShape(SlidesApp.ShapeType.CLOUD);\nslide.insertLine(\n SlidesApp.LineCategory.BENT,\n shape1.getConnectionSites()[0],\n shape2.getConnectionSites()[1],\n);\n```\n\nExample:\n```text\n// Insert a line in the first slide of the presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst startPoint = {\n left: 10,\n top: 10\n};\nconst endPoint = {\n left: 40,\n top: 40\n};\nslide.insertLine(\n SlidesApp.LineCategory.STRAIGHT,\n startPoint.left,\n startPoint.top,\n endPoint.left,\n endPoint.top,\n);\n```\n\nExample:\n```text\n// Copy a page element between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = otherPresentationSlide.getPageElements()[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertPageElement(pageElement);\n```\n\nExample:\n```text\n// Copy a shape between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst shape = otherPresentationSlide.getShapes[0];\ncurrentPresentationSlide.insertShape(\n shape); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert a shape in the first slide of the presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n\n// Also available for Layout, Master, and Page.\nslide.insertShape(SlidesApp.ShapeType.RECTANGLE);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertSheetsChart(chart);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 200,\n height: 200\n};\n\n// Also available for Layout, Master, and Page.\nslide.insertSheetsChart(\n chart,\n position.left,\n position.top,\n size.width,\n size.height,\n);\n```\n\nExample:\n```text\n// Copy a sheets chart between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst sheetsChart = otherPresentationSlide.getSheetsCharts[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertSheetsChart(sheetsChart);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertSheetsChartAsImage(\n chart); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 200,\n height: 200\n};\n\n// Also available for Layout, Master, and Page.\nslide.insertSheetsChartAsImage(\n chart,\n position.left,\n position.right,\n size.width,\n size.height,\n);\n```\n\nExample:\n```text\n// Copy a table between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst table = otherPresentationSlide.getTables[0];\ncurrentPresentationSlide.insertTable(\n table); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert text box with \"Hello\" on the first slide of presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertTextBox('Hello'); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert text box with \"Hello\" on the first slide of presentation. This text\n// box is a square with a length of 10 points on each side.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertTextBox(\n 'Hello', 0, 0, 10, 10); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Copy a video between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst video = otherPresentationSlide.getVideos[0];\ncurrentPresentationSlide.insertVideo(\n video); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Copy a word art between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst wordArt = otherPresentationSlide.getWordArts[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertWordArt(wordArt);\n```\n\nExample:\n```text\n// Select the first slide as the current page selection and replace any previous\n// selection.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.selectAsCurrentPage(); // Also available for Layout, Master, and Page.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.061Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":24,"totalLines":295,"estimatedTokens":1996}}1000{"id":"doc-class_getauthtyperesponse_apps_script_google_for-22bebff0","source":"documentation","title":"Class GetAuthTypeResponse | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/get-auth-type-response","text":"Example:\n```text\nfunction getAuthType() {\n const cc = DataStudioApp.createCommunityConnector();\n\n return cc.newAuthTypeResponse()\n .setAuthType(cc.AuthType.USER_PASS)\n .setHelpUrl('https://www.example.org/connector-auth-help')\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.062Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":13,"estimatedTokens":69}}1001{"id":"doc-class_bigquerydatasourcespecbuilder_apps_script_-23d7058b","source":"documentation","title":"Class BigQueryDataSourceSpecBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/big-query-data-source-spec-builder","text":"Example:\n```text\nconst bigQueryDataSourceSpec = SpreadsheetApp.newDataSourceSpec().asBigQuery();\n// TODO(developer): Replace with the required dataset, project and table IDs.\nbigQueryDataSourceSpec.setDatasetId('my data set id');\nbigQueryDataSourceSpec.setProjectId('my project id');\nbigQueryDataSourceSpec.setTableId('my table id');\n\nbigQueryDataSourceSpec.build();\n```\n\nExample:\n```text\nconst lookerDataSourceSpecBuilder =\n SpreadsheetApp.newDataSourceSpec().asLooker();\nconst lookerSpec = lookerDataSourceSpecBuilder.setExploreName('my explore name')\n .setInstanceUrl('my instance url')\n .setModelName('my model name')\n .build();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\n\nconst newSpec = spec.copy();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst parameters = spec.getParameters();\n```\n\nExample:\n```text\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\nconst spec = ss.getDataSources()[0].getSpec();\nconst type = spec.getType();\n```\n\nExample:\n```text\nconst specBuilder = SpreadsheetApp.newDataSourceSpec();\nspecBuilder.removeAllParameters();\n```\n\nExample:\n```text\nconst specBuilder = SpreadsheetApp.newDataSourceSpec();\nspecBuilder.removeParameter('x');\n```\n\nExample:\n```text\nconst specBuilder = SpreadsheetApp.newDataSourceSpec().asBigQuery();\nspecBuilder.setParameterFromCell('x', 'A1');\nconst bigQuerySpec = specBuilder.build();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.065Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":72,"estimatedTokens":472}}1002{"id":"doc-what_s_new_in_vault_google_vault_help-aeae6497","source":"documentation","title":"What's new in Vault - Google Vault Help","url":"https://developers.google.com/vault/guides/chat","text":"Search Help Center\n\nExample:\n```text\nParticipant 1: hi\n\n Participant 2: hello\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.067Z","totalSectionsIncluded":1,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":25}}1003{"id":"doc-slides_service_apps_script_google_for_developers-cdfeee02","source":"documentation","title":"Slides Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides","text":"Example:\n```text\n[ x2 ] [ scaleX shearX translateX ] [ x1 ]\n[ y2 ] = [ shearY scaleY translateY ] [ y1 ]\n[ 1 ] [ 0 0 1 ] [ 1 ]\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.073Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":8,"estimatedTokens":43}}1004{"id":"doc-class_presentation_apps_script_google_for_develo-0961ac3f","source":"documentation","title":"Class Presentation | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/presentation","text":"Example:\n```text\n// Copy a slide from another presentation and appends it.\nconst otherPresentation = SlidesApp.openById('presentationId');\nconst currentPresentation = SlidesApp.getActivePresentation();\nconst slide = otherPresentation.getSlides()[0];\ncurrentPresentation.appendSlide(slide);\n```\n\nExample:\n```text\n// Copy a slide from another presentation, then append and link it.\nconst sourcePresentation = SlidesApp.openById('presentationId');\nconst currentPresentation = SlidesApp.getActivePresentation();\nconst slide = sourcePresentation.getSlides()[0];\nconst appendedSlide = currentPresentation.appendSlide(\n slide,\n SlidesApp.SlideLinkingMode.LINKED,\n);\n```\n\nExample:\n```text\n// Gets the current active page that is selected in the active presentation.\nconst selection = SlidesApp.getActivePresentation().getSelection();\nconst currentPage = selection.getCurrentPage();\n```\n\nExample:\n```text\nconst presentation = SlidesApp.getActivePresentation();\n\n// Send out the link to open the presentation.\nMailApp.sendEmail(\n '<email-address>',\n presentation.getName(),\n presentation.getUrl(),\n);\n```\n\nExample:\n```text\n// Copy a slide from another presentation and inserts it.\nconst otherPresentation = SlidesApp.openById('presentationId');\nconst currentPresentation = SlidesApp.getActivePresentation();\nconst slide = otherPresentation.getSlides()[0];\nconst insertionIndex = 1;\ncurrentPresentation.insertSlide(insertionIndex, slide);\n```\n\nExample:\n```text\n// Copy a slide from another presentation, then insert and link it.\nconst sourcePresentation = SlidesApp.openById('presentationId');\nconst currentPresentation = SlidesApp.getActivePresentation();\nconst slide = sourcePresentation.getSlides()[0];\nconst insertionIndex = 1;\nconst insertedSlide = currentPresentation.insertSlide(\n insertionIndex,\n slide,\n SlidesApp.SlideLinkingMode.LINKED,\n);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.076Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":65,"estimatedTokens":471}}1005{"id":"doc-class_field_apps_script_google_for_developers-ae76146c","source":"documentation","title":"Class Field | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/field","text":"Example:\n```text\nconst cc = DataStudioApp.createCommunityConnector();\nconst fields = cc.getFields();\n\nconst field1 = fields.newDimension()\n .setId('field1_id')\n .setName('Field 1 ID')\n .setDescription('The first field.')\n .setType(cc.FieldType.YEAR_MONTH)\n .setGroup('DATETIME');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.077Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":14,"estimatedTokens":97}}1006{"id":"doc-class_page_apps_script_google_for_developers-9cebb4c2","source":"documentation","title":"Class Page | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/page","text":"Example:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst placeholder = slide.getPlaceholder(\n SlidesApp.PlaceholderType.CENTERED_TITLE,\n);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst placeholder = slide.getPlaceholder(\n SlidesApp.PlaceholderType.CENTERED_TITLE,\n 0,\n);\n```\n\nExample:\n```text\nconst master = SlidesApp.getActivePresentation().getMasters()[0];\nLogger.log(\n `Number of placeholders in the master: ${master.getPlaceholders().length}`,\n);\n```\n\nExample:\n```text\n// Copy a group between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst group = otherPresentationSlide.getGroups()[0];\ncurrentPresentationSlide.insertGroup(\n group); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// Get the Drive image file with the given ID.\nconst image = DriveApp.getFileById('123abc');\nslide.insertImage(image);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// Get the Drive image file with the given ID.\nconst image = DriveApp.getFileById('123abc');\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 300,\n height: 100\n};\nslide.insertImage(image, position.left, position.top, size.width, size.height);\n```\n\nExample:\n```text\n// Copy an image between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst image = otherPresentationSlide.getImages[0];\ncurrentPresentationSlide.insertImage(image);\n```\n\nExample:\n```text\n// Copy a line between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst line = otherPresentationSlide.getLines[0];\ncurrentPresentationSlide.insertLine(line);\n```\n\nExample:\n```text\n// Insert a line in the first slide of the presentation connecting two shapes.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst shape1 = slide.insertShape(SlidesApp.ShapeType.RECTANGLE);\nconst shape2 = slide.insertShape(SlidesApp.ShapeType.CLOUD);\nslide.insertLine(\n SlidesApp.LineCategory.BENT,\n shape1.getConnectionSites()[0],\n shape2.getConnectionSites()[1],\n);\n```\n\nExample:\n```text\n// Insert a line in the first slide of the presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst startPoint = {\n left: 10,\n top: 10\n};\nconst endPoint = {\n left: 40,\n top: 40\n};\nslide.insertLine(\n SlidesApp.LineCategory.STRAIGHT,\n startPoint.left,\n startPoint.top,\n endPoint.left,\n endPoint.top,\n);\n```\n\nExample:\n```text\n// Copy a page element between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = otherPresentationSlide.getPageElements()[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertPageElement(pageElement);\n```\n\nExample:\n```text\n// Copy a shape between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst shape = otherPresentationSlide.getShapes[0];\ncurrentPresentationSlide.insertShape(\n shape); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert a shape in the first slide of the presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n\n// Also available for Layout, Master, and Page.\nslide.insertShape(SlidesApp.ShapeType.RECTANGLE);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertSheetsChart(chart);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 200,\n height: 200\n};\n\n// Also available for Layout, Master, and Page.\nslide.insertSheetsChart(\n chart,\n position.left,\n position.top,\n size.width,\n size.height,\n);\n```\n\nExample:\n```text\n// Copy a sheets chart between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst sheetsChart = otherPresentationSlide.getSheetsCharts[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertSheetsChart(sheetsChart);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertSheetsChartAsImage(\n chart); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 200,\n height: 200\n};\n\n// Also available for Layout, Master, and Page.\nslide.insertSheetsChartAsImage(\n chart,\n position.left,\n position.right,\n size.width,\n size.height,\n);\n```\n\nExample:\n```text\n// Copy a table between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst table = otherPresentationSlide.getTables[0];\ncurrentPresentationSlide.insertTable(\n table); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert text box with \"Hello\" on the first slide of presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertTextBox('Hello'); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert text box with \"Hello\" on the first slide of presentation. This text\n// box is a square with a length of 10 points on each side.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertTextBox(\n 'Hello', 0, 0, 10, 10); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Copy a video between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst video = otherPresentationSlide.getVideos[0];\ncurrentPresentationSlide.insertVideo(\n video); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Copy a word art between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst wordArt = otherPresentationSlide.getWordArts[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertWordArt(wordArt);\n```\n\nExample:\n```text\n// Select the first slide as the current page selection and replace any previous\n// selection.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.selectAsCurrentPage(); // Also available for Layout, Master, and Page.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.084Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":24,"totalLines":295,"estimatedTokens":1996}}1007{"id":"doc-class_usererror_apps_script_google_for_developer-a439532a","source":"documentation","title":"Class UserError | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/user-error","text":"Example:\n```text\nconst cc = DataStudioApp.createCommunityConnector();\n\ncc.newUserError()\n .setText('This is the debug error text.')\n .setDebugText('This text is only shown to admins.')\n .throwException();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.086Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":11,"estimatedTokens":58}}1008{"id":"doc-class_video_apps_script_google_for_developers-0ee20233","source":"documentation","title":"Class Video | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/video","text":"Example:\n```text\nnewTransform = argument * existingTransform;\n```\n\nExample:\n```text\nconst element = SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\nelement.preconcatenateTransform(\n SlidesApp.newAffineTransformBuilder().setTranslateX(-36.0).build(),\n);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = slide.getPageElements()[0];\n// Only select this page element and replace any previous selection.\npageElement.select();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// First select the slide page, as the current page selection.\nslide.selectAsCurrentPage();\n// Then select all the page elements in the selected slide page.\nconst pageElements = slide.getPageElements();\nfor (let i = 0; i < pageElements.length; i++) {\n pageElements[i].select(false);\n}\n```\n\nExample:\n```text\n// Set the first page element's alt text description to \"new alt text\n// description\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setDescription('new alt text description');\nLogger.log(pageElement.getDescription());\n```\n\nExample:\n```text\n// Set the first page element's alt text title to \"new alt text title\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setTitle('new alt text title');\nLogger.log(pageElement.getTitle());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.088Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":53,"estimatedTokens":366}}1009{"id":"doc-class_layout_apps_script_google_for_developers-27195486","source":"documentation","title":"Class Layout | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/layout","text":"Example:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst placeholder = slide.getPlaceholder(\n SlidesApp.PlaceholderType.CENTERED_TITLE,\n);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst placeholder = slide.getPlaceholder(\n SlidesApp.PlaceholderType.CENTERED_TITLE,\n 0,\n);\n```\n\nExample:\n```text\nconst master = SlidesApp.getActivePresentation().getMasters()[0];\nLogger.log(\n `Number of placeholders in the master: ${master.getPlaceholders().length}`,\n);\n```\n\nExample:\n```text\n// Copy a group between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst group = otherPresentationSlide.getGroups()[0];\ncurrentPresentationSlide.insertGroup(\n group); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// Get the Drive image file with the given ID.\nconst image = DriveApp.getFileById('123abc');\nslide.insertImage(image);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// Get the Drive image file with the given ID.\nconst image = DriveApp.getFileById('123abc');\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 300,\n height: 100\n};\nslide.insertImage(image, position.left, position.top, size.width, size.height);\n```\n\nExample:\n```text\n// Copy an image between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst image = otherPresentationSlide.getImages[0];\ncurrentPresentationSlide.insertImage(image);\n```\n\nExample:\n```text\n// Copy a line between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst line = otherPresentationSlide.getLines[0];\ncurrentPresentationSlide.insertLine(line);\n```\n\nExample:\n```text\n// Insert a line in the first slide of the presentation connecting two shapes.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst shape1 = slide.insertShape(SlidesApp.ShapeType.RECTANGLE);\nconst shape2 = slide.insertShape(SlidesApp.ShapeType.CLOUD);\nslide.insertLine(\n SlidesApp.LineCategory.BENT,\n shape1.getConnectionSites()[0],\n shape2.getConnectionSites()[1],\n);\n```\n\nExample:\n```text\n// Insert a line in the first slide of the presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst startPoint = {\n left: 10,\n top: 10\n};\nconst endPoint = {\n left: 40,\n top: 40\n};\nslide.insertLine(\n SlidesApp.LineCategory.STRAIGHT,\n startPoint.left,\n startPoint.top,\n endPoint.left,\n endPoint.top,\n);\n```\n\nExample:\n```text\n// Copy a page element between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = otherPresentationSlide.getPageElements()[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertPageElement(pageElement);\n```\n\nExample:\n```text\n// Copy a shape between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst shape = otherPresentationSlide.getShapes[0];\ncurrentPresentationSlide.insertShape(\n shape); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert a shape in the first slide of the presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n\n// Also available for Layout, Master, and Page.\nslide.insertShape(SlidesApp.ShapeType.RECTANGLE);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertSheetsChart(chart);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 200,\n height: 200\n};\n\n// Also available for Layout, Master, and Page.\nslide.insertSheetsChart(\n chart,\n position.left,\n position.top,\n size.width,\n size.height,\n);\n```\n\nExample:\n```text\n// Copy a sheets chart between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst sheetsChart = otherPresentationSlide.getSheetsCharts[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertSheetsChart(sheetsChart);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertSheetsChartAsImage(\n chart); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 200,\n height: 200\n};\n\n// Also available for Layout, Master, and Page.\nslide.insertSheetsChartAsImage(\n chart,\n position.left,\n position.right,\n size.width,\n size.height,\n);\n```\n\nExample:\n```text\n// Copy a table between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst table = otherPresentationSlide.getTables[0];\ncurrentPresentationSlide.insertTable(\n table); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert text box with \"Hello\" on the first slide of presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertTextBox('Hello'); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert text box with \"Hello\" on the first slide of presentation. This text\n// box is a square with a length of 10 points on each side.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertTextBox(\n 'Hello', 0, 0, 10, 10); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Copy a video between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst video = otherPresentationSlide.getVideos[0];\ncurrentPresentationSlide.insertVideo(\n video); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Copy a word art between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst wordArt = otherPresentationSlide.getWordArts[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertWordArt(wordArt);\n```\n\nExample:\n```text\n// Select the first slide as the current page selection and replace any previous\n// selection.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.selectAsCurrentPage(); // Also available for Layout, Master, and Page.\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.093Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":24,"totalLines":295,"estimatedTokens":1996}}1010{"id":"doc-class_debugerror_apps_script_google_for_develope-729a7b73","source":"documentation","title":"Class DebugError | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/data-studio/debug-error","text":"Example:\n```text\nconst cc = DataStudioApp.createCommunityConnector();\n\ncc.newDebugError().setText('This is the debug error text.').throwException();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.094Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":8,"estimatedTokens":42}}1011{"id":"doc-class_shape_apps_script_google_for_developers-8edd771b","source":"documentation","title":"Class Shape | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/shape","text":"Example:\n```text\nconst shape = SlidesApp.getActivePresentation().getSlides()[0].getShapes()[0];\nconst link = shape.getLink();\nif (link != null) {\n Logger.log(`Shape has a link of type: ${link.getLinkType()}`);\n}\n```\n\nExample:\n```text\nnewTransform = argument * existingTransform;\n```\n\nExample:\n```text\nconst element = SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\nelement.preconcatenateTransform(\n SlidesApp.newAffineTransformBuilder().setTranslateX(-36.0).build(),\n);\n```\n\nExample:\n```text\nconst slides = SlidesApp.getActivePresentation().getSlides();\nslides[1].getShapes()[0].removeLink();\n```\n\nExample:\n```text\nconst shape = SlidesApp.getActivePresentation().getSlides()[0].getShapes()[0];\n// Get the Drive image file with the given ID.\nconst driveImage = DriveApp.getFileById('123abc');\nshape.replaceWithImage(driveImage);\n```\n\nExample:\n```text\nconst shape = SlidesApp.getActivePresentation().getSlides()[0].getShapes()[0];\n// Get the Drive image file with the given ID.\nconst driveImage = DriveApp.getFileById('123abc');\n// Replace and crop the replaced image.\nshape.replaceWithImage(driveImage, true);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Replace the shape with the Sheets chart.\nconst shape = SlidesApp.getActivePresentation().getSlides()[0].getShapes()[0];\nshape.replaceWithSheetsChart(chart);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Replace the shape with the Sheets chart as an image.\nconst shape = SlidesApp.getActivePresentation().getSlides()[0].getShapes()[0];\nshape.replaceWithSheetsChartAsImage(chart);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = slide.getPageElements()[0];\n// Only select this page element and replace any previous selection.\npageElement.select();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// First select the slide page, as the current page selection.\nslide.selectAsCurrentPage();\n// Then select all the page elements in the selected slide page.\nconst pageElements = slide.getPageElements();\nfor (let i = 0; i < pageElements.length; i++) {\n pageElements[i].select(false);\n}\n```\n\nExample:\n```text\n// Set the first page element's alt text description to \"new alt text\n// description\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setDescription('new alt text description');\nLogger.log(pageElement.getDescription());\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(0);\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(slides[0]);\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(SlidesApp.SlidePosition.FIRST_SLIDE);\n```\n\nExample:\n```text\n// Set a link to the URL.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkUrl('https://slides.google.com');\n```\n\nExample:\n```text\n// Set the first page element's alt text title to \"new alt text title\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setTitle('new alt text title');\nLogger.log(pageElement.getTitle());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.096Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":135,"estimatedTokens":953}}1012{"id":"doc-class_pageelement_apps_script_google_for_develop-5f419955","source":"documentation","title":"Class PageElement | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/page-element","text":"Example:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = slide.getPageElements()[0];\npageElement.asSpeakerSpotlight();\n```\n\nExample:\n```text\nnewTransform = argument * existingTransform;\n```\n\nExample:\n```text\nconst element = SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\nelement.preconcatenateTransform(\n SlidesApp.newAffineTransformBuilder().setTranslateX(-36.0).build(),\n);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = slide.getPageElements()[0];\n// Only select this page element and replace any previous selection.\npageElement.select();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// First select the slide page, as the current page selection.\nslide.selectAsCurrentPage();\n// Then select all the page elements in the selected slide page.\nconst pageElements = slide.getPageElements();\nfor (let i = 0; i < pageElements.length; i++) {\n pageElements[i].select(false);\n}\n```\n\nExample:\n```text\n// Set the first page element's alt text description to \"new alt text\n// description\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setDescription('new alt text description');\nLogger.log(pageElement.getDescription());\n```\n\nExample:\n```text\n// Set the first page element's alt text title to \"new alt text title\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setTitle('new alt text title');\nLogger.log(pageElement.getTitle());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.100Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":60,"estimatedTokens":408}}1013{"id":"doc-class_image_apps_script_google_for_developers-a7f67040","source":"documentation","title":"Class Image | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/image","text":"Example:\n```text\nconst shape = SlidesApp.getActivePresentation().getSlides()[0].getShapes()[0];\nconst link = shape.getLink();\nif (link != null) {\n Logger.log(`Shape has a link of type: ${link.getLinkType()}`);\n}\n```\n\nExample:\n```text\nnewTransform = argument * existingTransform;\n```\n\nExample:\n```text\nconst element = SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\nelement.preconcatenateTransform(\n SlidesApp.newAffineTransformBuilder().setTranslateX(-36.0).build(),\n);\n```\n\nExample:\n```text\nconst slides = SlidesApp.getActivePresentation().getSlides();\nslides[1].getShapes()[0].removeLink();\n```\n\nExample:\n```text\nconst image = SlidesApp.getActivePresentation().getSlides()[0].getImages()[0];\n// Get the Drive image file with the given ID.\nconst driveImage = DriveApp.getFileById(\"123abc\");\nimage.replace(driveImage);\n```\n\nExample:\n```text\nconst image = SlidesApp.getActivePresentation().getSlides()[0].getImages()[0];\n// Get the Drive image file with the given ID.\nconst driveImage = DriveApp.getFileById('123abc');\n// Replace and crop the drive image.\nimage.replace(driveImage, true);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = slide.getPageElements()[0];\n// Only select this page element and replace any previous selection.\npageElement.select();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// First select the slide page, as the current page selection.\nslide.selectAsCurrentPage();\n// Then select all the page elements in the selected slide page.\nconst pageElements = slide.getPageElements();\nfor (let i = 0; i < pageElements.length; i++) {\n pageElements[i].select(false);\n}\n```\n\nExample:\n```text\n// Set the first page element's alt text description to \"new alt text\n// description\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setDescription('new alt text description');\nLogger.log(pageElement.getDescription());\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(0);\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(slides[0]);\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(SlidesApp.SlidePosition.FIRST_SLIDE);\n```\n\nExample:\n```text\n// Set a link to the URL.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkUrl('https://slides.google.com');\n```\n\nExample:\n```text\n// Set the first page element's alt text title to \"new alt text title\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setTitle('new alt text title');\nLogger.log(pageElement.getTitle());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.103Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":117,"estimatedTokens":799}}1014{"id":"doc-class_group_apps_script_google_for_developers-37a810c6","source":"documentation","title":"Class Group | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/group","text":"Example:\n```text\nnewTransform = argument * existingTransform;\n```\n\nExample:\n```text\nconst element = SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\nelement.preconcatenateTransform(\n SlidesApp.newAffineTransformBuilder().setTranslateX(-36.0).build(),\n);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = slide.getPageElements()[0];\n// Only select this page element and replace any previous selection.\npageElement.select();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// First select the slide page, as the current page selection.\nslide.selectAsCurrentPage();\n// Then select all the page elements in the selected slide page.\nconst pageElements = slide.getPageElements();\nfor (let i = 0; i < pageElements.length; i++) {\n pageElements[i].select(false);\n}\n```\n\nExample:\n```text\n// Set the first page element's alt text description to \"new alt text\n// description\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setDescription('new alt text description');\nLogger.log(pageElement.getDescription());\n```\n\nExample:\n```text\n// Set the first page element's alt text title to \"new alt text title\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setTitle('new alt text title');\nLogger.log(pageElement.getTitle());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.105Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":53,"estimatedTokens":366}}1015{"id":"doc-class_line_apps_script_google_for_developers-e79db7a1","source":"documentation","title":"Class Line | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/line","text":"Example:\n```text\nconst shape = SlidesApp.getActivePresentation().getSlides()[0].getShapes()[0];\nconst link = shape.getLink();\nif (link != null) {\n Logger.log(`Shape has a link of type: ${link.getLinkType()}`);\n}\n```\n\nExample:\n```text\nnewTransform = argument * existingTransform;\n```\n\nExample:\n```text\nconst element = SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\nelement.preconcatenateTransform(\n SlidesApp.newAffineTransformBuilder().setTranslateX(-36.0).build(),\n);\n```\n\nExample:\n```text\nconst slides = SlidesApp.getActivePresentation().getSlides();\nslides[1].getShapes()[0].removeLink();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = slide.getPageElements()[0];\n// Only select this page element and replace any previous selection.\npageElement.select();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// First select the slide page, as the current page selection.\nslide.selectAsCurrentPage();\n// Then select all the page elements in the selected slide page.\nconst pageElements = slide.getPageElements();\nfor (let i = 0; i < pageElements.length; i++) {\n pageElements[i].select(false);\n}\n```\n\nExample:\n```text\n// Set the first page element's alt text description to \"new alt text\n// description\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setDescription('new alt text description');\nLogger.log(pageElement.getDescription());\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(0);\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(slides[0]);\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(SlidesApp.SlidePosition.FIRST_SLIDE);\n```\n\nExample:\n```text\n// Set a link to the URL.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkUrl('https://slides.google.com');\n```\n\nExample:\n```text\n// Set the first page element's alt text title to \"new alt text title\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setTitle('new alt text title');\nLogger.log(pageElement.getTitle());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.108Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":100,"estimatedTokens":675}}1016{"id":"doc-class_sheetschart_apps_script_google_for_develop-32a2dbff","source":"documentation","title":"Class SheetsChart | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/sheets-chart","text":"Example:\n```text\nconst shape = SlidesApp.getActivePresentation().getSlides()[0].getShapes()[0];\nconst link = shape.getLink();\nif (link != null) {\n Logger.log(`Shape has a link of type: ${link.getLinkType()}`);\n}\n```\n\nExample:\n```text\nnewTransform = argument * existingTransform;\n```\n\nExample:\n```text\nconst element = SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\nelement.preconcatenateTransform(\n SlidesApp.newAffineTransformBuilder().setTranslateX(-36.0).build(),\n);\n```\n\nExample:\n```text\nconst slides = SlidesApp.getActivePresentation().getSlides();\nslides[1].getShapes()[0].removeLink();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = slide.getPageElements()[0];\n// Only select this page element and replace any previous selection.\npageElement.select();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// First select the slide page, as the current page selection.\nslide.selectAsCurrentPage();\n// Then select all the page elements in the selected slide page.\nconst pageElements = slide.getPageElements();\nfor (let i = 0; i < pageElements.length; i++) {\n pageElements[i].select(false);\n}\n```\n\nExample:\n```text\n// Set the first page element's alt text description to \"new alt text\n// description\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setDescription('new alt text description');\nLogger.log(pageElement.getDescription());\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(0);\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(slides[0]);\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(SlidesApp.SlidePosition.FIRST_SLIDE);\n```\n\nExample:\n```text\n// Set a link to the URL.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkUrl('https://slides.google.com');\n```\n\nExample:\n```text\n// Set the first page element's alt text title to \"new alt text title\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setTitle('new alt text title');\nLogger.log(pageElement.getTitle());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.110Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":100,"estimatedTokens":675}}1017{"id":"doc-class_wordart_apps_script_google_for_developers-68202b0f","source":"documentation","title":"Class WordArt | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/word-art","text":"Example:\n```text\nconst shape = SlidesApp.getActivePresentation().getSlides()[0].getShapes()[0];\nconst link = shape.getLink();\nif (link != null) {\n Logger.log(`Shape has a link of type: ${link.getLinkType()}`);\n}\n```\n\nExample:\n```text\nnewTransform = argument * existingTransform;\n```\n\nExample:\n```text\nconst element = SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\nelement.preconcatenateTransform(\n SlidesApp.newAffineTransformBuilder().setTranslateX(-36.0).build(),\n);\n```\n\nExample:\n```text\nconst slides = SlidesApp.getActivePresentation().getSlides();\nslides[1].getShapes()[0].removeLink();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = slide.getPageElements()[0];\n// Only select this page element and replace any previous selection.\npageElement.select();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// First select the slide page, as the current page selection.\nslide.selectAsCurrentPage();\n// Then select all the page elements in the selected slide page.\nconst pageElements = slide.getPageElements();\nfor (let i = 0; i < pageElements.length; i++) {\n pageElements[i].select(false);\n}\n```\n\nExample:\n```text\n// Set the first page element's alt text description to \"new alt text\n// description\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setDescription('new alt text description');\nLogger.log(pageElement.getDescription());\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(0);\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(slides[0]);\n```\n\nExample:\n```text\n// Set a link to the first slide of the presentation.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkSlide(SlidesApp.SlidePosition.FIRST_SLIDE);\n```\n\nExample:\n```text\n// Set a link to the URL.\nconst slides = SlidesApp.getActivePresentation().getSlides();\nconst shape = slides[1].getShapes()[0];\nconst link = shape.setLinkUrl('https://slides.google.com');\n```\n\nExample:\n```text\n// Set the first page element's alt text title to \"new alt text title\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setTitle('new alt text title');\nLogger.log(pageElement.getTitle());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.113Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":100,"estimatedTokens":675}}1018{"id":"doc-class_table_apps_script_google_for_developers-2c710c51","source":"documentation","title":"Class Table | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/table","text":"Example:\n```text\nnewTransform = argument * existingTransform;\n```\n\nExample:\n```text\nconst element = SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\nelement.preconcatenateTransform(\n SlidesApp.newAffineTransformBuilder().setTranslateX(-36.0).build(),\n);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = slide.getPageElements()[0];\n// Only select this page element and replace any previous selection.\npageElement.select();\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// First select the slide page, as the current page selection.\nslide.selectAsCurrentPage();\n// Then select all the page elements in the selected slide page.\nconst pageElements = slide.getPageElements();\nfor (let i = 0; i < pageElements.length; i++) {\n pageElements[i].select(false);\n}\n```\n\nExample:\n```text\n// Set the first page element's alt text description to \"new alt text\n// description\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setDescription('new alt text description');\nLogger.log(pageElement.getDescription());\n```\n\nExample:\n```text\n// Set the first page element's alt text title to \"new alt text title\".\nconst pageElement =\n SlidesApp.getActivePresentation().getSlides()[0].getPageElements()[0];\npageElement.setTitle('new alt text title');\nLogger.log(pageElement.getTitle());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.115Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":53,"estimatedTokens":366}}1019{"id":"doc-class_overgridimage_apps_script_google_for_devel-5d5d755c","source":"documentation","title":"Class OverGridImage | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/over-grid-image","text":"Example:\n```text\n// Logs the height of all images in a spreadsheet\nconst images = SpreadsheetApp.getActiveSpreadsheet().getImages();\nfor (let i = 0; i < images.length; i++) {\n Logger.log(images[i].getHeight());\n}\n```\n\nExample:\n```text\n// Logs the parent sheet of all images in a spreadsheet\nconst images = SpreadsheetApp.getActiveSpreadsheet().getImages();\nfor (let i = 0; i < images.length; i++) {\n Logger.log(images[i].getSheet());\n}\n```\n\nExample:\n```text\n// Logs the width of all images in a spreadsheet\nconst images = SpreadsheetApp.getActiveSpreadsheet().getImages();\nfor (let i = 0; i < images.length; i++) {\n Logger.log(images[i].getWidth());\n}\n```\n\nExample:\n```text\n// Deletes all images in a spreadsheet\nconst images = SpreadsheetApp.getActiveSpreadsheet().getImages();\nfor (let i = 0; i < images.length; i++) {\n images[i].remove();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.123Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":37,"estimatedTokens":217}}1020{"id":"doc-class_slide_apps_script_google_for_developers-34d9a413","source":"documentation","title":"Class Slide | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/slides/slide","text":"Example:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst placeholder = slide.getPlaceholder(\n SlidesApp.PlaceholderType.CENTERED_TITLE,\n);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst placeholder = slide.getPlaceholder(\n SlidesApp.PlaceholderType.CENTERED_TITLE,\n 0,\n);\n```\n\nExample:\n```text\nconst master = SlidesApp.getActivePresentation().getMasters()[0];\nLogger.log(\n `Number of placeholders in the master: ${master.getPlaceholders().length}`,\n);\n```\n\nExample:\n```text\n// Copy a group between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst group = otherPresentationSlide.getGroups()[0];\ncurrentPresentationSlide.insertGroup(\n group); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// Get the Drive image file with the given ID.\nconst image = DriveApp.getFileById('123abc');\nslide.insertImage(image);\n```\n\nExample:\n```text\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n// Get the Drive image file with the given ID.\nconst image = DriveApp.getFileById('123abc');\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 300,\n height: 100\n};\nslide.insertImage(image, position.left, position.top, size.width, size.height);\n```\n\nExample:\n```text\n// Copy an image between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst image = otherPresentationSlide.getImages[0];\ncurrentPresentationSlide.insertImage(image);\n```\n\nExample:\n```text\n// Copy a line between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst line = otherPresentationSlide.getLines[0];\ncurrentPresentationSlide.insertLine(line);\n```\n\nExample:\n```text\n// Insert a line in the first slide of the presentation connecting two shapes.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst shape1 = slide.insertShape(SlidesApp.ShapeType.RECTANGLE);\nconst shape2 = slide.insertShape(SlidesApp.ShapeType.CLOUD);\nslide.insertLine(\n SlidesApp.LineCategory.BENT,\n shape1.getConnectionSites()[0],\n shape2.getConnectionSites()[1],\n);\n```\n\nExample:\n```text\n// Insert a line in the first slide of the presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst startPoint = {\n left: 10,\n top: 10\n};\nconst endPoint = {\n left: 40,\n top: 40\n};\nslide.insertLine(\n SlidesApp.LineCategory.STRAIGHT,\n startPoint.left,\n startPoint.top,\n endPoint.left,\n endPoint.top,\n);\n```\n\nExample:\n```text\n// Copy a page element between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst pageElement = otherPresentationSlide.getPageElements()[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertPageElement(pageElement);\n```\n\nExample:\n```text\n// Copy a shape between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst shape = otherPresentationSlide.getShapes[0];\ncurrentPresentationSlide.insertShape(\n shape); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert a shape in the first slide of the presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\n\n// Also available for Layout, Master, and Page.\nslide.insertShape(SlidesApp.ShapeType.RECTANGLE);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertSheetsChart(chart);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 200,\n height: 200\n};\n\n// Also available for Layout, Master, and Page.\nslide.insertSheetsChart(\n chart,\n position.left,\n position.top,\n size.width,\n size.height,\n);\n```\n\nExample:\n```text\n// Copy a sheets chart between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst sheetsChart = otherPresentationSlide.getSheetsCharts[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertSheetsChart(sheetsChart);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertSheetsChartAsImage(\n chart); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.openById('spreadsheetId').getSheets()[0];\nconst chart = sheet.getCharts()[0];\n// Insert the spreadsheet chart in the first slide.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nconst position = {\n left: 0,\n top: 0\n};\nconst size = {\n width: 200,\n height: 200\n};\n\n// Also available for Layout, Master, and Page.\nslide.insertSheetsChartAsImage(\n chart,\n position.left,\n position.right,\n size.width,\n size.height,\n);\n```\n\nExample:\n```text\n// Copy a table between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst table = otherPresentationSlide.getTables[0];\ncurrentPresentationSlide.insertTable(\n table); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert text box with \"Hello\" on the first slide of presentation.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertTextBox('Hello'); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Insert text box with \"Hello\" on the first slide of presentation. This text\n// box is a square with a length of 10 points on each side.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.insertTextBox(\n 'Hello', 0, 0, 10, 10); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Copy a video between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst video = otherPresentationSlide.getVideos[0];\ncurrentPresentationSlide.insertVideo(\n video); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\n// Copy a word art between presentations.\nconst otherPresentationSlide =\n SlidesApp.openById('presentationId').getSlides()[0];\nconst currentPresentationSlide =\n SlidesApp.getActivePresentation().getSlides()[0];\nconst wordArt = otherPresentationSlide.getWordArts[0];\n\n// Also available for Layout, Master, and Page.\ncurrentPresentationSlide.insertWordArt(wordArt);\n```\n\nExample:\n```text\nconst currentPresentation = SlidesApp.getActivePresentation();\nconst sourcePresentation = SlidesApp.openById('sourcePresentationId');\nconst sourceSlide = sourcePresentation.getSlides()[0];\nconst linkedSlide = currentPresentation.append(\n sourceSlide,\n SlidesApp.SlideLinkingMode.LINKED,\n);\n\nsourceSlide.insertText(\n 'hello world'); // Only the source slide has the text box.\n\nlinkedSlide.refreshSlide(); // The linked slide now has the text box.\n```\n\nExample:\n```text\n// Select the first slide as the current page selection and replace any previous\n// selection.\nconst slide = SlidesApp.getActivePresentation().getSlides()[0];\nslide.selectAsCurrentPage(); // Also available for Layout, Master, and Page.\n```\n\nExample:\n```text\nconst currentPresentation = SlidesApp.getActivePresentation();\nconst sourcePresentation = SlidesApp.openById('sourcePresentationId');\nconst sourceSlide = sourcePresentation.getSlides()[0];\nconst linkedSlide = currentPresentation.append(\n sourceSlide,\n SlidesApp.SlideLinkingMode.LINKED,\n);\n\nlinkedSlide.unlink();\n\nlinkedSlide.getSourcePresentationId(); // returns null\nlinkedSlide.getSourceSlideObjectId(); // returns null\nlinkedSlide\n .getSlideLinkingMode(); // returns SlidesApp.SlideLinkingMode.NOT_LINKED\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.127Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":26,"totalLines":329,"estimatedTokens":2251}}1021{"id":"doc-class_gmailattachment_apps_script_google_for_dev-40957ed8","source":"documentation","title":"Class GmailAttachment | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/gmail/gmail-attachment","text":"Example:\n```text\n// Logs information about any attachments in the first 100 inbox threads.\nconst threads = GmailApp.getInboxThreads(0, 100);\nconst msgs = GmailApp.getMessagesForThreads(threads);\nfor (let i = 0; i < msgs.length; i++) {\n for (let j = 0; j < msgs[i].length; j++) {\n const attachments = msgs[i][j].getAttachments();\n for (let k = 0; k < attachments.length; k++) {\n Logger.log(\n 'Message \"%s\" contains the attachment \"%s\" (%s bytes)',\n msgs[i][j].getSubject(),\n attachments[k].getName(),\n attachments[k].getSize(),\n );\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.130Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":21,"estimatedTokens":154}}1022{"id":"doc-extend_the_compose_ui_with_compose_actions_googl-6eb1e653","source":"documentation","title":"Extend the compose UI with compose actions | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/gmail/add-ons/how-tos/extending-compose-ui","text":"Example:\n```text\n/**\n * Compose trigger function that fires when the compose UI is\n * requested. Builds and returns a compose UI for inserting images.\n *\n * @param {event} e The compose trigger event object. Not used in\n * this example.\n * @return {Card[]}\n */\nfunction getComposeUI(e) {\n return [buildComposeCard()];\n}\n\n/**\n * Build a card to display interactive buttons to allow the user to\n * update the subject, and To, Cc, Bcc recipients.\n *\n * @return {Card}\n */\nfunction buildComposeCard() {\n\n var card = CardService.newCardBuilder();\n var cardSection = CardService.newCardSection().setHeader('Update email');\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update subject')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('applyUpdateSubjectAction')));\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update To recipients')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('updateToRecipients')));\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update Cc recipients')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('updateCcRecipients')));\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update Bcc recipients')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('updateBccRecipients')));\n return card.addSection(cardSection).build();\n}\n\n/**\n * Updates the subject field of the current email when the user clicks\n * on \"Update subject\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateSubjectAction() {\n // Get the new subject field of the email.\n // This function is not shown in this example.\n var subject = getSubject();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftSubjectAction(CardService.newUpdateDraftSubjectAction()\n .addUpdateSubject(subject))\n .build();\n return response;\n}\n\n/**\n * Updates the To recipients of the current email when the user clicks\n * on \"Update To recipients\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateToRecipientsAction() {\n // Get the new To recipients of the email.\n // This function is not shown in this example.\n var toRecipients = getToRecipients();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftToRecipientsAction(CardService.newUpdateDraftToRecipientsAction()\n .addUpdateToRecipients(toRecipients))\n .build();\n return response;\n}\n\n/**\n * Updates the Cc recipients of the current email when the user clicks\n * on \"Update Cc recipients\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateCcRecipientsAction() {\n // Get the new Cc recipients of the email.\n // This function is not shown in this example.\n var ccRecipients = getCcRecipients();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftCcRecipientsAction(CardService.newUpdateDraftCcRecipientsAction()\n .addUpdateToRecipients(ccRecipients))\n .build();\n return response;\n}\n\n/**\n * Updates the Bcc recipients of the current email when the user clicks\n * on \"Update Bcc recipients\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateBccRecipientsAction() {\n // Get the new Bcc recipients of the email.\n // This function is not shown in this example.\n var bccRecipients = getBccRecipients();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftBccRecipientsAction(CardService.newUpdateDraftBccRecipientsAction()\n .addUpdateToRecipients(bccRecipients))\n .build();\n return response;\n}\n```\n\nExample:\n```text\n/**\n * Compose trigger function that fires when the compose UI is\n * requested. Builds and returns a compose UI for inserting images.\n *\n * @param {event} e The compose trigger event object. Not used in\n * this example.\n * @return {Card[]}\n */\nfunction getInsertImageComposeUI(e) {\n return [buildImageComposeCard()];\n}\n\n/**\n * Build a card to display images from a third-party source.\n *\n * @return {Card}\n */\nfunction buildImageComposeCard() {\n // Get a short list of image URLs to display in the UI.\n // This function is not shown in this example.\n var imageUrls = getImageUrls();\n\n var card = CardService.newCardBuilder();\n var cardSection = CardService.newCardSection().setHeader('My Images');\n for (var i = 0; i < imageUrls.length; i++) {\n var imageUrl = imageUrls[i];\n cardSection.addWidget(\n CardService.newImage()\n .setImageUrl(imageUrl)\n .setOnClickAction(CardService.newAction()\n .setFunctionName('applyInsertImageAction')\n .setParameters({'url' : imageUrl})));\n }\n return card.addSection(cardSection).build();\n}\n\n/**\n * Adds an image to the current draft email when the image is clicked\n * in the compose UI. The image is inserted at the current cursor\n * location. If any content of the email draft is currently selected,\n * it is deleted and replaced with the image.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @param {event} e The incoming event object.\n * @return {UpdateDraftActionResponse}\n */\nfunction applyInsertImageAction(e) {\n var imageUrl = e.parameters.url;\n var imageHtmlContent = '<img style=\\\"display: block\\\" src=\\\"'\n + imageUrl + '\\\"/>';\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftBodyAction(CardService.newUpdateDraftBodyAction()\n .addUpdateContent(\n imageHtmlContent,\n CardService.ContentType.MUTABLE_HTML)\n .setUpdateType(\n CardService.UpdateDraftBodyType.IN_PLACE_INSERT))\n .build();\n return response;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.133Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":195,"estimatedTokens":1649}}1023{"id":"doc-class_updatedraftactionresponse_apps_script_goog-32865b35","source":"documentation","title":"Class UpdateDraftActionResponse | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/update-draft-action-response","text":"Example:\n```text\n// An UpdateDraftActionResponse that inserts a list of To recipients into an\n// email draft\nlet updateDraftActionResponse =\n CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateToRecipientsAction(\n CardService.newUpdateToRecipientsAction().addUpdateToRecipients([\n 'joe@example.com',\n 'wen@example.com',\n ]),\n )\n .build();\n\n// An UpdateDraftActionResponse that inserts a list of Cc recipients into an\n// email draft\nupdateDraftActionResponse =\n CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateCcRecipientsAction(\n CardService.newUpdateCcRecipientsAction().addUpdateCcRecipients([\n 'joe@example.com',\n 'wen@example.com',\n ]),\n )\n .build()\n\n // An UpdateDraftActionResponse that inserts a list of Bcc recipients\n // into an email draft\n .setUpdateCcRecipientsAction(\n CardService.newUpdateBccRecipientsAction().addUpdateBccRecipients([\n 'joe@example.com',\n 'wen@example.com',\n ]),\n );\n\n// An UpdateDraftActionResponse that inserts a subject line into an email draft\nupdateDraftActionResponse =\n CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftSubjectAction(\n CardService.newUpdateDraftSubjectAction().addUpdateSubject(\n 'example subject',\n ),\n )\n .build();\n\n// An UpdateDraftActionResponse that inserts non-editable content (a link in\n// this case) into an email draft.\nupdateDraftActionResponse =\n CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftBodyAction(\n CardService.newUpdateDraftBodyAction()\n .addUpdateContent(\n '<a href=\"https://www.google.com\">Google</a>',\n CardService.ContentType.IMMUTABLE_HTML,\n )\n .setUpdateType(CardService.UpdateDraftBodyType.IN_PLACE_INSERT),\n )\n .build();\n\n// An UpdateDraftActionResponse that inserts a link into an email draft. The\n// added content can be edited further.\nupdateDraftActionResponse =\n CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftBodyAction(\n CardService.newUpdateDraftBodyAction()\n .addUpdateContent(\n '<a href=\"https://www.google.com\">Google</a>',\n CardService.ContentType.MUTABLE_HTML,\n )\n .setUpdateType(CardService.UpdateDraftBodyType.IN_PLACE_INSERT),\n )\n .build();\n\n// An UpdateDraftActionResponse that inserts multiple values of different types.\n// The example action response inserts two lines next to each other in the email\n// draft, at the cursor position. Each line contains the content added by\n// {@link UpdateDraftActionResponseBuilder#addUpdateContent}.\nupdateDraftActionResponse =\n CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftBodyAction(\n CardService.newUpdateDraftBodyAction()\n .addUpdateContent(\n '<a href=\"https://www.google.com\">Google</a>',\n CardService.ContentType.MUTABLE_HTML,\n )\n .addUpdateContent(\n 'Above is a google link.', CardService.ContentType.PLAIN_TEXT)\n .setUpdateType(CardService.UpdateDraftBodyType.IN_PLACE_INSERT),\n )\n .build();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.134Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":93,"estimatedTokens":892}}1024{"id":"doc-html_service_restrictions_apps_script_google_for-70072a70","source":"documentation","title":"HTML Service: Restrictions | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/html/restrictions","text":"Example:\n```text\nfunction doGet() {\n var template = HtmlService.createTemplateFromFile('top');\n return template.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <body>\n <div>\n <a href=\"http://google.com\" target=\"_top\">Click Me!</a>\n </div>\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n </head>\n <body>\n <div>\n <a href=\"http://google.com\">Click Me!</a>\n </div>\n </body>\n</html>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.138Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":36,"estimatedTokens":131}}1025{"id":"doc-jdbc_apps_script_google_for_developers-5ef324d7","source":"documentation","title":"JDBC | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/jdbc","text":"Example:\n```text\n/**\n * Create a new database within a Cloud SQL instance.\n */\nfunction createDatabase() {\n try {\n const conn = Jdbc.getCloudSqlConnection(instanceUrl, root, rootPwd);\n conn.createStatement().execute(`CREATE DATABASE ${db}`);\n } catch (err) {\n // TODO(developer) - Handle exception from the API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n\n/**\n * Create a new user for your database with full privileges.\n */\nfunction createUser() {\n try {\n const conn = Jdbc.getCloudSqlConnection(dbUrl, root, rootPwd);\n\n const stmt = conn.prepareStatement(\"CREATE USER ? IDENTIFIED BY ?\");\n stmt.setString(1, user);\n stmt.setString(2, userPwd);\n stmt.execute();\n\n conn.createStatement().execute(`GRANT ALL ON \\`%\\`.* TO ${user}`);\n } catch (err) {\n // TODO(developer) - Handle exception from the API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n\n/**\n * Create a new table in the database.\n */\nfunction createTable() {\n try {\n const conn = Jdbc.getCloudSqlConnection(dbUrl, user, userPwd);\n conn\n .createStatement()\n .execute(\n \"CREATE TABLE entries \" +\n \"(guestName VARCHAR(255), content VARCHAR(255), \" +\n \"entryID INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(entryID));\",\n );\n } catch (err) {\n // TODO(developer) - Handle exception from the API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Write one row of data to a table.\n */\nfunction writeOneRecord() {\n try {\n const conn = Jdbc.getCloudSqlConnection(dbUrl, user, userPwd);\n\n const stmt = conn.prepareStatement(\n \"INSERT INTO entries \" + \"(guestName, content) values (?, ?)\",\n );\n stmt.setString(1, \"First Guest\");\n stmt.setString(2, \"Hello, world\");\n stmt.execute();\n } catch (err) {\n // TODO(developer) - Handle exception from the API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n\n/**\n * Write 500 rows of data to a table in a single batch.\n */\nfunction writeManyRecords() {\n try {\n const conn = Jdbc.getCloudSqlConnection(dbUrl, user, userPwd);\n conn.setAutoCommit(false);\n\n const start = new Date();\n const stmt = conn.prepareStatement(\n \"INSERT INTO entries \" + \"(guestName, content) values (?, ?)\",\n );\n for (let i = 0; i < 500; i++) {\n stmt.setString(1, `Name ${i}`);\n stmt.setString(2, `Hello, world ${i}`);\n stmt.addBatch();\n }\n\n const batch = stmt.executeBatch();\n conn.commit();\n conn.close();\n\n const end = new Date();\n console.log(\"Time elapsed: %sms for %s rows.\", end - start, batch.length);\n } catch (err) {\n // TODO(developer) - Handle exception from the API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n\n/**\n * Write 500 rows of data to a table in a single batch.\n * Recommended for faster writes\n */\nfunction writeManyRecordsUsingExecuteBatch() {\n try {\n const conn = Jdbc.getCloudSqlConnection(dbUrl, user, userPwd);\n conn.setAutoCommit(false);\n\n const start = new Date();\n const stmt = conn.prepareStatement(\n \"INSERT INTO entries \" + \"(guestName, content) values (?, ?)\",\n );\n const params = [];\n for (let i = 0; i < 500; i++) {\n params.push([`Name ${i}`, `Hello, world ${i}`]);\n }\n\n const batch = stmt.executeBatch(params);\n conn.commit();\n conn.close();\n\n const end = new Date();\n console.log(\"Time elapsed: %sms for %s rows.\", end - start, batch.length);\n } catch (err) {\n // TODO(developer) - Handle exception from the API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Read up to 1000 rows of data from the table and log them.\n */\nfunction readFromTable() {\n try {\n const conn = Jdbc.getCloudSqlConnection(dbUrl, user, userPwd);\n const start = new Date();\n const stmt = conn.createStatement();\n stmt.setMaxRows(1000);\n const results = stmt.executeQuery(\"SELECT * FROM entries\");\n const numCols = results.getMetaData().getColumnCount();\n\n while (results.next()) {\n let rowString = \"\";\n for (let col = 0; col < numCols; col++) {\n rowString += `${results.getString(col + 1)}\\t`;\n }\n console.log(rowString);\n }\n\n results.close();\n stmt.close();\n\n const end = new Date();\n console.log(\"Time elapsed: %sms\", end - start);\n } catch (err) {\n // TODO(developer) - Handle exception from the API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n\n/**\n * Read up to 1000 rows of data from the table and log them.\n * Recommended for faster reads\n */\nfunction readFromTableUsingGetRows() {\n try {\n const conn = Jdbc.getCloudSqlConnection(dbUrl, user, userPwd);\n const start = new Date();\n const stmt = conn.createStatement();\n stmt.setMaxRows(1000);\n const results = stmt.executeQuery(\"SELECT * FROM entries\");\n const numCols = results.getMetaData().getColumnCount();\n const getRowArgs = [];\n for (let col = 0; col < numCols; col++) {\n getRowArgs.push(`getString(${col + 1})`);\n }\n const rows = results.getRows(getRowArgs.join(\",\"));\n for (let i = 0; i < rows.length; i++) {\n console.log(rows[i].join(\"\\t\"));\n }\n\n results.close();\n stmt.close();\n\n const end = new Date();\n console.log(\"Time elapsed: %sms\", end - start);\n } catch (err) {\n // TODO(developer) - Handle exception from the API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.140Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":203,"estimatedTokens":1368}}1026{"id":"doc-class_cellimage_apps_script_google_for_developer-89ba2782","source":"documentation","title":"Class CellImage | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/cell-image","text":"Example:\n```text\nconst range = SpreadsheetApp.getActiveSpreadsheet().getRange(\"Sheet1!A1\");\nconst value = range.getValue();\nif (value.valueType == SpreadsheetApp.ValueType.IMAGE) {\n console.log(value.getContentUrl());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst range = ss.getRange(\"Sheet1!A1\");\nconst value = range.getValue();\nif (value.valueType == SpreadsheetApp.ValueType.IMAGE) {\n const newImage =\n value.toBuilder()\n .setSourceUrl(\n 'https://www.gstatic.com/images/branding/productlogos/apps_script/v10/web-64dp/logo_apps_script_color_1x_web_64dp.png',\n )\n .build();\n const newRange = ss.getRange(\"Sheet1!A2\");\n newRange.setValue(newImage);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.141Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":27,"estimatedTokens":189}}1027{"id":"doc-class_gradientcondition_apps_script_google_for_d-5d9c90de","source":"documentation","title":"Class GradientCondition | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/gradient-condition","text":"Example:\n```text\n// Logs all the information inside gradient conditional format rules on a sheet.\n// The below snippet assumes all colors have ColorType.RGB.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rules = sheet.getConditionalFormatRules();\nfor (let i = 0; i < rules.length; i++) {\n const gradient = rules[i].getGradientCondition();\n\n const minColor = gradient.getMinColorObject().asRgbColor().asHexString();\n const minType = gradient.getMinType();\n const minValue = gradient.getMinValue();\n const midColor = gradient.getMidColorObject().asRgbColor().asHexString();\n const midType = gradient.getMidType();\n const midValue = gradient.getMidValue();\n const maxColor = gradient.getMaxColorObject().asRgbColor().asHexString();\n const maxType = gradient.getMaxType();\n const maxValue = gradient.getMaxValue();\n\n Logger.log(`The conditional format gradient information for rule ${i}:\n MinColor ${minColor}, MinType ${minType}, MinValue ${minValue},\n MidColor ${midColor}, MidType ${midType}, MidValue ${midValue},\n MaxColor ${maxColor}, MaxType ${maxType}, MaxValue ${maxValue}`);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.143Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":27,"estimatedTokens":282}}1028{"id":"doc-class_booleancondition_apps_script_google_for_de-0ca81a67","source":"documentation","title":"Class BooleanCondition | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/boolean-condition","text":"Example:\n```text\n// Logs the boolean condition background color for each conditional format rule\n// on a sheet.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rules = sheet.getConditionalFormatRules();\nfor (const rule of rules) {\n const color = rule.getBooleanCondition().getBackgroundObject();\n Logger.log(`Background color: ${color.asRgbColor().asHexString()}`);\n}\n```\n\nExample:\n```text\n// Logs the boolean condition font weight for each conditional format rule on a\n// sheet.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rules = sheet.getConditionalFormatRules();\nfor (const rule of rules) {\n const bold = rule.getBooleanCondition().getBold();\n Logger.log(`Bold: ${bold}`);\n}\n```\n\nExample:\n```text\n// Log information about the conditional formats on the active sheet that use\n// boolean conditions.\n\nconst formats = SpreadsheetApp.getActiveSheet.getConditionalFormats();\nSpreadsheetApp.getActiveSheet.getConditionalFormats().forEach((format) => {\n const booleanCondition = format.getBooleanCondition();\n if (booleanCondition) {\n const criteria = booleanCondition.getCriteriaType();\n const args = booleanCondition.getCriteriaValues();\n Logger.log(`The conditional format rule is ${criteria} ${args}`);\n }\n});\n```\n\nExample:\n```text\n// Logs the boolean condition font color for each conditional format rule on a\n// sheet.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rules = sheet.getConditionalFormatRules();\nfor (const rule of rules) {\n const color = rule.getBooleanCondition().getFontColorObject();\n Logger.log(`Font color: ${color.asRgbColor().asHexString()}`);\n}\n```\n\nExample:\n```text\n// Logs the boolean condition font style for each conditional format rule on a\n// sheet.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rules = sheet.getConditionalFormatRules();\nfor (const rule of rules) {\n const italic = rule.getBooleanCondition().getItalic();\n Logger.log(`Italic: ${italic}`);\n}\n```\n\nExample:\n```text\n// Logs the boolean condition strikethrough setting for each conditional format\n// rule on a sheet.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rules = sheet.getConditionalFormatRules();\nfor (const rule of rules) {\n const strikethrough = rule.getBooleanCondition().getStrikethrough();\n Logger.log(`Strikethrough: ${strikethrough}`);\n}\n```\n\nExample:\n```text\n// Logs the boolean condition underline setting for each conditional format rule\n// on a sheet.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rules = sheet.getConditionalFormatRules();\nfor (const rule of rules) {\n const underline = rule.getBooleanCondition().getUnderline();\n Logger.log(`Underline: ${underline}`);\n}\n```\n\nExample:\n```text\n// Logs the boolean condition background color for each conditional format rule\n// on a sheet.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rules = sheet.getConditionalFormatRules();\nfor (const rule of rules) {\n const color = rule.getBooleanCondition().getBackground();\n Logger.log(`Background color: ${color}`);\n}\n```\n\nExample:\n```text\n// Logs the boolean condition font color for each conditional format rule on a\n// sheet.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rules = sheet.getConditionalFormatRules();\nfor (const rule of rules) {\n Logger.log(`Font color: ${rule.getBooleanCondition().getFontColor()}`);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.145Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":112,"estimatedTokens":832}}1029{"id":"doc-class_spreadsheetapp_apps_script_google_for_deve-e4e31335","source":"documentation","title":"Class SpreadsheetApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app","text":"Example:\n```text\n// The code below creates a new spreadsheet \"Finances\" and logs the URL for it\nconst ssNew = SpreadsheetApp.create('Finances');\nLogger.log(ssNew.getUrl());\n```\n\nExample:\n```text\n// The code below creates a new spreadsheet \"Finances\" with 50 rows and 5\n// columns and logs the URL for it\nconst ssNew = SpreadsheetApp.create('Finances', 50, 5);\nLogger.log(ssNew.getUrl());\n```\n\nExample:\n```text\n// Turns data execution on for all types of data sources.\nSpreadsheetApp.enableAllDataSourcesExecution();\n\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets the first data source sheet in the spreadsheet and refreshes the data.\nss.getDataSourceSheets()[0].refreshData();\n```\n\nExample:\n```text\n// Turns data execution on for BigQuery data sources.\nSpreadsheetApp.enableBigQueryExecution();\n\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets the first data source sheet in the spreadsheet and refreshes the\n// BigQuery data.\nss.getDataSourceSheets()[0].refreshData();\n```\n\nExample:\n```text\n// Turns data execution on for Looker data sources.\nSpreadsheetApp.enableLookerExecution();\n\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets the first data source sheet in the spreadsheet and refreshes the\n// associated Looker data.\nss.getDataSourceSheets()[0].refreshData();\n```\n\nExample:\n```text\n// The code below changes the background color of cells A1 and B1 twenty times.\n// You should be able to see the updates live in the spreadsheet. If flush() is\n// not called, the updates may be applied live or may all be applied at once\n// when the script completes.\nfunction colors() {\n const sheet = SpreadsheetApp.getActiveSheet();\n for (let i = 0; i < 20; i++) {\n if (i % 2 === 0) {\n sheet.getRange('A1').setBackground('green');\n sheet.getRange('B1').setBackground('red');\n } else {\n sheet.getRange('A1').setBackground('red');\n sheet.getRange('B1').setBackground('green');\n }\n SpreadsheetApp.flush();\n }\n}\n```\n\nExample:\n```text\n// The code below logs the URL for the active spreadsheet.\nLogger.log(SpreadsheetApp.getActive().getUrl());\n```\n\nExample:\n```text\n// The code below logs the background color for the active range.\nconst colorObject = SpreadsheetApp.getActiveRange().getBackgroundObject();\n// Assume the color has ColorType.RGB.\nLogger.log(colorObject.asRgbColor().asHexString());\n```\n\nExample:\n```text\n// Returns the list of active ranges.\nconst rangeList = SpreadsheetApp.getActiveRangeList();\n```\n\nExample:\n```text\n// The code below logs the name of the active sheet.\nLogger.log(SpreadsheetApp.getActiveSheet().getName());\n```\n\nExample:\n```text\n// The code below logs the URL for the active spreadsheet.\nLogger.log(SpreadsheetApp.getActiveSpreadsheet().getUrl());\n```\n\nExample:\n```text\n// Returns the current highlighted cell in the one of the active ranges.\nconst currentCell = SpreadsheetApp.getCurrentCell();\n```\n\nExample:\n```text\nconst selection = SpreadsheetApp.getSelection();\nconst currentCell = selection.getCurrentCell();\n```\n\nExample:\n```text\n// Add a custom menu to the active spreadsheet, including a separator and a\n// sub-menu.\nfunction onOpen(e) {\n SpreadsheetApp.getUi()\n .createMenu('My Menu')\n .addItem('My menu item', 'myFunction')\n .addSeparator()\n .addSubMenu(\n SpreadsheetApp.getUi()\n .createMenu('My sub-menu')\n .addItem('One sub-menu item', 'mySecondFunction')\n .addItem('Another sub-menu item', 'myThirdFunction'),\n )\n .addToUi();\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets cell A1 on Sheet1.\nconst range = sheet.getRange('A1');\n\n// Builds an image using a source URL.\nconst cellImage =\n SpreadsheetApp.newCellImage()\n .setSourceUrl(\n 'https://www.gstatic.com/images/branding/productlogos/apps_script/v10/web-64dp/logo_apps_script_color_1x_web_64dp.png',\n )\n .build();\n\n// Sets the image in cell A1.\nrange.setValue(cellImage);\n```\n\nExample:\n```text\nconst rgbColor = SpreadsheetApp.newColor().setRgbColor('#FF0000').build();\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes all cells in range\n// A1:B3 to turn red if they contain a number between 1 and 10.\nconst sheet = SpreadsheetApp.getActive().getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenNumberBetween(1, 10)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Enables BigQuery.\nSpreadsheetApp.enableBigQueryExecution();\n\n// Builds a data source specification.\n// TODO (developer): Update the project ID to your own Google Cloud project ID.\nconst dataSourceSpec = SpreadsheetApp.newDataSourceSpec()\n .asBigQuery()\n .setProjectId('project-id-1')\n .setTableProjectId('bigquery-public-data')\n .setDatasetId('ncaa_basketball')\n .setTableId('mbb_historical_teams_games')\n .build();\n\n// Adds the data source and its data to the spreadsheet.\nss.insertDataSourceSheet(dataSourceSpec);\n```\n\nExample:\n```text\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireNumberBetween(1, 100)\n .setAllowInvalid(false)\n .setHelpText('Number must be between 1 and 100.')\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Sets the range to A1:D20.\nconst range = sheet.getRange('A1:D20');\n\n// Creates a filter and applies it to the specified range.\nrange.createFilter();\n\n// Gets the current filter for the range and creates filter criteria that only\n// shows cells that aren't empty.\nconst filter = range.getFilter();\nconst criteria = SpreadsheetApp.newFilterCriteria().whenCellNotEmpty().build();\n\n// Sets the criteria to column C.\nfilter.setColumnFilterCriteria(3, criteria);\n```\n\nExample:\n```text\n// Sets cell A1 to have the text \"Hello world\", with \"Hello\" bolded.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst bold = SpreadsheetApp.newTextStyle().setBold(true).build();\nconst value = SpreadsheetApp.newRichTextValue()\n .setText('Hello world')\n .setTextStyle(0, 5, bold)\n .build();\ncell.setRichTextValue(value);\n```\n\nExample:\n```text\n// Sets range A1:B3 to have red, size 22, bolded, underlined text.\nconst range = SpreadsheetApp.getActive().getRange('A1:B3');\nconst style = SpreadsheetApp.newTextStyle()\n .setForegroundColor('red')\n .setFontSize(22)\n .setBold(true)\n .setUnderline(true)\n .build();\nrange.setTextStyle(style);\n```\n\nExample:\n```text\n// Get any starred spreadsheets from Google Drive, then open the spreadsheets\n// and log the name of the first sheet within each spreadsheet.\nconst files = DriveApp.searchFiles(\n `starred = true and mimeType = \"${MimeType.GOOGLE_SHEETS}\"`,\n);\nwhile (files.hasNext()) {\n const spreadsheet = SpreadsheetApp.open(files.next());\n const sheet = spreadsheet.getSheets()[0];\n Logger.log(sheet.getName());\n}\n```\n\nExample:\n```text\n// The code below opens a spreadsheet using its ID and logs the name for it.\n// Note that the spreadsheet is NOT physically opened on the client side.\n// It is opened on the server only (for modification by the script).\nconst ss = SpreadsheetApp.openById('abc1234567');\nLogger.log(ss.getName());\n```\n\nExample:\n```text\n// Opens a spreadsheet by its URL and logs its name.\n// Note that the spreadsheet doesn't physically open on the client side.\n// It opens on the server only (for modification by the script).\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc1234567/edit',\n);\nconsole.log(ss.getName());\n```\n\nExample:\n```text\n// The code below sets range C1:D4 in the first sheet as the active range.\nconst range =\n SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('C1:D4');\nSpreadsheetApp.setActiveRange(range);\n\nconst selection = SpreadsheetApp.getSelection();\n// Current cell: C1\nconst currentCell = selection.getCurrentCell();\n// Active Range: C1:D4\nconst activeRange = selection.getActiveRange();\n```\n\nExample:\n```text\n// The code below sets ranges [D4, B2:C4] in the active sheet as the active\n// ranges.\nconst rangeList = SpreadsheetApp.getActiveSheet().getRanges(['D4', 'B2:C4']);\nSpreadsheetApp.setActiveRangeList(rangeList);\n\nconst selection = SpreadsheetApp.getSelection();\n// Current cell: B2\nconst currentCell = selection.getCurrentCell();\n// Active range: B2:C4\nconst activeRange = selection.getActiveRange();\n// Active range list: [D4, B2:C4]\nconst activeRangeList = selection.getActiveRangeList();\n```\n\nExample:\n```text\n// The code below makes the 2nd sheet active in the active spreadsheet.\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nSpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[1]);\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nconst firstSheet = spreadsheet.getSheets()[0];\nconst secondSheet = spreadsheet.getSheets()[1];\n// Set the first sheet as the active sheet and select the range D4:F4.\nspreadsheet.setActiveSheet(firstSheet).getRange('D4:F4').activate();\n\n// Switch to the second sheet to do some work.\nspreadsheet.setActiveSheet(secondSheet);\n// Switch back to first sheet, and restore its selection.\nspreadsheet.setActiveSheet(firstSheet, true);\n\n// The selection of first sheet is restored, and it logs D4:F4\nconst range = spreadsheet.getActiveSheet().getSelection().getActiveRange();\nLogger.log(range.getA1Notation());\n```\n\nExample:\n```text\n// The code below makes the spreadsheet with key \"1234567890\" the active\n// spreadsheet\nconst ss = SpreadsheetApp.openById('1234567890');\nSpreadsheetApp.setActiveSpreadsheet(ss);\n```\n\nExample:\n```text\n// The code below sets the cell B5 in the first sheet as the current cell.\nconst cell =\n SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('B5');\nSpreadsheetApp.setCurrentCell(cell);\n\nconst selection = SpreadsheetApp.getSelection();\n// Current cell: B5\nconst currentCell = selection.getCurrentCell();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.147Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":31,"totalLines":393,"estimatedTokens":3018}}1030{"id":"doc-collect_and_process_information_from_google_chat-c9fc1862","source":"documentation","title":"Collect and process information from Google Chat users | Google for Developers","url":"https://developers.google.com/chat/ui/read-form-data","text":"Example:\n```text\n/**\n * The section of the contact card that contains the form input widgets. Used in a dialog and card message.\n * To add and preview widgets, use the Card Builder: https://addons.gsuite.google.com/uikit/builder\n */\nconst CONTACT_FORM_WIDGETS = [\n {\n \"textInput\": {\n \"name\": \"contactName\",\n \"label\": \"First and last name\",\n \"type\": \"SINGLE_LINE\"\n }\n },\n {\n \"dateTimePicker\": {\n \"name\": \"contactBirthdate\",\n \"label\": \"Birthdate\",\n \"type\": \"DATE_ONLY\"\n }\n },\n {\n \"selectionInput\": {\n \"name\": \"contactType\",\n \"label\": \"Contact type\",\n \"type\": \"RADIO_BUTTON\",\n \"items\": [\n {\n \"text\": \"Work\",\n \"value\": \"Work\",\n \"selected\": false\n },\n {\n \"text\": \"Personal\",\n \"value\": \"Personal\",\n \"selected\": false\n }\n ]\n }\n }\n];\n```\n\nExample:\n```text\n# The section of the contact card that contains the form input widgets. Used in a dialog and card message.\n# To add and preview widgets, use the Card Builder: https://addons.gsuite.google.com/uikit/builder\nCONTACT_FORM_WIDGETS = [\n {\n \"textInput\": {\n \"name\": \"contactName\",\n \"label\": \"First and last name\",\n \"type\": \"SINGLE_LINE\"\n }\n },\n {\n \"dateTimePicker\": {\n \"name\": \"contactBirthdate\",\n \"label\": \"Birthdate\",\n \"type\": \"DATE_ONLY\"\n }\n },\n {\n \"selectionInput\": {\n \"name\": \"contactType\",\n \"label\": \"Contact type\",\n \"type\": \"RADIO_BUTTON\",\n \"items\": [\n {\n \"text\": \"Work\",\n \"value\": \"Work\",\n \"selected\": False\n },\n {\n \"text\": \"Personal\",\n \"value\": \"Personal\",\n \"selected\": False\n }\n ]\n }\n }\n]\n```\n\nExample:\n```text\n// The section of the contact card that contains the form input widgets. Used in a dialog and card message.\n// To add and preview widgets, use the Card Builder: https://addons.gsuite.google.com/uikit/builder\nfinal static private List<GoogleAppsCardV1Widget> CONTACT_FORM_WIDGETS = List.of(\n new GoogleAppsCardV1Widget().setTextInput(new GoogleAppsCardV1TextInput()\n .setName(\"contactName\")\n .setLabel(\"First and last name\")\n .setType(\"SINGLE_LINE\")),\n new GoogleAppsCardV1Widget().setDateTimePicker(new GoogleAppsCardV1DateTimePicker()\n .setName(\"contactBirthdate\")\n .setLabel(\"Birthdate\")\n .setType(\"DATE_ONLY\")),\n new GoogleAppsCardV1Widget().setSelectionInput(new GoogleAppsCardV1SelectionInput()\n .setName(\"contactType\")\n .setLabel(\"Contact type\")\n .setType(\"RADIO_BUTTON\")\n .setItems(List.of(\n new GoogleAppsCardV1SelectionItem()\n .setText(\"Work\")\n .setValue(\"Work\")\n .setSelected(false),\n new GoogleAppsCardV1SelectionItem()\n .setText(\"Personal\")\n .setValue(\"Personal\")\n .setSelected(false)))));\n```\n\nExample:\n```text\n{\n \"type\": \"CARD_CLICKED\",\n \"common\": { \"formInputs\": {\n \"contactName\": { \"stringInputs\": {\n \"value\": [\"Kai 0\"]\n }},\n \"contactBirthdate\": { \"dateInput\": {\n \"msSinceEpoch\": 1000425600000\n }},\n \"contactType\": { \"stringInputs\": {\n \"value\": [\"Personal\"]\n }}\n }}\n}\n```\n\nExample:\n```text\n{\n \"type\": \"CARD_CLICKED\",\n \"common\": { \"formInputs\": {\n \"contactName\": { \"\": { \"stringInputs\": {\n \"value\": [\"Kai 0\"]\n }}},\n \"contactBirthdate\": { \"\": { \"dateInput\": {\n \"msSinceEpoch\": 1000425600000\n }}},\n \"contactType\": { \"\": { \"stringInputs\": {\n \"value\": [\"Personal\"]\n }}}\n }}\n}\n```\n\nExample:\n```text\n{\n \"type\": \"SUBMIT_FORM\",\n \"commonEventObject\": { \"formInputs\": {\n \"contactName\": { \"stringInputs\": {\n \"value\": [\"Kai 0\"]\n }},\n \"contactBirthdate\": { \"dateInput\": {\n \"msSinceEpoch\": 1000425600000\n }},\n \"contactType\": { \"stringInputs\": {\n \"value\": [\"Personal\"]\n }}\n }}\n}\n```\n\nExample:\n```text\n{\n \"type\": \"SUBMIT_FORM\",\n \"commonEventObject\": { \"formInputs\": {\n \"contactName\": { \"\": { \"stringInputs\": {\n \"value\": [\"Kai 0\"]\n }}},\n \"contactBirthdate\": { \"\": { \"dateInput\": {\n \"msSinceEpoch\": 1000425600000\n }}},\n \"contactType\": { \"\": { \"stringInputs\": {\n \"value\": [\"Personal\"]\n }}}\n }}\n}\n```\n\nExample:\n```text\nbuttonList: { buttons: [{\n text: \"Submit\",\n onClick: { action: {\n function: \"submitForm\",\n parameters: [{\n key: \"contactName\", value: name }, {\n key: \"contactBirthdate\", value: birthdate }, {\n key: \"contactType\", value: type\n }]\n }}\n}]}\n```\n\nExample:\n```text\n'buttonList': { 'buttons': [{\n 'text': \"Submit\",\n 'onClick': { 'action': {\n 'function': \"submitForm\",\n 'parameters': [{\n 'key': \"contactName\", 'value': name }, {\n 'key': \"contactBirthdate\", 'value': birthdate }, {\n 'key': \"contactType\", 'value': type\n }]\n }}\n}]}\n```\n\nExample:\n```text\nnew GoogleAppsCardV1Widget().setButtonList(new GoogleAppsCardV1ButtonList().setButtons(List.of(new GoogleAppsCardV1Button()\n .setText(\"Submit\")\n .setOnClick(new GoogleAppsCardV1OnClick().setAction(new GoogleAppsCardV1Action()\n .setFunction(\"submitForm\")\n .setParameters(List.of(\n new GoogleAppsCardV1ActionParameter().setKey(\"contactName\").setValue(name),\n new GoogleAppsCardV1ActionParameter().setKey(\"contactBirthdate\").setValue(birthdate),\n new GoogleAppsCardV1ActionParameter().setKey(\"contactType\").setValue(type))))))))));\n```\n\nExample:\n```text\nconst contactName = event.common.parameters[\"contactName\"];\n// Checks to make sure the user entered a contact name.\n// If no name value detected, returns an error message.\nconst errorMessage = \"Don't forget to name your new contact!\";\nif (!contactName && event.dialogEventType === \"SUBMIT_DIALOG\") {\n return { actionResponse: {\n type: \"DIALOG\",\n dialogAction: { actionStatus: {\n statusCode: \"INVALID_ARGUMENT\",\n userFacingMessage: errorMessage\n }}\n }};\n}\n```\n\nExample:\n```text\ncontact_name = event.get('common').get('parameters')[\"contactName\"]\n# Checks to make sure the user entered a contact name.\n# If no name value detected, returns an error message.\nerror_message = \"Don't forget to name your new contact!\"\nif contact_name == \"\" and \"SUBMIT_DIALOG\" == event.get('dialogEventType'):\n return { 'actionResponse': {\n 'type': \"DIALOG\",\n 'dialogAction': { 'actionStatus': {\n 'statusCode': \"INVALID_ARGUMENT\",\n 'userFacingMessage': error_message\n }}\n }}\n```\n\nExample:\n```text\nString contactName = event.at(\"/common/parameters/contactName\").asText();\n// Checks to make sure the user entered a contact name.\n// If no name value detected, returns an error message.\nString errorMessage = \"Don't forget to name your new contact!\";\nif (contactName.isEmpty() && event.at(\"/dialogEventType\") != null && \"SUBMIT_DIALOG\".equals(event.at(\"/dialogEventType\").asText())) {\n return new Message().setActionResponse(new ActionResponse()\n .setType(\"DIALOG\")\n .setDialogAction(new DialogAction().setActionStatus(new ActionStatus()\n .setStatusCode(\"INVALID_ARGUMENT\")\n .setUserFacingMessage(errorMessage))));\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.150Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":275,"estimatedTokens":1768}}1031{"id":"doc-class_gmailmessage_apps_script_google_for_develo-631b7197","source":"documentation","title":"Class GmailMessage | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/gmail/gmail-message","text":"Example:\n```text\n// Create a draft reply to the original message with an acknowledgment.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.createDraftReply('Got your message');\n```\n\nExample:\n```text\n// Create a draft response with an HTML text body.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.createDraftReply('incapable of HTML', {\n htmlBody: '<b>some HTML body text</b>',\n cc: 'another@example.com',\n});\n```\n\nExample:\n```text\n// Create a draft response to all recipients (except those bcc'd) with an\n// acknowledgment.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.createDraftReplyAll('Got your message');\n```\n\nExample:\n```text\n// Create a draft response to all recipients (except those bcc'd) using an HTML\n// text body.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.createDraftReplyAll('incapable of HTML', {\n htmlBody: '<b>some HTML body text</b>',\n cc: 'another@example.com',\n});\n```\n\nExample:\n```text\n// Forward first message of first inbox thread to recipient1 & recipient2,\n// both @example.com\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.forward('recipient1@example.com,recipient2@example.com');\n```\n\nExample:\n```text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.forward('recipient1@example.com,recipient2@example.com', {\n cc: 'myboss@example.com',\n bcc: 'mybosses-boss@example.com,vp@example.com',\n});\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getBcc()); // Log bcc'd addresses\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getBody()); // Log contents of the body\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getCc()); // Log cc'd addresses\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getDate()); // Log date and time of the message\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getFrom()); // Log from address of the message\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox.\nconst message = thread.getMessages()[0]; // Get the first message.\nLogger.log(\n message.getHeader('Message-ID')); // Logs the Message-ID RFC 2822 header.\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nconst id = message.getId();\nconst messageById = GmailApp.getMessageById(id);\nLogger.log(\n message.getSubject() === messageById.getMessage()); // Always logs true\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getPlainBody()); // Log contents of the body\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getReplyTo()); // Logs reply-to address\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getSubject()); // Log subject line\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(\n message.getThread().getFirstMessageSubject() ===\n thread.getFirstMessageSubject(),\n); // Always logs true\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getTo()); // Log the recipient of message\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is draft? ${message.isDraft()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is a chat? ${message.isInChats()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is in inbox? ${message.isInInbox()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getPriorityInboxThreads(\n 0, 1)[0]; // Get first thread in priority inbox\nconst messages = thread.getMessages();\nfor (let i = 0; i < messages.length; i++) {\n // At least one of the messages is in priority inbox\n Logger.log(`is in priority inbox? ${messages[i].isInPriorityInbox()}`);\n}\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is in the trash? ${message.isInTrash()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is starred? ${message.isStarred()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is unread? ${message.isUnread()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nmessage.markRead(); // Mark as read\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nmessage.markUnread(); // Mark as unread\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nmessage.moveToTrash(); // Move message to trash\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\n// .. Do bunch of stuff here\nmessage.refresh(); // Make sure it's up to date\n// Do more stuff to message\n```\n\nExample:\n```text\n// Respond to author of message with acknowledgment\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.reply('Got your message');\n```\n\nExample:\n```text\n// Respond with HTML body text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.reply('incapable of HTML', {\n htmlBody: '<b>some HTML body text</b>',\n noReply: true,\n});\n```\n\nExample:\n```text\n// Respond to all recipients (except bcc'd) of last email in thread with\n// acknowledgment\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.replyAll('Got your message');\n```\n\nExample:\n```text\n// Respond with HTML body text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.replyAll('incapable of HTML', {\n htmlBody: '<b>some HTML body text</b>',\n noReply: true,\n});\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nmessage.star(); // Star the message\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nmessage.unstar(); // Unstar the message\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.160Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":35,"totalLines":287,"estimatedTokens":2252}}1032{"id":"doc-class_file_apps_script_google_for_developers-2a8d5715","source":"documentation","title":"Class File | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/drive/file","text":"Example:\n```text\n// Trash every untitled spreadsheet that hasn't been updated in a week.\nconst files = DriveApp.getFilesByName('Untitled spreadsheet');\nwhile (files.hasNext()) {\n const file = files.next();\n if (new Date() - file.getLastUpdated() > 7 * 24 * 60 * 60 * 1000) {\n file.setTrashed(true);\n }\n}\n```\n\nExample:\n```text\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files\nwhile (files.hasNext()) {\n const file = files.next();\n file.addCommenter('hello@example.com');\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Adds the active user as a commenter.\nwhile (files.hasNext()) {\n const file = files.next();\n file.addCommenter(Session.getActiveUser());\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\nwhile (files.hasNext()) {\n const file = files.next();\n // TODO(developer): Replace 'cloudysanfrancisco@gmail.com' and\n // 'baklavainthebalkans@gmail.com' with the email addresses to add as\n // commenters.\n const emails = [\n 'cloudysanfrancisco@gmail.com',\n 'baklavainthebalkans@gmail.com',\n ];\n console.log(file.addCommenters(emails));\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files and logs the download URLs to the console.\nwhile (files.hasNext()) {\n const file = files.next();\n console.log(file.getDownloadUrl());\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files.\nwhile (files.hasNext()) {\n const file = files.next();\n\n // Adds the email addresses in the array as editors of each file.\n // TODO(developer): Replace 'cloudysanfrancisco@gmail.com'\n // and 'baklavainthebalkans@gmail.com' with valid email addresses.\n file.addEditors([\n 'cloudysanfrancisco@gmail.com',\n 'baklavainthebalkans@gmail.com',\n ]);\n\n // Gets a list of the file editors.\n const editors = file.getEditors();\n\n // For each file, logs the editors' email addresses to the console.\n for (const editor of editors) {\n console.log(editor.getEmail());\n }\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files and logs the MIME type to the console.\nwhile (files.hasNext()) {\n const file = files.next();\n console.log(file.getMimeType());\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files and logs the names of the file owners to the console.\nwhile (files.hasNext()) {\n const file = files.next();\n console.log(file.getOwner().getName());\n}\n```\n\nExample:\n```text\n// The ID of the file for which to make a shortcut and the ID of\n// the folder to which you want to add the shortcut.\n// TODO(developer): Replace the file and folder IDs with your IDs.\nconst fileId = 'abc123456';\nconst folderId = 'xyz987654';\n\n// Gets the folder to add the shortcut to.\nconst folder = DriveApp.getFolderById(folderId);\n\n// Creates a shortcut of the file and moves it to the specified folder.\nconst shortcut = DriveApp.createShortcut(fileId).moveTo(folder);\n\n// Logs the target ID of the shortcut.\nconsole.log(`${shortcut.getName()}=${shortcut.getTargetId()}`);\n```\n\nExample:\n```text\n// The ID of the file for which to make a shortcut and the ID of\n// the folder to which you want to add the shortcut.\n// TODO(developer): Replace the file and folder IDs with your IDs.\nconst fileId = 'abc123456';\nconst folderId = 'xyz987654';\n\n// Gets the folder to add the shortcut to.\nconst folder = DriveApp.getFolderById(folderId);\n\n// Creates a shortcut of the file and moves it to the specified folder.\nconst shortcut = DriveApp.createShortcut(fileId).moveTo(folder);\n\n// Logs the MIME type of the file that the shortcut points to.\nconsole.log(`MIME type of the shortcut: ${shortcut.getTargetMimeType()}`);\n```\n\nExample:\n```text\n// Gets a file by its ID.\n// TODO(developer): Replace 'abc123456' with your file ID.\nconst file = DriveApp.getFileById('abc123456');\n\n// If the file is a shortcut, returns the resource key of the file that it\n// points to.\nconsole.log(file.getTargetResourceKey());\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files.\nwhile (files.hasNext()) {\n const file = files.next();\n\n // Logs the thumbnail image for each file to the console as a blob,\n // or null if no thumbnail exists.\n console.log(file.getThumbnail());\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files.\nwhile (files.hasNext()) {\n const file = files.next();\n\n // For each file, logs the viewers' email addresses to the console.\n const viewers = file.getViewers();\n for (const viewer of viewers) {\n console.log(viewer.getEmail());\n }\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files.\nwhile (files.hasNext()) {\n const file = files.next();\n\n // Creates a copy of each file and logs the file name to the console.\n console.log(file.makeCopy().getName());\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files.\nwhile (files.hasNext()) {\n const file = files.next();\n\n // Creates a copy of each file and adds it to the specified folder.\n // TODO(developer): Replace the folder ID with your own.\n const destination = DriveApp.getFolderById('123456abcxyz');\n const copiedFile = file.makeCopy(destination);\n\n // Logs the file names to the console.\n console.log(copiedFile.getName());\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files.\nwhile (files.hasNext()) {\n const file = files.next();\n\n // Creates a copy of each file and sets the name to 'Test-Copy.'\n const filename = file.makeCopy('Test-Copy');\n\n // Logs the copied file's name to the console.\n console.log(filename.getName());\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files.\nwhile (files.hasNext()) {\n const file = files.next();\n\n // Creates a copy of each file, sets the file name, and adds the copied file\n // to the specified folder.\n // TODO(developer): Replace the folder ID with your own.\n const destination = DriveApp.getFolderById('123456abcxyz');\n const copiedFile = file.makeCopy('Test-Copy', destination);\n\n // Logs the file names to the console.\n console.log(copiedFile.getName());\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files.\nwhile (files.hasNext()) {\n const file = files.next();\n\n // Removes the given user from the list of commenters for each file.\n // TODO(developer): Replace the email with the email of the user you want to\n // remove.\n file.removeCommenter('cloudysanfrancisco@gmail.com');\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace the file name with your own.\nconst files = DriveApp.getFilesByName('Test');\n\n// Loops through the files.\nwhile (files.hasNext()) {\n const file = files.next();\n\n // Removes the given user from the list of commenters for each file.\n console.log(file.removeCommenter(Session.getActiveUser()));\n}\n```\n\nExample:\n```text\n// Creates a text file with the content 'Hello, world!'\nconst file = DriveApp.createFile('New Text File', 'Hello, world!');\n\n// Logs the content of the text file to the console.\nconsole.log(file.getBlob().getDataAsString());\n\n// Updates the content of the text file to 'Updated text!'\nfile.setContent('Updated text!');\n\n// Logs content of the text file to the console.\nconsole.log(file.getBlob().getDataAsString());\n```\n\nExample:\n```text\n// Creates a folder that anyone on the Internet can read from and write to.\n// (Domain administrators can prohibit this setting for users of a Google\n// Workspace domain.)\nconst folder = DriveApp.createFolder('Shared Folder');\nfolder.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.163Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":21,"totalLines":334,"estimatedTokens":2365}}1033{"id":"doc-class_cellimagebuilder_apps_script_google_for_de-244e30af","source":"documentation","title":"Class CellImageBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/cell-image-builder","text":"Example:\n```text\nconst range = SpreadsheetApp.getActiveSpreadsheet().getRange(\"Sheet1!A1\");\nconst value = range.getValue();\nif (value.valueType == SpreadsheetApp.ValueType.IMAGE) {\n console.log(value.getContentUrl());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst range = ss.getRange(\"Sheet1!A1\");\nconst value = range.getValue();\nif (value.valueType == SpreadsheetApp.ValueType.IMAGE) {\n const newImage =\n value.toBuilder()\n .setSourceUrl(\n 'https://www.gstatic.com/images/branding/productlogos/apps_script/v10/web-64dp/logo_apps_script_color_1x_web_64dp.png',\n )\n .build();\n const newRange = ss.getRange(\"Sheet1!A2\");\n newRange.setValue(newImage);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.164Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":27,"estimatedTokens":189}}1034{"id":"doc-class_folder_apps_script_google_for_developers-a56f62ee","source":"documentation","title":"Class Folder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/drive/folder","text":"Example:\n```text\n// Log the name of every folder in the user's Drive.\nconst folders = DriveApp.getFolders();\nwhile (folders.hasNext()) {\n const folder = folders.next();\n Logger.log(folder.getName());\n}\n```\n\nExample:\n```text\n// Create a text file with the content \"Hello, world!\"\nDriveApp.getRootFolder().createFile('New Text File', 'Hello, world!');\n```\n\nExample:\n```text\n// Create an HTML file with the content \"Hello, world!\"\nDriveApp.getRootFolder().createFile('New HTML File', '<b>Hello, world!</b>', MimeType.HTML);\n```\n\nExample:\n```text\n// Creates shortcuts for all folders in the user's drive that have a specific\n// name.\n// TODO(developer): Replace 'Test-Folder' with a valid folder name in your\n// drive.\nconst folders = DriveApp.getFoldersByName('Test-Folder');\n\n// Iterates through all folders named 'Test-Folder'.\nwhile (folders.hasNext()) {\n const folder = folders.next();\n\n // Creates a shortcut to the provided Drive item ID and resource key, and\n // returns it.\n DriveApp.createShortcutForTargetIdAndResourceKey(\n folder.getId(),\n folder.getResourceKey(),\n );\n}\n```\n\nExample:\n```text\n// Gets a folder by its ID.\n// TODO(developer): Replace the folder ID with your own.\nconst folder = DriveApp.getFolderById('1234567890abcdefghijklmnopqrstuvwxyz');\n\n// Gets the list of editors and logs their names to the console.\nconst editors = folder.getEditors();\nfor (const editor of editors) {\n console.log(editor.getName());\n}\n```\n\nExample:\n```text\n// Gets a folder by its ID.\n// TODO(developer): Replace the folder ID with your own.\nconst folder = DriveApp.getFolderById('1234567890abcdefghijklmnopqrstuvwxyz');\n\n// Gets the owner of the folder and logs the name to the console.\nconst folderOwner = folder.getOwner();\nconsole.log(folderOwner.getName());\n```\n\nExample:\n```text\n// Gets a folder by its ID.\n// TODO(developer): Replace the folder ID with your own.\nconst folder = DriveApp.getFolderById('1234567890abcdefghijklmnopqrstuvwxyz');\n\n// Gets the list of viewers and logs their names to the console.\nconst viewers = folder.getViewers();\nfor (const viewer of viewers) {\n console.log(viewer.getName());\n}\n```\n\nExample:\n```text\n// Logs the name of every file that are children of the current folder and modified after February 28,\n// 2022 whose name contains \"untitled.\"\"\nconst files = DriveApp.getRootFolder().searchFiles(\n 'modifiedDate > \"2022-02-28\" and title contains \"untitled\"');\nwhile (files.hasNext()) {\n const file = files.next();\n console.log(file.getName());\n}\n```\n\nExample:\n```text\n// Logs the name of every folder that are children of the current folder and you own and is starred.\nconst folders = DriveApp.getRootFolder().searchFolders('starred = true and \"me\" in owners');\nwhile (folders.hasNext()) {\n const folder = folders.next();\n console.log(folder.getName());\n}\n```\n\nExample:\n```text\n// Creates a folder that anyone on the Internet can read from and write to.\n// (Domain administrators can prohibit this setting for users of a Google\n// Workspace domain.)\nconst folder = DriveApp.createFolder('Shared Folder');\nfolder.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.167Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":112,"estimatedTokens":791}}1035{"id":"doc-class_gmailapp_apps_script_google_for_developers-39eec306","source":"documentation","title":"Class GmailApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/gmail/gmail-app","text":"Example:\n```text\n// The code below creates a draft email with the current date and time.\nconst now = new Date();\nGmailApp.createDraft(\n 'mike@example.com',\n 'current time',\n `The time is: ${now.toString()}`,\n);\n```\n\nExample:\n```text\n// Create a draft email with a file from Google Drive attached as a PDF.\nconst file = DriveApp.getFileById('1234567890abcdefghijklmnopqrstuvwxyz');\nGmailApp.createDraft(\n 'mike@example.com',\n 'Attachment example',\n 'Please see attached file.',\n {\n attachments: [file.getAs(MimeType.PDF)],\n name: 'Automatic Emailer Script',\n },\n);\n```\n\nExample:\n```text\n// Creates the label @FOO and logs label: FOO\nLogger.log(`label: ${GmailApp.createLabel('FOO')}`);\n```\n\nExample:\n```text\n// Have to get the label by name first\nconst label = GmailApp.getUserLabelByName('FOO');\nGmailApp.deleteLabel(label);\n```\n\nExample:\n```text\n// Log the aliases for this Gmail account and send an email as the first one.\nconst me = Session.getActiveUser().getEmail();\nconst aliases = GmailApp.getAliases();\nLogger.log(aliases);\nif (aliases.length > 0) {\n GmailApp.sendEmail(me, 'From an alias', 'A message from an alias!', {\n from: aliases[0],\n });\n} else {\n GmailApp.sendEmail(me, 'No aliases found', 'You have no aliases.');\n}\n```\n\nExample:\n```text\n// Get the first draft message in your drafts folder\nconst draft = GmailApp.getDrafts()[0];\n// Get its ID\nconst draftId = draft.getId();\n// Now fetch the same draft using that ID.\nconst draftById = GmailApp.getDraft(draftId);\n// Should always log true as they should be the same message\nLogger.log(\n draft.getMessage().getSubject() === draftById.getMessage().getSubject(),\n);\n```\n\nExample:\n```text\n// Logs the number of draft messages\nconst drafts = GmailApp.getDraftMessages();\nLogger.log(drafts.length);\n```\n\nExample:\n```text\nconst drafts = GmailApp.getDrafts();\nfor (let i = 0; i < drafts.length; i++) {\n Logger.log(drafts[i].getId());\n}\n```\n\nExample:\n```text\n// Log the subject lines of your Inbox\nconst threads = GmailApp.getInboxThreads();\nfor (let i = 0; i < threads.length; i++) {\n Logger.log(threads[i].getFirstMessageSubject());\n}\n```\n\nExample:\n```text\n// Log the subject lines of up to the first 50 emails in your Inbox\nconst threads = GmailApp.getInboxThreads(0, 50);\nfor (let i = 0; i < threads.length; i++) {\n Logger.log(threads[i].getFirstMessageSubject());\n}\n```\n\nExample:\n```text\nLogger.log(`Messages unread in inbox: ${GmailApp.getInboxUnreadCount()}`);\n```\n\nExample:\n```text\n// Get the first message in the first thread of your inbox\nconst message = GmailApp.getInboxThreads(0, 1)[0].getMessages()[0];\n// Get its ID\nconst messageId = message.getId();\n// Now fetch the same message using that ID.\nconst messageById = GmailApp.getMessageById(messageId);\n// Should always log true as they should be the same message\nLogger.log(message.getSubject() === messageById.getSubject());\n```\n\nExample:\n```text\n// Log all the subject lines in the first thread of your inbox\nconst thread = GmailApp.getInboxThreads(0, 1)[0];\nconst messages = GmailApp.getMessagesForThread(thread);\nfor (let i = 0; i < messages.length; i++) {\n Logger.log(`subject: ${messages[i].getSubject()}`);\n}\n```\n\nExample:\n```text\n// Log the subject lines of all messages in the first two threads of your inbox\nconst thread = GmailApp.getInboxThreads(0, 2);\nconst messages = GmailApp.getMessagesForThreads(thread);\nfor (let i = 0; i < messages.length; i++) {\n for (let j = 0; j < messages[i].length; j++) {\n Logger.log(`subject: ${messages[i][j].getSubject()}`);\n }\n}\n```\n\nExample:\n```text\nLogger.log(\n `# of messages in your Priority Inbox: ${\n GmailApp.getPriorityInboxThreads().length}`,\n);\n```\n\nExample:\n```text\n// Will log some number 2 or less\nLogger.log(\n `# of messages in your Priority Inbox: ${\n GmailApp.getPriorityInboxThreads(0, 2).length}`,\n);\n```\n\nExample:\n```text\nLogger.log(\n `Number of unread emails in your Priority Inbox : ${\n GmailApp.getPriorityInboxUnreadCount()}`,\n);\n```\n\nExample:\n```text\nLogger.log(`# of total spam threads: ${GmailApp.getSpamThreads().length}`);\n```\n\nExample:\n```text\n// Will log a number at most 5\nLogger.log(`# of total spam threads: ${GmailApp.getSpamThreads(0, 5).length}`);\n```\n\nExample:\n```text\n// Unless you actually read stuff in your spam folder, this should be the same\n// as the number of messages in your spam folder.\nLogger.log(`# unread threads that are spam: ${GmailApp.getSpamUnreadCount()}`);\n```\n\nExample:\n```text\n// Logs the number of starred threads\nLogger.log(`# Starred threads: ${GmailApp.getStarredThreads().length}`);\n```\n\nExample:\n```text\n// Logs the number of starred threads to a maximum of 5\nLogger.log(`# Starred threads: ${GmailApp.getStarredThreads(0, 5).length}`);\n```\n\nExample:\n```text\nLogger.log(`# unread and starred: ${GmailApp.getStarredUnreadCount()}`);\n```\n\nExample:\n```text\n// Gets the first inbox thread.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\n// Gets the same thread by ID.\nconst threadById = GmailApp.getThreadById(firstThread.getId());\n// Verifies that they are the same.\nconsole.log(\n firstThread.getFirstMessageSubject() ===\n threadById.getFirstMessageSubject(),\n);\n```\n\nExample:\n```text\nLogger.log(`# of total trash threads: ${GmailApp.getTrashThreads().length}`);\n```\n\nExample:\n```text\n// Will log a number at most 5\nLogger.log(\n `# of total trash threads: ${GmailApp.getTrashThreads(0, 5).length}`,\n);\n```\n\nExample:\n```text\nconst labelObject = GmailApp.getUserLabelByName('myLabel');\n```\n\nExample:\n```text\n// Logs all of the names of your labels\nconst labels = GmailApp.getUserLabels();\nfor (let i = 0; i < labels.length; i++) {\n Logger.log(`label: ${labels[i].getName()}`);\n}\n```\n\nExample:\n```text\n// Mark the first message in the first thread of your inbox as read\nconst message = GmailApp.getInboxThreads(0, 1)[0].getMessages()[0];\nGmailApp.markMessageRead(message);\n```\n\nExample:\n```text\n// Mark the first message in the first thread of your inbox as unread\nconst message = GmailApp.getInboxThreads(0, 1)[0].getMessages()[0];\nGmailApp.markMessageUnread(message);\n```\n\nExample:\n```text\n// Mark first three messages in the first inbox thread as read.\n// Assumes that the first inbox thread has 3 messages in it.\nconst threadMessages = GmailApp.getInboxThreads(0, 1)[0].getMessages();\nconst messages = [threadMessages[0], threadMessages[1], threadMessages[2]];\nGmailApp.markMessagesRead(messages);\n```\n\nExample:\n```text\n// Mark first three messages in the first inbox thread as unread.\n// Assumes that the first inbox thread has 3 messages in it\nconst threadMessages = GmailApp.getInboxThreads(0, 1)[0].getMessages();\nconst messages = [threadMessages[0], threadMessages[1], threadMessages[2]];\nGmailApp.markMessagesUnread(messages);\n```\n\nExample:\n```text\n// Marks first inbox thread as important\nconst thread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.markThreadImportant(thread);\n```\n\nExample:\n```text\n// Marks first inbox thread as read\nconst thread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.markThreadRead(thread);\n```\n\nExample:\n```text\n// Marks first inbox thread as unimportant\nconst thread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.markThreadUnimportant(thread);\n```\n\nExample:\n```text\n// Marks first inbox thread as unread\nconst thread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.markThreadUnread(thread);\n```\n\nExample:\n```text\n// Marks first two threads in inbox as important\nconst threads = GmailApp.getInboxThreads(0, 2);\nGmailApp.markThreadsImportant(threads);\n```\n\nExample:\n```text\n// Marks first two threads in inbox as read\nconst threads = GmailApp.getInboxThreads(0, 2);\nGmailApp.markThreadsRead(threads);\n```\n\nExample:\n```text\n// Marks first two threads in inbox as unimportant\nconst threads = GmailApp.getInboxThreads(0, 2);\nGmailApp.markThreadsUnimportant(threads);\n```\n\nExample:\n```text\n// Marks first two threads in inbox as unread\nconst threads = GmailApp.getInboxThreads(0, 2);\nGmailApp.markThreadsUnread(threads);\n```\n\nExample:\n```text\n// Move the first message in your inbox to trash\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst firstMessage = firstThread.getMessages()[0];\nGmailApp.moveMessageToTrash(firstMessage);\n```\n\nExample:\n```text\n// Move first two messages in your inbox to trash\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst messages = firstThread.getMessages();\nconst toDelete = [messages[0], messages[1]];\nGmailApp.moveMessagesToTrash(toDelete);\n```\n\nExample:\n```text\n// Archive the first thread in your inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.moveThreadToArchive(firstThread);\n```\n\nExample:\n```text\n// Find a thread not already in your inbox\nconst thread = GmailApp.search('-in:inbox')[0]; // Get the first one\nGmailApp.moveThreadToInbox(thread);\n```\n\nExample:\n```text\n// Tag first thread in inbox as spam\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.moveThreadToSpam(firstThread);\n```\n\nExample:\n```text\n// Move first thread in inbox to trash\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.moveThreadToTrash(firstThread);\n```\n\nExample:\n```text\n// Move first two threads in your inbox to the archive\nconst firstTwoThreads = GmailApp.getInboxThreads(0, 2);\nGmailApp.moveThreadsToArchive(firstTwoThreads);\n```\n\nExample:\n```text\n// Find two threads not already in your inbox\nconst firstTwoThreads = GmailApp.search('-in:inbox', 0, 2);\nGmailApp.moveThreadsToInbox(firstTwoThreads);\n```\n\nExample:\n```text\n// Move first two threads in your inbox to spam\nconst firstTwoThreads = GmailApp.getInboxThreads(0, 2);\nGmailApp.moveThreadsToSpam(firstTwoThreads);\n```\n\nExample:\n```text\n// Move first two threads in your inbox to trash\nconst firstTwoThreads = GmailApp.getInboxThreads(0, 2);\nGmailApp.moveThreadsToTrash(firstTwoThreads);\n```\n\nExample:\n```text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst firstMessage = firstThread.getMessages()[0];\n// ...Do something that may take a while here....\nGmailApp.refreshMessage(firstMessage);\n// ...Do more stuff with firstMessage...\n```\n\nExample:\n```text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst coupleOfMessages = firstThread.getMessages().slice(0, 2);\n// ...Do something that may take a while here....\nGmailApp.refreshMessages(coupleOfMessages);\n// ...Do more stuff with coupleOfMessages...\n```\n\nExample:\n```text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\n// ...Do something that may take a while here....\nGmailApp.refreshThread(firstThread);\n// ... Do more stuff with the thread ...\n```\n\nExample:\n```text\nconst threads = GmailApp.getInboxThreads(0, 3);\n// ...Do something that may take a while here....\nGmailApp.refreshThreads(threads);\n// ... Do more stuff with threads ...\n```\n\nExample:\n```text\n// Find starred messages with subject IMPORTANT\nconst threads = GmailApp.search('is:starred subject:\"IMPORTANT\"');\n```\n\nExample:\n```text\n// Find starred messages with subject IMPORTANT and return second batch of 10.\n// Assumes there are at least 11 of them, otherwise this will return an empty\n// array.\nconst threads = GmailApp.search('is:starred subject:\"IMPORTANT\"', 10, 10);\n```\n\nExample:\n```text\n// The code below will send an email with the current date and time.\nconst now = new Date();\nGmailApp.sendEmail(\n 'mike@example.com',\n 'current time',\n `The time is: ${now.toString()}`,\n);\n```\n\nExample:\n```text\n// Send an email with a file from Google Drive attached as a PDF.\nconst file = DriveApp.getFileById('1234567890abcdefghijklmnopqrstuvwxyz');\nGmailApp.sendEmail(\n 'mike@example.com',\n 'Attachment example',\n 'Please see the attached file.',\n {\n attachments: [file.getAs(MimeType.PDF)],\n name: 'Automatic Emailer Script',\n },\n);\n```\n\nExample:\n```text\nfunction handleAddonActionEvent(e) {\n GmailApp.setCurrentMessageAccessToken(e.messageMetadata.accessToken);\n const mailMessage = GmailApp.getMessageById(e.messageMetadata.messageId);\n // Do something with mailMessage\n}\n```\n\nExample:\n```text\n// Stars the first message in the first thread in your inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nGmailApp.starMessage(message);\n```\n\nExample:\n```text\n// Stars the first three messages in the first thread in your inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst coupleOfMessages = firstThread.getMessages().slice(0, 3);\nGmailApp.starMessages(coupleOfMessages);\n```\n\nExample:\n```text\n// Unstars the first message in the first thread in your inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nGmailApp.unstarMessage(message);\n```\n\nExample:\n```text\n// Unstars the first three messages in the first thread in your inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst coupleOfMessages = firstThread.getMessages().slice(0, 3);\nGmailApp.unstarMessages(coupleOfMessages);\n```\n\nExample:\n```text\nconst threads = GmailApp.getChatThreads();\nLogger.log(`# of chat threads: ${threads.length}`);\n```\n\nExample:\n```text\n// Get first 50 chat threads\nconst threads = GmailApp.getChatThreads(0, 50);\n// Will log no more than 50.0\nLogger.log(threads.length);\nLogger.log(threads[0].getFirstMessageSubject());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.170Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":65,"totalLines":532,"estimatedTokens":3315}}1036{"id":"doc-class_decoratedtext_apps_script_google_for_devel-b42e11fa","source":"documentation","title":"Class DecoratedText | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/card-service/decorated-text","text":"Example:\n```text\nconst decoratedText =\n CardService.newDecoratedText().setText('Text').setTopLabel('TopLabel');\n\nconst multilineDecoratedText = CardService.newDecoratedText()\n .setText('Text')\n .setTopLabel('TopLabel')\n .setWrapText(true)\n .setBottomLabel('BottomLabel');\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAuthorizationAction().setAuthorizationUrl('url');\nCardService.newTextButton().setText('Authorize').setAuthorizationAction(action);\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('composeEmailCallback');\nCardService.newTextButton()\n .setText('Compose Email')\n .setComposeAction(action, CardService.ComposedEmailType.REPLY_AS_DRAFT);\n\n// ...\n\nfunction composeEmailCallback(e) {\n const thread = GmailApp.getThreadById(e.threadId);\n const draft = thread.createDraftReply('This is a reply');\n return CardService.newComposeActionResponseBuilder()\n .setGmailDraft(draft)\n .build();\n}\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('notificationCallback');\nCardService.newTextButton()\n .setText('Create notification')\n .setOnClickAction(action);\n\n// ...\n\nfunction notificationCallback() {\n return CardService.newActionResponseBuilder()\n .setNotification(\n CardService.newNotification().setText('Some info to display to user'),\n )\n .build();\n}\n```\n\nExample:\n```text\n// ...\n\nconst action = CardService.newAction().setFunctionName('openLinkCallback');\nCardService.newTextButton()\n .setText('Open Link')\n .setOnClickOpenLinkAction(action);\n\n// ...\n\nfunction openLinkCallback() {\n return CardService.newActionResponseBuilder()\n .setOpenLink(CardService.newOpenLink().setUrl('https://www.google.com'))\n .build();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.172Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":79,"estimatedTokens":484}}1037{"id":"doc-simple_triggers_apps_script_google_for_developer-5d66b303","source":"documentation","title":"Simple Triggers | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/triggers","text":"Example:\n```text\n/**\n * The event handler triggered when opening the spreadsheet.\n * @param {Event} e The onOpen event.\n * @see https://developers.google.com/apps-script/guides/triggers#onopene\n */\nfunction onOpen(e) {\n // Add a custom menu to the spreadsheet.\n SpreadsheetApp.getUi() // Or DocumentApp, SlidesApp, or FormApp.\n .createMenu(\"Custom Menu\")\n .addItem(\"First item\", \"menuItem1\")\n .addToUi();\n}\n```\n\nExample:\n```text\n/**\n * The event handler triggered when installing the add-on.\n * @param {Event} e The onInstall event.\n * @see https://developers.google.com/apps-script/guides/triggers#oninstalle\n */\nfunction onInstall(e) {\n onOpen(e);\n}\n```\n\nExample:\n```text\n/**\n * The event handler triggered when editing the spreadsheet.\n * @param {Event} e The onEdit event.\n * @see https://developers.google.com/apps-script/guides/triggers#onedite\n */\nfunction onEdit(e) {\n // Set a comment on the edited cell to indicate when it was changed.\n const range = e.range;\n range.setNote(`Last modified: ${new Date()}`);\n}\n```\n\nExample:\n```text\n/**\n * The event handler triggered when the selection changes in the spreadsheet.\n * @param {Event} e The onSelectionChange event.\n * @see https://developers.google.com/apps-script/guides/triggers#onselectionchangee\n */\nfunction onSelectionChange(e) {\n // Set background to red if a single empty cell is selected.\n const range = e.range;\n if (\n range.getNumRows() === 1 &&\n range.getNumColumns() === 1 &&\n range.getCell(1, 1).getValue() === \"\"\n ) {\n range.setBackground(\"red\");\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.174Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":63,"estimatedTokens":394}}1038{"id":"doc-class_person_apps_script_google_for_developers-d3fa1331","source":"documentation","title":"Class Person | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/document/person","text":"Example:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Append a styled paragraph.\nconst par = body.appendParagraph('A bold, italicized paragraph.');\npar.setBold(true);\npar.setItalic(true);\n\n// Retrieve the paragraph's attributes.\nconst atts = par.getAttributes();\n\n// Log the paragraph attributes.\nfor (const att in atts) {\n Logger.log(`${att}:${atts[att]}`);\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Obtain the first element in the active tab's body.\n\nconst firstChild = body.getChild(0);\n\n// Use getType() to determine the element's type.\nif (firstChild.getType() === DocumentApp.ElementType.PARAGRAPH) {\n Logger.log('The first element is a paragraph.');\n} else {\n Logger.log('The first element is not a paragraph.');\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Example 1: Merge paragraphs\n// Append two paragraphs to the document's active tab.\nconst par1 = body.appendParagraph('Paragraph 1.');\nconst par2 = body.appendParagraph('Paragraph 2.');\n// Merge the newly added paragraphs into a single paragraph.\npar2.merge();\n\n// Example 2: Merge table cells\n// Create a two-dimensional array containing the table's cell contents.\nconst cells = [\n ['Row 1, Cell 1', 'Row 1, Cell 2'],\n ['Row 2, Cell 1', 'Row 2, Cell 2'],\n];\n// Build a table from the array.\nconst table = body.appendTable(cells);\n// Get the first row in the table.\nconst row = table.getRow(0);\n// Get the two cells in this row.\nconst cell1 = row.getCell(0);\nconst cell2 = row.getCell(1);\n// Merge the current cell into its preceding sibling element.\nconst merged = cell2.merge();\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Remove all images in the active tab's body.\nconst imgs = body.getImages();\nfor (let i = 0; i < imgs.length; i++) {\n imgs[i].removeFromParent();\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Define a custom paragraph style.\nconst style = {};\nstyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =\n DocumentApp.HorizontalAlignment.RIGHT;\nstyle[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';\nstyle[DocumentApp.Attribute.FONT_SIZE] = 18;\nstyle[DocumentApp.Attribute.BOLD] = true;\n\n// Append a plain paragraph.\nconst par = body.appendParagraph('A paragraph with custom style.');\n\n// Apply the custom style.\npar.setAttributes(style);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.175Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":103,"estimatedTokens":707}}1039{"id":"doc-class_date_apps_script_google_for_developers-9743b36e","source":"documentation","title":"Class Date | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/document/date","text":"Example:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Append a styled paragraph.\nconst par = body.appendParagraph('A bold, italicized paragraph.');\npar.setBold(true);\npar.setItalic(true);\n\n// Retrieve the paragraph's attributes.\nconst atts = par.getAttributes();\n\n// Log the paragraph attributes.\nfor (const att in atts) {\n Logger.log(`${att}:${atts[att]}`);\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Obtain the first element in the active tab's body.\n\nconst firstChild = body.getChild(0);\n\n// Use getType() to determine the element's type.\nif (firstChild.getType() === DocumentApp.ElementType.PARAGRAPH) {\n Logger.log('The first element is a paragraph.');\n} else {\n Logger.log('The first element is not a paragraph.');\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Example 1: Merge paragraphs\n// Append two paragraphs to the document's active tab.\nconst par1 = body.appendParagraph('Paragraph 1.');\nconst par2 = body.appendParagraph('Paragraph 2.');\n// Merge the newly added paragraphs into a single paragraph.\npar2.merge();\n\n// Example 2: Merge table cells\n// Create a two-dimensional array containing the table's cell contents.\nconst cells = [\n ['Row 1, Cell 1', 'Row 1, Cell 2'],\n ['Row 2, Cell 1', 'Row 2, Cell 2'],\n];\n// Build a table from the array.\nconst table = body.appendTable(cells);\n// Get the first row in the table.\nconst row = table.getRow(0);\n// Get the two cells in this row.\nconst cell1 = row.getCell(0);\nconst cell2 = row.getCell(1);\n// Merge the current cell into its preceding sibling element.\nconst merged = cell2.merge();\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Remove all images in the active tab's body.\nconst imgs = body.getImages();\nfor (let i = 0; i < imgs.length; i++) {\n imgs[i].removeFromParent();\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Define a custom paragraph style.\nconst style = {};\nstyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =\n DocumentApp.HorizontalAlignment.RIGHT;\nstyle[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';\nstyle[DocumentApp.Attribute.FONT_SIZE] = 18;\nstyle[DocumentApp.Attribute.BOLD] = true;\n\n// Append a plain paragraph.\nconst par = body.appendParagraph('A paragraph with custom style.');\n\n// Apply the custom style.\npar.setAttributes(style);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.177Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":103,"estimatedTokens":707}}1040{"id":"doc-document_service_apps_script_google_for_develope-ff495792","source":"documentation","title":"Document Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/document","text":"Example:\n```text\n// Open a document by ID.\nvar doc = DocumentApp.openById('DOCUMENT_ID');\n\n// Create and open a document.\ndoc = DocumentApp.create('Document Name');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.182Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":46}}1041{"id":"doc-class_sheet_apps_script_google_for_developers-d271d671","source":"documentation","title":"Class Sheet | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/sheet","text":"Example:\n```text\n// This example assumes there is a sheet named \"first\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('first');\nfirst.activate();\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Adds the key 'NAME' to the developer metadata for the sheet.\nsheet.addDeveloperMetadata('NAME');\n\n// Gets the updated metadata info and logs it to the console.\nconsole.log(sheet.getDeveloperMetadata()[0].getKey());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Adds the key 'NAME' and sets the developer metadata visibility to PROJECT\n// for the sheet.\nsheet.addDeveloperMetadata(\n 'NAME',\n SpreadsheetApp.DeveloperMetadataVisibility.PROJECT,\n);\n\n// Gets the updated metadata info and logs it to the console.\nconst developerMetaData = sheet.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(developerMetaData.getVisibility().toString());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Adds the key 'COMPANY' with the value 'TECH' to the developer metadata for\n// the sheet.\nsheet.addDeveloperMetadata('COMPANY', 'TECH');\n\n// Gets the updated metadata info and logs it to the console.\nconst developerMetaData = sheet.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(developerMetaData.getValue());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Adds the key 'COMPANY' with the value 'TECH' to the developer metadata and\n// sets the visibility to DOCUMENT for the sheet.\nsheet.addDeveloperMetadata(\n 'COMPANY',\n 'TECH',\n SpreadsheetApp.DeveloperMetadataVisibility.DOCUMENT,\n);\n\n// Gets the updated metadata info and logs it to the console.\nconst developerMetaData = sheet.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(developerMetaData.getValue());\nconsole.log(developerMetaData.getVisibility().toString());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Appends a new row with 3 columns to the bottom of the current\n// data region in the sheet containing the values in the array.\nsheet.appendRow(['a man', 'a plan', 'panama']);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can useSpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the data source sheet value if the sheet is of type\n// SpreadsheetApp.SheetType.DATASOURCE, otherwise this returns a null value.\nconst dataSourceSheet = sheet.asDataSourceSheet();\n\n// Gets the data source sheet value and logs it to the console.\nconsole.log(dataSourceSheet);\nconsole.log(sheet.getType().toString());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nsheet.getRange('a1').setValue(\n 'Whenever it is a damp, drizzly November in my soul...');\n\n// Sets the first column to a width which fits the text\nsheet.autoResizeColumn(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sets the first 15 columns to a width that fits their text.\nsheet.autoResizeColumns(1, 15);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sets the first 15 rows to a height that fits their text.\nsheet.autoResizeRows(1, 15);\n```\n\nExample:\n```text\n// This example assumes there is a sheet named \"first\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('first');\nfirst.clear();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nsheet.clear({formatOnly: true, contentsOnly: true});\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nsheet.clearConditionalFormatRules();\n```\n\nExample:\n```text\n// This example assumes there is a sheet named \"first\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('first');\nfirst.clearContents();\n```\n\nExample:\n```text\n// This example assumes there is a sheet named \"first\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('first');\nfirst.clearFormats();\n```\n\nExample:\n```text\n// This example assumes there is a sheet named \"first\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('first');\nfirst.clearNotes();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// All column groups on the sheet are collapsed.\nsheet.collapseAllColumnGroups();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// All row groups on the sheet are collapsed.\nsheet.collapseAllRowGroups();\n```\n\nExample:\n```text\nconst source = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = source.getSheets()[0];\n\nconst destination = SpreadsheetApp.openById('ID_GOES HERE');\nsheet.copyTo(destination);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Adds developer metadata for testing.\nsheet.addDeveloperMetadata('CITY', 'PARIS');\n\n// Creates the developer metadata finder.\nconst metadatafinder = sheet.createDeveloperMetadataFinder();\n\n// Finds the metadata with value 'PARIS' and displays its key in the console.\nconsole.log(metadatafinder.withValue('PARIS').find()[0].getKey());\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// Creates a text finder.\nconst textFinder = sheet.createTextFinder('dog');\n\n// Returns the first occurrence of 'dog' in the sheet.\nconst firstOccurrence = textFinder.findNext();\n\n// Replaces the last found occurrence of 'dog' with 'cat' and returns the number\n// of occurrences replaced.\nconst numOccurrencesReplaced = firstOccurrence.replaceWith('cat');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Columns start at \"1\" - this deletes the first column\nsheet.deleteColumn(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Columns start at \"1\" - this deletes the first two columns\nsheet.deleteColumns(1, 2);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Rows start at \"1\" - this deletes the first row\nsheet.deleteRow(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Rows start at \"1\" - this deletes the first two rows\nsheet.deleteRows(1, 2);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// All column groups on the sheet are expanded.\nsheet.expandAllColumnGroups();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// All row groups on the sheet are expanded.\nsheet.expandAllRowGroups();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// All column groups of depth 2 and lower are expanded, and groups with depth\n// 3 and higher are collapsed.\nsheet.expandColumnGroupsUpToDepth(2);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// All row groups of depth 2 and lower are expanded, and groups with depth\n// 3 and higher are collapsed.\nsheet.expandRowGroupsUpToDepth(2);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Returns the active cell\nconst cell = sheet.getActiveCell();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\nconst activeRange = sheet.getActiveRange();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\n// Returns the list of active ranges.\nconst activeRangeList = sheet.getActiveRangeList();\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the banding info for the sheet.\nconst bandings = sheet.getBandings();\n\n// Gets info on the bandings' second row color and logs it to the console.\nfor (const banding of bandings) {\n console.log(banding.getSecondRowColor());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst charts = sheet.getCharts();\n\nfor (const i in charts) {\n const chart = charts[i];\n // Do something with the chart\n}\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// Returns the group whose control index is at column 2 and has a depth of 1, or\n// null if the group doesn’t exist.\nconst columnGroup = sheet.getColumnGroup(2, 1);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// GroupControlTogglePosition.AFTER if the column grouping control toggle is\n// shown after the group.\nconst columnGroupControlPosition = sheet.getColumnGroupControlPosition();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// 1 if there is a group over columns 1 through 3\nconst groupDepth = sheet.getColumnGroupDepth(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Columns start at 1\nLogger.log(sheet.getColumnWidth(1));\n```\n\nExample:\n```text\n// Logs the conditional format rules in a sheet.\nconst rules = SpreadsheetApp.getActiveSheet().getConditionalFormatRules();\nfor (let i = 0; i < rules.length; i++) {\n const rule = rules[i];\n Logger.log(rule);\n}\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\n// Returns the current highlighted cell in the one of the active ranges.\nconst currentCell = sheet.getCurrentCell();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This represents ALL the data\nconst range = sheet.getDataRange();\nconst values = range.getValues();\n\n// This logs the spreadsheet in CSV format with a trailing comma\nfor (let i = 0; i < values.length; i++) {\n let row = '';\n for (let j = 0; j < values[i].length; j++) {\n if (values[i][j]) {\n row = row + values[i][j];\n }\n row = `${row},`;\n }\n Logger.log(row);\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet by its ID. If you created your script from within a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets an array of the data source formulas on Sheet1.\n// To get an array of data source formulas for the entire spreadsheet,\n// replace 'sheet' with 'ss'.\nconst dataSourceFormulas = sheet.getDataSourceFormulas();\n\n// Logs the first data source formula in the array.\nconsole.log(dataSourceFormulas[0].getFormula());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets an array of the data source pivot tables on Sheet1.\n// To get an array of data source pivot tables for the entire\n// spreadsheet, replace 'sheet' with 'ss'.\nconst dataSourcePivotTables = sheet.getDataSourcePivotTables();\n\n// Logs the last time that the first pivot table in the array was refreshed.\nconsole.log(dataSourcePivotTables[0].getStatus().getLastRefreshedTime());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets an array of data source tables on Sheet1.\n// To get an array of data source tables for the entire spreadsheet,\n// replace 'sheet' with 'ss'.\nconst dataSourceTables = sheet.getDataSourceTables();\n\n// Logs the last completed data execution time on the first data source table.\nconsole.log(dataSourceTables[0].getStatus().getLastExecutionTime());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Adds developer metadata for testing.\nsheet.addDeveloperMetadata('CITY', 'PARIS');\n\n// Gets all the developer metadata for the sheet.\nconst developerMetaDataList = sheet.getDeveloperMetadata();\n\n// Logs the developer metadata to the console.\nfor (const developerMetaData of developerMetaDataList) {\n console.log(developerMetaData.getKey());\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets all the drawings from the sheet.\nconst allDrawings = sheet.getDrawings();\n\n// Logs the number of drawings present on the sheet.\nconsole.log(allDrawings.length);\n```\n\nExample:\n```text\n// Gets the filter on the active sheet.\nconst ss = SpreadsheetApp.getActiveSheet();\nconst filter = ss.getFilter();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst url = sheet.getFormUrl();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nLogger.log('Number of frozen columns: %s', sheet.getFrozenColumns());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nLogger.log('Number of frozen rows: %s', sheet.getFrozenRows());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets spreadsheet, you can use\n// SpreadsheetApp.getActiveSpreadsheet() instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the over-the-grid images from Sheet1.\n// To get the over-the-grid images from the entire spreadsheet, use\n// ss.getImages() instead.\nconst images = sheet.getImages();\n\n// For each image, logs the anchor cell in A1 notation.\nfor (const image of images) {\n console.log(image.getAnchorCell().getA1Notation());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\n// Note that the JavaScript index is 0, but this logs 1\nconst sheet = ss.getSheets()[0];\n// ... because spreadsheets are 1-indexed\nLogger.log(sheet.getIndex());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This logs the value in the very last cell of this sheet\nconst lastRow = sheet.getLastRow();\nconst lastColumn = sheet.getLastColumn();\nconst lastCell = sheet.getRange(lastRow, lastColumn);\nLogger.log(lastCell.getValue());\n```\n\nExample:\n```text\n// This example assumes there is a sheet named \"first\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('first');\nLogger.log(first.getMaxColumns());\n```\n\nExample:\n```text\n// This example assumes there is a sheet named \"first\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('first');\nLogger.log(first.getMaxRows());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nLogger.log(sheet.getName());\n```\n\nExample:\n```text\n// The code below logs the name of the first named range.\nconst namedRanges = SpreadsheetApp.getActiveSheet().getNamedRanges();\nif (namedRanges.length > 1) {\n Logger.log(namedRanges[0].getName());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// parent is identical to ss\nconst parent = sheet.getParent();\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets all the pivot table info for the sheet.\nconst pivotTables = sheet.getPivotTables();\n\n// Logs the pivot tables to the console.\nfor (const pivotTable of pivotTables) {\n console.log(pivotTable.getSourceDataRange().getValues());\n}\n```\n\nExample:\n```text\n// Remove all range protections in the spreadsheet that the user has permission\n// to edit.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);\nfor (let i = 0; i < protections.length; i++) {\n const protection = protections[i];\n if (protection.canEdit()) {\n protection.remove();\n }\n}\n```\n\nExample:\n```text\n// Remove sheet protection from the active sheet, if the user has permission to\n// edit it.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst protection = sheet.getProtections(SpreadsheetApp.ProtectionType.SHEET)[0];\nif (protection?.canEdit()) {\n protection.remove();\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Passing only two arguments returns a \"range\" with a single cell.\nconst range = sheet.getRange(1, 1);\nconst values = range.getValues();\nLogger.log(values[0][0]);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// When the \"numRows\" argument is used, only a single column of data is\n// returned.\nconst range = sheet.getRange(1, 1, 3);\nconst values = range.getValues();\n\n// Prints 3 values from the first column, starting from row 1.\nfor (const row in values) {\n for (const col in values[row]) {\n Logger.log(values[row][col]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange(1, 1, 3, 3);\nconst values = range.getValues();\n\n// Print values from a 3x3 box.\nfor (const row in values) {\n for (const col in values[row]) {\n Logger.log(values[row][col]);\n }\n}\n```\n\nExample:\n```text\n// Get a range A1:D4 on sheet titled \"Invoices\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst range = ss.getRange('Invoices!A1:D4');\n\n// Get cell A1 on the first sheet\nconst sheet = ss.getSheets()[0];\nconst cell = sheet.getRange('A1');\n```\n\nExample:\n```text\n// Get a list of ranges A1:D4, F1:H4.\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\nconst rangeList = sheet.getRangeList(['A1:D4', 'F1:H4']);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// Returns the group whose control index is at row 2 and has a depth of 1, or\n// null if the group doesn’t exist.\nconst rowGroup = sheet.getRowGroup(2, 1);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// GroupControlTogglePosition.AFTER if the row grouping control toggle is shown\n// after the group.\nconst rowGroupControlPosition = sheet.getRowGroupControlPosition();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// 1 if there is a group over rows 1 through 3\nconst groupDepth = sheet.getRowGroupDepth(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Rows start at 1\nLogger.log(sheet.getRowHeight(1));\n```\n\nExample:\n```text\nconst selection = SpreadsheetApp.getActiveSpreadsheet().getSelection();\nconst currentCell = selection.getCurrentCell();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nLogger.log(sheet.getSheetId());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nLogger.log(sheet.getSheetName());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The two samples below produce the same output\nlet values = sheet.getSheetValues(1, 1, 3, 3);\nLogger.log(values);\n\nconst range = sheet.getRange(1, 1, 3, 3);\nvalues = range.getValues();\nLogger.log(values);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets all slicers in the spreadsheet.\nconst slicers = sheet.getSlicers();\n\n// Logs the slicer titles to the console.\nfor (const slicer of slicers) {\n console.log(slicer.getTitle());\n}\n```\n\nExample:\n```text\n// This example assumes there is a sheet named \"Sheet1\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('Sheet1');\nconst color = first.getTabColorObject();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nLogger.log(sheet.getType());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Checks if the spreadsheet has hidden gridelines and logs the result to the\n// console.\nconsole.log(sheet.hasHiddenGridlines());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This hides the first column\nlet range = sheet.getRange('A1');\nsheet.hideColumn(range);\n\n// This hides the first 3 columns\nrange = sheet.getRange('A:C');\nsheet.hideColumn(range);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Hides the first column\nsheet.hideColumns(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Hides the first three columns\nsheet.hideColumns(1, 3);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This hides the first row\nconst range = sheet.getRange('A1');\nsheet.hideRow(range);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Hides the first row\nsheet.hideRows(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Hides the first three rows\nsheet.hideRows(1, 3);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nsheet.hideSheet();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This creates a simple bar chart from the first three rows\n// of the first two columns of the spreadsheet\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B4'))\n .setPosition(5, 5, 0, 0)\n .setOption('title', 'Dynamic Chart')\n .build();\nsheet.insertChart(chart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts a column after the first column position\nsheet.insertColumnAfter(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts a column in the first column position\nsheet.insertColumnBefore(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Shifts all columns by one\nsheet.insertColumns(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Shifts all columns by three\nsheet.insertColumns(1, 3);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Inserts two columns after the first column on the first sheet of the\n// spreadsheet.\nsheet.insertColumnsAfter(1, 2);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts five columns before the first column\nsheet.insertColumnsBefore(1, 5);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst binaryData = []; // TODO(developer): Replace with your binary data.\nconst blob = Utilities.newBlob(binaryData, 'image/png', 'MyImageName');\nsheet.insertImage(blob, 1, 1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst binaryData = []; // TODO(developer): Replace with your binary data.\nconst blob = Utilities.newBlob(binaryData, 'image/png', 'MyImageName');\nsheet.insertImage(blob, 1, 1, 10, 10);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nsheet.insertImage('https://www.google.com/images/srpr/logo3w.png', 1, 1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nsheet.insertImage(\n 'https://www.google.com/images/srpr/logo3w.png',\n 1,\n 1,\n 10,\n 10,\n);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts a row after the first row position\nsheet.insertRowAfter(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts a row before the first row position\nsheet.insertRowBefore(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Shifts all rows down by one\nsheet.insertRows(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Shifts all rows down by three\nsheet.insertRows(1, 3);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts five rows after the first row\nsheet.insertRowsAfter(1, 5);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This inserts five rows before the first row\nsheet.insertRowsBefore(1, 5);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range of the sheet.\nconst range = sheet.getRange('A1:D10');\n\n// Inserts the slicer with a random range into the sheet.\nconst insertSlicers = sheet.insertSlicer(range.randomize(), 1, 10);\n\n// Logs the insert slicer result to the console.\nconsole.log(insertSlicers);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range.\nconst range = sheet.getRange('A1:D10');\n\n// Inserts a slicer using the random range function.\nconst insertSlicers = sheet.insertSlicer(range.randomize(), 1, 10, 0, 0);\n\n// Logs the insert slicer result to the console.\nconsole.log(insertSlicers);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Columns start at 1\nLogger.log(sheet.isColumnHiddenByUser(1));\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Checks if a spreadsheet is ordered from right to left and logs the result to\n// the console.\nconsole.log(sheet.isRightToLeft());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Rows start at 1\nLogger.log(sheet.isRowHiddenByFilter(1));\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Rows start at 1\nLogger.log(sheet.isRowHiddenByUser(1));\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nif (sheet.isSheetHidden()) {\n // do something...\n}\n```\n\nExample:\n```text\n// The code below moves rows A-B to destination index 5.\n// This results in those columns becoming columns C-D.\nconst sheet = SpreadsheetApp.getActiveSheet();\n// Selects column A and column B to be moved.\nconst columnSpec = sheet.getRange('A1:B1');\nsheet.moveColumns(columnSpec, 5);\n```\n\nExample:\n```text\n// The code below moves rows 1-2 to destination index 5.\n// This results in those rows becoming rows 3-4.\nconst sheet = SpreadsheetApp.getActiveSheet();\n// Selects row 1 and row 2 to be moved.\nconst rowSpec = sheet.getRange('A1:A2');\nsheet.moveRows(rowSpec, 5);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B8');\nconst chartBuilder = sheet.newChart();\nchartBuilder.addRange(range)\n .setChartType(Charts.ChartType.LINE)\n .setPosition(2, 2, 0, 0)\n .setOption('title', 'My Line Chart!');\nsheet.insertChart(chartBuilder.build());\n```\n\nExample:\n```text\n// Protect the active sheet, then remove all other users from the list of\n// editors.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst protection = sheet.protect().setDescription('Sample protected sheet');\n\n// Ensure the current user is an editor before removing others. Otherwise, if\n// the user's edit permission comes from a group, the script throws an exception\n// upon removing the group.\nconst me = Session.getEffectiveUser();\nprotection.addEditor(me);\nprotection.removeEditors(protection.getEditors());\nif (protection.canDomainEdit()) {\n protection.setDomainEdit(false);\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This removes all the embedded charts from the spreadsheet\nconst charts = sheet.getCharts();\nfor (const i in charts) {\n sheet.removeChart(charts[i]);\n}\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\nconst range = sheet.getRange('A1:D4');\nsheet.setActiveRange(range);\n\nconst selection = sheet.getSelection();\n// Current cell: A1\nconst currentCell = selection.getCurrentCell();\n// Active Range: A1:D4\nconst activeRange = selection.getActiveRange();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeList = sheet.getRangeList(['D4', 'B2:C4']);\nsheet.setActiveRangeList(rangeList);\n\nconst selection = sheet.getSelection();\n// Current cell: B2\nconst currentCell = selection.getCurrentCell();\n// Active range: B2:C4\nconst activeRange = selection.getActiveRange();\n// Active range list: [D4, B2:C4]\nconst activeRangeList = selection.getActiveRangeList();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D4');\nsheet.setActiveSelection(range);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nsheet.setActiveSelection('A1:D4');\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nsheet.setColumnGroupControlPosition(\n SpreadsheetApp.GroupControlTogglePosition.AFTER,\n);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sets the first column to a width of 200 pixels\nsheet.setColumnWidth(1, 200);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sets the first three columns to a width of 200 pixels\nsheet.setColumnWidths(1, 3, 200);\n```\n\nExample:\n```text\n// Remove one of the existing conditional format rules.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rules = sheet.getConditionalFormatRules();\nrules.splice(1, 1); // Deletes the 2nd format rule.\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\nconst cell = sheet.getRange('B5');\nsheet.setCurrentCell(cell);\n\nconst selection = sheet.getSelection();\n// Current cell: B5\nconst currentCell = selection.getCurrentCell();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Freezes the first column\nsheet.setFrozenColumns(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Freezes the first row\nsheet.setFrozenRows(1);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can us eSpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Hides the gridlines in the sheet.\nsheet.setHiddenGridlines(true);\n```\n\nExample:\n```text\n// This example assumes there is a sheet named \"first\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('first');\nfirst.setName('not first anymore');\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Sets the sheet layout, so that the sheet is ordered from right to left.\nsheet.setRightToLeft(true);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nsheet.setRowGroupControlPosition(\n SpreadsheetApp.GroupControlTogglePosition.AFTER,\n);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sets the first row to a height of 200 pixels\nsheet.setRowHeight(1, 200);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sets the first three rows to a height of 20 pixels\nsheet.setRowHeights(1, 3, 20);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sets the first three rows to a height of 5 pixels.\nsheet.setRowHeightsForced(1, 3, 5);\n```\n\nExample:\n```text\n// This example assumes there is a sheet named \"first\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('first');\nfirst.setTabColor('ff0000'); // Set the color to red.\nfirst.setTabColor(null); // Unset the color.\n```\n\nExample:\n```text\n// This example assumes there is a sheet named \"Sheet1\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('Sheet1');\nconst color = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT1)\n .build();\nfirst.setTabColorObject(color); // Set the color to theme accent 1.\nfirst.setTabColorObject(null); // Unset the color.\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Unhides the first column\nsheet.showColumns(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Unhides the first three columns\nsheet.showColumns(1, 3);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Unhides the first row\nsheet.showRows(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n// Unhides the first three rows\nsheet.showRows(1, 3);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nsheet.showSheet();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sorts the sheet by the first column, ascending\nsheet.sort(1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// Sorts the sheet by the first column, descending\nsheet.sort(1, false);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This unhides the first column if it was previously hidden\nconst range = sheet.getRange('A1');\nsheet.unhideColumn(range);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This unhides the first row if it was previously hidden\nconst range = sheet.getRange('A1');\nsheet.unhideRow(range);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This code is going to loop through all the charts and change them to\n// column charts\nconst charts = sheet.getCharts();\nfor (const i in charts) {\n const chart = charts[i];\n const newChart = chart.modify().setChartType(Charts.ChartType.COLUMN).build();\n sheet.updateChart(newChart);\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst permissions = sheet.getSheetProtection();\n\npermissions.setProtected(true);\npermissions.addUser('user@example.com');\n\n// Logs the users that have access to edit this sheet. Note that this\n// is different from access to the entire spreadsheet - getUsers()\n// only returns users if permissions.isProtected() is set to true.\nconst users = permissions.getUsers();\nLogger.log(users);\n```\n\nExample:\n```text\n// This example assumes there is a sheet named \"first\"\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst first = ss.getSheetByName('first');\nconst color = first.getTabColor();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst permissions = sheet.getSheetProtection();\n\n// This copies the permissions on the first sheet to the second sheet\nconst sheetToClonePermissionsTo = ss.getSheets()[1];\nsheetToClonePermissionsTo.setSheetProtection(permissions);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.193Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":147,"totalLines":1641,"estimatedTokens":10470}}1042{"id":"doc-class_range_apps_script_google_for_developers-e721e90a","source":"documentation","title":"Class Range | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/range","text":"Example:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getRange('A1:D10');\nrange.activate();\n\nconst selection = sheet.getSelection();\n// Current cell: A1\nconst currentCell = selection.getCurrentCell();\n// Active Range: A1:D10\nconst activeRange = selection.getActiveRange();\n```\n\nExample:\n```text\n// Gets the first sheet of the spreadsheet.\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// Gets the cell B5 and sets it as the active cell.\nconst range = sheet.getRange('B5');\nconst currentCell = range.activateAsCurrentCell();\n\n// Logs the activated cell.\nconsole.log(currentCell.getA1Notation());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on the sheet.\nconst range = sheet.getRange('2:2');\n\n// Adds the key 'NAME' to the developer metadata for row 2.\nrange.addDeveloperMetadata('NAME');\n\n// Gets the metadata and logs it to the console.\nconst developerMetaData = range.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on Sheet1.\nconst range = sheet.getRange('2:2');\n\n// Adds the key 'NAME' and sets the developer metadata visibility to 'DOCUMENT'\n// for row 2 on Sheet1.\nrange.addDeveloperMetadata(\n 'NAME',\n SpreadsheetApp.DeveloperMetadataVisibility.DOCUMENT,\n);\n\n// Gets the updated metadata info and logs it to the console.\nconst developerMetaData = range.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(developerMetaData.getVisibility().toString());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 of Sheet1.\nconst range = sheet.getRange('2:2');\n\n// Adds the key 'NAME' and sets the value to 'GOOGLE' for the metadata of row 2.\nrange.addDeveloperMetadata('NAME', 'GOOGLE');\n\n// Gets the metadata and logs it to the console.\nconst developerMetaData = range.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(developerMetaData.getValue());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on the sheet.\nconst range = sheet.getRange('2:2');\n\n// Adds the key 'NAME', sets the value to 'GOOGLE', and sets the visibility\n// to PROJECT for row 2 on the sheet.\nrange.addDeveloperMetadata(\n 'NAME',\n 'GOOGLE',\n SpreadsheetApp.DeveloperMetadataVisibility.PROJECT,\n);\n\n// Gets the updated metadata info and logs it to the console.\nconst developerMetaData = range.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(developerMetaData.getValue());\nconsole.log(developerMetaData.getVisibility().toString());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on the sheet.\nconst range = sheet.getRange('2:2');\n\n// Applies column banding to row 2.\nconst colBanding = range.applyColumnBanding();\n\n// Gets the first banding on the sheet and logs the color of the header column.\nconsole.log(\n sheet.getBandings()[0]\n .getHeaderColumnColorObject()\n .asRgbColor()\n .asHexString(),\n);\n\n// Gets the first banding on the sheet and logs the color of the second column.\nconsole.log(\n sheet.getBandings()[0]\n .getSecondColumnColorObject()\n .asRgbColor()\n .asHexString(),\n);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on the sheet.\nconst range = sheet.getRange('2:2');\n\n// Applies the INDIGO color banding theme to the columns in row 2.\nconst colBanding = range.applyColumnBanding(SpreadsheetApp.BandingTheme.INDIGO);\n\n// Gets the first banding on the sheet and logs the color of the second column.\nconsole.log(\n sheet.getBandings()[0]\n .getSecondColumnColorObject()\n .asRgbColor()\n .asHexString(),\n);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets rows 12-22 on the sheet.\nconst range = sheet.getRange('12:22');\n\n// Applies the BLUE color banding theme to rows 12-22.\n// Sets the header visibility to false and the footer visibility to true.\nconst colBanding = range.applyColumnBanding(\n SpreadsheetApp.BandingTheme.BLUE,\n false,\n true,\n);\n\n// Gets the banding color and logs it to the console.\nconsole.log(\n sheet.getBandings()[0]\n .getSecondColumnColorObject()\n .asRgbColor()\n .asHexString(),\n);\n\n// Gets the header color object and logs it to the console. Returns null because\n// the header visibility is set to false.\nconsole.log(sheet.getBandings()[0].getHeaderColumnColorObject());\n\n// Gets the footer color and logs it to the console.\nconsole.log(\n sheet.getBandings()[0]\n .getFooterColumnColorObject()\n .asRgbColor()\n .asHexString(),\n);\n```\n\nExample:\n```text\n// Opens the spreadsheet by its URL. If you created your script from within a\n// Google Sheets spreadsheet, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets rows 1-30 on Sheet1.\nconst range = sheet.getRange('1:30');\n\n// Applies row banding to rows 1-30.\nrange.applyRowBanding();\n\n// Gets the hex color of the second banded row.\nconst secondRowColor =\n range.getBandings()[0].getSecondRowColorObject().asRgbColor().asHexString();\n\n// Logs the hex color to console.\nconsole.log(secondRowColor);\n```\n\nExample:\n```text\n// Opens the spreadsheet by its URL. If you created your script from within a\n// Google Sheets spreadsheet, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets rows 1-30 on Sheet1.\nconst range = sheet.getRange('1:30');\n\n// Applies the INDIGO row banding theme to rows 1-30.\nrange.applyRowBanding(SpreadsheetApp.BandingTheme.INDIGO);\n\n// Gets the hex color of the second banded row.\nconst secondRowColor =\n range.getBandings()[0].getSecondRowColorObject().asRgbColor().asHexString();\n\n// Logs the hex color to console.\nconsole.log(secondRowColor);\n```\n\nExample:\n```text\n// Opens the spreadsheet by its URL. If you created your script from within a\n// Google Sheets spreadsheet, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets rows 1-30 on Sheet1.\nconst range = sheet.getRange('1:30');\n\n// Applies the INDIGO row banding to rows 1-30 and\n// specifies to hide the header and show the footer.\nrange.applyRowBanding(SpreadsheetApp.BandingTheme.INDIGO, false, true);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\n\n// Has values [1, 2, 3, 4].\nconst sourceRange = sheet.getRange('A1:A4');\n// The range to fill with values.\nconst destination = sheet.getRange('A1:A20');\n\n// Inserts new values in A5:A20, continuing the pattern expressed in A1:A4\nsourceRange.autoFill(destination, SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\n\n// A1:A20 has values [1, 2, 3, ... 20].\n// B1:B4 has values [1/1/2017, 1/2/2017, ...]\nconst sourceRange = sheet.getRange('B1:B4');\n\n// Results in B5:B20 having values [1/5/2017, ... 1/20/2017]\nsourceRange.autoFillToNeighbor(SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES);\n```\n\nExample:\n```text\n// Opens the spreadsheet by its URL. If you created your script from within a\n// Google Sheets spreadsheet, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:C6 on Sheet1.\nconst range = sheet.getRange('A1:C6');\n\n// Unmerges the range A1:C6 into individual cells.\nrange.breakApart();\n```\n\nExample:\n```text\n// Opens the spreadsheet by its URL. If you created your script from within a\n// Google Sheets spreadsheet, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:C6 on Sheet1.\nconst range = sheet.getRange('A1:C6');\n\n// Logs whether the user has permission to edit every cell in the range.\nconsole.log(range.canEdit());\n```\n\nExample:\n```text\n// Changes the state of cells which currently contain either the checked or\n// unchecked value configured in the range A1:B10 to 'checked'.\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\nrange.check();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.clear();\n```\n\nExample:\n```text\n// The code below clears range C2:G7 in the active sheet, but preserves the\n// format, data validation rules, and comments.\nSpreadsheetApp.getActiveSheet().getRange(2, 3, 6, 5).clear({\n contentsOnly: true\n});\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.clearContent();\n```\n\nExample:\n```text\n// Clear the data validation rules for cells A1:B5.\nconst range = SpreadsheetApp.getActive().getRange('A1:B5');\nrange.clearDataValidations();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.clearFormat();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.clearNote();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getActiveRange();\n\n// All row and column groups within the range are collapsed.\nrange.collapseGroups();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst source = ss.getSheets()[0];\n\nconst range = source.getRange('B2:D4');\n\n// This copies the formatting in B2:D4 in the source sheet to\n// D4:F6 in the sheet with gridId 1555299895. Note that you can get the gridId\n// of a sheet by calling sheet.getSheetId() or range.getGridId().\nrange.copyFormatToRange(1555299895, 4, 6, 4, 6);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst source = ss.getSheets()[0];\nconst destination = ss.getSheets()[1];\n\nconst range = source.getRange('B2:D4');\n\n// This copies the formatting in B2:D4 in the source sheet to\n// D4:F6 in the second sheet\nrange.copyFormatToRange(destination, 4, 6, 4, 6);\n```\n\nExample:\n```text\n// The code below copies the first 5 columns over to the 6th column.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeToCopy = sheet.getRange(1, 1, sheet.getMaxRows(), 5);\nrangeToCopy.copyTo(sheet.getRange(1, 6));\n```\n\nExample:\n```text\n// The code below copies only the values of the first 5 columns over to the 6th\n// column.\nconst sheet = SpreadsheetApp.getActiveSheet();\nsheet.getRange('A:E').copyTo(\n sheet.getRange('F1'),\n SpreadsheetApp.CopyPasteType.PASTE_VALUES,\n false,\n);\n```\n\nExample:\n```text\n// The code below copies only the values of the first 5 columns over to the 6th\n// column.\nconst sheet = SpreadsheetApp.getActiveSheet();\nsheet.getRange('A:E').copyTo(sheet.getRange('F1'), {contentsOnly: true});\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst source = ss.getSheets()[0];\n\nconst range = source.getRange('B2:D4');\n\n// This copies the data in B2:D4 in the source sheet to\n// D4:F6 in the sheet with gridId 0\nrange.copyValuesToRange(0, 4, 6, 4, 6);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst source = ss.getSheets()[0];\nconst destination = ss.getSheets()[1];\n\nconst range = source.getRange('B2:D4');\n\n// This copies the data in B2:D4 in the source sheet to\n// D4:F6 in the second sheet\nrange.copyValuesToRange(destination, 4, 6, 4, 6);\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nconst anchorCell = spreadsheet.getSheets()[0].getRange('A1');\nconst dataSource = spreadsheet.getDataSources()[0];\n\nconst pivotTable = anchorCell.createDataSourcePivotTable(dataSource);\npivotTable.addRowGroup('dataColumnA');\npivotTable.addColumnGroup('dataColumnB');\npivotTable.addPivotValue(\n 'dataColumnC',\n SpreadsheetApp.PivotTableSummarizeFunction.SUM,\n);\npivotTable.addFilter(\n 'dataColumnA',\n SpreadsheetApp.newFilterCriteria().whenTextStartsWith('A').build(),\n);\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nconst anchorCell = spreadsheet.getSheets()[0].getRange('A1');\nconst dataSource = spreadsheet.getDataSources()[0];\n\nconst dataSourceTable =\n anchorCell.createDataSourceTable(dataSource)\n .addColumns('dataColumnA', 'dataColumnB', 'dataColumnC')\n .addSortSpec('dataColumnA', true) // ascending=true\n .addSortSpec('dataColumnB', false); // ascending=false\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:C6.\nconst range = sheet.getRange('A1:C6');\n\n// Creates a developer metadata finder to search for metadata in the scope of\n// this range.\nconst developerMetaDataFinder = range.createDeveloperMetadataFinder();\n\n// Logs information about the developer metadata finder to the console.\nconst developerMetaData = developerMetaDataFinder.find()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(developerMetaData.getValue());\nconsole.log(developerMetaData.getVisibility().toString());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSheet();\nconst range = ss.getRange('A1:C20');\n\n// Creates a new filter and applies it to the range A1:C20 on the active sheet.\nfunction createFilter() {\n range.createFilter();\n}\n// Gets the filter and applies criteria that only shows cells that aren't empty.\nfunction getFilterAddCriteria() {\n const filter = range.getFilter();\n const criteria =\n SpreadsheetApp.newFilterCriteria().whenCellNotEmpty().build();\n filter.setColumnFilterCriteria(2, criteria);\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets cell A1 as a range in order to place the pivot table.\nconst range = sheet.getRange('A1');\n\n// Gets the range of the source data for the pivot table.\nconst dataRange = sheet.getRange('E12:G20');\n\n// Creates an empty pivot table from the specified source data.\nconst pivotTable = range.createPivotTable(dataRange);\n\n// Logs the values from the pivot table's source data to the console.\nconsole.log(pivotTable.getSourceDataRange().getValues());\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getActiveRange();\n\n// Creates a text finder for the range.\nconst textFinder = range.createTextFinder('dog');\n\n// Returns the first occurrence of 'dog'.\nconst firstOccurrence = textFinder.findNext();\n\n// Replaces the last found occurrence of 'dog' with 'cat' and returns the number\n// of occurrences replaced.\nconst numOccurrencesReplaced = textFinder.replaceWith('cat');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.deleteCells(SpreadsheetApp.Dimension.COLUMNS);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getActiveRange();\n\n// All row and column groups within the range are expanded.\nrange.expandGroups();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange(1, 1, 2, 5);\n\n// Logs \"A1:E2\"\nLogger.log(range.getA1Notation());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B5');\nLogger.log(cell.getBackground());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B5');\nLogger.log(cell.getBackgroundObject().asRgbColor().asHexString());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5:C6');\nconst bgColors = range.getBackgroundObjects();\nfor (const i in bgColors) {\n for (const j in bgColors[i]) {\n Logger.log(bgColors[i][j].asRgbColor().asHexString());\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5:C6');\nconst bgColors = range.getBackgrounds();\nfor (const i in bgColors) {\n for (const j in bgColors[i]) {\n Logger.log(bgColors[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Sets a range.\nconst range = sheet.getRange('A1:K50');\n\n// Gets the banding info for the range.\nconst bandings = range.getBandings();\n\n// Logs the second row color for each banding to the console.\nfor (const banding of bandings) {\n console.log(banding.getSecondRowColor());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D4');\n\n// The row and column here are relative to the range\n// getCell(1,1) in this code returns the cell at B2\nconst cell = range.getCell(1, 1);\nLogger.log(cell.getValue());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D4');\n// Logs \"2.0\"\nLogger.log(range.getColumn());\n```\n\nExample:\n```text\n// Assume the active spreadsheet is blank.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nsheet.getRange('C2').setValue(100);\nsheet.getRange('B3').setValue(100);\nsheet.getRange('D3').setValue(100);\nsheet.getRange('C4').setValue(100);\n// Logs \"B2:D4\"\nLogger.log(sheet.getRange('C3').getDataRegion().getA1Notation());\n```\n\nExample:\n```text\n// Assume the active spreadsheet is blank.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nsheet.getRange('C2').setValue(100);\nsheet.getRange('B3').setValue(100);\nsheet.getRange('D3').setValue(100);\nsheet.getRange('C4').setValue(100);\n// Logs \"C2:C4\"\nLogger.log(\n sheet.getRange('C3')\n .getDataRegion(SpreadsheetApp.Dimension.ROWS)\n .getA1Notation(),\n);\n// Logs \"B3:D3\"\nLogger.log(\n sheet.getRange('C3')\n .getDataRegion(SpreadsheetApp.Dimension.COLUMNS)\n .getA1Notation(),\n);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1 on Sheet1.\nconst range = sheet.getRange('A1');\n\n// Gets the data source formula from cell A1.\nconst dataSourceFormula = range.getDataSourceFormula();\n\n// Gets the formula.\nconst formula = dataSourceFormula.getFormula();\n\n// Logs the formula.\nconsole.log(formula);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:B5 on Sheet1.\nconst range = sheet.getRange('A1:B5');\n\n// Gets an array of the data source formulas in the range A1:B5.\nconst dataSourceFormulas = range.getDataSourceFormulas();\n\n// Logs the first formula in the array.\nconsole.log(dataSourceFormulas[0].getFormula());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:G50 on Sheet1.\nconst range = sheet.getRange('A1:G50');\n\n// Gets an array of the data source pivot tables in the range A1:G50.\nconst dataSourcePivotTables = range.getDataSourcePivotTables();\n\n// Logs the last time that the first pivot table in the array was refreshed.\nconsole.log(dataSourcePivotTables[0].getStatus().getLastRefreshedTime());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:G50 on Sheet1.\nconst range = sheet.getRange('A1:G50');\n\n// Gets the first data source table in the range A1:G50.\nconst dataSourceTable = range.getDataSourceTables()[0];\n\n// Logs the time of the last completed data execution on the data source table.\nconsole.log(dataSourceTable.getStatus().getLastExecutionTime());\n```\n\nExample:\n```text\nfunction doGet() {\n const ss = SpreadsheetApp.openById(\n '1khO6hBWTNNyvyyxvob7aoZTI9ZvlqqASNeq0e29Tw2c',\n );\n const sheet = ss.getSheetByName('ContinentData');\n const range = sheet.getRange('A1:B8');\n\n const template = HtmlService.createTemplateFromFile('piechart');\n template.dataSourceUrl = range.getDataSourceUrl();\n return template.evaluate();\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <!--Load the AJAX API-->\n <script type=\"text/javascript\" src=\"https://www.gstatic.com/charts/loader.js\"></script>\n <script type=\"text/javascript\">\n // Load the Visualization API and the corechart package.\n google.charts.load('current', {'packages': ['corechart']});\n\n // Set a callback to run when the Google Visualization API is loaded.\n google.charts.setOnLoadCallback(queryData);\n\n function queryData() {\n var query = new google.visualization.Query('<?= dataSourceUrl ?>');\n query.send(drawChart);\n }\n\n // Callback that creates and populates a data table,\n // instantiates the pie chart, passes in the data and\n // draws it.\n function drawChart(response) {\n if (response.isError()) {\n alert('Error: ' + response.getMessage() + ' ' + response.getDetailedMessage());\n return;\n }\n var data = response.getDataTable();\n\n // Set chart options.\n var options = {\n title: 'Population by Continent',\n width: 400,\n height: 300\n };\n\n // Instantiate and draw the chart, passing in some options.\n var chart = new google.visualization.PieChart(document.getElementById('chart_div'));\n chart.draw(data, options);\n }\n </script>\n </head>\n <body>\n <!-- Div that holds the pie chart. -->\n <div id=\"chart_div\"></div>\n </body>\n</html>\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:B7 on Sheet1.\nconst range = sheet.getRange('A1:B7');\n\n// Gets the range A1:B7 as a data table. The values in each column must be of\n// the same type.\nconst datatable = range.getDataTable();\n\n// Uses the Charts service to build a bar chart from the data table.\n// This doesn't build an embedded chart. To do that, use\n// sheet.newChart().addRange() instead.\nconst chart = Charts.newBarChart()\n .setDataTable(datatable)\n .setOption('title', 'Your Chart Title Here')\n .build();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('A1:B7');\n\n// Calling this method with \"true\" sets the first line to be the title of the\n// axes\nconst datatable = range.getDataTable(true);\n\n// Note that this doesn't build an EmbeddedChart, so you can't just use\n// Sheet#insertChart(). To do that, use sheet.newChart().addRange() instead.\nconst chart = Charts.newBarChart()\n .setDataTable(datatable)\n .setOption('title', 'Your Title Here')\n .build();\n```\n\nExample:\n```text\n// Log information about the data validation rule for cell A1.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = cell.getDataValidation();\nif (rule != null) {\n const criteria = rule.getCriteriaType();\n const args = rule.getCriteriaValues();\n Logger.log('The data validation rule is %s %s', criteria, args);\n} else {\n Logger.log('The cell does not have a data validation rule.');\n}\n```\n\nExample:\n```text\n// Change existing data validation rules that require a date in 2013 to require\n// a date in 2014.\nconst oldDates = [new Date('1/1/2013'), new Date('12/31/2013')];\nconst newDates = [new Date('1/1/2014'), new Date('12/31/2014')];\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns());\nconst rules = range.getDataValidations();\n\nfor (let i = 0; i < rules.length; i++) {\n for (let j = 0; j < rules[i].length; j++) {\n const rule = rules[i][j];\n\n if (rule != null) {\n const criteria = rule.getCriteriaType();\n const args = rule.getCriteriaValues();\n\n if (criteria === SpreadsheetApp.DataValidationCriteria.DATE_BETWEEN &&\n args[0].getTime() === oldDates[0].getTime() &&\n args[1].getTime() === oldDates[1].getTime()) {\n // Create a builder from the existing rule, then change the dates.\n rules[i][j] = rule.copy().withCriteria(criteria, newDates).build();\n }\n }\n }\n}\nrange.setDataValidations(rules);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on Sheet1.\nconst range = sheet.getRange('2:2');\n\n// Adds metadata to row 2.\nrange.addDeveloperMetadata('NAME', 'GOOGLE');\n\n// Logs the metadata to console.\nfor (const metadata of range.getDeveloperMetadata()) {\n console.log(`${metadata.getKey()}: ${metadata.getValue()}`);\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets cell A30 and sets its value to 'Test code.'\nconst cell = sheet.getRange('A30');\ncell.setValue('Test code');\n\n// Gets the value and logs it to the console.\nconsole.log(cell.getDisplayValue());\n```\n\nExample:\n```text\n// The code below gets the displayed values for the range C2:G8\n// in the active spreadsheet. Note that this is a JavaScript array.\nconst values =\n SpreadsheetApp.getActiveSheet().getRange(2, 3, 6, 4).getDisplayValues();\nLogger.log(values[0][0]);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSheet();\nconst range = ss.getRange('A1:C20');\n// Gets the existing filter on the sheet that the given range belongs to.\nconst filter = range.getFilter();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontColorObject().asRgbColor().asHexString());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontColorObjects();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j].asRgbColor().asHexString());\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontFamilies();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontFamily());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontLine());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontLines();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontSize());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontSizes();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontStyle());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontStyles();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontWeight());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontWeights();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This assumes you have a function in B5 that sums up\n// B2:B4\nconst range = sheet.getRange('B5');\n\n// Logs the calculated value and the formula\nLogger.log(\n 'Calculated value: %s Formula: %s',\n range.getValue(),\n range.getFormula(),\n);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5');\nconst formula = range.getFormulaR1C1();\nLogger.log(formula);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5:C6');\nconst formulas = range.getFormulas();\nfor (const i in formulas) {\n for (const j in formulas[i]) {\n Logger.log(formulas[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5:C6');\nconst formulas = range.getFormulasR1C1();\nfor (const i in formulas) {\n for (const j in formulas[i]) {\n Logger.log(formulas[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Log the grid ID of the first sheet (by tab position) in the spreadsheet.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getGridId());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D4');\n// logs 3.0\nLogger.log(range.getHeight());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getHorizontalAlignment());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getHorizontalAlignments();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D4');\n// Logs \"4.0\"\nLogger.log(range.getLastColumn());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D4');\n// Logs \"4.0\"\nLogger.log(range.getLastRow());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B3');\n\nconst mergedRanges = range.getMergedRanges();\nfor (let i = 0; i < mergedRanges.length; i++) {\n Logger.log(mergedRanges[i].getA1Notation());\n Logger.log(mergedRanges[i].getDisplayValue());\n}\n```\n\nExample:\n```text\n// Assume the active spreadsheet is blank.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('C3:E5');\n// Logs \"C1\"\nLogger.log(range.getNextDataCell(SpreadsheetApp.Direction.UP).getA1Notation());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getNote());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getNotes();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D5');\nLogger.log(range.getNumColumns());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D5');\nLogger.log(range.getNumRows());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('C4');\nLogger.log(cell.getNumberFormat());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5:C6');\nconst formats = range.getNumberFormats();\nfor (const i in formats) {\n for (const j in formats[i]) {\n Logger.log(formats[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Gets the Rich Text value of cell D4.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('D4:F6');\nconst richText = range.getRichTextValue();\nconsole.log(richText.getText());\n```\n\nExample:\n```text\n// Gets the Rich Text values for all cells in range B5:C6\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B5:C6');\nconst values = range.getRichTextValues();\n\nfor (let i = 0; i < values.length; i++) {\n for (let j = 0; j < values[i].length; j++) {\n console.log(values[i][j].getText());\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2');\nLogger.log(range.getRow());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2');\nLogger.log(range.getRowIndex());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Gets the sheet that the range belongs to.\nconst rangeSheet = range.getSheet();\n\n// Gets the sheet name and logs it to the console.\nconsole.log(rangeSheet.getName());\n```\n\nExample:\n```text\n// Get the text direction of cell B1.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B1:D4');\nLogger.log(range.getTextDirection());\n```\n\nExample:\n```text\n// Get the text directions for all cells in range B5:C6\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B5:C6');\nconst directions = range.getTextDirections();\n\nfor (let i = 0; i < directions.length; i++) {\n for (let j = 0; j < directions[i].length; j++) {\n Logger.log(directions[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Log the text rotation settings for a cell.\nconst sheet = SpreadsheetApp.getActiveSheet();\n\nconst cell = sheet.getRange('A1');\nLogger.log(cell.getTextRotation());\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getTextRotations();\n\nfor (const i in results) {\n for (const j in results[i]) {\n const rotation = results[i][j];\n Logger.log('Cell [%s, %s] has text rotation: %v', i, j, rotation);\n }\n}\n```\n\nExample:\n```text\n// Get the text style of cell D4.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('D4:F6');\nconst style = range.getTextStyle();\nLogger.log(style);\n```\n\nExample:\n```text\n// Get the text styles for all cells in range B5:C6\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B5:C6');\nconst styles = range.getTextStyles();\n\nfor (let i = 0; i < styles.length; i++) {\n for (let j = 0; j < styles[i].length; j++) {\n Logger.log(styles[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Gets the value of the top-left cell in the range and logs it to the console.\nconsole.log(range.getValue());\n```\n\nExample:\n```text\n// The code below gets the values for the range C2:G8\n// in the active spreadsheet. Note that this is a JavaScript array.\nconst values = SpreadsheetApp.getActiveSheet().getRange(2, 3, 6, 4).getValues();\nLogger.log(values[0][0]);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getVerticalAlignment());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getVerticalAlignments();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Gets the width of the range in number of columns and logs it to the console.\nconsole.log(range.getWidth());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getWrap());\n```\n\nExample:\n```text\n// Get the text wrapping strategies for all cells in range B5:C6\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B5:C6');\nconst strategies = range.getWrapStrategies();\n\nfor (let i = 0; i < strategies.length; i++) {\n for (let j = 0; j < strategies[i].length; j++) {\n Logger.log(strategies[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Get the text wrapping strategy of cell B1.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B1:D4');\nLogger.log(range.getWrapStrategy());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getVerticalAlignments();\n\nfor (const i in results) {\n for (const j in results[i]) {\n const isWrapped = results[i][j];\n if (isWrapped) {\n Logger.log('Cell [%s, %s] has wrapped text', i, j);\n }\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.insertCells(SpreadsheetApp.Dimension.COLUMNS);\n```\n\nExample:\n```text\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\n\n// Inserts checkboxes into each cell in the range A1:B10 configured with 'true'\n// for checked and 'false' for unchecked. Also, sets the value of each cell in\n// the range A1:B10 to 'false'.\nrange.insertCheckboxes();\n```\n\nExample:\n```text\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\n\n// Inserts checkboxes into each cell in the range A1:B10 configured with 'yes'\n// for checked and the empty string for unchecked. Also, sets the value of each\n// cell in the range A1:B10 to\n// the empty string.\nrange.insertCheckboxes('yes');\n```\n\nExample:\n```text\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\n\n// Inserts checkboxes into each cell in the range A1:B10 configured with 'yes'\n// for checked and 'no' for unchecked. Also, sets the value of each cell in the\n// range A1:B10 to 'no'.\nrange.insertCheckboxes('yes', 'no');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.isBlank());\n```\n\nExample:\n```text\nconst range = SpreadsheetApp.getActive().getRange('A1:A3');\n\n// Inserts checkboxes and sets each cell value to 'no' in the range A1:A3.\nrange.insertCheckboxes('yes', 'no');\n\nconst range1 = SpreadsheetApp.getActive().getRange('A1');\nrange1.setValue('yes');\n// Sets the value of isRange1Checked as true as it contains the checked value.\nconst isRange1Checked = range1.isChecked();\n\nconst range2 = SpreadsheetApp.getActive().getRange('A2');\nrange2.setValue('no');\n// Sets the value of isRange2Checked as false as it contains the unchecked\n// value.\nconst isRange2Checked = range2.isChecked();\n\nconst range3 = SpreadsheetApp.getActive().getRange('A3');\nrange3.setValue('random');\n// Sets the value of isRange3Checked as null, as it contains an invalid checkbox\n// value.\nconst isRange3Checked = range3.isChecked();\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Determines if the end of the range is bound to a particular column and logs\n// it to the console.\nconsole.log(range.isEndColumnBounded());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Determines if the end of the range is bound to a particular row and logs it\n// to the console.\nconsole.log(range.isEndRowBounded());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B3');\n\n// True if any of the cells in A1:B3 is included in a merge.\nconst isPartOfMerge = range.isPartOfMerge();\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Determines if the start of the range is bound to a particular column and logs\n// it to the console.\nconsole.log(range.isStartColumnBounded());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Determines if the start of the range is bound to a particular row and logs it\n// to the console.\nconsole.log(range.isStartRowBounded());\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\n\n// The code below 2-dimensionally merges the cells in A1 to B3\nsheet.getRange('A1:B3').merge();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The code below merges cells C5:E5 into one cell\nconst range1 = sheet.getRange('C5:E5');\nrange1.mergeAcross();\n\n// The code below creates 2 horizontal cells, F5:H5 and F6:H6\nconst range2 = sheet.getRange('F5:H6');\nrange2.mergeAcross();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\n\n// The code below vertically merges the cells in A1 to A10\nsheet.getRange('A1:A10').mergeVertically();\n\n// The code below creates 3 merged columns: B1 to B10, C1 to C10, and D1 to D10\nsheet.getRange('B1:D10').mergeVertically();\n```\n\nExample:\n```text\n// The code below moves the first 5 columns over to the 6th column\nconst sheet = SpreadsheetApp.getActiveSheet();\nsheet.getRange('A1:E').moveTo(sheet.getRange('F1'));\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('A1');\n\n// newCell references B2\nconst newCell = cell.offset(1, 1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('A1');\n\n// newCell references B2:B3\nconst newRange = cell.offset(1, 1, 2);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('A1');\n\n// newCell references B2:C3\nconst newRange = cell.offset(1, 1, 2, 2);\n```\n\nExample:\n```text\n// Protect range A1:B10, then remove all other users from the list of editors.\nconst ss = SpreadsheetApp.getActive();\nconst range = ss.getRange('A1:B10');\nconst protection = range.protect().setDescription('Sample protected range');\n\n// Ensure the current user is an editor before removing others. Otherwise, if\n// the user's edit permission comes from a group, the script throws an exception\n// upon removing the group.\nconst me = Session.getEffectiveUser();\nprotection.addEditor(me);\nprotection.removeEditors(protection.getEditors());\nif (protection.canDomainEdit()) {\n protection.setDomainEdit(false);\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('A1:C7');\n\n// Randomizes the range\nrange.randomize();\n```\n\nExample:\n```text\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\n\n// Inserts checkboxes and sets each cell value to 'no' in the range A1:B10.\nrange.insertCheckboxes('yes', 'no');\n\nconst range1 = SpreadsheetApp.getActive().getRange('A1');\nrange1.setValue('yes');\n// Removes the checkbox data validation in cell A1 and clears its value.\nrange1.removeCheckboxes();\n\nconst range2 = SpreadsheetApp.getActive().getRange('A2');\nrange2.setValue('random');\n// Removes the checkbox data validation in cell A2 but does not clear its value.\nrange2.removeCheckboxes();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B1:D7');\n\n// Remove duplicate rows in the range.\nrange.removeDuplicates();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B1:D7');\n\n// Remove rows which have duplicate values in column B.\nrange.removeDuplicates([2]);\n\n// Remove rows which have duplicate values in both columns B and D.\nrange.removeDuplicates([2, 4]);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D5');\nrange.setBackground('red');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst bgColor = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.BACKGROUND)\n .build();\n\nconst range = sheet.getRange('B2:D5');\nrange.setBackgroundObject(bgColor);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst colorAccent1 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT1)\n .build();\nconst colorAccent2 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT2)\n .build();\nconst colorAccent3 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT3)\n .build();\nconst colorAccent4 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT4)\n .build();\n\nconst colors = [\n [colorAccent1, colorAccent2],\n [colorAccent3, colorAccent4],\n];\n\nconst cell = sheet.getRange('B5:C6');\ncell.setBackgroundObjects(colors);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\n\n// Sets the background to white\ncell.setBackgroundRGB(255, 255, 255);\n\n// Sets the background to red\ncell.setBackgroundRGB(255, 0, 0);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst colors = [\n ['red', 'white', 'blue'],\n ['#FF0000', '#FFFFFF', '#0000FF'], // These are the hex equivalents\n];\n\nconst cell = sheet.getRange('B5:D6');\ncell.setBackgrounds(colors);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\n// Sets borders on the top and bottom, but leaves the left and right unchanged\ncell.setBorder(true, null, true, null, false, false);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\n// Sets borders on the top and bottom, but leaves the left and right unchanged\n// Also sets the color to \"red\", and the border to \"DASHED\".\ncell.setBorder(\n true,\n null,\n true,\n null,\n false,\n false,\n 'red',\n SpreadsheetApp.BorderStyle.DASHED,\n);\n```\n\nExample:\n```text\n// Set the data validation rule for cell A1 to require a value from B1:B10.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst range = SpreadsheetApp.getActive().getRange('B1:B10');\nconst rule =\n SpreadsheetApp.newDataValidation().requireValueInRange(range).build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation rules for Sheet1!A1:B5 to require a value from\n// Sheet2!A1:A10.\nconst destinationRange =\n SpreadsheetApp.getActive().getSheetByName('Sheet1').getRange('A1:B5');\nconst sourceRange =\n SpreadsheetApp.getActive().getSheetByName('Sheet2').getRange('A1:A10');\nconst rule =\n SpreadsheetApp.newDataValidation().requireValueInRange(sourceRange).build();\nconst rules = destinationRange.getDataValidations();\nfor (let i = 0; i < rules.length; i++) {\n for (let j = 0; j < rules[i].length; j++) {\n rules[i][j] = rule;\n }\n}\ndestinationRange.setDataValidations(rules);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontColor('red');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst color = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.TEXT)\n .build();\n\nconst cell = sheet.getRange('B2');\ncell.setFontColor(color);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst colorAccent1 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT1)\n .build();\nconst colorAccent2 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT2)\n .build();\nconst colorAccent3 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT3)\n .build();\nconst colorAccent4 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT4)\n .build();\n\nconst colors = [\n [colorAccent1, colorAccent2],\n [colorAccent3, colorAccent4],\n];\n\nconst cell = sheet.getRange('B5:C6');\ncell.setFontColorObjects(colors);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst colors = [\n ['red', 'white', 'blue'],\n ['#FF0000', '#FFFFFF', '#0000FF'], // These are the hex equivalents\n];\n\nconst cell = sheet.getRange('B5:D6');\ncell.setFontColors(colors);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst fonts = [\n ['Arial', 'Helvetica', 'Verdana'],\n ['Courier New', 'Arial', 'Helvetica'],\n];\n\nconst cell = sheet.getRange('B2:D3');\ncell.setFontFamilies(fonts);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontFamily('Helvetica');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontLine('line-through');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst fontLines = [['underline', 'line-through', 'none']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setFontLines(fontLines);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontSize(20);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst fontSizes = [[16, 20, 24]];\n\nconst range = sheet.getRange('B2:D2');\nrange.setFontSizes(fontSizes);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontStyle('italic');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst fontStyles = [['italic', 'normal']];\n\nconst range = sheet.getRange('B2:C2');\nrange.setFontStyles(fontStyles);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontWeight('bold');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst fontStyles = [['bold', 'bold', 'normal']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setFontWeights(fontStyles);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B5');\ncell.setFormula('=SUM(B3:B4)');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B5');\n// This sets the formula to be the sum of the 3 rows above B5\ncell.setFormulaR1C1('=SUM(R[-3]C[0]:R[-1]C[0])');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This sets the formulas to be a row of sums, followed by a row of averages\n// right below. The size of the two-dimensional array must match the size of the\n// range.\nconst formulas = [\n ['=SUM(B2:B4)', '=SUM(C2:C4)', '=SUM(D2:D4)'],\n ['=AVERAGE(B2:B4)', '=AVERAGE(C2:C4)', '=AVERAGE(D2:D4)'],\n];\n\nconst cell = sheet.getRange('B5:D6');\ncell.setFormulas(formulas);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This creates formulas for a row of sums, followed by a row of averages.\nconst sumOfRowsAbove = '=SUM(R[-3]C[0]:R[-1]C[0])';\nconst averageOfRowsAbove = '=AVERAGE(R[-4]C[0]:R[-2]C[0])';\n\n// The size of the two-dimensional array must match the size of the range.\nconst formulas = [\n [sumOfRowsAbove, sumOfRowsAbove, sumOfRowsAbove],\n [averageOfRowsAbove, averageOfRowsAbove, averageOfRowsAbove],\n];\n\nconst cell = sheet.getRange('B5:D6');\n// This sets the formula to be the sum of the 3 rows above B5.\ncell.setFormulasR1C1(formulas);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setHorizontalAlignment('center');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst horizontalAlignments = [['left', 'right', 'center']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setHorizontalAlignments(horizontalAlignments);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setNote('This is a note');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst notes = [\n ['it goes', 'like this', 'the fourth, the fifth'],\n ['the minor fall', 'and the', 'major lift'],\n];\n\nconst cell = sheet.getRange('B2:D3');\ncell.setNotes(notes);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\n// Always show 3 decimal points\ncell.setNumberFormat('0.000');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst formats = [['0.000', '0,000,000', '$0.00']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setNumberFormats(formats);\n```\n\nExample:\n```text\n// Sets all cells in range B2:D4 to have the text \"Hello world\", with \"Hello\"\n// bolded.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B2:D4');\nconst bold = SpreadsheetApp.newTextStyle().setBold(true).build();\nconst richText = SpreadsheetApp.newRichTextValue()\n .setText('Hello world')\n .setTextStyle(0, 5, bold)\n .build();\nrange.setRichTextValue(richText);\n```\n\nExample:\n```text\n// Sets the cells in range A1:A2 to have Rich Text values.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:A2');\nconst bold = SpreadsheetApp.newTextStyle().setBold(true).build();\nconst italic = SpreadsheetApp.newTextStyle().setItalic(true).build();\nconst richTextA1 = SpreadsheetApp.newRichTextValue()\n .setText('This cell is bold')\n .setTextStyle(bold)\n .build();\nconst richTextA2 = SpreadsheetApp.newRichTextValue()\n .setText('bold words, italic words')\n .setTextStyle(0, 11, bold)\n .setTextStyle(12, 24, italic)\n .build();\nrange.setRichTextValues([[richTextA1], [richTextA2]]);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can useSpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets cell A30 and sets its hyperlink value.\nconst range = sheet.getRange('A30');\nrange.setValue('https://www.example.com');\n\n// Sets cell A30 to show hyperlinks.\nrange.setShowHyperlink(true);\n```\n\nExample:\n```text\n// Sets right-to-left text direction for the range.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B5:C6');\nrange.setTextDirection(SpreadsheetApp.TextDirection.RIGHT_TO_LEFT);\n```\n\nExample:\n```text\n// Copies all of the text directions from range A1:B2 over to range C5:D6.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range1 = sheet.getRange('A1:B2');\nconst range2 = sheet.getRange('C5:D6');\n\nrange2.setTextRotations(range1.getTextDirections());\n```\n\nExample:\n```text\n// Sets all cell's in range B2:D4 to have text rotated up 45 degrees.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B2:D4');\n\nrange.setTextRotation(45);\n```\n\nExample:\n```text\n// Sets all cell's in range B2:D4 to have the same text rotation settings as\n// cell A1.\nconst sheet = SpreadsheetApp.getActiveSheet();\n\nconst rotation = sheet.getRange('A1').getTextRotation();\n\nsheet.getRange('B2:D4').setTextRotation(rotation);\n```\n\nExample:\n```text\n// Copies all of the text rotations from range A1:B2 over to range C5:D6.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range1 = sheet.getRange('A1:B2');\nconst range2 = sheet.getRange('C5:D6');\n\nrange2.setTextRotations(range1.getTextRotations());\n```\n\nExample:\n```text\n// Sets the cells in range C5:D6 to have underlined size 15 font.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('C5:D6');\nconst style =\n SpreadsheetApp.newTextStyle().setFontSize(15).setUnderline(true).build();\nrange.setTextStyle(style);\n```\n\nExample:\n```text\n// Sets text styles for cells in range A1:B2\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B2');\nconst bold = SpreadsheetApp.newTextStyle().setBold(true).build();\nconst otherStyle = SpreadsheetApp.newTextStyle()\n .setBold(true)\n .setUnderline(true)\n .setItalic(true)\n .setForegroundColor('#335522')\n .setFontSize(44)\n .build();\nrange.setTextStyles([\n [bold, otherStyle],\n [otherStyle, bold],\n]);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setValue(100);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst values = [['2.000', '1,000,000', '$2.99']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setValues(values);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setVerticalAlignment('middle');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst alignments = [['top', 'middle', 'bottom']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setVerticalAlignments(alignments);\n```\n\nExample:\n```text\n// Sets all cell's in range B2:D4 to have vertically stacked text.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B2:D4');\n\nrange.setVerticalText(true);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setWrap(true);\n```\n\nExample:\n```text\n// Copies all of the wrap strategies from range A1:B2 over to range C5:D6.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range1 = sheet.getRange('A1:B2');\nconst range2 = sheet.getRange('C5:D6');\n\nrange2.setWrapStrategies(range1.getWrapStrategies());\n```\n\nExample:\n```text\n// Sets all cells in range B2:D4 to use the clip wrap strategy.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B2:D4');\n\nrange.setWrapStrategy(SpreadsheetApp.WrapStrategy.CLIP);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst wraps = [[true, true, false]];\n\nconst range = sheet.getRange('B2:D2');\nrange.setWraps(wraps);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getActiveRange();\n\n// The column grouping depth is increased by 1.\nrange.shiftColumnGroupDepth(1);\n\n// The column grouping depth is decreased by 1.\nrange.shiftColumnGroupDepth(-1);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getActiveRange();\n\n// The row grouping depth is increased by 1.\nrange.shiftRowGroupDepth(1);\n\n// The row grouping depth is decreased by 1.\nrange.shiftRowGroupDepth(-1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('A1:C7');\n\n// Sorts by the values in the first column (A)\nrange.sort(1);\n\n// Sorts by the values in the second column (B)\nrange.sort(2);\n\n// Sorts descending by column B\nrange.sort({column: 2, ascending: false});\n\n// Sorts descending by column B, then ascending by column A\n// Note the use of an array\nrange.sort([\n {column: 2, ascending: false},\n {column: 1, ascending: true},\n]);\n\n// For rows that are sorted in ascending order, the \"ascending\" parameter is\n// optional, and just an integer with the column can be used instead. Note that\n// in general, keeping the sort specification consistent results in more\n// readable code. You can express the earlier sort as:\nrange.sort([{column: 2, ascending: false}, 1]);\n\n// Alternatively, if you want all columns to be in ascending order, you can use\n// the following (this makes column 2 ascending)\nrange.sort([2, 1]);\n// ... which is equivalent to\nrange.sort([\n {column: 2, ascending: true},\n {column: 1, ascending: true},\n]);\n```\n\nExample:\n```text\n// A1:A3 has the following values:\n// A B C\n// 1 |one,one,one | | |\n// 2 |two,two,two | | |\n// 3 |three,three,three| | |\n\nconst range = SpreadsheetApp.getActiveSheet().getRange('A1:A3');\nrange.splitTextToColumns();\n\n// Result after splitting the text to columns:\n// A B C\n// 1 |one |one |one |\n// 2 |two |two |two |\n// 3 |three |three |three |\n```\n\nExample:\n```text\n// A1:A3 has the following values:\n// A B C\n// 1 |one#one#one | | |\n// 2 |two#two#two | | |\n// 3 |three#three#three| | |\n\nconst range = SpreadsheetApp.getActiveSheet().getRange('A1:A3');\nrange.splitTextToColumns('#');\n\n// Result after splitting the text to columns:\n// A B C\n// 1 |one |one |one |\n// 2 |two |two |two |\n// 3 |three |three |three |\n```\n\nExample:\n```text\n// A1:A3 has the following values:\n// A B C\n// 1 |one;one;one | | |\n// 2 |two;two;two | | |\n// 3 |three;three;three| | |\n\nconst range = SpreadsheetApp.getActiveSheet().getRange('A1:A3');\nrange.splitTextToColumns(SpreadsheetApp.TextToColumnsDelimiter.SEMICOLON);\n\n// Result after splitting the text to columns:\n// A B C\n// 1 |one |one |one |\n// 2 |two |two |two |\n// 3 |three |three |three |\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getRange('A1:A4');\nrange.activate();\nrange.setValues([\n ' preceding space',\n 'following space ',\n 'two middle spaces',\n ' =SUM(1,2)',\n]);\n\nrange.trimWhitespace();\n\nconst values = range.getValues();\n// Values are ['preceding space', 'following space', 'two middle spaces',\n// '=SUM(1,2)']\n```\n\nExample:\n```text\n// Changes the state of cells which currently contain either the checked or\n// unchecked value configured in the range A1:B10 to 'unchecked'.\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\nrange.uncheck();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontColor());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontColors();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.204Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":198,"totalLines":2842,"estimatedTokens":18780}}1043{"id":"doc-class_richlink_apps_script_google_for_developers-2357e49b","source":"documentation","title":"Class RichLink | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/document/rich-link","text":"Example:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Append a styled paragraph.\nconst par = body.appendParagraph('A bold, italicized paragraph.');\npar.setBold(true);\npar.setItalic(true);\n\n// Retrieve the paragraph's attributes.\nconst atts = par.getAttributes();\n\n// Log the paragraph attributes.\nfor (const att in atts) {\n Logger.log(`${att}:${atts[att]}`);\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Obtain the first element in the active tab's body.\n\nconst firstChild = body.getChild(0);\n\n// Use getType() to determine the element's type.\nif (firstChild.getType() === DocumentApp.ElementType.PARAGRAPH) {\n Logger.log('The first element is a paragraph.');\n} else {\n Logger.log('The first element is not a paragraph.');\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Example 1: Merge paragraphs\n// Append two paragraphs to the document's active tab.\nconst par1 = body.appendParagraph('Paragraph 1.');\nconst par2 = body.appendParagraph('Paragraph 2.');\n// Merge the newly added paragraphs into a single paragraph.\npar2.merge();\n\n// Example 2: Merge table cells\n// Create a two-dimensional array containing the table's cell contents.\nconst cells = [\n ['Row 1, Cell 1', 'Row 1, Cell 2'],\n ['Row 2, Cell 1', 'Row 2, Cell 2'],\n];\n// Build a table from the array.\nconst table = body.appendTable(cells);\n// Get the first row in the table.\nconst row = table.getRow(0);\n// Get the two cells in this row.\nconst cell1 = row.getCell(0);\nconst cell2 = row.getCell(1);\n// Merge the current cell into its preceding sibling element.\nconst merged = cell2.merge();\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Remove all images in the active tab's body.\nconst imgs = body.getImages();\nfor (let i = 0; i < imgs.length; i++) {\n imgs[i].removeFromParent();\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Define a custom paragraph style.\nconst style = {};\nstyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =\n DocumentApp.HorizontalAlignment.RIGHT;\nstyle[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';\nstyle[DocumentApp.Attribute.FONT_SIZE] = 18;\nstyle[DocumentApp.Attribute.BOLD] = true;\n\n// Append a plain paragraph.\nconst par = body.appendParagraph('A paragraph with custom style.');\n\n// Apply the custom style.\npar.setAttributes(style);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.206Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":103,"estimatedTokens":707}}1044{"id":"doc-class_embeddedareachartbuilder_apps_script_googl-f005d9bb","source":"documentation","title":"Class EmbeddedAreaChartBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/embedded-area-chart-builder","text":"Example:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B8'))\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This code updates the chart to use only the new ranges while preserving the\n// existing formatting of the chart.\nconst chart = sheet.getCharts()[0];\nconst newChart = chart.modify()\n .clearRanges()\n .addRange(sheet.getRange('A1:A5'))\n .addRange(sheet.getRange('B1:B5'))\n .build();\nsheet.updateChart(newChart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst chartBuilder = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B8'))\n .setPosition(5, 5, 0, 0);\n\n// This method returns the exact same data as Chart#getContainerInfo()\nconst containerInfo = chartBuilder.getContainer();\n\n// Logs the values used in setPosition()\nLogger.log(\n 'Anchor Column: %s\\r\\nAnchor Row %s\\r\\nOffset X %s\\r\\nOffset Y %s',\n containerInfo.getAnchorColumn(),\n containerInfo.getAnchorRow(),\n containerInfo.getOffsetX(),\n containerInfo.getOffsetY(),\n);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst chartBuilder = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B8'))\n .setPosition(5, 5, 0, 0);\n\nconst ranges = chartBuilder.getRanges();\n\n// There's only one range as a data source for this chart,\n// so this logs \"A1:B8\"\nfor (const i in ranges) {\n const range = ranges[i];\n Logger.log(range.getA1Notation());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst firstRange = sheet.getRange('A1:B5');\nconst secondRange = sheet.getRange('A6:B8');\n\nconst chartBuilder = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(firstRange)\n // This range renders in a different color\n .addRange(secondRange)\n .setPosition(5, 5, 0, 0);\n\n// Note that you can use either of these two formats, but the range\n// MUST match up with a range that was added via addRange(), or it\n// is not removed, and does not throw an exception\nchartBuilder.removeRange(firstRange);\nchartBuilder.removeRange(sheet.getRange('A6:B8'));\n\nconst chart = chartBuilder.build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\n// Creates a pie chart builder and sets drawing of the slices in a\n// counter-clockwise manner.\nconst builder = Charts.newPieChart();\nbuilder.reverseCategories();\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the background color to gray\nconst builder = Charts.newLineChart();\nbuilder.setBackgroundColor('gray');\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the first two lines to be drawn in\n// green and red, respectively.\nconst builder = Charts.newLineChart();\nbuilder.setColors(['green', 'red']);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setHiddenDimensionStrategy(\n Charts.ChartHiddenDimensionStrategy.IGNORE_COLUMNS,\n )\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the legend position to right.\nconst builder = Charts.newLineChart();\nbuilder.setLegendPosition(Charts.Position.RIGHT);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets it up for a blue, 26-point legend.\nconst textStyleBuilder =\n Charts.newTextStyle().setColor('#0000FF').setFontSize(26);\nconst style = textStyleBuilder.build();\nconst builder = Charts.newLineChart();\nbuilder.setLegendTextStyle(style);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B10');\nconst range2 = sheet.getRange('C:C10');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .addRange(range2)\n .setMergeStrategy(Charts.ChartMergeStrategy.MERGE_ROWS)\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setNumHeaders(1)\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = spreadsheet.getSheets()[0];\nconst chart = sheet.newChart()\n .setOption('title', 'Earnings projections')\n .setOption('legend', {\n position: 'top',\n textStyle: { color: 'blue', fontSize: 16 },\n }).build();\n```\n\nExample:\n```text\n// Creates a line chart builder and sets large point style.\nconst builder = Charts.newLineChart();\nbuilder.setPointStyle(Charts.PointStyle.LARGE);\n```\n\nExample:\n```text\n// Creates a line chart builder and title to 'My Line Chart'.\nconst builder = Charts.newLineChart();\nbuilder.setTitle('My Line Chart');\n```\n\nExample:\n```text\n// Creates a line chart builder and sets it up for a blue, 26-point title.\nconst textStyleBuilder =\n Charts.newTextStyle().setColor('#0000FF').setFontSize(26);\nconst style = textStyleBuilder.build();\nconst builder = Charts.newLineChart();\nbuilder.setTitleTextStyle(style);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setTransposeRowsAndColumns(true)\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the X-axis text style to blue, 18-point\n// font.\nconst textStyle =\n Charts.newTextStyle().setColor('blue').setFontSize(18).build();\nconst builder = Charts.newLineChart();\nbuilder.setXAxisTextStyle(textStyle);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the X-axis title.\nconst builder = Charts.newLineChart();\nbuilder.setTitle('X-axis Title');\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the X-axis title text style to blue,\n// 18-point font.\nconst textStyle =\n Charts.newTextStyle().setColor('blue').setFontSize(18).build();\nconst builder = Charts.newLineChart();\nbuilder.setXAxisTitleTextStyle(textStyle);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the Y-axis text style to blue, 18-point\n// font.\nconst textStyle =\n Charts.newTextStyle().setColor('blue').setFontSize(18).build();\nconst builder = Charts.newLineChart();\nbuilder.setYAxisTextStyle(textStyle);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the Y-axis title.\nconst builder = Charts.newLineChart();\nbuilder.setYAxisTitle('Y-axis Title');\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the Y-axis title text style to blue,\n// 18-point font.\nconst textStyle =\n Charts.newTextStyle().setColor('blue').setFontSize(18).build();\nconst builder = Charts.newLineChart();\nbuilder.setYAxisTitleTextStyle(textStyle);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.209Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":25,"totalLines":313,"estimatedTokens":2136}}1045{"id":"doc-class_datavalidationbuilder_apps_script_google_f-38c26367","source":"documentation","title":"Class DataValidationBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/data-validation-builder","text":"Example:\n```text\n// Set the data validation for cell A1 to require a value from B1:B10.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst range = SpreadsheetApp.getActive().getRange('B1:B10');\nconst rule =\n SpreadsheetApp.newDataValidation().requireValueInRange(range).build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Change existing data validation rules that require a date in 2013 to require\n// a date in 2014.\nconst oldDates = [new Date('1/1/2013'), new Date('12/31/2013')];\nconst newDates = [new Date('1/1/2014'), new Date('12/31/2014')];\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns());\nconst rules = range.getDataValidations();\n\nfor (let i = 0; i < rules.length; i++) {\n for (let j = 0; j < rules[i].length; j++) {\n const rule = rules[i][j];\n\n if (rule != null) {\n const criteria = rule.getCriteriaType();\n const args = rule.getCriteriaValues();\n\n if (criteria === SpreadsheetApp.DataValidationCriteria.DATE_BETWEEN &&\n args[0].getTime() === oldDates[0].getTime() &&\n args[1].getTime() === oldDates[1].getTime()) {\n // Create a builder from the existing rule, then change the dates.\n rules[i][j] = rule.copy().withCriteria(criteria, newDates).build();\n }\n }\n }\n}\nrange.setDataValidations(rules);\n```\n\nExample:\n```text\n// Log information about the data validation rule for cell A1.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = cell.getDataValidation();\nif (rule != null) {\n const criteria = rule.getCriteriaType();\n const args = rule.getCriteriaValues();\n Logger.log('The data validation rule is %s %s', criteria, args);\n} else {\n Logger.log('The cell does not have a data validation rule.');\n}\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a boolean value; the value is\n// rendered as a checkbox.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation().requireCheckbox().build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a custom checked value that is\n// rendered as a checkbox.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation().requireCheckbox('APPROVED').build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require custom checked values that are\n// rendered as a checkbox.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireCheckbox('APPROVED', 'PENDING')\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a date.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation().requireDate().build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a date after January 1, 2013.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireDateAfter(new Date('1/1/2013'))\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a date before January 1, 2013.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireDateBefore(new Date('1/1/2013'))\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a date in 2013.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation()\n .requireDateBetween(new Date('1/1/2013'), new Date('12/31/2013'))\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a date equal to January 1,\n// 2013.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireDateEqualTo(new Date('1/1/2013'))\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a date not in 2013.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation()\n .requireDateNotBetween(new Date('1/1/2013'), new Date('12/31/2013'))\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a date on or after January 1,\n// 2013.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireDateOnOrAfter(new Date('1/1/2013'))\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a date on or before January 1,\n// 2013.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireDateOnOrBefore(new Date('1/1/2013'))\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to equal B1 with a custom formula.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireFormulaSatisfied('=EQ(A1,B1)')\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a number between 1 and 10.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation().requireNumberBetween(1, 10).build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a number equal\n// to 3.1415926536.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation().requireNumberEqualTo(Math.PI).build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a number greater than 0.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation().requireNumberGreaterThan(0).build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a number greater than or equal\n// to 0.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireNumberGreaterThanOrEqualTo(0)\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a number less than 0.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation().requireNumberLessThan(0).build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a number less than or equal to\n// 0.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireNumberLessThanOrEqualTo(0)\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a number not between 1 and 10.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation().requireNumberNotBetween(1, 10).build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a number not equal to 0.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation().requireNumberNotEqualTo(0).build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require any value that includes\n// \"Google\".\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation().requireTextContains('Google').build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require any value that does not\n// include \"@\".\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation().requireTextDoesNotContain('@').build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require \"Yes\".\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule =\n SpreadsheetApp.newDataValidation().requireTextEqualTo('Yes').build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require text in the form of an email\n// address.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation().requireTextIsEmail().build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require text in the form of a URL.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation().requireTextIsUrl().build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require \"Yes\" or \"No\", with a dropdown\n// menu.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireValueInList(['Yes', 'No'])\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require \"Yes\" or \"No\", with no\n// dropdown menu.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireValueInList(['Yes', 'No'], false)\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require a value from B1:B10, with a\n// dropdown menu.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst range = SpreadsheetApp.getActive().getRange('B1:B10');\nconst rule =\n SpreadsheetApp.newDataValidation().requireValueInRange(range).build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation for cell A1 to require value from B1:B10, with no\n// dropdown menu.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst range = SpreadsheetApp.getActive().getRange('B1:B10');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireValueInRange(range, false)\n .build();\ncell.setDataValidation(rule);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.211Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":32,"totalLines":346,"estimatedTokens":2664}}1046{"id":"doc-class_document_apps_script_google_for_developers-43a250e9","source":"documentation","title":"Class Document | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/document/document","text":"Example:\n```text\n// Open a document by ID.\nlet doc = DocumentApp.openById('<my-id>');\n\n// Create and open a document.\ndoc = DocumentApp.create('Document Title');\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the active or first tab's body and adds a paragraph.\nconst paragraph = doc.getBody().appendParagraph('My new paragraph.');\n\n// Creates a position at the first character of the paragraph text.\nconst position = doc.newPosition(paragraph.getChild(0), 0);\n\n// Adds a bookmark at the first character of the paragraph text.\nconst bookmark = doc.addBookmark(position);\n\n// Logs the bookmark ID to the console.\nconsole.log(bookmark.getId());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Adds a footer to the document's active or first tab.\nconst footer = doc.addFooter();\n\n// Sets the footer text to 'This is a footer.'\nfooter.setText('This is a footer');\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Adds a header to the document's active or first tab.\nconst header = doc.addHeader();\n\n// Sets the header text to 'This is a header.'\nheader.setText('This is a header');\n```\n\nExample:\n```text\n// Creates a named range that includes every table in the active tab.\nconst doc = DocumentApp.getActiveDocument();\nconst rangeBuilder = doc.newRange();\nconst tables = doc.getBody().getTables();\nfor (let i = 0; i < tables.length; i++) {\n rangeBuilder.addElement(tables[i]);\n}\n// Adds the named range to the document's active tab.\ndoc.addNamedRange('Document tables', rangeBuilder.build());\n```\n\nExample:\n```text\n// Display a dialog box that shows the title of the tab that the\n// user is currently viewing.\nconst tab = DocumentApp.getActiveDocument().getActiveTab();\nDocumentApp.getUi().alert(`ID of selected tab: ${tab.getTitle()}`);\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the document as a PDF.\nconst pdf = doc.getAs('application/pdf');\n\n// Logs the name of the PDF to the console.\nconsole.log(pdf.getName());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Retrieves the current document's contents as a blob and logs it to the\n// console.\nconsole.log(doc.getBlob().getContentType());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the active or first tab's body.\nconst body = doc.getBody();\n\n// Gets the body text and logs it to the console.\nconsole.log(body.getText());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the bookmark by its ID in the document's active or first tab.\nconst bookmark = doc.getBookmark('id.xyz654321');\n\n// If the bookmark exists, logs the character offset of its position to the\n// console. otherwise, logs 'No bookmark exists with the given ID.' to the\n// console.\nif (bookmark) {\n console.log(bookmark.getPosition().getOffset());\n} else {\n console.log('No bookmark exists with the given ID.');\n}\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets all of the bookmarks in the document's active or first tab.\nconst bookmarks = doc.getBookmarks();\n\n// Logs the number of bookmarks in the tab to the console.\nconsole.log(bookmarks.length);\n```\n\nExample:\n```text\n// Insert some text at the cursor position and make it bold.\nconst cursor = DocumentApp.getActiveDocument().getCursor();\nif (cursor) {\n // Attempt to insert text at the cursor position. If the insertion returns\n // null, the cursor's containing element doesn't allow insertions, so show the\n // user an error message.\n const element = cursor.insertText('ಠ‿ಠ');\n if (element) {\n element.setBold(true);\n } else {\n DocumentApp.getUi().alert('Cannot insert text here.');\n }\n} else {\n DocumentApp.getUi().alert('Cannot find a cursor.');\n}\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the text of the active or first tab's footer and logs it to the console.\nconsole.log(doc.getFooter().getText());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the first footnote in the active or first tab's body.\nconst footnote = doc.getFootnotes()[0];\n\n// Logs footnote contents to the console.\nconsole.log(footnote.getFootnoteContents().getText());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the text of the active or first tab's header and logs it to the console.\nconsole.log(doc.getHeader().getText());\n```\n\nExample:\n```text\n// Display a dialog box that tells the user how many elements are included in\n// the selection.\nconst selection = DocumentApp.getActiveDocument().getSelection();\nif (selection) {\n const elements = selection.getRangeElements();\n DocumentApp.getUi().alert(`Number of selected elements: ${elements.length}`);\n} else {\n DocumentApp.getUi().alert('Nothing is selected.');\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\n\n// Send out the link to open the document.\nMailApp.sendEmail('<email-address>', doc.getName(), doc.getUrl());\n```\n\nExample:\n```text\n// Append a paragraph to the active tab, then place the user's cursor after the\n// first word of the new paragraph.\nconst doc = DocumentApp.getActiveDocument();\nconst paragraph = doc.getBody().appendParagraph('My new paragraph.');\nconst position = doc.newPosition(paragraph.getChild(0), 2);\ndoc.setCursor(position);\n```\n\nExample:\n```text\n// Change the user's selection to a range that includes every table in the\n// active tab.\nconst doc = DocumentApp.getActiveDocument();\nconst rangeBuilder = doc.newRange();\nconst tables = doc.getBody().getTables();\nfor (let i = 0; i < tables.length; i++) {\n rangeBuilder.addElement(tables[i]);\n}\ndoc.setSelection(rangeBuilder.build());\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\n\n// Sets the user's selected tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst tab = doc.setActiveTab('123abc');\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\n\n// Append a paragraph, then place the user's cursor after the first word of the\n// new paragraph.\nconst paragraph = documentTab.getBody().appendParagraph('My new paragraph.');\nconst position = documentTab.newPosition(paragraph.getChild(0), 2);\ndoc.setCursor(position);\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\n\n// Change the user's selection to a range that includes every table in the\n// document.\nconst rangeBuilder = documentTab.newRange();\nconst tables = documentTab.getBody().getTables();\nfor (let i = 0; i < tables.length; i++) {\n rangeBuilder.addElement(tables[i]);\n}\ndoc.setSelection(rangeBuilder.build());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.214Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":22,"totalLines":287,"estimatedTokens":2211}}1047{"id":"doc-class_urlfetchapp_apps_script_google_for_develop-44c523b4","source":"documentation","title":"Class UrlFetchApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app","text":"Example:\n```text\n// The code below logs the HTML code of the Google home page.\nconst response = UrlFetchApp.fetch('http://www.google.com/');\nLogger.log(response.getContentText());\n```\n\nExample:\n```text\n// Make a GET request and log the returned content.\nconst response = UrlFetchApp.fetch('http://www.google.com/');\nLogger.log(response.getContentText());\n```\n\nExample:\n```text\n// Make a POST request with form data.\nconst resumeBlob = Utilities.newBlob('Hire me!', 'text/plain', 'resume.txt');\nconst formData = {\n name: 'Bob Smith',\n email: 'bob@example.com',\n resume: resumeBlob,\n};\n// Because payload is a JavaScript object, it is interpreted as\n// as form data. (No need to specify contentType; it automatically\n// defaults to either 'application/x-www-form-urlencoded'\n// or 'multipart/form-data')\nconst options = {\n method: 'post',\n payload: formData,\n};\nUrlFetchApp.fetch('https://httpbin.org/post', options);\n```\n\nExample:\n```text\n// Make a POST request with a JSON payload.\nconst data = {\n name: 'Bob Smith',\n age: 35,\n pets: ['fido', 'fluffy'],\n};\nconst options = {\n method: 'post',\n contentType: 'application/json',\n // Convert the JavaScript object to a JSON string.\n payload: JSON.stringify(data),\n};\nUrlFetchApp.fetch('https://httpbin.org/post', options);\n```\n\nExample:\n```text\n// Make both a POST request with form data, and a GET request.\nconst resumeBlob = Utilities.newBlob('Hire me!', 'text/plain', 'resume.txt');\nconst formData = {\n name: 'Bob Smith',\n email: 'bob@example.com',\n resume: resumeBlob,\n};\n// Because payload is a JavaScript object, it is interpreted as\n// as form data. (No need to specify contentType; it defaults to either\n// 'application/x-www-form-urlencoded' or 'multipart/form-data')\nconst request1 = {\n url: 'https://httpbin.org/post',\n method: 'post',\n payload: formData,\n};\n// A request may also just be a URL.\nconst request2 = 'https://httpbin.org/get?key=value';\nUrlFetchApp.fetchAll([request1, request2]);\n```\n\nExample:\n```text\n// The code below logs the value for every key of the returned map.\nconst response = UrlFetchApp.getRequest('http://www.google.com/');\nfor (const i in response) {\n Logger.log(`${i}: ${response[i]}`);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.216Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":83,"estimatedTokens":553}}1048{"id":"doc-class_service_apps_script_google_for_developers-0f89337f","source":"documentation","title":"Class Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/script/service","text":"Example:\n```text\n// Mail the URL of the published web app.\nMailApp.sendMail(\n 'myself@example.com',\n 'My Snazzy App',\n `My new app is now available at ${ScriptApp.getService().getUrl()}`,\n);\n```\n\nExample:\n```text\nScriptApp.getService().disable();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.222Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":16,"estimatedTokens":69}}1049{"id":"doc-use_the_command_line_interface_with_clasp_apps_s-a5d5b34f","source":"documentation","title":"Use the command-line interface with clasp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/clasp","text":"Example:\n```text\n# On script.google.com:\n├── tests/slides.gs\n└── tests/sheets.gs\n\n# Locally:\n├── tests/\n│ ├─ slides.gs\n│ └─ sheets.gs\n```\n\nExample:\n```text\nnpm install @google/clasp -g\n```\n\nExample:\n```text\nclasp login\n```\n\nExample:\n```text\nclasp logout\n```\n\nExample:\n```text\nclasp create [scriptTitle] [--type <projectType>] [--parentId <parentId>]\n```\n\nExample:\n```text\nclasp pull\n```\n\nExample:\n```text\nclasp push\n```\n\nExample:\n```text\nclasp versions\n```\n\nExample:\n```text\nclasp version [description]\n```\n\nExample:\n```text\nclasp deploy [version] [description]\nclasp undeploy <deploymentId>\n```\n\nExample:\n```text\nclasp redeploy <deploymentId> <version> <description>\n```\n\nExample:\n```text\nclasp deployments\n```\n\nExample:\n```text\nclasp open-script\n```\n\nExample:\n```text\nname: CI\non:\n pull_request:\n branches: [main]\n\njobs:\n check:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v6.3\n - uses: actions/setup-node@v6.3\n with:\n node-version: \"20\"\n cache: npm\n - run: npm ci\n - run: npm run lint\n```\n\nExample:\n```text\nname: Deploy\non:\n push:\n branches: [main]\n\njobs:\n deploy:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: \"20\"\n cache: npm\n - run: npm ci\n - run: npm run lint && npm test\n - name: Setup clasp credentials\n run: |\n\n echo '${{ secrets.CLASPRC_JSON }}' > ~/.clasprc.json\n echo '${{ secrets.CLASP_JSON }}' > .clasp.json\n\n - name: Push and version\n run: |\n npx @google/clasp push --force\n npx @google/clasp version \"$(git rev-parse --short HEAD)\"\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.222Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":15,"totalLines":124,"estimatedTokens":431}}1050{"id":"doc-advanced_calendar_service_apps_script_google_for-070fd2e5","source":"documentation","title":"Advanced Calendar Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/advanced/calendar","text":"Example:\n```text\n/**\n * Creates an event in the user's default calendar.\n * @see https://developers.google.com/calendar/api/v3/reference/events/insert\n */\nfunction createEvent() {\n const calendarId = \"primary\";\n const start = getRelativeDate(1, 12);\n const end = getRelativeDate(1, 13);\n // event details for creating event.\n let event = {\n summary: \"Lunch Meeting\",\n location: \"The Deli\",\n description: \"To discuss our plans for the presentation next week.\",\n start: {\n dateTime: start.toISOString(),\n },\n end: {\n dateTime: end.toISOString(),\n },\n attendees: [\n { email: \"gduser1@workspacesample.dev\" },\n { email: \"gduser2@workspacesample.dev\" },\n ],\n // Red background. Use Calendar.Colors.get() for the full list.\n colorId: 11,\n };\n try {\n // call method to insert/create new event in provided calandar\n event = Calendar.Events.insert(event, calendarId);\n console.log(`Event ID: ${event.id}`);\n } catch (err) {\n console.log(\"Failed with error %s\", err.message);\n }\n}\n\n/**\n * Helper function to get a new Date object relative to the current date.\n * @param {number} daysOffset The number of days in the future for the new date.\n * @param {number} hour The hour of the day for the new date, in the time zone\n * of the script.\n * @return {Date} The new date.\n */\nfunction getRelativeDate(daysOffset, hour) {\n const date = new Date();\n date.setDate(date.getDate() + daysOffset);\n date.setHours(hour);\n date.setMinutes(0);\n date.setSeconds(0);\n date.setMilliseconds(0);\n return date;\n}\n```\n\nExample:\n```text\n/**\n * Lists the calendars shown in the user's calendar list.\n * @see https://developers.google.com/calendar/api/v3/reference/calendarList/list\n */\nfunction listCalendars() {\n let calendars;\n let pageToken;\n do {\n calendars = Calendar.CalendarList.list({\n maxResults: 100,\n pageToken: pageToken,\n });\n if (!calendars.items || calendars.items.length === 0) {\n console.log(\"No calendars found.\");\n return;\n }\n // Print the calendar id and calendar summary\n for (const calendar of calendars.items) {\n console.log(\"%s (ID: %s)\", calendar.summary, calendar.id);\n }\n pageToken = calendars.nextPageToken;\n } while (pageToken);\n}\n```\n\nExample:\n```text\n/**\n * Lists the next 10 upcoming events in the user's default calendar.\n * @see https://developers.google.com/calendar/api/v3/reference/events/list\n */\nfunction listNext10Events() {\n const calendarId = \"primary\";\n const now = new Date();\n const events = Calendar.Events.list(calendarId, {\n timeMin: now.toISOString(),\n singleEvents: true,\n orderBy: \"startTime\",\n maxResults: 10,\n });\n if (!events.items || events.items.length === 0) {\n console.log(\"No events found.\");\n return;\n }\n for (const event of events.items) {\n if (event.start.date) {\n // All-day event.\n const start = new Date(event.start.date);\n console.log(\"%s (%s)\", event.summary, start.toLocaleDateString());\n continue;\n }\n const start = new Date(event.start.dateTime);\n console.log(\"%s (%s)\", event.summary, start.toLocaleString());\n }\n}\n```\n\nExample:\n```text\n/**\n * Creates an event in the user's default calendar, waits 30 seconds, then\n * attempts to update the event's location, on the condition that the event\n * has not been changed since it was created. If the event is changed during\n * the 30-second wait, then the subsequent update will throw a 'Precondition\n * Failed' error.\n *\n * The conditional update is accomplished by setting the 'If-Match' header\n * to the etag of the new event when it was created.\n */\nfunction conditionalUpdate() {\n const calendarId = \"primary\";\n const start = getRelativeDate(1, 12);\n const end = getRelativeDate(1, 13);\n let event = {\n summary: \"Lunch Meeting\",\n location: \"The Deli\",\n description: \"To discuss our plans for the presentation next week.\",\n start: {\n dateTime: start.toISOString(),\n },\n end: {\n dateTime: end.toISOString(),\n },\n attendees: [\n { email: \"gduser1@workspacesample.dev\" },\n { email: \"gduser2@workspacesample.dev\" },\n ],\n // Red background. Use Calendar.Colors.get() for the full list.\n colorId: 11,\n };\n event = Calendar.Events.insert(event, calendarId);\n console.log(`Event ID: ${event.getId()}`);\n // Wait 30 seconds to see if the event has been updated outside this script.\n Utilities.sleep(30 * 1000);\n // Try to update the event, on the condition that the event state has not\n // changed since the event was created.\n event.location = \"The Coffee Shop\";\n try {\n event = Calendar.Events.update(\n event,\n calendarId,\n event.id,\n {},\n { \"If-Match\": event.etag },\n );\n console.log(`Successfully updated event: ${event.id}`);\n } catch (e) {\n console.log(`Fetch threw an exception: ${e}`);\n }\n}\n```\n\nExample:\n```text\n/**\n * Creates an event in the user's default calendar, then re-fetches the event\n * every second, on the condition that the event has changed since the last\n * fetch.\n *\n * The conditional fetch is accomplished by setting the 'If-None-Match' header\n * to the etag of the last known state of the event.\n */\nfunction conditionalFetch() {\n const calendarId = \"primary\";\n const start = getRelativeDate(1, 12);\n const end = getRelativeDate(1, 13);\n let event = {\n summary: \"Lunch Meeting\",\n location: \"The Deli\",\n description: \"To discuss our plans for the presentation next week.\",\n start: {\n dateTime: start.toISOString(),\n },\n end: {\n dateTime: end.toISOString(),\n },\n attendees: [\n { email: \"gduser1@workspacesample.dev\" },\n { email: \"gduser2@workspacesample.dev\" },\n ],\n // Red background. Use Calendar.Colors.get() for the full list.\n colorId: 11,\n };\n try {\n // insert event\n event = Calendar.Events.insert(event, calendarId);\n console.log(`Event ID: ${event.getId()}`);\n // Re-fetch the event each second, but only get a result if it has changed.\n for (let i = 0; i < 30; i++) {\n Utilities.sleep(1000);\n event = Calendar.Events.get(\n calendarId,\n event.id,\n {},\n { \"If-None-Match\": event.etag },\n );\n console.log(`New event description: ${event.start.dateTime}`);\n }\n } catch (e) {\n console.log(`Fetch threw an exception: ${e}`);\n }\n}\n```\n\nExample:\n```text\n/**\n * Retrieve and log events from the given calendar that have been modified\n * since the last sync. If the sync token is missing or invalid, log all\n * events from up to a month ago (a full sync).\n *\n * @param {string} calendarId The ID of the calender to retrieve events from.\n * @param {boolean} fullSync If true, throw out any existing sync token and\n * perform a full sync; if false, use the existing sync token if possible.\n */\nfunction logSyncedEvents(calendarId, fullSync) {\n const properties = PropertiesService.getUserProperties();\n const options = {\n maxResults: 100,\n };\n const syncToken = properties.getProperty(\"syncToken\");\n if (syncToken && !fullSync) {\n options.syncToken = syncToken;\n } else {\n // Sync events up to thirty days in the past.\n options.timeMin = getRelativeDate(-30, 0).toISOString();\n }\n // Retrieve events one page at a time.\n let events;\n let pageToken;\n do {\n try {\n options.pageToken = pageToken;\n events = Calendar.Events.list(calendarId, options);\n } catch (e) {\n // Check to see if the sync token was invalidated by the server;\n // if so, perform a full sync instead.\n if (\n e.message === \"Sync token is no longer valid, a full sync is required.\"\n ) {\n properties.deleteProperty(\"syncToken\");\n logSyncedEvents(calendarId, true);\n return;\n }\n throw new Error(e.message);\n }\n if (events.items && events.items.length === 0) {\n console.log(\"No events found.\");\n return;\n }\n for (const event of events.items) {\n if (event.status === \"cancelled\") {\n console.log(\"Event id %s was cancelled.\", event.id);\n return;\n }\n if (event.start.date) {\n const start = new Date(event.start.date);\n console.log(\"%s (%s)\", event.summary, start.toLocaleDateString());\n return;\n }\n // Events that don't last all day; they have defined start times.\n const start = new Date(event.start.dateTime);\n console.log(\"%s (%s)\", event.summary, start.toLocaleString());\n }\n pageToken = events.nextPageToken;\n } while (pageToken);\n properties.setProperty(\"syncToken\", events.nextSyncToken);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.228Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":288,"estimatedTokens":2153}}1051{"id":"doc-execute_functions_with_the_google_apps_script_ap-b4016aa0","source":"documentation","title":"Execute functions with the Google Apps Script API | Google for Developers","url":"https://developers.google.com/apps-script/api/how-tos/execute","text":"Example:\n```text\nList<String> mylist = (List<String>)(op.getResponse().get(\"result\"));\n```\n\nExample:\n```text\nreturn Utilities.base64Encode(myByteArray); // returns a string.\n```\n\nExample:\n```text\nif (credential.getExpiresInSeconds() <= 360) {\n credential.refreshToken();\n}\n```\n\nExample:\n```text\n/**\n * Return the set of folder names contained in the user's root folder as an\n * object (with folder IDs as keys).\n * @return {Object} A set of folder names keyed by folder ID.\n */\nfunction getFoldersUnderRoot() {\n const root = DriveApp.getRootFolder();\n const folders = root.getFolders();\n const folderSet = {};\n while (folders.hasNext()) {\n const folder = folders.next();\n folderSet[folder.getId()] = folder.getName();\n }\n return folderSet;\n}target.js\n```\n\nExample:\n```text\n/**\n * Create a HttpRequestInitializer from the given one, except set\n * the HTTP read timeout to be longer than the default (to allow\n * called scripts time to execute).\n *\n * @param {HttpRequestInitializer} requestInitializer the initializer\n * to copy and adjust; typically a Credential object.\n * @return an initializer with an extended read timeout.\n */\nprivate static HttpRequestInitializer setHttpTimeout(\n final HttpRequestInitializer requestInitializer) {\n return new HttpRequestInitializer() {\n @Override\n public void initialize(HttpRequest httpRequest) throws IOException {\n requestInitializer.initialize(httpRequest);\n // This allows the API to call (and avoid timing out on)\n // functions that take up to 6 minutes to complete (the maximum\n // allowed script run time), plus a little overhead.\n httpRequest.setReadTimeout(380000);\n }\n };\n}\n\n/**\n * Build and return an authorized Script client service.\n *\n * @param {Credential} credential an authorized Credential object\n * @return an authorized Script client service\n */\npublic static Script getScriptService() throws IOException {\n Credential credential = authorize();\n return new Script.Builder(\n HTTP_TRANSPORT, JSON_FACTORY, setHttpTimeout(credential))\n .setApplicationName(APPLICATION_NAME)\n .build();\n}\n\n/**\n * Interpret an error response returned by the API and return a String\n * summary.\n *\n * @param {Operation} op the Operation returning an error response\n * @return summary of error response, or null if Operation returned no\n * error\n */\npublic static String getScriptError(Operation op) {\n if (op.getError() == null) {\n return null;\n }\n\n // Extract the first (and only) set of error details and cast as a Map.\n // The values of this map are the script's 'errorMessage' and\n // 'errorType', and an array of stack trace elements (which also need to\n // be cast as Maps).\n Map<String, Object> detail = op.getError().getDetails().get(0);\n List<Map<String, Object>> stacktrace =\n (List<Map<String, Object>>) detail.get(\"scriptStackTraceElements\");\n\n java.lang.StringBuilder sb =\n new StringBuilder(\"\\nScript error message: \");\n sb.append(detail.get(\"errorMessage\"));\n sb.append(\"\\nScript error type: \");\n sb.append(detail.get(\"errorType\"));\n\n if (stacktrace != null) {\n // There may not be a stacktrace if the script didn't start\n // executing.\n sb.append(\"\\nScript error stacktrace:\");\n for (Map<String, Object> elem : stacktrace) {\n sb.append(\"\\n \");\n sb.append(elem.get(\"function\"));\n sb.append(\":\");\n sb.append(elem.get(\"lineNumber\"));\n }\n }\n sb.append(\"\\n\");\n return sb.toString();\n}\n\npublic static void main(String[] args) throws IOException {\n // ID of the script to call. Acquire this from the Apps Script editor,\n // under Publish > Deploy as API executable.\n String scriptId = \"ENTER_YOUR_SCRIPT_ID_HERE\";\n Script service = getScriptService();\n\n // Create an execution request object.\n ExecutionRequest request = new ExecutionRequest()\n .setFunction(\"getFoldersUnderRoot\");\n\n try {\n // Make the API request.\n Operation op =\n service.scripts().run(scriptId, request).execute();\n\n // Print results of request.\n if (op.getError() != null) {\n // The API executed, but the script returned an error.\n System.out.println(getScriptError(op));\n } else {\n // The result provided by the API needs to be cast into\n // the correct type, based upon what types the Apps\n // Script function returns. Here, the function returns\n // an Apps Script Object with String keys and values,\n // so must be cast into a Java Map (folderSet).\n Map<String, String> folderSet =\n (Map<String, String>) (op.getResponse().get(\"result\"));\n if (folderSet.size() == 0) {\n System.out.println(\"No folders returned!\");\n } else {\n System.out.println(\"Folders under your root folder:\");\n for (String id : folderSet.keySet()) {\n System.out.printf(\n \"\\t%s (%s)\\n\", folderSet.get(id), id);\n }\n }\n }\n } catch (GoogleJsonResponseException e) {\n // The API encountered a problem before the script was called.\n e.printStackTrace(System.out);\n }\n}Execute.java\n```\n\nExample:\n```text\n/**\n * Load the API and make an API call. Display the results on the screen.\n */\nfunction callScriptFunction() {\n const scriptId = '<ENTER_YOUR_SCRIPT_ID_HERE>';\n\n // Call the Apps Script API run method\n // 'scriptId' is the URL parameter that states what script to run\n // 'resource' describes the run request body (with the function name\n // to execute)\n try {\n gapi.client.script.scripts.run({\n 'scriptId': scriptId,\n 'resource': {\n 'function': 'getFoldersUnderRoot',\n },\n }).then(function(resp) {\n const result = resp.result;\n if (result.error && result.error.status) {\n // The API encountered a problem before the script\n // started executing.\n appendPre('Error calling API:');\n appendPre(JSON.stringify(result, null, 2));\n } else if (result.error) {\n // The API executed, but the script returned an error.\n\n // Extract the first (and only) set of error details.\n // The values of this object are the script's 'errorMessage' and\n // 'errorType', and an array of stack trace elements.\n const error = result.error.details[0];\n appendPre('Script error message: ' + error.errorMessage);\n\n if (error.scriptStackTraceElements) {\n // There may not be a stacktrace if the script didn't start\n // executing.\n appendPre('Script error stacktrace:');\n for (let i = 0; i < error.scriptStackTraceElements.length; i++) {\n const trace = error.scriptStackTraceElements[i];\n appendPre('\\t' + trace.function + ':' + trace.lineNumber);\n }\n }\n } else {\n // The structure of the result will depend upon what the Apps\n // Script function returns. Here, the function returns an Apps\n // Script Object with String keys and values, and so the result\n // is treated as a JavaScript object (folderSet).\n\n const folderSet = result.response.result;\n if (Object.keys(folderSet).length == 0) {\n appendPre('No folders returned!');\n } else {\n appendPre('Folders under your root folder:');\n Object.keys(folderSet).forEach(function(id) {\n appendPre('\\t' + folderSet[id] + ' (' + id + ')');\n });\n }\n }\n });\n } catch (err) {\n document.getElementById('content').innerText = err.message;\n return;\n }\n}\nindex.js\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Calls an Apps Script function to list the folders in the user's root Drive folder.\n */\nasync function callAppsScript() {\n // The ID of the Apps Script project to call.\n const scriptId = '1xGOh6wCm7hlIVSVPKm0y_dL-YqetspS5DEVmMzaxd_6AAvI-_u8DSgBT';\n\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Apps Script API client.\n const script = google.script({version: 'v1', auth});\n\n const resp = await script.scripts.run({\n auth,\n requestBody: {\n // The name of the function to call in the Apps Script project.\n function: 'getFoldersUnderRoot',\n },\n scriptId,\n });\n\n if (resp.data.error?.details?.[0]) {\n // The API executed, but the script returned an error.\n // Extract the error details.\n const error = resp.data.error.details[0];\n console.log(`Script error message: ${error.errorMessage}`);\n console.log('Script error stacktrace:');\n\n if (error.scriptStackTraceElements) {\n // Log the stack trace.\n for (let i = 0; i < error.scriptStackTraceElements.length; i++) {\n const trace = error.scriptStackTraceElements[i];\n console.log('\\t%s: %s', trace.function, trace.lineNumber);\n }\n }\n } else {\n // The script executed successfully.\n // The structure of the response depends on the Apps Script function's return value.\n const folderSet = resp.data.response ?? {};\n if (Object.keys(folderSet).length === 0) {\n console.log('No folders returned!');\n } else {\n console.log('Folders under your root folder:');\n Object.keys(folderSet).forEach((id) => {\n console.log('\\t%s (%s)', folderSet[id], id);\n });\n }\n }\n}index.js\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef main():\n \"\"\"Runs the sample.\"\"\"\n # pylint: disable=maybe-no-member\n script_id = \"1VFBDoJFy6yb9z7-luOwRv3fCmeNOzILPnR4QVmR0bGJ7gQ3QMPpCW-yt\"\n\n creds, _ = google.auth.default()\n service = build(\"script\", \"v1\", credentials=creds)\n\n # Create an execution request object.\n request = {\"function\": \"getFoldersUnderRoot\"}\n\n try:\n # Make the API request.\n response = service.scripts().run(scriptId=script_id, body=request).execute()\n if \"error\" in response:\n # The API executed, but the script returned an error.\n # Extract the first (and only) set of error details. The values of\n # this object are the script's 'errorMessage' and 'errorType', and\n # a list of stack trace elements.\n error = response[\"error\"][\"details\"][0]\n print(f\"Script error message: {0}.{format(error['errorMessage'])}\")\n\n if \"scriptStackTraceElements\" in error:\n # There may not be a stacktrace if the script didn't start\n # executing.\n print(\"Script error stacktrace:\")\n for trace in error[\"scriptStackTraceElements\"]:\n print(f\"\\t{0}: {1}.{format(trace['function'], trace['lineNumber'])}\")\n else:\n # The structure of the result depends upon what the Apps Script\n # function returns. Here, the function returns an Apps Script\n # Object with String keys and values, and so the result is\n # treated as a Python dictionary (folder_set).\n folder_set = response[\"response\"].get(\"result\", {})\n if not folder_set:\n print(\"No folders returned!\")\n else:\n print(\"Folders under your root folder:\")\n for folder_id, folder in folder_set.items():\n print(f\"\\t{0} ({1}).{format(folder, folder_id)}\")\n\n except HttpError as error:\n # The API encountered a problem before the script started executing.\n print(f\"An error occurred: {error}\")\n print(error.content)\n\n\nif __name__ == \"__main__\":\n main()execute.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.230Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":348,"estimatedTokens":2887}}1052{"id":"doc-widgets_google_workspace_add_ons_google_for_deve-b4ee7103","source":"documentation","title":"Widgets | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/gmail/add-ons/concepts/widgets","text":"Example:\n```text\nvar fixedFooter = CardService.newFixedFooter()\n .setPrimaryButton(\n CardService.newTextButton()\n .setText(\"Primary\")\n .setOpenLink(CardService.newOpenLink()\n .setUrl(\"https://www.google.com\")))\n .setSecondaryButton(\n CardService.newTextButton()\n .setText(\"Secondary\")\n .setOnClickAction(\n CardService.newAction()\n .setFunctionName(\n \"secondaryCallback\")));\n\nvar card = CardService.newCardBuilder()\n // (...)\n .setFixedFooter(fixedFooter)\n .build();\n```\n\nExample:\n```text\nvar peekHeader = CardService.newCardHeader()\n .setTitle('Contextual Cat')\n .setImageUrl('https://www.gstatic.com/images/\n icons/material/system/1x/pets_black_48dp.png')\n .setSubtitle(text);\n\n. . .\n\nvar card = CardService.newCardBuilder()\n .setDisplayStyle(CardService.DisplayStyle.PEEK)\n .setPeekCardHeader(peekHeader);\n```\n\nExample:\n```text\nvar decoratedText = CardService.newDecoratedText()\n // (...)\n .setSwitch(CardService.newSwitch()\n .setFieldName('form_input_switch_key')\n .setValue('switch_is_on')\n .setControlType(\n CardService.SwitchControlType.CHECK_BOX));\n```\n\nExample:\n```text\nvar dateOnlyPicker = CardService.newDatePicker()\n .setTitle(\"Enter a date\")\n .setFieldName(\"date_field\")\n // Set default value as May 24 2019. Either a\n // number or string is acceptable.\n .setValueInMsSinceEpoch(1558668600000)\n .setOnChangeAction(CardService.newAction()\n .setFunctionName(\"handleDateChange\"));\n\nvar timeOnlyPicker = CardService.newTimePicker()\n .setTitle(\"Enter a time\")\n .setFieldName(\"time_field\")\n // Set default value as 23:30.\n .setHours(23)\n .setMinutes(30)\n .setOnChangeAction(CardService.newAction()\n .setFunctionName(\"handleTimeChange\"));\n\nvar dateTimePicker = CardService.newDateTimePicker()\n .setTitle(\"Enter a date and time\")\n .setFieldName(\"date_time_field\")\n // Set default value as May 24 2019 03:30 AM UTC.\n // Either a number or string is acceptable.\n .setValueInMsSinceEpoch(1558668600000)\n // EDT time is 4 hours behind UTC.\n .setTimeZoneOffsetInMins(-4 * 60)\n .setOnChangeAction(CardService.newAction()\n .setFunctionName(\"handleDateTimeChange\"));\n```\n\nExample:\n```text\nfunction handleDateTimeChange(event) {\n var dateTimeInput =\n event.commonEventObject.formInputs[\"myDateTimePickerWidgetID\"];\n var msSinceEpoch = dateTimeInput.msSinceEpoch;\n var hasDate = dateTimeInput.hasDate;\n var hasTime = dateTimeInput.hadTime;\n\n // The following requires you to configure the add-on to read user locale\n // and timezone.\n // See:\n // https://developers.google.com/workspace/add-ons/how-tos/access-user-locale\n var userTimezoneId = event.userTimezone.id;\n\n // Format and log the date-time selected using the user's timezone.\n var formattedDateTime;\n if (hasDate && hasTime) {\n formattedDateTime = Utilities.formatDate(\n new Date(msSinceEpoch), userTimezoneId, \"yyy/MM/dd hh:mm:ss\");\n } else if (hasDate) {\n formattedDateTime = Utilities.formatDate(\n new Date(msSinceEpoch), userTimezoneId, \"yyy/MM/dd\")\n + \", Time unspecified\";\n } else if (hasTime) {\n formattedDateTime = \"Date unspecified, \"\n + Utilities.formatDate(\n new Date(msSinceEpoch), userTimezoneId, \"hh:mm a\");\n }\n\n if (formattedDateTime) {\n console.log(formattedDateTime);\n }\n}\n```\n\nExample:\n```text\nvar gridItem = CardService.newGridItem()\n .setIdentifier(\"item_001\")\n .setTitle(\"Lucian R.\")\n .setSubtitle(\"Chief Information Officer\")\n .setImage(imageComponent);\n\nvar cropStyle = CardService.newImageCropStyle()\n .setImageCropType(CardService.ImageCropType.RECTANGLE_4_3);\n\nvar imageComponent = CardService.newImageComponent()\n .setImageUrl(\"https://developers.google.com/workspace/\n images/cymbal/people/person1.jpeg\")\n .setCropStyle(cropStyle)\n\nvar grid = CardService.newGrid()\n .setTitle(\"Recently viewed\")\n .addItem(gridItem)\n .setNumColumns(2)\n .setOnClickAction(CardService.newAction()\n .setFunctionName(\"handleGridItemClick\"));\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.233Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":141,"estimatedTokens":1047}}1053{"id":"doc-class_gmaildraft_apps_script_google_for_develope-6f6adee1","source":"documentation","title":"Class GmailDraft | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/gmail/gmail-draft","text":"Example:\n```text\nconst draft =\n GmailApp.getDrafts()[0]; // The first draft message in the drafts folder\ndraft.deleteDraft();\ndraft.getMessage(); // Throws exception.\n```\n\nExample:\n```text\nconst draft =\n GmailApp.getDrafts()[0]; // The first draft message in the drafts folder\nconst draftId = draft.getId();\nconst draftById = GmailApp.getDraft(draftId);\nLogger.log(\n draft.getMessage().getSubject() === draftById.getMessage().getSubject(),\n);\n```\n\nExample:\n```text\nconst draft =\n GmailApp.getDrafts()[0]; // The first draft message in the drafts folder\nconst message = draft.getMessage();\nLogger.log(message.getSubject());\n```\n\nExample:\n```text\nconst draft =\n GmailApp.getDrafts()[0]; // The first draft message in the drafts folder\nconst messageId = draft.getMessageId();\nLogger.log(messageId === draft.getMessage().getId());\n```\n\nExample:\n```text\nconst draft =\n GmailApp.getDrafts()[0]; // The first draft message in the drafts folder\nconst msg = draft.send(); // Send it\nLogger.log(msg.getDate()); // Should be approximately the current timestamp\n```\n\nExample:\n```text\n// The code below will update a draft email with the current date and time.\nconst draft =\n GmailApp.getDrafts()[0]; // The first draft message in the drafts folder\nconst now = new Date();\ndraft.update(\n 'mike@example.com',\n 'current time',\n `The time is: ${now.toString()}`,\n);\n```\n\nExample:\n```text\n// Update a draft email with a file from Google Drive attached as a PDF.\nconst draft =\n GmailApp.getDrafts()[0]; // The first draft message in the drafts folder\nconst file = DriveApp.getFileById('1234567890abcdefghijklmnopqrstuvwxyz');\ndraft.update(\n 'mike@example.com',\n 'Attachment example',\n 'Please see attached file.',\n {\n attachments: [file.getAs(MimeType.PDF)],\n name: 'Automatic Emailer Script',\n },\n);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.234Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":74,"estimatedTokens":469}}1054{"id":"doc-logging_apps_script_google_for_developers-f5edf33c","source":"documentation","title":"Logging | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/logging","text":"Example:\n```text\n/**\n * Logs Google Sheet information.\n * @param {number} rowNumber The spreadsheet row number.\n * @param {string} email The email to send with the row data.\n */\nfunction emailDataRow(rowNumber, email) {\n console.log(`Emailing data row ${rowNumber} to ${email}`);\n const sheet = SpreadsheetApp.getActiveSheet();\n const data = sheet.getDataRange().getValues();\n const rowData = data[rowNumber - 1].join(\" \");\n console.log(`Row ${rowNumber} data: ${rowData}`);\n MailApp.sendEmail(email, `Data in row ${rowNumber}`, rowData);\n}\n```\n\nExample:\n```text\n> [16-09-12 13:50:42:193 PDT] Emailing data row 2 to john@example.com\n> [16-09-12 13:50:42:271 PDT] Row 2 data: Cost 103.24\n```\n\nExample:\n```text\n/**\n * A placeholder function to be timed.\n * @param {Object} parameters\n */\nfunction myFunction(parameters) {\n // Placeholder for the function being timed.\n}\n\n/**\n * Logs the time taken to execute 'myFunction'.\n */\nfunction measuringExecutionTime() {\n // A simple INFO log message, using sprintf() formatting.\n console.info(\"Timing the %s function (%d arguments)\", \"myFunction\", 1);\n\n // Log a JSON object at a DEBUG level. The log is labeled\n // with the message string in the log viewer, and the JSON content\n // is displayed in the expanded log structure under \"jsonPayload\".\n const parameters = {\n isValid: true,\n content: \"some string\",\n timestamp: new Date(),\n };\n console.log({ message: \"Function Input\", initialData: parameters });\n const label = \"myFunction() time\"; // Labels the timing log entry.\n console.time(label); // Starts the timer.\n try {\n myFunction(parameters); // Function to time.\n } catch (e) {\n // Logs an ERROR message.\n console.error(`myFunction() yielded an error: ${e}`);\n }\n console.timeEnd(label); // Stops the timer, logs execution duration.\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.235Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":62,"estimatedTokens":461}}1055{"id":"doc-web_apps_apps_script_google_for_developers-af634159","source":"documentation","title":"Web Apps | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/web","text":"Example:\n```text\nname=alice&n=1&n=2\n```\n\nExample:\n```text\n{\"name\": \"alice\", \"n\": \"1\"}\n```\n\nExample:\n```text\n{\"name\": [\"alice\"], \"n\": [\"1\", \"2\"]}\n```\n\nExample:\n```text\n332\n```\n\nExample:\n```text\ntext/csv\n```\n\nExample:\n```text\nAlice,21\n```\n\nExample:\n```text\npostData\n```\n\nExample:\n```text\nhttps://script.google.com/.../exec?username=jsmith&age=21\n```\n\nExample:\n```text\nfunction doGet(e) {\n var params = JSON.stringify(e);\n return ContentService.createTextOutput(params).setMimeType(ContentService.MimeType.JSON);\n}\n```\n\nExample:\n```text\n{\n \"queryString\": \"username=jsmith&age=21\",\n \"parameter\": {\n \"username\": \"jsmith\",\n \"age\": \"21\"\n },\n \"contextPath\": \"\",\n \"parameters\": {\n \"username\": [\n \"jsmith\"\n ],\n \"age\": [\n \"21\"\n ]\n },\n \"contentLength\": -1\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.236Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":70,"estimatedTokens":200}}1056{"id":"doc-class_calendarapp_apps_script_google_for_develop-b72e178d","source":"documentation","title":"Class CalendarApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/calendar/calendar-app","text":"Example:\n```text\n// Creates an all-day event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the Woodstock festival (August 15th to 17th) and\n// logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Woodstock Festival',\n new Date('August 15, 1969'),\n new Date('August 18, 1969'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the Woodstock festival (August 15th to 17th) and\n// logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Woodstock Festival',\n new Date('August 15, 1969'),\n new Date('August 18, 1969'),\n {location: 'Bethel, White Lake, New York, U.S.', sendInvites: true},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969'),\n {location: 'The Moon'},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a no-meetings day, taking place every Wednesday\n// in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries(\n 'No Meetings',\n new Date('January 2, 2013 03:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014')),\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a no-meetings day, taking place every Wednesday\n// in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries(\n 'No Meetings',\n new Date('January 2, 2013 03:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014')),\n {guests: 'everyone@example.com'},\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates a new calendar named \"Travel Plans\".\nconst calendar = CalendarApp.createCalendar('Travel Plans');\nLogger.log(\n 'Created the calendar \"%s\", with the ID \"%s\".',\n calendar.getName(),\n calendar.getId(),\n);\n```\n\nExample:\n```text\n// Creates a new calendar named \"Travel Plans\" with a description and color.\nconst calendar = CalendarApp.createCalendar('Travel Plans', {\n description: 'A calendar to plan my travel schedule.',\n color: CalendarApp.Color.BLUE,\n});\nLogger.log(\n 'Created the calendar \"%s\", with the ID \"%s\".',\n calendar.getName(),\n calendar.getId(),\n);\n```\n\nExample:\n```text\n// Creates an event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:00:00 UTC'),\n new Date('July 21, 1969 21:00:00 UTC'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:00:00 UTC'),\n new Date('July 20, 1969 21:00:00 UTC'),\n {location: 'The Moon'},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates a new event and logs its ID.\nconst event = CalendarApp.getDefaultCalendar().createEventFromDescription(\n 'Lunch with Mary, Friday at 1PM',\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a team meeting, taking place every Tuesday and\n// Thursday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createEventSeries(\n 'Team Meeting',\n new Date('January 1, 2013 03:00:00 PM EST'),\n new Date('January 1, 2013 04:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekdays(\n [CalendarApp.Weekday.TUESDAY, CalendarApp.Weekday.THURSDAY])\n .until(new Date('January 1, 2014')),\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a team meeting, taking place every Tuesday and\n// Thursday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createEventSeries(\n 'Team Meeting',\n new Date('January 1, 2013 03:00:00 PM EST'),\n new Date('January 1, 2013 04:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekdays(\n [CalendarApp.Weekday.TUESDAY, CalendarApp.Weekday.THURSDAY])\n .until(new Date('January 1, 2014')),\n {location: 'Conference Room'},\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Determines how many calendars the user can access.\nconst calendars = CalendarApp.getAllCalendars();\nLogger.log(\n 'This user owns or is subscribed to %s calendars.',\n calendars.length,\n);\n```\n\nExample:\n```text\n// Determines how many calendars the user owns.\nconst calendars = CalendarApp.getAllOwnedCalendars();\nLogger.log('This user owns %s calendars.', calendars.length);\n```\n\nExample:\n```text\n// Gets the public calendar \"US Holidays\" by ID.\nconst calendar = CalendarApp.getCalendarById(\n 'en.usa#holiday@group.v.calendar.google.com',\n);\nLogger.log('The calendar is named \"%s\".', calendar.getName());\n```\n\nExample:\n```text\n// Gets the public calendar named \"US Holidays\".\nconst calendars = CalendarApp.getCalendarsByName('US Holidays');\nLogger.log('Found %s matching calendars.', calendars.length);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the color of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getColor() instead.\nconst calendarColor = calendar.getColor();\nconsole.log(calendarColor);\n```\n\nExample:\n```text\n// Determines the time zone of the user's default calendar.\nconst calendar = CalendarApp.getDefaultCalendar();\nLogger.log(\n 'My default calendar is set to the time zone \"%s\".',\n calendar.getTimeZone(),\n);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Sets the description of the calendar to 'Test description.'\ncalendar.setDescription('Test description');\n\n// Gets the description of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getDescription() instead.\nconst description = calendar.getDescription();\nconsole.log(description);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Creates an event for the moon landing.\nconst event = calendar.createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:05:00 UTC'),\n new Date('July 20, 1969 20:17:00 UTC'),\n);\n\n// Gets the calendar event ID and logs it to the console.\nconst iCalId = event.getId();\nconsole.log(iCalId);\n\n// Gets the event by its ID and logs the title of the event to the console.\n// For the default calendar, you can use CalendarApp.getEventById(iCalId)\n// instead.\nconst myEvent = calendar.getEventById(iCalId);\nconsole.log(myEvent.getTitle());\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Creates an event series for a daily team meeting from 1 PM to 2 PM.\n// The series adds the daily event from January 1, 2023 through December 31,\n// 2023.\nconst eventSeries = calendar.createEventSeries(\n 'Team meeting',\n new Date('Jan 1, 2023 13:00:00'),\n new Date('Jan 1, 2023 14:00:00'),\n CalendarApp.newRecurrence().addDailyRule().until(new Date('Jan 1, 2024')),\n);\n\n// Gets the ID of the event series.\nconst iCalId = eventSeries.getId();\n\n// Gets the event series by its ID and logs the series title to the console.\n// For the default calendar, you can use CalendarApp.getEventSeriesById(iCalId)\n// instead.\nconsole.log(calendar.getEventSeriesById(iCalId).getTitle());\n```\n\nExample:\n```text\n// Determines how many events are happening in the next two hours.\nconst now = new Date();\nconst twoHoursFromNow = new Date(now.getTime() + 2 * 60 * 60 * 1000);\nconst events = CalendarApp.getDefaultCalendar().getEvents(now, twoHoursFromNow);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening in the next two hours that contain\n// the term \"meeting\".\nconst now = new Date();\nconst twoHoursFromNow = new Date(now.getTime() + 2 * 60 * 60 * 1000);\nconst events = CalendarApp.getDefaultCalendar().getEvents(\n now,\n twoHoursFromNow,\n {search: 'meeting'},\n);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening today.\nconst today = new Date();\nconst events = CalendarApp.getDefaultCalendar().getEventsForDay(today);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening today and contain the term\n// \"meeting\".\nconst today = new Date();\nconst events = CalendarApp.getDefaultCalendar().getEventsForDay(today, {\n search: 'meeting',\n});\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// To get the user's default calendar, use CalendarApp.getDefaultCalendar().\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the ID of the calendar and logs it to the console.\nconst calendarId = calendar.getId();\nconsole.log(calendarId);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the name of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getName() instead.\nconst calendarName = calendar.getName();\nconsole.log(calendarName);\n```\n\nExample:\n```text\n// Gets a (non-existent) private calendar by ID.\nconst calendar = CalendarApp.getOwnedCalendarById(\n '123456789@group.calendar.google.com',\n);\nLogger.log('The calendar is named \"%s\".', calendar.getName());\n```\n\nExample:\n```text\n// Gets a private calendar named \"Travel Plans\".\nconst calendars = CalendarApp.getOwnedCalendarsByName('Travel Plans');\nLogger.log('Found %s matching calendars.', calendars.length);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the time zone of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getTimeZone() instead.\nconst timeZone = calendar.getTimeZone();\nconsole.log(timeZone);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Determines whether the calendar is hidden in the user interface and logs it\n// to the console. For the default calendar, you can use CalendarApp.isHidden()\n// instead.\nconst isHidden = calendar.isHidden();\nconsole.log(isHidden);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Determines whether the calendar is the default calendar for\n// the effective user and logs it to the console.\n// For the default calendar, you can use CalendarApp.isMyPrimaryCalendar()\n// instead.\nconst isMyPrimaryCalendar = calendar.isMyPrimaryCalendar();\nconsole.log(isMyPrimaryCalendar);\n```\n\nExample:\n```text\n// Gets a calendar by its ID. To get the user's default calendar, use\n// CalendarApp.getDefault() instead.\n// TODO(developer): Replace the ID with the calendar ID that you want to use.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Determines whether the calendar is owned by you and logs it.\nconsole.log(calendar.isOwnedByMe());\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Determines whether the calendar's events are displayed in the user interface\n// and logs it.\nconsole.log(calendar.isSelected());\n```\n\nExample:\n```text\n// Creates an event series for a no-meetings day, taking place every Wednesday\n// in 2013.\nconst recurrence = CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014'));\nconst eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries(\n 'No Meetings',\n new Date('January 2, 2013 03:00:00 PM EST'),\n recurrence,\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Sets the color of the calendar to pink using the Calendar Color enum.\n// For the default calendar, you can use CalendarApp.setColor() instead.\ncalendar.setColor(CalendarApp.Color.PINK);\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the description of the calendar.\n// TODO(developer): Update the string with the description that you want to use.\ncalendar.setDescription('Updated calendar description.');\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the name of the calendar.\n// TODO(developer): Update the string with the name that you want to use.\ncalendar.setName('Example calendar name');\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Selects the calendar so that its events are displayed in the user interface.\n// To unselect the calendar, set the parameter to false.\ncalendar.setSelected(true);\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the time zone of the calendar to America/New York (US/Eastern) time.\ncalendar.setTimeZone('America/New_York');\n```\n\nExample:\n```text\n// Subscribe to the calendar \"US Holidays\".\nconst calendar = CalendarApp.subscribeToCalendar(\n 'en.usa#holiday@group.v.calendar.google.com',\n);\nLogger.log('Subscribed to the calendar \"%s\".', calendar.getName());\n```\n\nExample:\n```text\n// Subscribe to the calendar \"US Holidays\", and set it to the color blue.\nconst calendar = CalendarApp.subscribeToCalendar(\n 'en.usa#holiday@group.v.calendar.google.com',\n {color: CalendarApp.Color.BLUE},\n);\nLogger.log('Subscribed to the calendar \"%s\".', calendar.getName());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.241Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":43,"totalLines":523,"estimatedTokens":3891}}1057{"id":"doc-class_calendar_apps_script_google_for_developers-86898507","source":"documentation","title":"Class Calendar | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/calendar/calendar","text":"Example:\n```text\n// Creates an all-day event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the Woodstock festival (August 15th to 17th) and\n// logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Woodstock Festival',\n new Date('August 15, 1969'),\n new Date('August 18, 1969'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the Woodstock festival (August 15th to 17th) and\n// logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Woodstock Festival',\n new Date('August 15, 1969'),\n new Date('August 18, 1969'),\n {location: 'Bethel, White Lake, New York, U.S.', sendInvites: true},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969'),\n {location: 'The Moon'},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a no-meetings day, taking place every Wednesday\n// in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries(\n 'No Meetings',\n new Date('January 2, 2013 03:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014')),\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a no-meetings day, taking place every Wednesday\n// in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries(\n 'No Meetings',\n new Date('January 2, 2013 03:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014')),\n {guests: 'everyone@example.com'},\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates an event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:00:00 UTC'),\n new Date('July 21, 1969 21:00:00 UTC'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:00:00 UTC'),\n new Date('July 20, 1969 21:00:00 UTC'),\n {location: 'The Moon'},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates a new event and logs its ID.\nconst event = CalendarApp.getDefaultCalendar().createEventFromDescription(\n 'Lunch with Mary, Friday at 1PM',\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a team meeting, taking place every Tuesday and\n// Thursday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createEventSeries(\n 'Team Meeting',\n new Date('January 1, 2013 03:00:00 PM EST'),\n new Date('January 1, 2013 04:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekdays(\n [CalendarApp.Weekday.TUESDAY, CalendarApp.Weekday.THURSDAY])\n .until(new Date('January 1, 2014')),\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a team meeting, taking place every Tuesday and\n// Thursday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createEventSeries(\n 'Team Meeting',\n new Date('January 1, 2013 03:00:00 PM EST'),\n new Date('January 1, 2013 04:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekdays(\n [CalendarApp.Weekday.TUESDAY, CalendarApp.Weekday.THURSDAY])\n .until(new Date('January 1, 2014')),\n {location: 'Conference Room'},\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates a calendar to delete.\nconst calendar = CalendarApp.createCalendar('Test');\n\n// Deletes the 'Test' calendar permanently.\ncalendar.deleteCalendar();\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the color of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getColor() instead.\nconst calendarColor = calendar.getColor();\nconsole.log(calendarColor);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Sets the description of the calendar to 'Test description.'\ncalendar.setDescription('Test description');\n\n// Gets the description of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getDescription() instead.\nconst description = calendar.getDescription();\nconsole.log(description);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Creates an event for the moon landing.\nconst event = calendar.createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:05:00 UTC'),\n new Date('July 20, 1969 20:17:00 UTC'),\n);\n\n// Gets the calendar event ID and logs it to the console.\nconst iCalId = event.getId();\nconsole.log(iCalId);\n\n// Gets the event by its ID and logs the title of the event to the console.\n// For the default calendar, you can use CalendarApp.getEventById(iCalId)\n// instead.\nconst myEvent = calendar.getEventById(iCalId);\nconsole.log(myEvent.getTitle());\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Creates an event series for a daily team meeting from 1 PM to 2 PM.\n// The series adds the daily event from January 1, 2023 through December 31,\n// 2023.\nconst eventSeries = calendar.createEventSeries(\n 'Team meeting',\n new Date('Jan 1, 2023 13:00:00'),\n new Date('Jan 1, 2023 14:00:00'),\n CalendarApp.newRecurrence().addDailyRule().until(new Date('Jan 1, 2024')),\n);\n\n// Gets the ID of the event series.\nconst iCalId = eventSeries.getId();\n\n// Gets the event series by its ID and logs the series title to the console.\n// For the default calendar, you can use CalendarApp.getEventSeriesById(iCalId)\n// instead.\nconsole.log(calendar.getEventSeriesById(iCalId).getTitle());\n```\n\nExample:\n```text\n// Determines how many events are happening in the next two hours.\nconst now = new Date();\nconst twoHoursFromNow = new Date(now.getTime() + 2 * 60 * 60 * 1000);\nconst events = CalendarApp.getDefaultCalendar().getEvents(now, twoHoursFromNow);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening in the next two hours that contain\n// the term \"meeting\".\nconst now = new Date();\nconst twoHoursFromNow = new Date(now.getTime() + 2 * 60 * 60 * 1000);\nconst events = CalendarApp.getDefaultCalendar().getEvents(\n now,\n twoHoursFromNow,\n {search: 'meeting'},\n);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening today.\nconst today = new Date();\nconst events = CalendarApp.getDefaultCalendar().getEventsForDay(today);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening today and contain the term\n// \"meeting\".\nconst today = new Date();\nconst events = CalendarApp.getDefaultCalendar().getEventsForDay(today, {\n search: 'meeting',\n});\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// To get the user's default calendar, use CalendarApp.getDefaultCalendar().\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the ID of the calendar and logs it to the console.\nconst calendarId = calendar.getId();\nconsole.log(calendarId);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the name of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getName() instead.\nconst calendarName = calendar.getName();\nconsole.log(calendarName);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the time zone of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getTimeZone() instead.\nconst timeZone = calendar.getTimeZone();\nconsole.log(timeZone);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Determines whether the calendar is hidden in the user interface and logs it\n// to the console. For the default calendar, you can use CalendarApp.isHidden()\n// instead.\nconst isHidden = calendar.isHidden();\nconsole.log(isHidden);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Determines whether the calendar is the default calendar for\n// the effective user and logs it to the console.\n// For the default calendar, you can use CalendarApp.isMyPrimaryCalendar()\n// instead.\nconst isMyPrimaryCalendar = calendar.isMyPrimaryCalendar();\nconsole.log(isMyPrimaryCalendar);\n```\n\nExample:\n```text\n// Gets a calendar by its ID. To get the user's default calendar, use\n// CalendarApp.getDefault() instead.\n// TODO(developer): Replace the ID with the calendar ID that you want to use.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Determines whether the calendar is owned by you and logs it.\nconsole.log(calendar.isOwnedByMe());\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Determines whether the calendar's events are displayed in the user interface\n// and logs it.\nconsole.log(calendar.isSelected());\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Sets the color of the calendar to pink using the Calendar Color enum.\n// For the default calendar, you can use CalendarApp.setColor() instead.\ncalendar.setColor(CalendarApp.Color.PINK);\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the description of the calendar.\n// TODO(developer): Update the string with the description that you want to use.\ncalendar.setDescription('Updated calendar description.');\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the name of the calendar.\n// TODO(developer): Update the string with the name that you want to use.\ncalendar.setName('Example calendar name');\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Selects the calendar so that its events are displayed in the user interface.\n// To unselect the calendar, set the parameter to false.\ncalendar.setSelected(true);\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the time zone of the calendar to America/New York (US/Eastern) time.\ncalendar.setTimeZone('America/New_York');\n```\n\nExample:\n```text\n// Gets the calendar by its ID.\n// TODO(developer): Replace the calendar ID with the calendar ID that you want\n// to get.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Unsubscribes the user from the calendar.\nconst result = calendar.unsubscribeFromCalendar();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.245Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":33,"totalLines":426,"estimatedTokens":3217}}1058{"id":"doc-class_checkboxgriditem_apps_script_google_for_de-31c278fb","source":"documentation","title":"Class CheckboxGridItem | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/forms/checkbox-grid-item","text":"Example:\n```text\n// Open a form by ID and add a new checkbox grid item.\nconst form = FormApp.openById('1234567890abcdefghijklmnopqrstuvwxyz');\nconst item = form.addCheckboxGridItem();\nitem.setTitle('Where did you celebrate New Years?')\n .setRows(['New York', 'San Francisco', 'London'])\n .setColumns(['2014', '2015', '2016', '2017']);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.247Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":11,"estimatedTokens":90}}1059{"id":"doc-class_embeddedchartbuilder_apps_script_google_fo-32c38c4f","source":"documentation","title":"Class EmbeddedChartBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_embeddedchartbuilder","text":"Example:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B8');\nlet chart = sheet.getCharts()[0];\nchart = chart.modify()\n .addRange(range)\n .setOption('title', 'Updated!')\n .setOption('animation.duration', 500)\n .setPosition(2, 2, 0, 0)\n .build();\nsheet.updateChart(chart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B8'))\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This code updates the chart to use only the new ranges while preserving the\n// existing formatting of the chart.\nconst chart = sheet.getCharts()[0];\nconst newChart = chart.modify()\n .clearRanges()\n .addRange(sheet.getRange('A1:A5'))\n .addRange(sheet.getRange('B1:B5'))\n .build();\nsheet.updateChart(newChart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst chartBuilder = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B8'))\n .setPosition(5, 5, 0, 0);\n\n// This method returns the exact same data as Chart#getContainerInfo()\nconst containerInfo = chartBuilder.getContainer();\n\n// Logs the values used in setPosition()\nLogger.log(\n 'Anchor Column: %s\\r\\nAnchor Row %s\\r\\nOffset X %s\\r\\nOffset Y %s',\n containerInfo.getAnchorColumn(),\n containerInfo.getAnchorRow(),\n containerInfo.getOffsetX(),\n containerInfo.getOffsetY(),\n);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst chartBuilder = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B8'))\n .setPosition(5, 5, 0, 0);\n\nconst ranges = chartBuilder.getRanges();\n\n// There's only one range as a data source for this chart,\n// so this logs \"A1:B8\"\nfor (const i in ranges) {\n const range = ranges[i];\n Logger.log(range.getA1Notation());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst firstRange = sheet.getRange('A1:B5');\nconst secondRange = sheet.getRange('A6:B8');\n\nconst chartBuilder = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(firstRange)\n // This range renders in a different color\n .addRange(secondRange)\n .setPosition(5, 5, 0, 0);\n\n// Note that you can use either of these two formats, but the range\n// MUST match up with a range that was added via addRange(), or it\n// is not removed, and does not throw an exception\nchartBuilder.removeRange(firstRange);\nchartBuilder.removeRange(sheet.getRange('A6:B8'));\n\nconst chart = chartBuilder.build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setHiddenDimensionStrategy(\n Charts.ChartHiddenDimensionStrategy.IGNORE_COLUMNS,\n )\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B10');\nconst range2 = sheet.getRange('C:C10');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .addRange(range2)\n .setMergeStrategy(Charts.ChartMergeStrategy.MERGE_ROWS)\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setNumHeaders(1)\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = spreadsheet.getSheets()[0];\nconst chart = sheet.newChart()\n .setOption('title', 'Earnings projections')\n .setOption('legend', {\n position: 'top',\n textStyle: { color: 'blue', fontSize: 16 },\n }).build();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setTransposeRowsAndColumns(true)\n .setPosition(5, 5, 0, 0)\n .build();\n\nsheet.insertChart(chart);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.249Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":209,"estimatedTokens":1454}}1060{"id":"doc-class_jdbc_apps_script_google_for_developers-4a587878","source":"documentation","title":"Class Jdbc | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_jdbc","text":"Example:\n```text\nconst conn = Jdbc.getConnection(\n 'jdbc:mysql://yoursqlserver.example.com:3306/database_name',\n);\n```\n\nExample:\n```text\nconst conn = Jdbc.getConnection(\n 'jdbc:mysql://yoursqlserver.example.com:3306/database_name',\n {user: 'username', password: 'password'},\n);\n```\n\nExample:\n```text\nconst conn = Jdbc.getConnection(\n 'jdbc:mysql://yoursqlserver.example.com:3306/database_name',\n 'username',\n 'password',\n);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.250Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":25,"estimatedTokens":115}}1061{"id":"doc-class_gmailthread_apps_script_google_for_develop-a2e91f11","source":"documentation","title":"Class GmailThread | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/gmail/gmail-thread","text":"Example:\n```text\n// Add label MyLabel to the first thread in the inbox\nconst label = GmailApp.getUserLabelByName('MyLabel');\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.addLabel(label);\n```\n\nExample:\n```text\n// Create a draft reply to the message author with an acknowledgement.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.createDraftReply('Got your message');\n```\n\nExample:\n```text\n// Create a draft response with an HTML text body.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.createDraftReply('incapable of HTML', {\n htmlBody: '<b>some HTML body text</b>',\n cc: 'another@example.com',\n});\n```\n\nExample:\n```text\n// Create a draft reply to all recipients (except those bcc'd) of the last email\n// in this thread.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.createDraftReplyAll('Got your message');\n```\n\nExample:\n```text\n// Create a draft reply, using an HTML text body, to all recipients (except\n// those bcc'd) of the last email of in this thread.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.createDraftReplyAll('incapable of HTML', {\n htmlBody: '<b>some HTML body text</b>',\n cc: 'another@example.com',\n});\n```\n\nExample:\n```text\n// Log the subject of the first message in the first thread in the inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nLogger.log(firstThread.getFirstMessageSubject());\n```\n\nExample:\n```text\n// Log the subject of the first message in the first thread in the inbox.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst id = firstThread.getId();\n// Get same thread by its ID.\nconst thread = GmailApp.getThreadById(id);\nLogger.log(\n thread.getFirstMessageSubject() === firstThread.getFirstMessageSubject(),\n); // True\n```\n\nExample:\n```text\n// Log the names of the labels attached to the first thread in the inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst labels = firstThread.getLabels();\nfor (let i = 0; i < labels.length; i++) {\n Logger.log(labels[i].getName());\n}\n```\n\nExample:\n```text\n// Log the date of the most recent message on the first thread in the inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nLogger.log(firstThread.getLastMessageDate());\n```\n\nExample:\n```text\n// Log the number of messages in the thread\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nLogger.log(firstThread.getMessageCount());\n```\n\nExample:\n```text\n// Log the subjects of the messages in the thread\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst messages = firstThread.getMessages();\nfor (let i = 0; i < messages.length; i++) {\n Logger.log(messages[i].getSubject());\n}\n```\n\nExample:\n```text\n// Logs the permalink for the first thread in the inbox\nconst thread = GmailApp.getInboxThreads(0, 1)[0];\nLogger.log(thread.getPermalink());\n```\n\nExample:\n```text\n// Log if this thread has starred messages\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nLogger.log(`has starred : ${firstThread.hasStarredMessages()}`);\n```\n\nExample:\n```text\n// Log if this thread is marked as important\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nLogger.log(`Important? : ${firstThread.isImportant()}`);\n```\n\nExample:\n```text\n// Log if this thread is a chat\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nLogger.log(`is in chats? : ${firstThread.isInChats()}`);\n```\n\nExample:\n```text\n// Log if this thread is in the inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nLogger.log(`is in the inbox? : ${firstThread.isInInbox()}`);\n```\n\nExample:\n```text\n// Log if this thread is in the priority inbox\nconst firstThread = GmailApp.getPriorityInboxThreads(0, 1)[0];\nLogger.log(`is in priority inbox? ${firstThread.isInPriorityInbox()}`);\n```\n\nExample:\n```text\n// Log if this thread is in the spam folder\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nLogger.log(`Spam? ${firstThread.isInSpam()}`);\n```\n\nExample:\n```text\n// Log if this thread is in the trash\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nLogger.log(`Trashed? ${firstThread.isInTrash()}`);\n```\n\nExample:\n```text\n// Log if this thread is unread\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nLogger.log(`Unread? ${firstThread.isUnread()}`);\n```\n\nExample:\n```text\n// Mark first inbox thread as important\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.markImportant();\n```\n\nExample:\n```text\n// Mark first inbox thread as read\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.markRead();\n```\n\nExample:\n```text\n// Mark first inbox thread as unimportant\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.markUnimportant();\n```\n\nExample:\n```text\n// Mark first inbox thread as unread\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.markUnread();\n```\n\nExample:\n```text\n// Archive first inbox thread\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.moveToArchive();\n```\n\nExample:\n```text\n// Move first non-inbox thread to inbox\nconst firstThread = GmailApp.search('-in:inbox')[0];\nfirstThread.moveToInbox();\n```\n\nExample:\n```text\n// Move first inbox thread to spam\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.moveToSpam();\n```\n\nExample:\n```text\n// Move first inbox thread to trash\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.moveToTrash();\n```\n\nExample:\n```text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\n// ...Do something that may take a while here....\nfirstThread.refresh(); // Make sure it's up-to-date\n// ...Do more stuff with firstThread ...\n```\n\nExample:\n```text\nconst myLabel = GmailApp.getUserLabelByName('<your label>');\nconst threads = myLabel.getThreads();\nfor (const thread of threads) {\n thread.removeLabel(myLabel);\n}\n```\n\nExample:\n```text\n// Respond to author of last email in thread with acknowledgment\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.reply('Got your message');\n```\n\nExample:\n```text\n// Respond with HTML body text.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.reply('incapable of HTML', {\n htmlBody: 'some HTML body text',\n noReply: true,\n});\n```\n\nExample:\n```text\n// Respond to all with acknowledgment to the first thread in the inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.replyAll('Got your message');\n```\n\nExample:\n```text\n// Respond with HTML body text.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nfirstThread.replyAll('incapable of HTML', {\n htmlBody: 'some HTML body text',\n noReply: true,\n});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.253Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":34,"totalLines":269,"estimatedTokens":1655}}1062{"id":"doc-class_documentapp_apps_script_google_for_develop-96531acd","source":"documentation","title":"Class DocumentApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_documentapp","text":"Example:\n```text\n// Open a document by ID.\n// TODO(developer): Replace the ID with your own.\nlet doc = DocumentApp.openById('DOCUMENT_ID');\n\n// Create and open a document.\ndoc = DocumentApp.create('Document Name');\n```\n\nExample:\n```text\n// Create and open a new document.\nconst doc = DocumentApp.create('Document Name');\n```\n\nExample:\n```text\n// Get the document to which this script is bound.\nconst doc = DocumentApp.getActiveDocument();\n```\n\nExample:\n```text\n// Add a custom menu to the active document, including a separator and a\n// sub-menu.\nfunction onOpen(e) {\n DocumentApp.getUi()\n .createMenu('My Menu')\n .addItem('My menu item', 'myFunction')\n .addSeparator()\n .addSubMenu(\n DocumentApp.getUi()\n .createMenu('My sub-menu')\n .addItem('One sub-menu item', 'mySecondFunction')\n .addItem('Another sub-menu item', 'myThirdFunction'),\n )\n .addToUi();\n}\n```\n\nExample:\n```text\n// Open a document by ID.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('DOCUMENT_ID');\n```\n\nExample:\n```text\n// Open a document by URL.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/1234567890abcdefghijklmnopqrstuvwxyz_a1b2c3/edit',\n);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.256Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":57,"estimatedTokens":321}}1063{"id":"doc-html_service_create_and_serve_html_apps_script_g-22429c15","source":"documentation","title":"HTML Service: Create and Serve HTML | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/html_service","text":"Example:\n```text\nfunction doGet() {\n return HtmlService.createHtmlOutputFromFile('Index');\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n </head>\n <body>\n Hello, World!\n </body>\n</html>\n```\n\nExample:\n```text\n// Use this code for Google Docs, Slides, Forms, or Sheets.\nfunction onOpen() {\n SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.\n .createMenu('Dialog')\n .addItem('Open', 'openDialog')\n .addToUi();\n}\n\nfunction openDialog() {\n var html = HtmlService.createHtmlOutputFromFile('Index');\n SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.\n .showModalDialog(html, 'Dialog title');\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n </head>\n <body>\n Hello, World!\n <input type=\"button\" value=\"Close\"\n onclick=\"google.script.host.close()\" />\n </body>\n</html>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.257Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":53,"estimatedTokens":231}}1064{"id":"doc-class_spreadsheetapp_apps_script_google_for_deve-33595c75","source":"documentation","title":"Class SpreadsheetApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_spreadsheetapp","text":"Example:\n```text\n// The code below creates a new spreadsheet \"Finances\" and logs the URL for it\nconst ssNew = SpreadsheetApp.create('Finances');\nLogger.log(ssNew.getUrl());\n```\n\nExample:\n```text\n// The code below creates a new spreadsheet \"Finances\" with 50 rows and 5\n// columns and logs the URL for it\nconst ssNew = SpreadsheetApp.create('Finances', 50, 5);\nLogger.log(ssNew.getUrl());\n```\n\nExample:\n```text\n// Turns data execution on for all types of data sources.\nSpreadsheetApp.enableAllDataSourcesExecution();\n\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets the first data source sheet in the spreadsheet and refreshes the data.\nss.getDataSourceSheets()[0].refreshData();\n```\n\nExample:\n```text\n// Turns data execution on for BigQuery data sources.\nSpreadsheetApp.enableBigQueryExecution();\n\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets the first data source sheet in the spreadsheet and refreshes the\n// BigQuery data.\nss.getDataSourceSheets()[0].refreshData();\n```\n\nExample:\n```text\n// Turns data execution on for Looker data sources.\nSpreadsheetApp.enableLookerExecution();\n\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets the first data source sheet in the spreadsheet and refreshes the\n// associated Looker data.\nss.getDataSourceSheets()[0].refreshData();\n```\n\nExample:\n```text\n// The code below changes the background color of cells A1 and B1 twenty times.\n// You should be able to see the updates live in the spreadsheet. If flush() is\n// not called, the updates may be applied live or may all be applied at once\n// when the script completes.\nfunction colors() {\n const sheet = SpreadsheetApp.getActiveSheet();\n for (let i = 0; i < 20; i++) {\n if (i % 2 === 0) {\n sheet.getRange('A1').setBackground('green');\n sheet.getRange('B1').setBackground('red');\n } else {\n sheet.getRange('A1').setBackground('red');\n sheet.getRange('B1').setBackground('green');\n }\n SpreadsheetApp.flush();\n }\n}\n```\n\nExample:\n```text\n// The code below logs the URL for the active spreadsheet.\nLogger.log(SpreadsheetApp.getActive().getUrl());\n```\n\nExample:\n```text\n// The code below logs the background color for the active range.\nconst colorObject = SpreadsheetApp.getActiveRange().getBackgroundObject();\n// Assume the color has ColorType.RGB.\nLogger.log(colorObject.asRgbColor().asHexString());\n```\n\nExample:\n```text\n// Returns the list of active ranges.\nconst rangeList = SpreadsheetApp.getActiveRangeList();\n```\n\nExample:\n```text\n// The code below logs the name of the active sheet.\nLogger.log(SpreadsheetApp.getActiveSheet().getName());\n```\n\nExample:\n```text\n// The code below logs the URL for the active spreadsheet.\nLogger.log(SpreadsheetApp.getActiveSpreadsheet().getUrl());\n```\n\nExample:\n```text\n// Returns the current highlighted cell in the one of the active ranges.\nconst currentCell = SpreadsheetApp.getCurrentCell();\n```\n\nExample:\n```text\nconst selection = SpreadsheetApp.getSelection();\nconst currentCell = selection.getCurrentCell();\n```\n\nExample:\n```text\n// Add a custom menu to the active spreadsheet, including a separator and a\n// sub-menu.\nfunction onOpen(e) {\n SpreadsheetApp.getUi()\n .createMenu('My Menu')\n .addItem('My menu item', 'myFunction')\n .addSeparator()\n .addSubMenu(\n SpreadsheetApp.getUi()\n .createMenu('My sub-menu')\n .addItem('One sub-menu item', 'mySecondFunction')\n .addItem('Another sub-menu item', 'myThirdFunction'),\n )\n .addToUi();\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets cell A1 on Sheet1.\nconst range = sheet.getRange('A1');\n\n// Builds an image using a source URL.\nconst cellImage =\n SpreadsheetApp.newCellImage()\n .setSourceUrl(\n 'https://www.gstatic.com/images/branding/productlogos/apps_script/v10/web-64dp/logo_apps_script_color_1x_web_64dp.png',\n )\n .build();\n\n// Sets the image in cell A1.\nrange.setValue(cellImage);\n```\n\nExample:\n```text\nconst rgbColor = SpreadsheetApp.newColor().setRgbColor('#FF0000').build();\n```\n\nExample:\n```text\n// Adds a conditional format rule to a sheet that causes all cells in range\n// A1:B3 to turn red if they contain a number between 1 and 10.\nconst sheet = SpreadsheetApp.getActive().getActiveSheet();\nconst range = sheet.getRange('A1:B3');\nconst rule = SpreadsheetApp.newConditionalFormatRule()\n .whenNumberBetween(1, 10)\n .setBackground('#FF0000')\n .setRanges([range])\n .build();\nconst rules = sheet.getConditionalFormatRules();\nrules.push(rule);\nsheet.setConditionalFormatRules(rules);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Enables BigQuery.\nSpreadsheetApp.enableBigQueryExecution();\n\n// Builds a data source specification.\n// TODO (developer): Update the project ID to your own Google Cloud project ID.\nconst dataSourceSpec = SpreadsheetApp.newDataSourceSpec()\n .asBigQuery()\n .setProjectId('project-id-1')\n .setTableProjectId('bigquery-public-data')\n .setDatasetId('ncaa_basketball')\n .setTableId('mbb_historical_teams_games')\n .build();\n\n// Adds the data source and its data to the spreadsheet.\nss.insertDataSourceSheet(dataSourceSpec);\n```\n\nExample:\n```text\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = SpreadsheetApp.newDataValidation()\n .requireNumberBetween(1, 100)\n .setAllowInvalid(false)\n .setHelpText('Number must be between 1 and 100.')\n .build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Sets the range to A1:D20.\nconst range = sheet.getRange('A1:D20');\n\n// Creates a filter and applies it to the specified range.\nrange.createFilter();\n\n// Gets the current filter for the range and creates filter criteria that only\n// shows cells that aren't empty.\nconst filter = range.getFilter();\nconst criteria = SpreadsheetApp.newFilterCriteria().whenCellNotEmpty().build();\n\n// Sets the criteria to column C.\nfilter.setColumnFilterCriteria(3, criteria);\n```\n\nExample:\n```text\n// Sets cell A1 to have the text \"Hello world\", with \"Hello\" bolded.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst bold = SpreadsheetApp.newTextStyle().setBold(true).build();\nconst value = SpreadsheetApp.newRichTextValue()\n .setText('Hello world')\n .setTextStyle(0, 5, bold)\n .build();\ncell.setRichTextValue(value);\n```\n\nExample:\n```text\n// Sets range A1:B3 to have red, size 22, bolded, underlined text.\nconst range = SpreadsheetApp.getActive().getRange('A1:B3');\nconst style = SpreadsheetApp.newTextStyle()\n .setForegroundColor('red')\n .setFontSize(22)\n .setBold(true)\n .setUnderline(true)\n .build();\nrange.setTextStyle(style);\n```\n\nExample:\n```text\n// Get any starred spreadsheets from Google Drive, then open the spreadsheets\n// and log the name of the first sheet within each spreadsheet.\nconst files = DriveApp.searchFiles(\n `starred = true and mimeType = \"${MimeType.GOOGLE_SHEETS}\"`,\n);\nwhile (files.hasNext()) {\n const spreadsheet = SpreadsheetApp.open(files.next());\n const sheet = spreadsheet.getSheets()[0];\n Logger.log(sheet.getName());\n}\n```\n\nExample:\n```text\n// The code below opens a spreadsheet using its ID and logs the name for it.\n// Note that the spreadsheet is NOT physically opened on the client side.\n// It is opened on the server only (for modification by the script).\nconst ss = SpreadsheetApp.openById('abc1234567');\nLogger.log(ss.getName());\n```\n\nExample:\n```text\n// Opens a spreadsheet by its URL and logs its name.\n// Note that the spreadsheet doesn't physically open on the client side.\n// It opens on the server only (for modification by the script).\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc1234567/edit',\n);\nconsole.log(ss.getName());\n```\n\nExample:\n```text\n// The code below sets range C1:D4 in the first sheet as the active range.\nconst range =\n SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('C1:D4');\nSpreadsheetApp.setActiveRange(range);\n\nconst selection = SpreadsheetApp.getSelection();\n// Current cell: C1\nconst currentCell = selection.getCurrentCell();\n// Active Range: C1:D4\nconst activeRange = selection.getActiveRange();\n```\n\nExample:\n```text\n// The code below sets ranges [D4, B2:C4] in the active sheet as the active\n// ranges.\nconst rangeList = SpreadsheetApp.getActiveSheet().getRanges(['D4', 'B2:C4']);\nSpreadsheetApp.setActiveRangeList(rangeList);\n\nconst selection = SpreadsheetApp.getSelection();\n// Current cell: B2\nconst currentCell = selection.getCurrentCell();\n// Active range: B2:C4\nconst activeRange = selection.getActiveRange();\n// Active range list: [D4, B2:C4]\nconst activeRangeList = selection.getActiveRangeList();\n```\n\nExample:\n```text\n// The code below makes the 2nd sheet active in the active spreadsheet.\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nSpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[1]);\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nconst firstSheet = spreadsheet.getSheets()[0];\nconst secondSheet = spreadsheet.getSheets()[1];\n// Set the first sheet as the active sheet and select the range D4:F4.\nspreadsheet.setActiveSheet(firstSheet).getRange('D4:F4').activate();\n\n// Switch to the second sheet to do some work.\nspreadsheet.setActiveSheet(secondSheet);\n// Switch back to first sheet, and restore its selection.\nspreadsheet.setActiveSheet(firstSheet, true);\n\n// The selection of first sheet is restored, and it logs D4:F4\nconst range = spreadsheet.getActiveSheet().getSelection().getActiveRange();\nLogger.log(range.getA1Notation());\n```\n\nExample:\n```text\n// The code below makes the spreadsheet with key \"1234567890\" the active\n// spreadsheet\nconst ss = SpreadsheetApp.openById('1234567890');\nSpreadsheetApp.setActiveSpreadsheet(ss);\n```\n\nExample:\n```text\n// The code below sets the cell B5 in the first sheet as the current cell.\nconst cell =\n SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('B5');\nSpreadsheetApp.setCurrentCell(cell);\n\nconst selection = SpreadsheetApp.getSelection();\n// Current cell: B5\nconst currentCell = selection.getCurrentCell();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.259Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":31,"totalLines":393,"estimatedTokens":3018}}1065{"id":"doc-class_clocktriggerbuilder_apps_script_google_for-83df4de5","source":"documentation","title":"Class ClockTriggerBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_clocktriggerbuilder","text":"Example:\n```text\n// Creates a trigger that runs 10 minutes later\nScriptApp.newTrigger('myFunction').timeBased().after(10 * 60 * 1000).create();\n```\n\nExample:\n```text\n// Creates a trigger for December 1, 2012\nconst triggerDay = new Date(2012, 11, 1);\nScriptApp.newTrigger('myFunction').timeBased().at(triggerDay).create();\n```\n\nExample:\n```text\n// Schedules for January 1st, 2013\nScriptApp.newTrigger('myFunction').timeBased().atDate(2013, 1, 1).create();\n```\n\nExample:\n```text\n// Runs between 5am-6am in the timezone of the script\nScriptApp.newTrigger('myFunction')\n .timeBased()\n .atHour(5)\n .everyDays(\n 1) // Frequency is required if you are using atHour() or nearMinute()\n .create();\n```\n\nExample:\n```text\nScriptApp.newTrigger('myFunction').timeBased().everyDays(3).create();\n```\n\nExample:\n```text\nScriptApp.newTrigger('myFunction').timeBased().everyHours(12).create();\n```\n\nExample:\n```text\nScriptApp.newTrigger('myFunction').timeBased().everyMinutes(10).create();\n```\n\nExample:\n```text\nScriptApp.newTrigger('myFunction')\n .timeBased()\n .everyWeeks(2)\n .onWeekDay(ScriptApp.WeekDay.FRIDAY)\n .create();\n```\n\nExample:\n```text\n// Schedule the trigger to execute at noon every day in the US/Pacific time zone\nScriptApp.newTrigger('myFunction')\n .timeBased()\n .atHour(12)\n .everyDays(1)\n .inTimezone('America/Los_Angeles')\n .create();\n```\n\nExample:\n```text\n// Runs at approximately 5:30am in the timezone of the script\nScriptApp.newTrigger('myFunction')\n .timeBased()\n .atHour(5)\n .nearMinute(30)\n .everyDays(\n 1) // Frequency is required if you are using atHour() or nearMinute()\n .create();\n```\n\nExample:\n```text\n// Schedules for the first of every month\nScriptApp.newTrigger('myFunction').timeBased().onMonthDay(1).create();\n```\n\nExample:\n```text\nScriptApp.newTrigger('myFunction')\n .timeBased()\n .onWeekDay(ScriptApp.WeekDay.FRIDAY)\n .create();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.261Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":92,"estimatedTokens":487}}1066{"id":"doc-libraries_apps_script_google_for_developers-e7d3c8fc","source":"documentation","title":"Libraries | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/libraries","text":"Example:\n```text\n/**\n * Raises a number to the given power, and returns the result.\n *\n * @param {number} base the number we're raising to a power\n * @param {number} exp the exponent we're raising the base to\n * @return {number} the result of the exponential calculation\n */\nfunction power(base, exp) { ... }\n```\n\nExample:\n```text\nfunction getLibraryProperty(key) {\n const scriptProperties = PropertiesService.getScriptProperties();\n return scriptProperties.getProperty(key);\n }\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.263Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":21,"estimatedTokens":126}}1067{"id":"doc-content_service_apps_script_google_for_developer-54b0a446","source":"documentation","title":"Content Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/service_content","text":"Example:\n```text\nfunction doGet() {\n return ContentService.createTextOutput('Hello, world!');\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.264Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":8,"estimatedTokens":29}}1068{"id":"doc-authorization_for_google_services_apps_script_go-eb7f8088","source":"documentation","title":"Authorization for Google Services | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guide_security","text":"Example:\n```text\n/**\n * @OnlyCurrentDoc\n */\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.264Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":8,"estimatedTokens":16}}1069{"id":"doc-xml_service_apps_script_google_for_developers-568b24d1","source":"documentation","title":"XML Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_soapservice","text":"Example:\n```text\n// Log the title and labels for the first page of blog posts on\n// Google's The Keyword blog.\nfunction parseXml() {\n let url = 'https://blog.google/rss/';\n let xml = UrlFetchApp.fetch(url).getContentText();\n let document = XmlService.parse(xml);\n let root = document.getRootElement();\n\n let channel = root.getChild('channel');\n let items = channel.getChildren('item');\n items.forEach(item => {\n let title = item.getChild('title').getText();\n let categories = item.getChildren('category');\n let labels = categories.map(category => category.getText());\n console.log('%s (%s)', title, labels.join(', '));\n });\n}\n\n// Create and log an XML representation of first 10 threads in your Gmail inbox.\nfunction createXml() {\n let root = XmlService.createElement('threads');\n let threads = GmailApp.getInboxThreads()\n threads = threads.slice(0,10); // Just the first 10\n threads.forEach(thread => {\n let child = XmlService.createElement('thread')\n .setAttribute('messageCount', thread.getMessageCount())\n .setAttribute('isUnread', thread.isUnread())\n .setText(thread.getFirstMessageSubject());\n root.addContent(child);\n });\n let document = XmlService.createDocument(root);\n let xml = XmlService.getPrettyFormat().format(document);\n console.log(xml);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.267Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":39,"estimatedTokens":333}}1070{"id":"doc-web_apps_apps_script_google_for_developers-6a91dbc0","source":"documentation","title":"Web Apps | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/execution_web_apps","text":"Example:\n```text\nname=alice&n=1&n=2\n```\n\nExample:\n```text\n{\"name\": \"alice\", \"n\": \"1\"}\n```\n\nExample:\n```text\n{\"name\": [\"alice\"], \"n\": [\"1\", \"2\"]}\n```\n\nExample:\n```text\n332\n```\n\nExample:\n```text\ntext/csv\n```\n\nExample:\n```text\nAlice,21\n```\n\nExample:\n```text\npostData\n```\n\nExample:\n```text\nhttps://script.google.com/.../exec?username=jsmith&age=21\n```\n\nExample:\n```text\nfunction doGet(e) {\n var params = JSON.stringify(e);\n return ContentService.createTextOutput(params).setMimeType(ContentService.MimeType.JSON);\n}\n```\n\nExample:\n```text\n{\n \"queryString\": \"username=jsmith&age=21\",\n \"parameter\": {\n \"username\": \"jsmith\",\n \"age\": \"21\"\n },\n \"contextPath\": \"\",\n \"parameters\": {\n \"username\": [\n \"jsmith\"\n ],\n \"age\": [\n \"21\"\n ]\n },\n \"contentLength\": -1\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.268Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":10,"totalLines":70,"estimatedTokens":200}}1071{"id":"doc-class_utilities_apps_script_google_for_developer-fbef6ae8","source":"documentation","title":"Class Utilities | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_utilities","text":"Example:\n```text\n// This is the base64 encoded form of \"Google グループ\"\nconst base64data = 'R29vZ2xlIOOCsOODq+ODvOODlw==';\n\n// This logs:\n// [71, 111, 111, 103, 108, 101, 32, -29, -126, -80,\n// -29, -125, -85, -29, -125, -68, -29, -125, -105]\nconst decoded = Utilities.base64Decode(base64data);\nLogger.log(decoded);\n\n// If you want a String instead of a byte array:\n// This logs the original \"Google グループ\"\nLogger.log(Utilities.newBlob(decoded).getDataAsString());\n```\n\nExample:\n```text\n// This is the base64 encoded form of \"Google グループ\"\nconst base64data = 'R29vZ2xlIOOCsOODq+ODvOODlw==';\n\nconst decoded = Utilities.base64Decode(base64data, Utilities.Charset.UTF_8);\n\n// This logs:\n// [71, 111, 111, 103, 108, 101, 32, -29, -126, -80,\n// -29, -125, -85, -29, -125, -68, -29, -125, -105]\nLogger.log(decoded);\n\n// If you want a String instead of a byte array:\n// This logs the original \"Google グループ\"\nLogger.log(Utilities.newBlob(decoded).getDataAsString());\n```\n\nExample:\n```text\n// This is the base64 web-safe encoded form of \"Google グループ\"\nconst base64data = 'R29vZ2xlIOOCsOODq-ODvOODlw==';\n\nconst decoded = Utilities.base64DecodeWebSafe(base64data);\n\n// This logs:\n// [71, 111, 111, 103, 108, 101, 32, -29, -126, -80,\n// -29, -125, -85, -29, -125, -68, -29, -125, -105]\nLogger.log(decoded);\n\n// If you want a String instead of a byte array:\n// This logs the original \"Google グループ\"\nLogger.log(Utilities.newBlob(decoded).getDataAsString());\n```\n\nExample:\n```text\n// This is the base64 web-safe encoded form of \"Google グループ\"\nconst base64data = 'R29vZ2xlIOOCsOODq-ODvOODlw==';\n\nconst decoded = Utilities.base64DecodeWebSafe(\n base64data,\n Utilities.Charset.UTF_8,\n);\n\n// This logs:\n// [71, 111, 111, 103, 108, 101, 32, -29, -126, -80,\n// -29, -125, -85, -29, -125, -68, -29, -125, -105]\nLogger.log(decoded);\n\n// If you want a String instead of a byte array:\n// This logs the original \"Google グループ\"\nLogger.log(Utilities.newBlob(decoded).getDataAsString());\n```\n\nExample:\n```text\n// Instantiates a blob here for clarity\nconst blob = Utilities.newBlob('A string here');\n\n// Writes 'QSBzdHJpbmcgaGVyZQ==' to the log.\nconst encoded = Utilities.base64Encode(blob.getBytes());\nLogger.log(encoded);\n```\n\nExample:\n```text\n// Writes 'QSBzdHJpbmcgaGVyZQ==' to the log.\nconst encoded = Utilities.base64Encode('A string here');\nLogger.log(encoded);\n```\n\nExample:\n```text\n// \"Google Groups\" in Katakana (Japanese)\nconst input = 'Google グループ';\n\n// Writes \"R29vZ2xlIOOCsOODq+ODvOODlw==\" to the log\nconst encoded = Utilities.base64Encode(input, Utilities.Charset.UTF_8);\nLogger.log(encoded);\n```\n\nExample:\n```text\n// Instantiates a blob here for clarity\nconst blob = Utilities.newBlob('A string here');\n\n// Writes 'QSBzdHJpbmcgaGVyZQ==' to the log.\nconst encoded = Utilities.base64EncodeWebSafe(blob.getBytes());\nLogger.log(encoded);\n```\n\nExample:\n```text\n// Writes 'QSBzdHJpbmcgaGVyZQ==' to the log.\nconst encoded = Utilities.base64EncodeWebSafe('A string here');\nLogger.log(encoded);\n```\n\nExample:\n```text\n// \"Google Groups\" in Katakana (Japanese)\nconst input = 'Google グループ';\n\n// Writes \"R29vZ2xlIOOCsOODq-ODvOODlw==\" to the log\nconst encoded = Utilities.base64EncodeWebSafe(input, Utilities.Charset.UTF_8);\nLogger.log(encoded);\n```\n\nExample:\n```text\nconst input = Utilities.base64Decode(\n 'aW5wdXQgdG8gaGFzaA0K'); // == base64encode(\"input to hash\")\nconst digest = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, input);\nLogger.log(digest);\n```\n\nExample:\n```text\nconst digest = Utilities.computeDigest(\n Utilities.DigestAlgorithm.MD5,\n 'input to hash',\n);\nLogger.log(digest);\n```\n\nExample:\n```text\nconst digest = Utilities.computeDigest(\n Utilities.DigestAlgorithm.MD5,\n 'input to hash',\n Utilities.Charset.US_ASCII,\n);\nLogger.log(digest);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst input = Utilities.base64Decode(\n 'aW5wdXQgdG8gaGFzaA0K'); // == base64encode(\"input to hash\")\nconst key = Utilities.base64Decode('a2V5'); // == base64encode(\"key\")\nconst signature = Utilities.computeHmacSha256Signature(input, key);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeHmacSha256Signature(\n 'this is my input',\n 'my key - use a stronger one',\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeHmacSha256Signature(\n 'this is my input',\n 'my key - use a stronger one',\n Utilities.Charset.US_ASCII,\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst input = Utilities.base64Decode(\n 'aW5wdXQgdG8gaGFzaA0K'); // == base64encode(\"input to hash\")\nconst key = Utilities.base64Decode('a2V5'); // == base64encode(\"key\")\nconst signature = Utilities.computeHmacSignature(\n Utilities.MacAlgorithm.HMAC_MD5,\n input,\n key,\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeHmacSignature(\n Utilities.MacAlgorithm.HMAC_MD5,\n 'input to hash',\n 'key',\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeHmacSignature(\n Utilities.MacAlgorithm.HMAC_MD5,\n 'input to hash',\n 'key',\n Utilities.Charset.US_ASCII,\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeRsaSha1Signature(\n 'this is my input',\n PropertiesService.getScriptProperties().getProperty('YOUR_PRIVATE_KEY'),\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeRsaSha1Signature(\n 'this is my input',\n PropertiesService.getScriptProperties().getProperty('YOUR_PRIVATE_KEY'),\n Utilities.Charset.US_ASCII,\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeRsaSha256Signature(\n 'this is my input',\n PropertiesService.getScriptProperties().getProperty('YOUR_PRIVATE_KEY'),\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeRsaSignature(\n Utilities.RsaAlgorithm.RSA_SHA_256,\n 'this is my input',\n PropertiesService.getScriptProperties().getProperty('YOUR_PRIVATE_KEY'),\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This writes an array of bytes to the log.\nconst signature = Utilities.computeRsaSignature(\n Utilities.RsaAlgorithm.RSA_SHA_256,\n 'this is my input',\n PropertiesService.getScriptProperties().getProperty('YOUR_PRIVATE_KEY'),\n Utilities.Charset.US_ASCII,\n);\nLogger.log(signature);\n```\n\nExample:\n```text\n// This formats the date as Greenwich Mean Time in the format\n// year-month-dateThour-minute-second.\nconst formattedDate = Utilities.formatDate(\n new Date(),\n 'GMT',\n 'yyyy-MM-dd\\'T\\'HH:mm:ss\\'Z\\'',\n);\nLogger.log(formattedDate);\n```\n\nExample:\n```text\n// \" 123.456000\"\nUtilities.formatString('%11.6f', 123.456);\n\n// \" abc\"\nUtilities.formatString('%6s', 'abc');\n```\n\nExample:\n```text\n// This assigns a UUID as a temporary ID for a data object you are creating in\n// your script.\nconst myDataObject = {\n tempId: Utilities.getUuid(),\n};\n```\n\nExample:\n```text\nconst textBlob = Utilities.newBlob(\n 'Some text to compress using gzip compression',\n);\n\n// Create the compressed blob.\nconst gzipBlob = Utilities.gzip(textBlob);\n```\n\nExample:\n```text\nconst textBlob = Utilities.newBlob(\n 'Some text to compress using gzip compression',\n);\n\n// Create the compressed blob.\nconst gzipBlob = Utilities.gzip(textBlob, 'text.gz');\n```\n\nExample:\n```text\n// Creates a blob object from a byte array.\nconst data = [71, 79, 79, 71, 76, 69];\nconst blob = Utilities.newBlob(data);\n\n// Logs the blob data as a string to the console.\nconsole.log(blob.getDataAsString());\n```\n\nExample:\n```text\n// Declares a byte array.\nconst data = [71, 79, 79, 71, 76, 69];\n\n// Declares the content type of the blob.\nconst contentType = 'application/json';\n\n// Creates a blob object from the byte array and content type.\nconst blob = Utilities.newBlob(data, contentType);\n\n// Logs the blob data as a string to the console.\nconsole.log(blob.getDataAsString());\n\n// Logs the content type of the blob to the console.\nconsole.log(blob.getContentType());\n```\n\nExample:\n```text\n// Declares a byte array.\nconst data = [71, 79, 79, 71, 76, 69];\n\n// Declares the content type of the blob.\nconst contentType = 'application/json';\n\n// Declares the name of the blob.\nconst name = 'Example blob';\n\n// Creates a blob object from the byte array, content type, and name.\nconst blob = Utilities.newBlob(data, contentType, name);\n\n// Logs the blob data as a string to the console.\nconsole.log('Blob data:', blob.getDataAsString());\n\n// Logs the content type of the blob to the console.\nconsole.log('Blob content type:', blob.getContentType());\n\n// Logs the name of the blob to the console.\nconsole.log('Blob name:', blob.getName());\n```\n\nExample:\n```text\n// Declares a string for the blob.\nconst data = 'GOOGLE';\n\n// Creates a blob object from a string.\nconst blob = Utilities.newBlob(data);\n\n// Logs the blob data in byte array to the console.\nconsole.log('Blob Data:', blob.getBytes());\n```\n\nExample:\n```text\n// Declares a string for the blob.\nconst data = 'GOOGLE';\n\n// Declares the content type of blob.\nconst contentType = 'application/json';\n\n// Creates a blob object from the string and content type.\nconst blob = Utilities.newBlob(data, contentType);\n\n// Logs the blob data in byte array to the console.\nconsole.log('Blob data:', blob.getBytes());\n\n// Logs the content type of the blob to the console.\nconsole.log(blob.getContentType());\n```\n\nExample:\n```text\n// Declares a string for the blob.\nconst data = 'GOOGLE';\n\n// Declares the content type of the blob.\nconst contentType = 'application/json';\n\n// Declares the name of the blob.\nconst name = 'Example blob';\n\n// Create a blob object from the string, content type, and name.\nconst blob = Utilities.newBlob(data, contentType, name);\n\n// Logs the blob data in byte array to the console.\nconsole.log('Blob data:', blob.getBytes());\n\n// Logs the content type of the blob to the console.\nconsole.log('Blob content type:', blob.getContentType());\n\n// Logs the name of the blob to the console.\nconsole.log('Blob name:', blob.getName());\n```\n\nExample:\n```text\n// This creates a two-dimensional array of the format [[a, b, c], [d, e, f]]\nconst csvString = 'a,b,c\\nd,e,f';\nconst data = Utilities.parseCsv(csvString);\n```\n\nExample:\n```text\n// This creates a two-dimensional array of the format [[a, b, c], [d, e, f]]\nconst csvString = 'a\\tb\\tc\\nd\\te\\tf';\nconst data = Utilities.parseCsv(csvString, '\\t');\n```\n\nExample:\n```text\n// This set of parameters parses the given string as a date in Greenwich Mean\n// Time, formatted as year-month-dateThour-minute-second.\nconst date = Utilities.parseDate(\n '1970-01-01 00:00:00',\n 'GMT',\n 'yyyy-MM-dd\\' \\'HH:mm:ss',\n);\nLogger.log(date);\n```\n\nExample:\n```text\n// Creates a blob object from a string.\nconst data = 'GOOGLE';\nconst blob = Utilities.newBlob(data);\n\n// Puts the script to sleep for 10,000 milliseconds (10 seconds).\nUtilities.sleep(10000);\n\n// Logs the blob data in byte array to the console.\nconsole.log(blob.getBytes());\n```\n\nExample:\n```text\nconst textBlob = Utilities.newBlob(\n 'Some text to compress using gzip compression',\n);\n\n// Create the compressed blob.\nconst gzipBlob = Utilities.gzip(textBlob, 'text.gz');\n\n// Uncompress the data.\nconst uncompressedBlob = Utilities.ungzip(gzipBlob);\n```\n\nExample:\n```text\nconst googleFavIconUrl = 'https://www.google.com/favicon.ico';\nconst googleLogoUrl = 'https://www.google.com/images/srpr/logo3w.png';\n\n// Fetch the Google favicon.ico file and get the Blob data\nconst faviconBlob = UrlFetchApp.fetch(googleFavIconUrl).getBlob();\nconst logoBlob = UrlFetchApp.fetch(googleLogoUrl).getBlob();\n\n// zip now references a blob containing an archive of both faviconBlob and\n// logoBlob\nconst zip = Utilities.zip([faviconBlob, logoBlob], 'google_images.zip');\n\n// This now unzips the blobs\nconst files = Utilities.unzip(zip);\n```\n\nExample:\n```text\nconst googleFavIconUrl = 'https://www.google.com/favicon.ico';\nconst googleLogoUrl = 'https://www.google.com/images/srpr/logo3w.png';\n\n// Fetch the Google favicon.ico file and get the Blob data\nconst faviconBlob = UrlFetchApp.fetch(googleFavIconUrl).getBlob();\nconst logoBlob = UrlFetchApp.fetch(googleLogoUrl).getBlob();\n\n// zip now references a blob containing an archive of both faviconBlob and\n// logoBlob\nconst zip = Utilities.zip([faviconBlob, logoBlob]);\n```\n\nExample:\n```text\nconst googleFavIconUrl = 'https://www.google.com/favicon.ico';\nconst googleLogoUrl = 'https://www.google.com/images/srpr/logo3w.png';\n\n// Fetch the Google favicon.ico file and get the Blob data\nconst faviconBlob = UrlFetchApp.fetch(googleFavIconUrl).getBlob();\nconst logoBlob = UrlFetchApp.fetch(googleLogoUrl).getBlob();\n\n// zip now references a blob containing an archive of both faviconBlob and\n// logoBlob\nconst zip = Utilities.zip([faviconBlob, logoBlob], 'google_images.zip');\n```\n\nExample:\n```text\n// Returns the object { name: \"John Smith\", company: \"Virginia Company\"}\nconst obj = Utilities.jsonParse(\n '{\"name\":\"John Smith\",\"company\":\"Virginia Company\"}',\n);\n```\n\nExample:\n```text\n// Logs: {\"name\":\"John Smith\",\"company\":\"Virginia Company\"}\nconst person = {\n name: 'John Smith',\n company: 'Virginia Company',\n};\nconst json = Utilities.jsonStringify(person);\nLogger.log(json);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.271Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":45,"totalLines":546,"estimatedTokens":3421}}1072{"id":"doc-enum_horizontalalignment_apps_script_google_for_-37130557","source":"documentation","title":"Enum HorizontalAlignment | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_documentapp_horizontalalignment","text":"Example:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Insert a paragraph and a table at the start of the tab.\nconst par1 = body.insertParagraph(0, 'Center');\nconst table = body.insertTable(1, [['Left', 'Right']]);\nconst par2 = table.getCell(0, 0).getChild(0).asParagraph();\nconst par3 = table.getCell(0, 0).getChild(0).asParagraph();\n\n// Center align the first paragraph.\npar1.setAlignment(DocumentApp.HorizontalAlignment.CENTER);\n\n// Left align the first cell.\npar2.setAlignment(DocumentApp.HorizontalAlignment.LEFT);\n\n// Right align the second cell.\npar3.setAlignment(DocumentApp.HorizontalAlignment.RIGHT);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.273Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":22,"estimatedTokens":172}}1073{"id":"doc-class_userproperties_apps_script_google_for_deve-7c7ead63","source":"documentation","title":"Class UserProperties | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_userproperties","text":"Example:\n```text\nUserProperties.deleteAllProperties();\n```\n\nExample:\n```text\nUserProperties.deleteProperty('special');\n```\n\nExample:\n```text\nUserProperties.setProperties({\n \"cow\" : \"moo\",\n \"sheep\" : \"baa\",\n \"chicken\" : \"cluck\"\n});\n\n// Logs \"A cow goes: moo\"\nLogger.log(\"A cow goes: %s\", UserProperties.getProperty(\"cow\"));\n\n// This makes a copy. Any changes that happen here will not\n// be written back to properties.\nvar animalSounds = UserProperties.getProperties();\n\n// Logs:\n// A chicken goes cluck!\n// A cow goes moo!\n// A sheep goes baa!\nfor(var kind in animalSounds) {\n Logger.log(\"A %s goes %s!\", kind, animalSounds[kind]);\n}\n```\n\nExample:\n```text\nconst specialValue = UserProperties.getProperty('special');\n```\n\nExample:\n```text\nUserProperties.setProperties({special: 'sauce', 'meaning': 42});\n```\n\nExample:\n```text\n// This deletes all other properties\nUserProperties.setProperties({special: 'sauce', 'meaning': 42}, true);\n```\n\nExample:\n```text\nUserProperties.setProperty('special', 'sauce');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.274Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":56,"estimatedTokens":258}}1074{"id":"doc-class_areachartbuilder_apps_script_google_for_de-8b8dbb47","source":"documentation","title":"Class AreaChartBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_areachartbuilder","text":"Example:\n```text\n// Create a data table with some sample data.\nconst sampleData = Charts.newDataTable()\n .addColumn(Charts.ColumnType.STRING, 'Month')\n .addColumn(Charts.ColumnType.NUMBER, 'Dining')\n .addColumn(Charts.ColumnType.NUMBER, 'Total')\n .addRow(['Jan', 60, 520])\n .addRow(['Feb', 50, 430])\n .addRow(['Mar', 53, 440])\n .addRow(['Apr', 70, 410])\n .addRow(['May', 80, 390])\n .addRow(['Jun', 60, 500])\n .addRow(['Jul', 100, 450])\n .addRow(['Aug', 140, 431])\n .addRow(['Sep', 75, 488])\n .addRow(['Oct', 70, 521])\n .addRow(['Nov', 58, 388])\n .addRow(['Dec', 63, 400])\n .build();\n\nconst chart = Charts.newAreaChart()\n .setTitle('Yearly Spending')\n .setXAxisTitle('Month')\n .setYAxisTitle('Spending (USD)')\n .setDimensions(600, 500)\n .setStacked()\n .setColors(['red', 'green'])\n .setDataTable(sampleData)\n .build();\n```\n\nExample:\n```text\n// Creates a pie chart builder and sets drawing of the slices in a\n// counter-clockwise manner.\nconst builder = Charts.newPieChart();\nbuilder.reverseCategories();\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the background color to gray\nconst builder = Charts.newLineChart();\nbuilder.setBackgroundColor('gray');\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the first two lines to be drawn in\n// green and red, respectively.\nconst builder = Charts.newLineChart();\nbuilder.setColors(['green', 'red']);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the legend position to right.\nconst builder = Charts.newLineChart();\nbuilder.setLegendPosition(Charts.Position.RIGHT);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets it up for a blue, 26-point legend.\nconst textStyleBuilder =\n Charts.newTextStyle().setColor('#0000FF').setFontSize(26);\nconst style = textStyleBuilder.build();\nconst builder = Charts.newLineChart();\nbuilder.setLegendTextStyle(style);\n```\n\nExample:\n```text\n// Build an area chart with a 1-second animation duration.\nconst builder = Charts.newAreaChart();\nbuilder.setOption('animation.duration', 1000);\nconst chart = builder.build();\n```\n\nExample:\n```text\n// Creates a line chart builder and sets large point style.\nconst builder = Charts.newLineChart();\nbuilder.setPointStyle(Charts.PointStyle.LARGE);\n```\n\nExample:\n```text\n// Creates a line chart builder and title to 'My Line Chart'.\nconst builder = Charts.newLineChart();\nbuilder.setTitle('My Line Chart');\n```\n\nExample:\n```text\n// Creates a line chart builder and sets it up for a blue, 26-point title.\nconst textStyleBuilder =\n Charts.newTextStyle().setColor('#0000FF').setFontSize(26);\nconst style = textStyleBuilder.build();\nconst builder = Charts.newLineChart();\nbuilder.setTitleTextStyle(style);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the X-axis text style to blue, 18-point\n// font.\nconst textStyle =\n Charts.newTextStyle().setColor('blue').setFontSize(18).build();\nconst builder = Charts.newLineChart();\nbuilder.setXAxisTextStyle(textStyle);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the X-axis title.\nconst builder = Charts.newLineChart();\nbuilder.setTitle('X-axis Title');\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the X-axis title text style to blue,\n// 18-point font.\nconst textStyle =\n Charts.newTextStyle().setColor('blue').setFontSize(18).build();\nconst builder = Charts.newLineChart();\nbuilder.setXAxisTitleTextStyle(textStyle);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the Y-axis text style to blue, 18-point\n// font.\nconst textStyle =\n Charts.newTextStyle().setColor('blue').setFontSize(18).build();\nconst builder = Charts.newLineChart();\nbuilder.setYAxisTextStyle(textStyle);\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the Y-axis title.\nconst builder = Charts.newLineChart();\nbuilder.setYAxisTitle('Y-axis Title');\n```\n\nExample:\n```text\n// Creates a line chart builder and sets the Y-axis title text style to blue,\n// 18-point font.\nconst textStyle =\n Charts.newTextStyle().setColor('blue').setFontSize(18).build();\nconst builder = Charts.newLineChart();\nbuilder.setYAxisTitleTextStyle(textStyle);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.275Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":159,"estimatedTokens":1156}}1075{"id":"doc-class_logger_apps_script_google_for_developers-8e744c14","source":"documentation","title":"Class Logger | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_logger","text":"Example:\n```text\n// Generate a log, then email it to the person who ran the script.\nconst files = DriveApp.getFiles();\nwhile (files.hasNext()) {\n Logger.log(files.next().getName());\n}\nconst recipient = Session.getActiveUser().getEmail();\nconst subject = 'A list of files in your Google Drive';\nconst body = Logger.getLog();\nMailApp.sendEmail(recipient, subject, body);\n```\n\nExample:\n```text\nLogger.log(\"my log message\");\n// Info my logmessage\nLogger.log({ key: \"value\" });\n// Info {key=value}\nLogger.log({ message: \"my log message\", data: { key: \"value\" } })\n// Info my logmessage\n```\n\nExample:\n```text\n{\n \"insertId\": \"w5eib...\",\n \"jsonPayload\": {\n \"message\": \"my log message\",\n \"serviceContext\": {\n \"service\": \"AKfyc...\"\n },\n \"data\": {\n \"key\": \"value\"\n }\n },\n \"resource\": {\n \"type\": \"app_script_function\",\n \"labels\": {\n \"invocation_type\": \"editor\",\n \"function_name\": \"unknown\",\n \"project_id\": \"1234567890\"\n }\n },\n \"timestamp\": \"2024-11-15T23:28:19.448591Z\",\n \"severity\": \"INFO\",\n \"labels\": {\n \"script.googleapis.com/user_key\": \"AOX2d...\",\n \"script.googleapis.com/process_id\": \"EAEA1...\",\n \"script.googleapis.com/project_key\": \"MQXvl...\",\n \"script.googleapis.com/deployment_id\": \"AKfyc...\"\n },\n \"logName\": \"projects/[PROJECT_ID]/logs/script.googleapis.com%2Fconsole_logs\",\n \"receiveTimestamp\": \"2024-11-15T23:28:20.363790313Z\"\n}\n```\n\nExample:\n```text\n// Log the number of Google Groups you belong to.\nconst groups = GroupsApp.getGroups();\nLogger.log('You are a member of %s Google Groups.', groups.length);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.277Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":65,"estimatedTokens":401}}1076{"id":"doc-class_embeddedchart_apps_script_google_for_devel-e1689522","source":"documentation","title":"Class EmbeddedChart | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_embeddedchart","text":"Example:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A2:B8');\nlet chart = sheet.getCharts()[0];\nchart = chart.modify()\n .addRange(range)\n .setOption('title', 'Updated!')\n .setOption('animation.duration', 500)\n .setPosition(2, 2, 0, 0)\n .build();\nsheet.updateChart(chart);\n```\n\nExample:\n```text\nfunction newChart(range) {\n const sheet = SpreadsheetApp.getActiveSheet();\n const chartBuilder = sheet.newChart();\n chartBuilder.addRange(range)\n .setChartType(Charts.ChartType.LINE)\n .setOption('title', 'My Line Chart!');\n sheet.insertChart(chartBuilder.build());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B8'))\n .setPosition(5, 5, 0, 0)\n .build();\n\nconst containerInfo = chart.getContainerInfo();\n\n// Logs the values used in setPosition()\nLogger.log(\n 'Anchor Column: %s\\r\\nAnchor Row %s\\r\\nOffset X %s\\r\\nOffset Y %s',\n containerInfo.getAnchorColumn(),\n containerInfo.getAnchorRow(),\n containerInfo.getOffsetX(),\n containerInfo.getOffsetY(),\n);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setHiddenDimensionStrategy(\n Charts.ChartHiddenDimensionStrategy.IGNORE_COLUMNS,\n )\n .setPosition(5, 5, 0, 0)\n .build();\n\n// Logs the strategy to use for hidden rows and columns which is\n// Charts.ChartHiddenDimensionStrategy.IGNORE_COLUMNS in this case.\nLogger.log(chart.getHiddenDimensionStrategy());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B10');\nconst range2 = sheet.getRange('C1:C10');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .addRange(range2)\n .setMergeStrategy(Charts.ChartMergeStrategy.MERGE_ROWS)\n .setPosition(5, 5, 0, 0)\n .build();\n\n// Logs whether rows of multiple ranges are merged, which is MERGE_ROWS in this\n// case.\nLogger.log(chart.getMergeStrategy());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(range)\n .setNumHeaders(1)\n .setPosition(5, 5, 0, 0)\n .build();\n\n// Logs the number of rows or columns to use as headers, which is 1 in this\n// case.\nLogger.log(chart.getHeaders());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst chart = sheet.newChart()\n .setChartType(Charts.ChartType.BAR)\n .addRange(sheet.getRange('A1:B8'))\n .setPosition(5, 5, 0, 0)\n .build();\n\nconst ranges = chart.getRanges();\n\n// There's only one range as a data source for this chart,\n// so this logs \"A1:B8\"\nfor (const i in ranges) {\n const range = ranges[i];\n Logger.log(range.getA1Notation());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B5');\nconst chart = sheet.newChart()\n .addRange(range)\n .setChartType(Charts.ChartType.BAR)\n .setTransposeRowsAndColumns(true)\n .setPosition(5, 5, 0, 0)\n .build();\n\n// Logs whether rows and columns should be transposed, which is true in this\n// case.\nLogger.log(chart.getTransposeRowsAndColumns());\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nlet chart = sheet.getCharts()[0];\nchart = chart.modify()\n .setOption('width', 800)\n .setOption('height', 640)\n .setPosition(5, 5, 0, 0)\n .build();\nsheet.updateChart(chart);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.278Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":159,"estimatedTokens":1105}}1077{"id":"doc-class_scriptproperties_apps_script_google_for_de-3fdfcc29","source":"documentation","title":"Class ScriptProperties | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_scriptproperties","text":"Example:\n```text\nScriptProperties.deleteAllProperties();\n```\n\nExample:\n```text\nScriptProperties.deleteProperty('special');\n```\n\nExample:\n```text\nScriptProperties.setProperties({\n \"cow\" : \"moo\",\n \"sheep\" : \"baa\",\n \"chicken\" : \"cluck\"\n});\n\n// Logs \"A cow goes: moo\"\nLogger.log(\"A cow goes: %s\", ScriptProperties.getProperty(\"cow\"));\n\n// This makes a copy. Any changes that happen here will not\n// be written back to properties.\nvar animalSounds = ScriptProperties.getProperties();\n\n// Logs:\n// A chicken goes cluck!\n// A cow goes moo!\n// A sheep goes baa!\nfor(var kind in animalSounds) {\n Logger.log(\"A %s goes %s!\", kind, animalSounds[kind]);\n}\n```\n\nExample:\n```text\nconst specialValue = ScriptProperties.getProperty('special');\n```\n\nExample:\n```text\nScriptProperties.setProperties({special: 'sauce', 'meaning': 42});\n```\n\nExample:\n```text\n// This deletes all other properties\nScriptProperties.setProperties({special: 'sauce', 'meaning': 42}, true);\n```\n\nExample:\n```text\nScriptProperties.setProperty('special', 'sauce');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.279Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":56,"estimatedTokens":262}}1078{"id":"doc-groups_service_apps_script_google_for_developers-3e07b495","source":"documentation","title":"Groups Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/service_groups","text":"Example:\n```text\nvar groups = GroupsApp.getGroups();\nLogger.log('You are a member of %s Google Groups.', groups.length);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.282Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":7,"estimatedTokens":35}}1079{"id":"doc-event_objects_apps_script_google_for_developers-7f90e4e0","source":"documentation","title":"Event Objects | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guide_events","text":"Example:\n```text\nfunction onEdit(e){\n // Set a comment on the edited cell to indicate when it was changed.\n var range = e.range;\n range.setNote('Last modified: ' + new Date());\n}\n```\n\nExample:\n```text\nLIMITED\n```\n\nExample:\n```text\nSpreadsheet\n```\n\nExample:\n```text\n4034124084959907503\n```\n\nExample:\n```text\namin@example.com\n```\n\nExample:\n```text\nFULL\n```\n\nExample:\n```text\nINSERT_ROW\n```\n\nExample:\n```text\n1234\n```\n\nExample:\n```text\nRange\n```\n\nExample:\n```text\n10\n```\n\nExample:\n```text\n{\n 'First Name': ['Jane'],\n 'Timestamp': ['6/7/2015 20:54:13'],\n 'Last Name': ['Doe']\n}\n```\n\nExample:\n```text\n['2015/05/04 15:00', 'amin@example.com', 'Bob', '27', 'Bill',\n'28', 'Susan', '25']\n```\n\nExample:\n```text\nDocument\n```\n\nExample:\n```text\nPresentation\n```\n\nExample:\n```text\nForm\n```\n\nExample:\n```text\nFormResponse\n```\n\nExample:\n```text\nsusan@example.com\n```\n\nExample:\n```text\n31\n```\n\nExample:\n```text\n7\n```\n\nExample:\n```text\n23\n```\n\nExample:\n```text\n59\n```\n\nExample:\n```text\n12\n```\n\nExample:\n```text\nUTC\n```\n\nExample:\n```text\n52\n```\n\nExample:\n```text\n2015\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.287Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":25,"totalLines":135,"estimatedTokens":268}}1080{"id":"doc-class_gmailmessage_apps_script_google_for_develo-d00ef80e","source":"documentation","title":"Class GmailMessage | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_gmailmessage","text":"Example:\n```text\n// Create a draft reply to the original message with an acknowledgment.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.createDraftReply('Got your message');\n```\n\nExample:\n```text\n// Create a draft response with an HTML text body.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.createDraftReply('incapable of HTML', {\n htmlBody: '<b>some HTML body text</b>',\n cc: 'another@example.com',\n});\n```\n\nExample:\n```text\n// Create a draft response to all recipients (except those bcc'd) with an\n// acknowledgment.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.createDraftReplyAll('Got your message');\n```\n\nExample:\n```text\n// Create a draft response to all recipients (except those bcc'd) using an HTML\n// text body.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.createDraftReplyAll('incapable of HTML', {\n htmlBody: '<b>some HTML body text</b>',\n cc: 'another@example.com',\n});\n```\n\nExample:\n```text\n// Forward first message of first inbox thread to recipient1 & recipient2,\n// both @example.com\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.forward('recipient1@example.com,recipient2@example.com');\n```\n\nExample:\n```text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.forward('recipient1@example.com,recipient2@example.com', {\n cc: 'myboss@example.com',\n bcc: 'mybosses-boss@example.com,vp@example.com',\n});\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getBcc()); // Log bcc'd addresses\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getBody()); // Log contents of the body\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getCc()); // Log cc'd addresses\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getDate()); // Log date and time of the message\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getFrom()); // Log from address of the message\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox.\nconst message = thread.getMessages()[0]; // Get the first message.\nLogger.log(\n message.getHeader('Message-ID')); // Logs the Message-ID RFC 2822 header.\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nconst id = message.getId();\nconst messageById = GmailApp.getMessageById(id);\nLogger.log(\n message.getSubject() === messageById.getMessage()); // Always logs true\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getPlainBody()); // Log contents of the body\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getReplyTo()); // Logs reply-to address\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getSubject()); // Log subject line\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(\n message.getThread().getFirstMessageSubject() ===\n thread.getFirstMessageSubject(),\n); // Always logs true\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(message.getTo()); // Log the recipient of message\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is draft? ${message.isDraft()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is a chat? ${message.isInChats()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is in inbox? ${message.isInInbox()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getPriorityInboxThreads(\n 0, 1)[0]; // Get first thread in priority inbox\nconst messages = thread.getMessages();\nfor (let i = 0; i < messages.length; i++) {\n // At least one of the messages is in priority inbox\n Logger.log(`is in priority inbox? ${messages[i].isInPriorityInbox()}`);\n}\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is in the trash? ${message.isInTrash()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is starred? ${message.isStarred()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nLogger.log(`is unread? ${message.isUnread()}`);\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nmessage.markRead(); // Mark as read\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nmessage.markUnread(); // Mark as unread\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nmessage.moveToTrash(); // Move message to trash\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\n// .. Do bunch of stuff here\nmessage.refresh(); // Make sure it's up to date\n// Do more stuff to message\n```\n\nExample:\n```text\n// Respond to author of message with acknowledgment\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.reply('Got your message');\n```\n\nExample:\n```text\n// Respond with HTML body text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.reply('incapable of HTML', {\n htmlBody: '<b>some HTML body text</b>',\n noReply: true,\n});\n```\n\nExample:\n```text\n// Respond to all recipients (except bcc'd) of last email in thread with\n// acknowledgment\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.replyAll('Got your message');\n```\n\nExample:\n```text\n// Respond with HTML body text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nmessage.replyAll('incapable of HTML', {\n htmlBody: '<b>some HTML body text</b>',\n noReply: true,\n});\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nmessage.star(); // Star the message\n```\n\nExample:\n```text\nconst thread = GmailApp.getInboxThreads(0, 1)[0]; // Get first thread in inbox\nconst message = thread.getMessages()[0]; // Get first message\nmessage.unstar(); // Unstar the message\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.290Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":35,"totalLines":287,"estimatedTokens":2252}}1081{"id":"doc-class_calendar_apps_script_google_for_developers-4652d551","source":"documentation","title":"Class Calendar | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_calendar","text":"Example:\n```text\n// Creates an all-day event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the Woodstock festival (August 15th to 17th) and\n// logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Woodstock Festival',\n new Date('August 15, 1969'),\n new Date('August 18, 1969'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the Woodstock festival (August 15th to 17th) and\n// logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Woodstock Festival',\n new Date('August 15, 1969'),\n new Date('August 18, 1969'),\n {location: 'Bethel, White Lake, New York, U.S.', sendInvites: true},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969'),\n {location: 'The Moon'},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a no-meetings day, taking place every Wednesday\n// in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries(\n 'No Meetings',\n new Date('January 2, 2013 03:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014')),\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a no-meetings day, taking place every Wednesday\n// in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries(\n 'No Meetings',\n new Date('January 2, 2013 03:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014')),\n {guests: 'everyone@example.com'},\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates an event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:00:00 UTC'),\n new Date('July 21, 1969 21:00:00 UTC'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:00:00 UTC'),\n new Date('July 20, 1969 21:00:00 UTC'),\n {location: 'The Moon'},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates a new event and logs its ID.\nconst event = CalendarApp.getDefaultCalendar().createEventFromDescription(\n 'Lunch with Mary, Friday at 1PM',\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a team meeting, taking place every Tuesday and\n// Thursday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createEventSeries(\n 'Team Meeting',\n new Date('January 1, 2013 03:00:00 PM EST'),\n new Date('January 1, 2013 04:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekdays(\n [CalendarApp.Weekday.TUESDAY, CalendarApp.Weekday.THURSDAY])\n .until(new Date('January 1, 2014')),\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a team meeting, taking place every Tuesday and\n// Thursday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createEventSeries(\n 'Team Meeting',\n new Date('January 1, 2013 03:00:00 PM EST'),\n new Date('January 1, 2013 04:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekdays(\n [CalendarApp.Weekday.TUESDAY, CalendarApp.Weekday.THURSDAY])\n .until(new Date('January 1, 2014')),\n {location: 'Conference Room'},\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates a calendar to delete.\nconst calendar = CalendarApp.createCalendar('Test');\n\n// Deletes the 'Test' calendar permanently.\ncalendar.deleteCalendar();\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the color of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getColor() instead.\nconst calendarColor = calendar.getColor();\nconsole.log(calendarColor);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Sets the description of the calendar to 'Test description.'\ncalendar.setDescription('Test description');\n\n// Gets the description of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getDescription() instead.\nconst description = calendar.getDescription();\nconsole.log(description);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Creates an event for the moon landing.\nconst event = calendar.createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:05:00 UTC'),\n new Date('July 20, 1969 20:17:00 UTC'),\n);\n\n// Gets the calendar event ID and logs it to the console.\nconst iCalId = event.getId();\nconsole.log(iCalId);\n\n// Gets the event by its ID and logs the title of the event to the console.\n// For the default calendar, you can use CalendarApp.getEventById(iCalId)\n// instead.\nconst myEvent = calendar.getEventById(iCalId);\nconsole.log(myEvent.getTitle());\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Creates an event series for a daily team meeting from 1 PM to 2 PM.\n// The series adds the daily event from January 1, 2023 through December 31,\n// 2023.\nconst eventSeries = calendar.createEventSeries(\n 'Team meeting',\n new Date('Jan 1, 2023 13:00:00'),\n new Date('Jan 1, 2023 14:00:00'),\n CalendarApp.newRecurrence().addDailyRule().until(new Date('Jan 1, 2024')),\n);\n\n// Gets the ID of the event series.\nconst iCalId = eventSeries.getId();\n\n// Gets the event series by its ID and logs the series title to the console.\n// For the default calendar, you can use CalendarApp.getEventSeriesById(iCalId)\n// instead.\nconsole.log(calendar.getEventSeriesById(iCalId).getTitle());\n```\n\nExample:\n```text\n// Determines how many events are happening in the next two hours.\nconst now = new Date();\nconst twoHoursFromNow = new Date(now.getTime() + 2 * 60 * 60 * 1000);\nconst events = CalendarApp.getDefaultCalendar().getEvents(now, twoHoursFromNow);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening in the next two hours that contain\n// the term \"meeting\".\nconst now = new Date();\nconst twoHoursFromNow = new Date(now.getTime() + 2 * 60 * 60 * 1000);\nconst events = CalendarApp.getDefaultCalendar().getEvents(\n now,\n twoHoursFromNow,\n {search: 'meeting'},\n);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening today.\nconst today = new Date();\nconst events = CalendarApp.getDefaultCalendar().getEventsForDay(today);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening today and contain the term\n// \"meeting\".\nconst today = new Date();\nconst events = CalendarApp.getDefaultCalendar().getEventsForDay(today, {\n search: 'meeting',\n});\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// To get the user's default calendar, use CalendarApp.getDefaultCalendar().\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the ID of the calendar and logs it to the console.\nconst calendarId = calendar.getId();\nconsole.log(calendarId);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the name of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getName() instead.\nconst calendarName = calendar.getName();\nconsole.log(calendarName);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the time zone of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getTimeZone() instead.\nconst timeZone = calendar.getTimeZone();\nconsole.log(timeZone);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Determines whether the calendar is hidden in the user interface and logs it\n// to the console. For the default calendar, you can use CalendarApp.isHidden()\n// instead.\nconst isHidden = calendar.isHidden();\nconsole.log(isHidden);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Determines whether the calendar is the default calendar for\n// the effective user and logs it to the console.\n// For the default calendar, you can use CalendarApp.isMyPrimaryCalendar()\n// instead.\nconst isMyPrimaryCalendar = calendar.isMyPrimaryCalendar();\nconsole.log(isMyPrimaryCalendar);\n```\n\nExample:\n```text\n// Gets a calendar by its ID. To get the user's default calendar, use\n// CalendarApp.getDefault() instead.\n// TODO(developer): Replace the ID with the calendar ID that you want to use.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Determines whether the calendar is owned by you and logs it.\nconsole.log(calendar.isOwnedByMe());\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Determines whether the calendar's events are displayed in the user interface\n// and logs it.\nconsole.log(calendar.isSelected());\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Sets the color of the calendar to pink using the Calendar Color enum.\n// For the default calendar, you can use CalendarApp.setColor() instead.\ncalendar.setColor(CalendarApp.Color.PINK);\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the description of the calendar.\n// TODO(developer): Update the string with the description that you want to use.\ncalendar.setDescription('Updated calendar description.');\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the name of the calendar.\n// TODO(developer): Update the string with the name that you want to use.\ncalendar.setName('Example calendar name');\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Selects the calendar so that its events are displayed in the user interface.\n// To unselect the calendar, set the parameter to false.\ncalendar.setSelected(true);\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the time zone of the calendar to America/New York (US/Eastern) time.\ncalendar.setTimeZone('America/New_York');\n```\n\nExample:\n```text\n// Gets the calendar by its ID.\n// TODO(developer): Replace the calendar ID with the calendar ID that you want\n// to get.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Unsubscribes the user from the calendar.\nconst result = calendar.unsubscribeFromCalendar();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.293Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":33,"totalLines":426,"estimatedTokens":3217}}1082{"id":"doc-adsense_service_apps_script_google_for_developer-85c483d3","source":"documentation","title":"AdSense Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/service_adsense","text":"Example:\n```text\n/**\n * Lists available AdSense accounts.\n */\nfunction listAccounts() {\n let pageToken;\n do {\n const response = AdSense.Accounts.list({ pageToken: pageToken });\n if (!response.accounts) {\n console.log(\"No accounts found.\");\n return;\n }\n for (const account of response.accounts) {\n console.log(\n 'Found account with resource name \"%s\" and display name \"%s\".',\n account.name,\n account.displayName,\n );\n }\n pageToken = response.nextPageToken;\n } while (pageToken);\n}\n```\n\nExample:\n```text\n/**\n * Logs available Ad clients for an account.\n *\n * @param {string} accountName The resource name of the account that owns the\n * collection of ad clients.\n */\nfunction listAdClients(accountName) {\n let pageToken;\n do {\n const response = AdSense.Accounts.Adclients.list(accountName, {\n pageToken: pageToken,\n });\n if (!response.adClients) {\n console.log(\"No ad clients found for this account.\");\n return;\n }\n for (const adClient of response.adClients) {\n console.log(\n 'Found ad client for product \"%s\" with resource name \"%s\".',\n adClient.productCode,\n adClient.name,\n );\n console.log(\n \"Reporting dimension ID: %s\",\n adClient.reportingDimensionId ?? \"None\",\n );\n }\n pageToken = response.nextPageToken;\n } while (pageToken);\n}\n```\n\nExample:\n```text\n/**\n * Lists ad units.\n * @param {string} adClientName The resource name of the ad client that owns the collection\n * of ad units.\n */\nfunction listAdUnits(adClientName) {\n let pageToken;\n do {\n const response = AdSense.Accounts.Adclients.Adunits.list(adClientName, {\n pageSize: 50,\n pageToken: pageToken,\n });\n if (!response.adUnits) {\n console.log(\"No ad units found for this ad client.\");\n return;\n }\n for (const adUnit of response.adUnits) {\n console.log(\n 'Found ad unit with resource name \"%s\" and display name \"%s\".',\n adUnit.name,\n adUnit.displayName,\n );\n }\n\n pageToken = response.nextPageToken;\n } while (pageToken);\n}\n```\n\nExample:\n```text\n/**\n * Generates a spreadsheet report for a specific ad client in an account.\n * @param {string} accountName The resource name of the account.\n * @param {string} adClientReportingDimensionId The reporting dimension ID\n * of the ad client.\n */\nfunction generateReport(accountName, adClientReportingDimensionId) {\n // Prepare report.\n const today = new Date();\n const oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);\n\n const report = AdSense.Accounts.Reports.generate(accountName, {\n // Specify the desired ad client using a filter.\n filters: [\n `AD_CLIENT_ID==${escapeFilterParameter(adClientReportingDimensionId)}`,\n ],\n metrics: [\n \"PAGE_VIEWS\",\n \"AD_REQUESTS\",\n \"AD_REQUESTS_COVERAGE\",\n \"CLICKS\",\n \"AD_REQUESTS_CTR\",\n \"COST_PER_CLICK\",\n \"AD_REQUESTS_RPM\",\n \"ESTIMATED_EARNINGS\",\n ],\n dimensions: [\"DATE\"],\n ...dateToJson(\"startDate\", oneWeekAgo),\n ...dateToJson(\"endDate\", today),\n // Sort by ascending date.\n orderBy: [\"+DATE\"],\n });\n\n if (!report.rows) {\n console.log(\"No rows returned.\");\n return;\n }\n const spreadsheet = SpreadsheetApp.create(\"AdSense Report\");\n const sheet = spreadsheet.getActiveSheet();\n\n // Append the headers.\n sheet.appendRow(report.headers.map((header) => header.name));\n\n // Append the results.\n sheet\n .getRange(2, 1, report.rows.length, report.headers.length)\n .setValues(report.rows.map((row) => row.cells.map((cell) => cell.value)));\n\n console.log(\"Report spreadsheet created: %s\", spreadsheet.getUrl());\n}\n\n/**\n * Escape special characters for a parameter being used in a filter.\n * @param {string} parameter The parameter to be escaped.\n * @return {string} The escaped parameter.\n */\nfunction escapeFilterParameter(parameter) {\n return parameter.replace(\"\\\\\", \"\\\\\\\\\").replace(\",\", \"\\\\,\");\n}\n\n/**\n * Returns the JSON representation of a Date object (as a google.type.Date).\n *\n * @param {string} paramName the name of the date parameter\n * @param {Date} value the date\n * @return {object} formatted date\n */\nfunction dateToJson(paramName, value) {\n return {\n [`${paramName}.year`]: value.getFullYear(),\n [`${paramName}.month`]: value.getMonth() + 1,\n [`${paramName}.day`]: value.getDate(),\n };\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.297Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":169,"estimatedTokens":1107}}1083{"id":"doc-class_mailapp_apps_script_google_for_developers-4a8c06a0","source":"documentation","title":"Class MailApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_mailapp","text":"Example:\n```text\nconst emailQuotaRemaining = MailApp.getRemainingDailyQuota();\nLogger.log(`Remaining email quota: ${emailQuotaRemaining}`);\n```\n\nExample:\n```text\n// This code fetches the Google and YouTube logos, inlines them in an email\n// and sends the email\nfunction inlineImage() {\n const googleLogoUrl =\n 'https://www.gstatic.com/images/branding/googlelogo/1x/googlelogo_color_74x24dp.png';\n const youtubeLogoUrl =\n 'https://developers.google.com/youtube/images/YouTube_logo_standard_white.png';\n const googleLogoBlob =\n UrlFetchApp.fetch(googleLogoUrl).getBlob().setName('googleLogoBlob');\n const youtubeLogoBlob =\n UrlFetchApp.fetch(youtubeLogoUrl).getBlob().setName('youtubeLogoBlob');\n MailApp.sendEmail({\n to: 'recipient@example.com',\n subject: 'Logos',\n htmlBody: 'inline Google Logo<img src=\\'cid:googleLogo\\'> images! <br>' +\n 'inline YouTube Logo <img src=\\'cid:youtubeLogo\\'>',\n inlineImages: {\n googleLogo: googleLogoBlob,\n youtubeLogo: youtubeLogoBlob,\n },\n });\n}\n```\n\nExample:\n```text\nMailApp.sendEmail(\n 'recipient@example.com',\n 'TPS reports',\n 'Where are the TPS reports?',\n);\n```\n\nExample:\n```text\n// Send an email with two attachments: a file from Google Drive (as a PDF) and\n// an HTML file.\nconst file = DriveApp.getFileById('1234567890abcdefghijklmnopqrstuvwxyz');\nconst blob = Utilities.newBlob(\n 'Insert any HTML content here',\n 'text/html',\n 'my_document.html',\n);\nMailApp.sendEmail(\n 'mike@example.com',\n 'Attachment example',\n 'Two files are attached.',\n {\n name: 'Automatic Emailer Script',\n attachments: [file.getAs(MimeType.PDF), blob],\n },\n);\n```\n\nExample:\n```text\nMailApp.sendEmail(\n 'recipient@example.com',\n 'replies@example.com',\n 'TPS report status',\n 'What is the status of those TPS reports?',\n);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.298Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":73,"estimatedTokens":468}}1084{"id":"doc-class_range_apps_script_google_for_developers-11e61c81","source":"documentation","title":"Class Range | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_range","text":"Example:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getRange('A1:D10');\nrange.activate();\n\nconst selection = sheet.getSelection();\n// Current cell: A1\nconst currentCell = selection.getCurrentCell();\n// Active Range: A1:D10\nconst activeRange = selection.getActiveRange();\n```\n\nExample:\n```text\n// Gets the first sheet of the spreadsheet.\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\n\n// Gets the cell B5 and sets it as the active cell.\nconst range = sheet.getRange('B5');\nconst currentCell = range.activateAsCurrentCell();\n\n// Logs the activated cell.\nconsole.log(currentCell.getA1Notation());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on the sheet.\nconst range = sheet.getRange('2:2');\n\n// Adds the key 'NAME' to the developer metadata for row 2.\nrange.addDeveloperMetadata('NAME');\n\n// Gets the metadata and logs it to the console.\nconst developerMetaData = range.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on Sheet1.\nconst range = sheet.getRange('2:2');\n\n// Adds the key 'NAME' and sets the developer metadata visibility to 'DOCUMENT'\n// for row 2 on Sheet1.\nrange.addDeveloperMetadata(\n 'NAME',\n SpreadsheetApp.DeveloperMetadataVisibility.DOCUMENT,\n);\n\n// Gets the updated metadata info and logs it to the console.\nconst developerMetaData = range.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(developerMetaData.getVisibility().toString());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 of Sheet1.\nconst range = sheet.getRange('2:2');\n\n// Adds the key 'NAME' and sets the value to 'GOOGLE' for the metadata of row 2.\nrange.addDeveloperMetadata('NAME', 'GOOGLE');\n\n// Gets the metadata and logs it to the console.\nconst developerMetaData = range.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(developerMetaData.getValue());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on the sheet.\nconst range = sheet.getRange('2:2');\n\n// Adds the key 'NAME', sets the value to 'GOOGLE', and sets the visibility\n// to PROJECT for row 2 on the sheet.\nrange.addDeveloperMetadata(\n 'NAME',\n 'GOOGLE',\n SpreadsheetApp.DeveloperMetadataVisibility.PROJECT,\n);\n\n// Gets the updated metadata info and logs it to the console.\nconst developerMetaData = range.getDeveloperMetadata()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(developerMetaData.getValue());\nconsole.log(developerMetaData.getVisibility().toString());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on the sheet.\nconst range = sheet.getRange('2:2');\n\n// Applies column banding to row 2.\nconst colBanding = range.applyColumnBanding();\n\n// Gets the first banding on the sheet and logs the color of the header column.\nconsole.log(\n sheet.getBandings()[0]\n .getHeaderColumnColorObject()\n .asRgbColor()\n .asHexString(),\n);\n\n// Gets the first banding on the sheet and logs the color of the second column.\nconsole.log(\n sheet.getBandings()[0]\n .getSecondColumnColorObject()\n .asRgbColor()\n .asHexString(),\n);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on the sheet.\nconst range = sheet.getRange('2:2');\n\n// Applies the INDIGO color banding theme to the columns in row 2.\nconst colBanding = range.applyColumnBanding(SpreadsheetApp.BandingTheme.INDIGO);\n\n// Gets the first banding on the sheet and logs the color of the second column.\nconsole.log(\n sheet.getBandings()[0]\n .getSecondColumnColorObject()\n .asRgbColor()\n .asHexString(),\n);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets rows 12-22 on the sheet.\nconst range = sheet.getRange('12:22');\n\n// Applies the BLUE color banding theme to rows 12-22.\n// Sets the header visibility to false and the footer visibility to true.\nconst colBanding = range.applyColumnBanding(\n SpreadsheetApp.BandingTheme.BLUE,\n false,\n true,\n);\n\n// Gets the banding color and logs it to the console.\nconsole.log(\n sheet.getBandings()[0]\n .getSecondColumnColorObject()\n .asRgbColor()\n .asHexString(),\n);\n\n// Gets the header color object and logs it to the console. Returns null because\n// the header visibility is set to false.\nconsole.log(sheet.getBandings()[0].getHeaderColumnColorObject());\n\n// Gets the footer color and logs it to the console.\nconsole.log(\n sheet.getBandings()[0]\n .getFooterColumnColorObject()\n .asRgbColor()\n .asHexString(),\n);\n```\n\nExample:\n```text\n// Opens the spreadsheet by its URL. If you created your script from within a\n// Google Sheets spreadsheet, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets rows 1-30 on Sheet1.\nconst range = sheet.getRange('1:30');\n\n// Applies row banding to rows 1-30.\nrange.applyRowBanding();\n\n// Gets the hex color of the second banded row.\nconst secondRowColor =\n range.getBandings()[0].getSecondRowColorObject().asRgbColor().asHexString();\n\n// Logs the hex color to console.\nconsole.log(secondRowColor);\n```\n\nExample:\n```text\n// Opens the spreadsheet by its URL. If you created your script from within a\n// Google Sheets spreadsheet, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets rows 1-30 on Sheet1.\nconst range = sheet.getRange('1:30');\n\n// Applies the INDIGO row banding theme to rows 1-30.\nrange.applyRowBanding(SpreadsheetApp.BandingTheme.INDIGO);\n\n// Gets the hex color of the second banded row.\nconst secondRowColor =\n range.getBandings()[0].getSecondRowColorObject().asRgbColor().asHexString();\n\n// Logs the hex color to console.\nconsole.log(secondRowColor);\n```\n\nExample:\n```text\n// Opens the spreadsheet by its URL. If you created your script from within a\n// Google Sheets spreadsheet, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets rows 1-30 on Sheet1.\nconst range = sheet.getRange('1:30');\n\n// Applies the INDIGO row banding to rows 1-30 and\n// specifies to hide the header and show the footer.\nrange.applyRowBanding(SpreadsheetApp.BandingTheme.INDIGO, false, true);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\n\n// Has values [1, 2, 3, 4].\nconst sourceRange = sheet.getRange('A1:A4');\n// The range to fill with values.\nconst destination = sheet.getRange('A1:A20');\n\n// Inserts new values in A5:A20, continuing the pattern expressed in A1:A4\nsourceRange.autoFill(destination, SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\n\n// A1:A20 has values [1, 2, 3, ... 20].\n// B1:B4 has values [1/1/2017, 1/2/2017, ...]\nconst sourceRange = sheet.getRange('B1:B4');\n\n// Results in B5:B20 having values [1/5/2017, ... 1/20/2017]\nsourceRange.autoFillToNeighbor(SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES);\n```\n\nExample:\n```text\n// Opens the spreadsheet by its URL. If you created your script from within a\n// Google Sheets spreadsheet, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:C6 on Sheet1.\nconst range = sheet.getRange('A1:C6');\n\n// Unmerges the range A1:C6 into individual cells.\nrange.breakApart();\n```\n\nExample:\n```text\n// Opens the spreadsheet by its URL. If you created your script from within a\n// Google Sheets spreadsheet, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:C6 on Sheet1.\nconst range = sheet.getRange('A1:C6');\n\n// Logs whether the user has permission to edit every cell in the range.\nconsole.log(range.canEdit());\n```\n\nExample:\n```text\n// Changes the state of cells which currently contain either the checked or\n// unchecked value configured in the range A1:B10 to 'checked'.\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\nrange.check();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.clear();\n```\n\nExample:\n```text\n// The code below clears range C2:G7 in the active sheet, but preserves the\n// format, data validation rules, and comments.\nSpreadsheetApp.getActiveSheet().getRange(2, 3, 6, 5).clear({\n contentsOnly: true\n});\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.clearContent();\n```\n\nExample:\n```text\n// Clear the data validation rules for cells A1:B5.\nconst range = SpreadsheetApp.getActive().getRange('A1:B5');\nrange.clearDataValidations();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.clearFormat();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.clearNote();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getActiveRange();\n\n// All row and column groups within the range are collapsed.\nrange.collapseGroups();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst source = ss.getSheets()[0];\n\nconst range = source.getRange('B2:D4');\n\n// This copies the formatting in B2:D4 in the source sheet to\n// D4:F6 in the sheet with gridId 1555299895. Note that you can get the gridId\n// of a sheet by calling sheet.getSheetId() or range.getGridId().\nrange.copyFormatToRange(1555299895, 4, 6, 4, 6);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst source = ss.getSheets()[0];\nconst destination = ss.getSheets()[1];\n\nconst range = source.getRange('B2:D4');\n\n// This copies the formatting in B2:D4 in the source sheet to\n// D4:F6 in the second sheet\nrange.copyFormatToRange(destination, 4, 6, 4, 6);\n```\n\nExample:\n```text\n// The code below copies the first 5 columns over to the 6th column.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst rangeToCopy = sheet.getRange(1, 1, sheet.getMaxRows(), 5);\nrangeToCopy.copyTo(sheet.getRange(1, 6));\n```\n\nExample:\n```text\n// The code below copies only the values of the first 5 columns over to the 6th\n// column.\nconst sheet = SpreadsheetApp.getActiveSheet();\nsheet.getRange('A:E').copyTo(\n sheet.getRange('F1'),\n SpreadsheetApp.CopyPasteType.PASTE_VALUES,\n false,\n);\n```\n\nExample:\n```text\n// The code below copies only the values of the first 5 columns over to the 6th\n// column.\nconst sheet = SpreadsheetApp.getActiveSheet();\nsheet.getRange('A:E').copyTo(sheet.getRange('F1'), {contentsOnly: true});\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst source = ss.getSheets()[0];\n\nconst range = source.getRange('B2:D4');\n\n// This copies the data in B2:D4 in the source sheet to\n// D4:F6 in the sheet with gridId 0\nrange.copyValuesToRange(0, 4, 6, 4, 6);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst source = ss.getSheets()[0];\nconst destination = ss.getSheets()[1];\n\nconst range = source.getRange('B2:D4');\n\n// This copies the data in B2:D4 in the source sheet to\n// D4:F6 in the second sheet\nrange.copyValuesToRange(destination, 4, 6, 4, 6);\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nconst anchorCell = spreadsheet.getSheets()[0].getRange('A1');\nconst dataSource = spreadsheet.getDataSources()[0];\n\nconst pivotTable = anchorCell.createDataSourcePivotTable(dataSource);\npivotTable.addRowGroup('dataColumnA');\npivotTable.addColumnGroup('dataColumnB');\npivotTable.addPivotValue(\n 'dataColumnC',\n SpreadsheetApp.PivotTableSummarizeFunction.SUM,\n);\npivotTable.addFilter(\n 'dataColumnA',\n SpreadsheetApp.newFilterCriteria().whenTextStartsWith('A').build(),\n);\n```\n\nExample:\n```text\nconst spreadsheet = SpreadsheetApp.getActiveSpreadsheet();\nconst anchorCell = spreadsheet.getSheets()[0].getRange('A1');\nconst dataSource = spreadsheet.getDataSources()[0];\n\nconst dataSourceTable =\n anchorCell.createDataSourceTable(dataSource)\n .addColumns('dataColumnA', 'dataColumnB', 'dataColumnC')\n .addSortSpec('dataColumnA', true) // ascending=true\n .addSortSpec('dataColumnB', false); // ascending=false\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:C6.\nconst range = sheet.getRange('A1:C6');\n\n// Creates a developer metadata finder to search for metadata in the scope of\n// this range.\nconst developerMetaDataFinder = range.createDeveloperMetadataFinder();\n\n// Logs information about the developer metadata finder to the console.\nconst developerMetaData = developerMetaDataFinder.find()[0];\nconsole.log(developerMetaData.getKey());\nconsole.log(developerMetaData.getValue());\nconsole.log(developerMetaData.getVisibility().toString());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSheet();\nconst range = ss.getRange('A1:C20');\n\n// Creates a new filter and applies it to the range A1:C20 on the active sheet.\nfunction createFilter() {\n range.createFilter();\n}\n// Gets the filter and applies criteria that only shows cells that aren't empty.\nfunction getFilterAddCriteria() {\n const filter = range.getFilter();\n const criteria =\n SpreadsheetApp.newFilterCriteria().whenCellNotEmpty().build();\n filter.setColumnFilterCriteria(2, criteria);\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets cell A1 as a range in order to place the pivot table.\nconst range = sheet.getRange('A1');\n\n// Gets the range of the source data for the pivot table.\nconst dataRange = sheet.getRange('E12:G20');\n\n// Creates an empty pivot table from the specified source data.\nconst pivotTable = range.createPivotTable(dataRange);\n\n// Logs the values from the pivot table's source data to the console.\nconsole.log(pivotTable.getSourceDataRange().getValues());\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getActiveRange();\n\n// Creates a text finder for the range.\nconst textFinder = range.createTextFinder('dog');\n\n// Returns the first occurrence of 'dog'.\nconst firstOccurrence = textFinder.findNext();\n\n// Replaces the last found occurrence of 'dog' with 'cat' and returns the number\n// of occurrences replaced.\nconst numOccurrencesReplaced = textFinder.replaceWith('cat');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.deleteCells(SpreadsheetApp.Dimension.COLUMNS);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getActiveRange();\n\n// All row and column groups within the range are expanded.\nrange.expandGroups();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange(1, 1, 2, 5);\n\n// Logs \"A1:E2\"\nLogger.log(range.getA1Notation());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B5');\nLogger.log(cell.getBackground());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B5');\nLogger.log(cell.getBackgroundObject().asRgbColor().asHexString());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5:C6');\nconst bgColors = range.getBackgroundObjects();\nfor (const i in bgColors) {\n for (const j in bgColors[i]) {\n Logger.log(bgColors[i][j].asRgbColor().asHexString());\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5:C6');\nconst bgColors = range.getBackgrounds();\nfor (const i in bgColors) {\n for (const j in bgColors[i]) {\n Logger.log(bgColors[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Sets a range.\nconst range = sheet.getRange('A1:K50');\n\n// Gets the banding info for the range.\nconst bandings = range.getBandings();\n\n// Logs the second row color for each banding to the console.\nfor (const banding of bandings) {\n console.log(banding.getSecondRowColor());\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D4');\n\n// The row and column here are relative to the range\n// getCell(1,1) in this code returns the cell at B2\nconst cell = range.getCell(1, 1);\nLogger.log(cell.getValue());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D4');\n// Logs \"2.0\"\nLogger.log(range.getColumn());\n```\n\nExample:\n```text\n// Assume the active spreadsheet is blank.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nsheet.getRange('C2').setValue(100);\nsheet.getRange('B3').setValue(100);\nsheet.getRange('D3').setValue(100);\nsheet.getRange('C4').setValue(100);\n// Logs \"B2:D4\"\nLogger.log(sheet.getRange('C3').getDataRegion().getA1Notation());\n```\n\nExample:\n```text\n// Assume the active spreadsheet is blank.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nsheet.getRange('C2').setValue(100);\nsheet.getRange('B3').setValue(100);\nsheet.getRange('D3').setValue(100);\nsheet.getRange('C4').setValue(100);\n// Logs \"C2:C4\"\nLogger.log(\n sheet.getRange('C3')\n .getDataRegion(SpreadsheetApp.Dimension.ROWS)\n .getA1Notation(),\n);\n// Logs \"B3:D3\"\nLogger.log(\n sheet.getRange('C3')\n .getDataRegion(SpreadsheetApp.Dimension.COLUMNS)\n .getA1Notation(),\n);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1 on Sheet1.\nconst range = sheet.getRange('A1');\n\n// Gets the data source formula from cell A1.\nconst dataSourceFormula = range.getDataSourceFormula();\n\n// Gets the formula.\nconst formula = dataSourceFormula.getFormula();\n\n// Logs the formula.\nconsole.log(formula);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:B5 on Sheet1.\nconst range = sheet.getRange('A1:B5');\n\n// Gets an array of the data source formulas in the range A1:B5.\nconst dataSourceFormulas = range.getDataSourceFormulas();\n\n// Logs the first formula in the array.\nconsole.log(dataSourceFormulas[0].getFormula());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:G50 on Sheet1.\nconst range = sheet.getRange('A1:G50');\n\n// Gets an array of the data source pivot tables in the range A1:G50.\nconst dataSourcePivotTables = range.getDataSourcePivotTables();\n\n// Logs the last time that the first pivot table in the array was refreshed.\nconsole.log(dataSourcePivotTables[0].getStatus().getLastRefreshedTime());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:G50 on Sheet1.\nconst range = sheet.getRange('A1:G50');\n\n// Gets the first data source table in the range A1:G50.\nconst dataSourceTable = range.getDataSourceTables()[0];\n\n// Logs the time of the last completed data execution on the data source table.\nconsole.log(dataSourceTable.getStatus().getLastExecutionTime());\n```\n\nExample:\n```text\nfunction doGet() {\n const ss = SpreadsheetApp.openById(\n '1khO6hBWTNNyvyyxvob7aoZTI9ZvlqqASNeq0e29Tw2c',\n );\n const sheet = ss.getSheetByName('ContinentData');\n const range = sheet.getRange('A1:B8');\n\n const template = HtmlService.createTemplateFromFile('piechart');\n template.dataSourceUrl = range.getDataSourceUrl();\n return template.evaluate();\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <!--Load the AJAX API-->\n <script type=\"text/javascript\" src=\"https://www.gstatic.com/charts/loader.js\"></script>\n <script type=\"text/javascript\">\n // Load the Visualization API and the corechart package.\n google.charts.load('current', {'packages': ['corechart']});\n\n // Set a callback to run when the Google Visualization API is loaded.\n google.charts.setOnLoadCallback(queryData);\n\n function queryData() {\n var query = new google.visualization.Query('<?= dataSourceUrl ?>');\n query.send(drawChart);\n }\n\n // Callback that creates and populates a data table,\n // instantiates the pie chart, passes in the data and\n // draws it.\n function drawChart(response) {\n if (response.isError()) {\n alert('Error: ' + response.getMessage() + ' ' + response.getDetailedMessage());\n return;\n }\n var data = response.getDataTable();\n\n // Set chart options.\n var options = {\n title: 'Population by Continent',\n width: 400,\n height: 300\n };\n\n // Instantiate and draw the chart, passing in some options.\n var chart = new google.visualization.PieChart(document.getElementById('chart_div'));\n chart.draw(data, options);\n }\n </script>\n </head>\n <body>\n <!-- Div that holds the pie chart. -->\n <div id=\"chart_div\"></div>\n </body>\n</html>\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its ID. If you created your script from a\n// Google Sheets file, use SpreadsheetApp.getActiveSpreadsheet().\n// TODO(developer): Replace the ID with your own.\nconst ss = SpreadsheetApp.openById('abc123456');\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:B7 on Sheet1.\nconst range = sheet.getRange('A1:B7');\n\n// Gets the range A1:B7 as a data table. The values in each column must be of\n// the same type.\nconst datatable = range.getDataTable();\n\n// Uses the Charts service to build a bar chart from the data table.\n// This doesn't build an embedded chart. To do that, use\n// sheet.newChart().addRange() instead.\nconst chart = Charts.newBarChart()\n .setDataTable(datatable)\n .setOption('title', 'Your Chart Title Here')\n .build();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('A1:B7');\n\n// Calling this method with \"true\" sets the first line to be the title of the\n// axes\nconst datatable = range.getDataTable(true);\n\n// Note that this doesn't build an EmbeddedChart, so you can't just use\n// Sheet#insertChart(). To do that, use sheet.newChart().addRange() instead.\nconst chart = Charts.newBarChart()\n .setDataTable(datatable)\n .setOption('title', 'Your Title Here')\n .build();\n```\n\nExample:\n```text\n// Log information about the data validation rule for cell A1.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = cell.getDataValidation();\nif (rule != null) {\n const criteria = rule.getCriteriaType();\n const args = rule.getCriteriaValues();\n Logger.log('The data validation rule is %s %s', criteria, args);\n} else {\n Logger.log('The cell does not have a data validation rule.');\n}\n```\n\nExample:\n```text\n// Change existing data validation rules that require a date in 2013 to require\n// a date in 2014.\nconst oldDates = [new Date('1/1/2013'), new Date('12/31/2013')];\nconst newDates = [new Date('1/1/2014'), new Date('12/31/2014')];\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns());\nconst rules = range.getDataValidations();\n\nfor (let i = 0; i < rules.length; i++) {\n for (let j = 0; j < rules[i].length; j++) {\n const rule = rules[i][j];\n\n if (rule != null) {\n const criteria = rule.getCriteriaType();\n const args = rule.getCriteriaValues();\n\n if (criteria === SpreadsheetApp.DataValidationCriteria.DATE_BETWEEN &&\n args[0].getTime() === oldDates[0].getTime() &&\n args[1].getTime() === oldDates[1].getTime()) {\n // Create a builder from the existing rule, then change the dates.\n rules[i][j] = rule.copy().withCriteria(criteria, newDates).build();\n }\n }\n }\n}\nrange.setDataValidations(rules);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets row 2 on Sheet1.\nconst range = sheet.getRange('2:2');\n\n// Adds metadata to row 2.\nrange.addDeveloperMetadata('NAME', 'GOOGLE');\n\n// Logs the metadata to console.\nfor (const metadata of range.getDeveloperMetadata()) {\n console.log(`${metadata.getKey()}: ${metadata.getValue()}`);\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets cell A30 and sets its value to 'Test code.'\nconst cell = sheet.getRange('A30');\ncell.setValue('Test code');\n\n// Gets the value and logs it to the console.\nconsole.log(cell.getDisplayValue());\n```\n\nExample:\n```text\n// The code below gets the displayed values for the range C2:G8\n// in the active spreadsheet. Note that this is a JavaScript array.\nconst values =\n SpreadsheetApp.getActiveSheet().getRange(2, 3, 6, 4).getDisplayValues();\nLogger.log(values[0][0]);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSheet();\nconst range = ss.getRange('A1:C20');\n// Gets the existing filter on the sheet that the given range belongs to.\nconst filter = range.getFilter();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontColorObject().asRgbColor().asHexString());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontColorObjects();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j].asRgbColor().asHexString());\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontFamilies();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontFamily());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontLine());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontLines();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontSize());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontSizes();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontStyle());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontStyles();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontWeight());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontWeights();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This assumes you have a function in B5 that sums up\n// B2:B4\nconst range = sheet.getRange('B5');\n\n// Logs the calculated value and the formula\nLogger.log(\n 'Calculated value: %s Formula: %s',\n range.getValue(),\n range.getFormula(),\n);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5');\nconst formula = range.getFormulaR1C1();\nLogger.log(formula);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5:C6');\nconst formulas = range.getFormulas();\nfor (const i in formulas) {\n for (const j in formulas[i]) {\n Logger.log(formulas[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5:C6');\nconst formulas = range.getFormulasR1C1();\nfor (const i in formulas) {\n for (const j in formulas[i]) {\n Logger.log(formulas[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Log the grid ID of the first sheet (by tab position) in the spreadsheet.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getGridId());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D4');\n// logs 3.0\nLogger.log(range.getHeight());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getHorizontalAlignment());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getHorizontalAlignments();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D4');\n// Logs \"4.0\"\nLogger.log(range.getLastColumn());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D4');\n// Logs \"4.0\"\nLogger.log(range.getLastRow());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B3');\n\nconst mergedRanges = range.getMergedRanges();\nfor (let i = 0; i < mergedRanges.length; i++) {\n Logger.log(mergedRanges[i].getA1Notation());\n Logger.log(mergedRanges[i].getDisplayValue());\n}\n```\n\nExample:\n```text\n// Assume the active spreadsheet is blank.\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('C3:E5');\n// Logs \"C1\"\nLogger.log(range.getNextDataCell(SpreadsheetApp.Direction.UP).getA1Notation());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getNote());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getNotes();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D5');\nLogger.log(range.getNumColumns());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D5');\nLogger.log(range.getNumRows());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('C4');\nLogger.log(cell.getNumberFormat());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B5:C6');\nconst formats = range.getNumberFormats();\nfor (const i in formats) {\n for (const j in formats[i]) {\n Logger.log(formats[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Gets the Rich Text value of cell D4.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('D4:F6');\nconst richText = range.getRichTextValue();\nconsole.log(richText.getText());\n```\n\nExample:\n```text\n// Gets the Rich Text values for all cells in range B5:C6\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B5:C6');\nconst values = range.getRichTextValues();\n\nfor (let i = 0; i < values.length; i++) {\n for (let j = 0; j < values[i].length; j++) {\n console.log(values[i][j].getText());\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2');\nLogger.log(range.getRow());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2');\nLogger.log(range.getRowIndex());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Gets the sheet that the range belongs to.\nconst rangeSheet = range.getSheet();\n\n// Gets the sheet name and logs it to the console.\nconsole.log(rangeSheet.getName());\n```\n\nExample:\n```text\n// Get the text direction of cell B1.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B1:D4');\nLogger.log(range.getTextDirection());\n```\n\nExample:\n```text\n// Get the text directions for all cells in range B5:C6\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B5:C6');\nconst directions = range.getTextDirections();\n\nfor (let i = 0; i < directions.length; i++) {\n for (let j = 0; j < directions[i].length; j++) {\n Logger.log(directions[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Log the text rotation settings for a cell.\nconst sheet = SpreadsheetApp.getActiveSheet();\n\nconst cell = sheet.getRange('A1');\nLogger.log(cell.getTextRotation());\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getTextRotations();\n\nfor (const i in results) {\n for (const j in results[i]) {\n const rotation = results[i][j];\n Logger.log('Cell [%s, %s] has text rotation: %v', i, j, rotation);\n }\n}\n```\n\nExample:\n```text\n// Get the text style of cell D4.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('D4:F6');\nconst style = range.getTextStyle();\nLogger.log(style);\n```\n\nExample:\n```text\n// Get the text styles for all cells in range B5:C6\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B5:C6');\nconst styles = range.getTextStyles();\n\nfor (let i = 0; i < styles.length; i++) {\n for (let j = 0; j < styles[i].length; j++) {\n Logger.log(styles[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Gets the value of the top-left cell in the range and logs it to the console.\nconsole.log(range.getValue());\n```\n\nExample:\n```text\n// The code below gets the values for the range C2:G8\n// in the active spreadsheet. Note that this is a JavaScript array.\nconst values = SpreadsheetApp.getActiveSheet().getRange(2, 3, 6, 4).getValues();\nLogger.log(values[0][0]);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getVerticalAlignment());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getVerticalAlignments();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Gets the width of the range in number of columns and logs it to the console.\nconsole.log(range.getWidth());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getWrap());\n```\n\nExample:\n```text\n// Get the text wrapping strategies for all cells in range B5:C6\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B5:C6');\nconst strategies = range.getWrapStrategies();\n\nfor (let i = 0; i < strategies.length; i++) {\n for (let j = 0; j < strategies[i].length; j++) {\n Logger.log(strategies[i][j]);\n }\n}\n```\n\nExample:\n```text\n// Get the text wrapping strategy of cell B1.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B1:D4');\nLogger.log(range.getWrapStrategy());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getVerticalAlignments();\n\nfor (const i in results) {\n for (const j in results[i]) {\n const isWrapped = results[i][j];\n if (isWrapped) {\n Logger.log('Cell [%s, %s] has wrapped text', i, j);\n }\n }\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:D10');\nrange.insertCells(SpreadsheetApp.Dimension.COLUMNS);\n```\n\nExample:\n```text\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\n\n// Inserts checkboxes into each cell in the range A1:B10 configured with 'true'\n// for checked and 'false' for unchecked. Also, sets the value of each cell in\n// the range A1:B10 to 'false'.\nrange.insertCheckboxes();\n```\n\nExample:\n```text\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\n\n// Inserts checkboxes into each cell in the range A1:B10 configured with 'yes'\n// for checked and the empty string for unchecked. Also, sets the value of each\n// cell in the range A1:B10 to\n// the empty string.\nrange.insertCheckboxes('yes');\n```\n\nExample:\n```text\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\n\n// Inserts checkboxes into each cell in the range A1:B10 configured with 'yes'\n// for checked and 'no' for unchecked. Also, sets the value of each cell in the\n// range A1:B10 to 'no'.\nrange.insertCheckboxes('yes', 'no');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.isBlank());\n```\n\nExample:\n```text\nconst range = SpreadsheetApp.getActive().getRange('A1:A3');\n\n// Inserts checkboxes and sets each cell value to 'no' in the range A1:A3.\nrange.insertCheckboxes('yes', 'no');\n\nconst range1 = SpreadsheetApp.getActive().getRange('A1');\nrange1.setValue('yes');\n// Sets the value of isRange1Checked as true as it contains the checked value.\nconst isRange1Checked = range1.isChecked();\n\nconst range2 = SpreadsheetApp.getActive().getRange('A2');\nrange2.setValue('no');\n// Sets the value of isRange2Checked as false as it contains the unchecked\n// value.\nconst isRange2Checked = range2.isChecked();\n\nconst range3 = SpreadsheetApp.getActive().getRange('A3');\nrange3.setValue('random');\n// Sets the value of isRange3Checked as null, as it contains an invalid checkbox\n// value.\nconst isRange3Checked = range3.isChecked();\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Determines if the end of the range is bound to a particular column and logs\n// it to the console.\nconsole.log(range.isEndColumnBounded());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Determines if the end of the range is bound to a particular row and logs it\n// to the console.\nconsole.log(range.isEndRowBounded());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('A1:B3');\n\n// True if any of the cells in A1:B3 is included in a merge.\nconst isPartOfMerge = range.isPartOfMerge();\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Determines if the start of the range is bound to a particular column and logs\n// it to the console.\nconsole.log(range.isStartColumnBounded());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range A1:D10 on Sheet1.\nconst range = sheet.getRange('A1:D10');\n\n// Determines if the start of the range is bound to a particular row and logs it\n// to the console.\nconsole.log(range.isStartRowBounded());\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\n\n// The code below 2-dimensionally merges the cells in A1 to B3\nsheet.getRange('A1:B3').merge();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The code below merges cells C5:E5 into one cell\nconst range1 = sheet.getRange('C5:E5');\nrange1.mergeAcross();\n\n// The code below creates 2 horizontal cells, F5:H5 and F6:H6\nconst range2 = sheet.getRange('F5:H6');\nrange2.mergeAcross();\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\n\n// The code below vertically merges the cells in A1 to A10\nsheet.getRange('A1:A10').mergeVertically();\n\n// The code below creates 3 merged columns: B1 to B10, C1 to C10, and D1 to D10\nsheet.getRange('B1:D10').mergeVertically();\n```\n\nExample:\n```text\n// The code below moves the first 5 columns over to the 6th column\nconst sheet = SpreadsheetApp.getActiveSheet();\nsheet.getRange('A1:E').moveTo(sheet.getRange('F1'));\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('A1');\n\n// newCell references B2\nconst newCell = cell.offset(1, 1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('A1');\n\n// newCell references B2:B3\nconst newRange = cell.offset(1, 1, 2);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('A1');\n\n// newCell references B2:C3\nconst newRange = cell.offset(1, 1, 2, 2);\n```\n\nExample:\n```text\n// Protect range A1:B10, then remove all other users from the list of editors.\nconst ss = SpreadsheetApp.getActive();\nconst range = ss.getRange('A1:B10');\nconst protection = range.protect().setDescription('Sample protected range');\n\n// Ensure the current user is an editor before removing others. Otherwise, if\n// the user's edit permission comes from a group, the script throws an exception\n// upon removing the group.\nconst me = Session.getEffectiveUser();\nprotection.addEditor(me);\nprotection.removeEditors(protection.getEditors());\nif (protection.canDomainEdit()) {\n protection.setDomainEdit(false);\n}\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('A1:C7');\n\n// Randomizes the range\nrange.randomize();\n```\n\nExample:\n```text\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\n\n// Inserts checkboxes and sets each cell value to 'no' in the range A1:B10.\nrange.insertCheckboxes('yes', 'no');\n\nconst range1 = SpreadsheetApp.getActive().getRange('A1');\nrange1.setValue('yes');\n// Removes the checkbox data validation in cell A1 and clears its value.\nrange1.removeCheckboxes();\n\nconst range2 = SpreadsheetApp.getActive().getRange('A2');\nrange2.setValue('random');\n// Removes the checkbox data validation in cell A2 but does not clear its value.\nrange2.removeCheckboxes();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B1:D7');\n\n// Remove duplicate rows in the range.\nrange.removeDuplicates();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B1:D7');\n\n// Remove rows which have duplicate values in column B.\nrange.removeDuplicates([2]);\n\n// Remove rows which have duplicate values in both columns B and D.\nrange.removeDuplicates([2, 4]);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst range = sheet.getRange('B2:D5');\nrange.setBackground('red');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst bgColor = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.BACKGROUND)\n .build();\n\nconst range = sheet.getRange('B2:D5');\nrange.setBackgroundObject(bgColor);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst colorAccent1 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT1)\n .build();\nconst colorAccent2 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT2)\n .build();\nconst colorAccent3 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT3)\n .build();\nconst colorAccent4 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT4)\n .build();\n\nconst colors = [\n [colorAccent1, colorAccent2],\n [colorAccent3, colorAccent4],\n];\n\nconst cell = sheet.getRange('B5:C6');\ncell.setBackgroundObjects(colors);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\n\n// Sets the background to white\ncell.setBackgroundRGB(255, 255, 255);\n\n// Sets the background to red\ncell.setBackgroundRGB(255, 0, 0);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst colors = [\n ['red', 'white', 'blue'],\n ['#FF0000', '#FFFFFF', '#0000FF'], // These are the hex equivalents\n];\n\nconst cell = sheet.getRange('B5:D6');\ncell.setBackgrounds(colors);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\n// Sets borders on the top and bottom, but leaves the left and right unchanged\ncell.setBorder(true, null, true, null, false, false);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\n// Sets borders on the top and bottom, but leaves the left and right unchanged\n// Also sets the color to \"red\", and the border to \"DASHED\".\ncell.setBorder(\n true,\n null,\n true,\n null,\n false,\n false,\n 'red',\n SpreadsheetApp.BorderStyle.DASHED,\n);\n```\n\nExample:\n```text\n// Set the data validation rule for cell A1 to require a value from B1:B10.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst range = SpreadsheetApp.getActive().getRange('B1:B10');\nconst rule =\n SpreadsheetApp.newDataValidation().requireValueInRange(range).build();\ncell.setDataValidation(rule);\n```\n\nExample:\n```text\n// Set the data validation rules for Sheet1!A1:B5 to require a value from\n// Sheet2!A1:A10.\nconst destinationRange =\n SpreadsheetApp.getActive().getSheetByName('Sheet1').getRange('A1:B5');\nconst sourceRange =\n SpreadsheetApp.getActive().getSheetByName('Sheet2').getRange('A1:A10');\nconst rule =\n SpreadsheetApp.newDataValidation().requireValueInRange(sourceRange).build();\nconst rules = destinationRange.getDataValidations();\nfor (let i = 0; i < rules.length; i++) {\n for (let j = 0; j < rules[i].length; j++) {\n rules[i][j] = rule;\n }\n}\ndestinationRange.setDataValidations(rules);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontColor('red');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst color = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.TEXT)\n .build();\n\nconst cell = sheet.getRange('B2');\ncell.setFontColor(color);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst colorAccent1 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT1)\n .build();\nconst colorAccent2 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT2)\n .build();\nconst colorAccent3 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT3)\n .build();\nconst colorAccent4 = SpreadsheetApp.newColor()\n .setThemeColor(SpreadsheetApp.ThemeColorType.ACCENT4)\n .build();\n\nconst colors = [\n [colorAccent1, colorAccent2],\n [colorAccent3, colorAccent4],\n];\n\nconst cell = sheet.getRange('B5:C6');\ncell.setFontColorObjects(colors);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst colors = [\n ['red', 'white', 'blue'],\n ['#FF0000', '#FFFFFF', '#0000FF'], // These are the hex equivalents\n];\n\nconst cell = sheet.getRange('B5:D6');\ncell.setFontColors(colors);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst fonts = [\n ['Arial', 'Helvetica', 'Verdana'],\n ['Courier New', 'Arial', 'Helvetica'],\n];\n\nconst cell = sheet.getRange('B2:D3');\ncell.setFontFamilies(fonts);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontFamily('Helvetica');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontLine('line-through');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst fontLines = [['underline', 'line-through', 'none']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setFontLines(fontLines);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontSize(20);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst fontSizes = [[16, 20, 24]];\n\nconst range = sheet.getRange('B2:D2');\nrange.setFontSizes(fontSizes);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontStyle('italic');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst fontStyles = [['italic', 'normal']];\n\nconst range = sheet.getRange('B2:C2');\nrange.setFontStyles(fontStyles);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setFontWeight('bold');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst fontStyles = [['bold', 'bold', 'normal']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setFontWeights(fontStyles);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B5');\ncell.setFormula('=SUM(B3:B4)');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B5');\n// This sets the formula to be the sum of the 3 rows above B5\ncell.setFormulaR1C1('=SUM(R[-3]C[0]:R[-1]C[0])');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This sets the formulas to be a row of sums, followed by a row of averages\n// right below. The size of the two-dimensional array must match the size of the\n// range.\nconst formulas = [\n ['=SUM(B2:B4)', '=SUM(C2:C4)', '=SUM(D2:D4)'],\n ['=AVERAGE(B2:B4)', '=AVERAGE(C2:C4)', '=AVERAGE(D2:D4)'],\n];\n\nconst cell = sheet.getRange('B5:D6');\ncell.setFormulas(formulas);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// This creates formulas for a row of sums, followed by a row of averages.\nconst sumOfRowsAbove = '=SUM(R[-3]C[0]:R[-1]C[0])';\nconst averageOfRowsAbove = '=AVERAGE(R[-4]C[0]:R[-2]C[0])';\n\n// The size of the two-dimensional array must match the size of the range.\nconst formulas = [\n [sumOfRowsAbove, sumOfRowsAbove, sumOfRowsAbove],\n [averageOfRowsAbove, averageOfRowsAbove, averageOfRowsAbove],\n];\n\nconst cell = sheet.getRange('B5:D6');\n// This sets the formula to be the sum of the 3 rows above B5.\ncell.setFormulasR1C1(formulas);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setHorizontalAlignment('center');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst horizontalAlignments = [['left', 'right', 'center']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setHorizontalAlignments(horizontalAlignments);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setNote('This is a note');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst notes = [\n ['it goes', 'like this', 'the fourth, the fifth'],\n ['the minor fall', 'and the', 'major lift'],\n];\n\nconst cell = sheet.getRange('B2:D3');\ncell.setNotes(notes);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\n// Always show 3 decimal points\ncell.setNumberFormat('0.000');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst formats = [['0.000', '0,000,000', '$0.00']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setNumberFormats(formats);\n```\n\nExample:\n```text\n// Sets all cells in range B2:D4 to have the text \"Hello world\", with \"Hello\"\n// bolded.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B2:D4');\nconst bold = SpreadsheetApp.newTextStyle().setBold(true).build();\nconst richText = SpreadsheetApp.newRichTextValue()\n .setText('Hello world')\n .setTextStyle(0, 5, bold)\n .build();\nrange.setRichTextValue(richText);\n```\n\nExample:\n```text\n// Sets the cells in range A1:A2 to have Rich Text values.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:A2');\nconst bold = SpreadsheetApp.newTextStyle().setBold(true).build();\nconst italic = SpreadsheetApp.newTextStyle().setItalic(true).build();\nconst richTextA1 = SpreadsheetApp.newRichTextValue()\n .setText('This cell is bold')\n .setTextStyle(bold)\n .build();\nconst richTextA2 = SpreadsheetApp.newRichTextValue()\n .setText('bold words, italic words')\n .setTextStyle(0, 11, bold)\n .setTextStyle(12, 24, italic)\n .build();\nrange.setRichTextValues([[richTextA1], [richTextA2]]);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can useSpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets Sheet1 by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets cell A30 and sets its hyperlink value.\nconst range = sheet.getRange('A30');\nrange.setValue('https://www.example.com');\n\n// Sets cell A30 to show hyperlinks.\nrange.setShowHyperlink(true);\n```\n\nExample:\n```text\n// Sets right-to-left text direction for the range.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B5:C6');\nrange.setTextDirection(SpreadsheetApp.TextDirection.RIGHT_TO_LEFT);\n```\n\nExample:\n```text\n// Copies all of the text directions from range A1:B2 over to range C5:D6.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range1 = sheet.getRange('A1:B2');\nconst range2 = sheet.getRange('C5:D6');\n\nrange2.setTextRotations(range1.getTextDirections());\n```\n\nExample:\n```text\n// Sets all cell's in range B2:D4 to have text rotated up 45 degrees.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B2:D4');\n\nrange.setTextRotation(45);\n```\n\nExample:\n```text\n// Sets all cell's in range B2:D4 to have the same text rotation settings as\n// cell A1.\nconst sheet = SpreadsheetApp.getActiveSheet();\n\nconst rotation = sheet.getRange('A1').getTextRotation();\n\nsheet.getRange('B2:D4').setTextRotation(rotation);\n```\n\nExample:\n```text\n// Copies all of the text rotations from range A1:B2 over to range C5:D6.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range1 = sheet.getRange('A1:B2');\nconst range2 = sheet.getRange('C5:D6');\n\nrange2.setTextRotations(range1.getTextRotations());\n```\n\nExample:\n```text\n// Sets the cells in range C5:D6 to have underlined size 15 font.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('C5:D6');\nconst style =\n SpreadsheetApp.newTextStyle().setFontSize(15).setUnderline(true).build();\nrange.setTextStyle(style);\n```\n\nExample:\n```text\n// Sets text styles for cells in range A1:B2\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('A1:B2');\nconst bold = SpreadsheetApp.newTextStyle().setBold(true).build();\nconst otherStyle = SpreadsheetApp.newTextStyle()\n .setBold(true)\n .setUnderline(true)\n .setItalic(true)\n .setForegroundColor('#335522')\n .setFontSize(44)\n .build();\nrange.setTextStyles([\n [bold, otherStyle],\n [otherStyle, bold],\n]);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setValue(100);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst values = [['2.000', '1,000,000', '$2.99']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setValues(values);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setVerticalAlignment('middle');\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst alignments = [['top', 'middle', 'bottom']];\n\nconst range = sheet.getRange('B2:D2');\nrange.setVerticalAlignments(alignments);\n```\n\nExample:\n```text\n// Sets all cell's in range B2:D4 to have vertically stacked text.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B2:D4');\n\nrange.setVerticalText(true);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\nconst cell = sheet.getRange('B2');\ncell.setWrap(true);\n```\n\nExample:\n```text\n// Copies all of the wrap strategies from range A1:B2 over to range C5:D6.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range1 = sheet.getRange('A1:B2');\nconst range2 = sheet.getRange('C5:D6');\n\nrange2.setWrapStrategies(range1.getWrapStrategies());\n```\n\nExample:\n```text\n// Sets all cells in range B2:D4 to use the clip wrap strategy.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange('B2:D4');\n\nrange.setWrapStrategy(SpreadsheetApp.WrapStrategy.CLIP);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\n\n// The size of the two-dimensional array must match the size of the range.\nconst wraps = [[true, true, false]];\n\nconst range = sheet.getRange('B2:D2');\nrange.setWraps(wraps);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getActiveRange();\n\n// The column grouping depth is increased by 1.\nrange.shiftColumnGroupDepth(1);\n\n// The column grouping depth is decreased by 1.\nrange.shiftColumnGroupDepth(-1);\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getActiveRange();\n\n// The row grouping depth is increased by 1.\nrange.shiftRowGroupDepth(1);\n\n// The row grouping depth is decreased by 1.\nrange.shiftRowGroupDepth(-1);\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('A1:C7');\n\n// Sorts by the values in the first column (A)\nrange.sort(1);\n\n// Sorts by the values in the second column (B)\nrange.sort(2);\n\n// Sorts descending by column B\nrange.sort({column: 2, ascending: false});\n\n// Sorts descending by column B, then ascending by column A\n// Note the use of an array\nrange.sort([\n {column: 2, ascending: false},\n {column: 1, ascending: true},\n]);\n\n// For rows that are sorted in ascending order, the \"ascending\" parameter is\n// optional, and just an integer with the column can be used instead. Note that\n// in general, keeping the sort specification consistent results in more\n// readable code. You can express the earlier sort as:\nrange.sort([{column: 2, ascending: false}, 1]);\n\n// Alternatively, if you want all columns to be in ascending order, you can use\n// the following (this makes column 2 ascending)\nrange.sort([2, 1]);\n// ... which is equivalent to\nrange.sort([\n {column: 2, ascending: true},\n {column: 1, ascending: true},\n]);\n```\n\nExample:\n```text\n// A1:A3 has the following values:\n// A B C\n// 1 |one,one,one | | |\n// 2 |two,two,two | | |\n// 3 |three,three,three| | |\n\nconst range = SpreadsheetApp.getActiveSheet().getRange('A1:A3');\nrange.splitTextToColumns();\n\n// Result after splitting the text to columns:\n// A B C\n// 1 |one |one |one |\n// 2 |two |two |two |\n// 3 |three |three |three |\n```\n\nExample:\n```text\n// A1:A3 has the following values:\n// A B C\n// 1 |one#one#one | | |\n// 2 |two#two#two | | |\n// 3 |three#three#three| | |\n\nconst range = SpreadsheetApp.getActiveSheet().getRange('A1:A3');\nrange.splitTextToColumns('#');\n\n// Result after splitting the text to columns:\n// A B C\n// 1 |one |one |one |\n// 2 |two |two |two |\n// 3 |three |three |three |\n```\n\nExample:\n```text\n// A1:A3 has the following values:\n// A B C\n// 1 |one;one;one | | |\n// 2 |two;two;two | | |\n// 3 |three;three;three| | |\n\nconst range = SpreadsheetApp.getActiveSheet().getRange('A1:A3');\nrange.splitTextToColumns(SpreadsheetApp.TextToColumnsDelimiter.SEMICOLON);\n\n// Result after splitting the text to columns:\n// A B C\n// 1 |one |one |one |\n// 2 |two |two |two |\n// 3 |three |three |three |\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];\nconst range = sheet.getRange('A1:A4');\nrange.activate();\nrange.setValues([\n ' preceding space',\n 'following space ',\n 'two middle spaces',\n ' =SUM(1,2)',\n]);\n\nrange.trimWhitespace();\n\nconst values = range.getValues();\n// Values are ['preceding space', 'following space', 'two middle spaces',\n// '=SUM(1,2)']\n```\n\nExample:\n```text\n// Changes the state of cells which currently contain either the checked or\n// unchecked value configured in the range A1:B10 to 'unchecked'.\nconst range = SpreadsheetApp.getActive().getRange('A1:B10');\nrange.uncheck();\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nLogger.log(range.getFontColor());\n```\n\nExample:\n```text\nconst ss = SpreadsheetApp.getActiveSpreadsheet();\nconst sheet = ss.getSheets()[0];\nconst range = sheet.getRange('B2:D4');\n\nconst results = range.getFontColors();\n\nfor (const i in results) {\n for (const j in results[i]) {\n Logger.log(results[i][j]);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.312Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":198,"totalLines":2842,"estimatedTokens":18780}}1085{"id":"doc-class_tablecell_apps_script_google_for_developer-b928f3a5","source":"documentation","title":"Class TableCell | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_tablecell","text":"Example:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Insert two paragraphs separated by a paragraph containing an\n// horizontal rule.\nbody.insertParagraph(0, 'An editAsText sample.');\nbody.insertHorizontalRule(0);\nbody.insertParagraph(0, 'An example.');\n\n// Delete \" sample.\\n\\n An\" removing the horizontal rule in the process.\nbody.editAsText().deleteText(14, 25);\n```\n\nExample:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Define the search parameters.\n\nlet searchResult = null;\n\n// Search until the paragraph is found.\nwhile (\n (searchResult = body.findElement(\n DocumentApp.ElementType.PARAGRAPH,\n searchResult,\n ))) {\n const par = searchResult.getElement().asParagraph();\n if (par.getHeading() === DocumentApp.ParagraphHeading.HEADING1) {\n // Found one, update and stop.\n par.setText('This is the first header.');\n break;\n }\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Append a styled paragraph.\nconst par = body.appendParagraph('A bold, italicized paragraph.');\npar.setBold(true);\npar.setItalic(true);\n\n// Retrieve the paragraph's attributes.\nconst atts = par.getAttributes();\n\n// Log the paragraph attributes.\nfor (const att in atts) {\n Logger.log(`${att}:${atts[att]}`);\n}\n```\n\nExample:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Obtain the first element in the tab.\nconst firstChild = body.getChild(0);\n\n// If it's a paragraph, set its contents.\nif (firstChild.getType() === DocumentApp.ElementType.PARAGRAPH) {\n firstChild.asParagraph().setText('This is the first paragraph.');\n}\n```\n\nExample:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Log the number of elements in the tab.\nLogger.log(`There are ${body.getNumChildren()} elements in the tab's body.`);\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Obtain the first element in the active tab's body.\n\nconst firstChild = body.getChild(0);\n\n// Use getType() to determine the element's type.\nif (firstChild.getType() === DocumentApp.ElementType.PARAGRAPH) {\n Logger.log('The first element is a paragraph.');\n} else {\n Logger.log('The first element is not a paragraph.');\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Example 1: Merge paragraphs\n// Append two paragraphs to the document's active tab.\nconst par1 = body.appendParagraph('Paragraph 1.');\nconst par2 = body.appendParagraph('Paragraph 2.');\n// Merge the newly added paragraphs into a single paragraph.\npar2.merge();\n\n// Example 2: Merge table cells\n// Create a two-dimensional array containing the table's cell contents.\nconst cells = [\n ['Row 1, Cell 1', 'Row 1, Cell 2'],\n ['Row 2, Cell 1', 'Row 2, Cell 2'],\n];\n// Build a table from the array.\nconst table = body.appendTable(cells);\n// Get the first row in the table.\nconst row = table.getRow(0);\n// Get the two cells in this row.\nconst cell1 = row.getCell(0);\nconst cell2 = row.getCell(1);\n// Merge the current cell into its preceding sibling element.\nconst merged = cell2.merge();\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Remove all images in the active tab's body.\nconst imgs = body.getImages();\nfor (let i = 0; i < imgs.length; i++) {\n imgs[i].removeFromParent();\n}\n```\n\nExample:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Clear the text surrounding \"Apps Script\", with or without text.\nbody.replaceText('^.*Apps ?Script.*$', 'Apps Script');\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Define a custom paragraph style.\nconst style = {};\nstyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =\n DocumentApp.HorizontalAlignment.RIGHT;\nstyle[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';\nstyle[DocumentApp.Attribute.FONT_SIZE] = 18;\nstyle[DocumentApp.Attribute.BOLD] = true;\n\n// Append a plain paragraph.\nconst par = body.appendParagraph('A paragraph with custom style.');\n\n// Apply the custom style.\npar.setAttributes(style);\n```\n\nExample:\n```text\n// Make the entire first paragraph in the active tab be superscript.\nconst documentTab =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab();\nconst text = documentTab.getBody().getParagraphs()[0].editAsText();\ntext.setTextAlignment(DocumentApp.TextAlignment.SUPERSCRIPT);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.315Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":11,"totalLines":183,"estimatedTokens":1242}}1086{"id":"doc-class_document_apps_script_google_for_developers-e60e6e6e","source":"documentation","title":"Class Document | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_document","text":"Example:\n```text\n// Open a document by ID.\nlet doc = DocumentApp.openById('<my-id>');\n\n// Create and open a document.\ndoc = DocumentApp.create('Document Title');\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the active or first tab's body and adds a paragraph.\nconst paragraph = doc.getBody().appendParagraph('My new paragraph.');\n\n// Creates a position at the first character of the paragraph text.\nconst position = doc.newPosition(paragraph.getChild(0), 0);\n\n// Adds a bookmark at the first character of the paragraph text.\nconst bookmark = doc.addBookmark(position);\n\n// Logs the bookmark ID to the console.\nconsole.log(bookmark.getId());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Adds a footer to the document's active or first tab.\nconst footer = doc.addFooter();\n\n// Sets the footer text to 'This is a footer.'\nfooter.setText('This is a footer');\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Adds a header to the document's active or first tab.\nconst header = doc.addHeader();\n\n// Sets the header text to 'This is a header.'\nheader.setText('This is a header');\n```\n\nExample:\n```text\n// Creates a named range that includes every table in the active tab.\nconst doc = DocumentApp.getActiveDocument();\nconst rangeBuilder = doc.newRange();\nconst tables = doc.getBody().getTables();\nfor (let i = 0; i < tables.length; i++) {\n rangeBuilder.addElement(tables[i]);\n}\n// Adds the named range to the document's active tab.\ndoc.addNamedRange('Document tables', rangeBuilder.build());\n```\n\nExample:\n```text\n// Display a dialog box that shows the title of the tab that the\n// user is currently viewing.\nconst tab = DocumentApp.getActiveDocument().getActiveTab();\nDocumentApp.getUi().alert(`ID of selected tab: ${tab.getTitle()}`);\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the document as a PDF.\nconst pdf = doc.getAs('application/pdf');\n\n// Logs the name of the PDF to the console.\nconsole.log(pdf.getName());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Retrieves the current document's contents as a blob and logs it to the\n// console.\nconsole.log(doc.getBlob().getContentType());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the active or first tab's body.\nconst body = doc.getBody();\n\n// Gets the body text and logs it to the console.\nconsole.log(body.getText());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the bookmark by its ID in the document's active or first tab.\nconst bookmark = doc.getBookmark('id.xyz654321');\n\n// If the bookmark exists, logs the character offset of its position to the\n// console. otherwise, logs 'No bookmark exists with the given ID.' to the\n// console.\nif (bookmark) {\n console.log(bookmark.getPosition().getOffset());\n} else {\n console.log('No bookmark exists with the given ID.');\n}\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets all of the bookmarks in the document's active or first tab.\nconst bookmarks = doc.getBookmarks();\n\n// Logs the number of bookmarks in the tab to the console.\nconsole.log(bookmarks.length);\n```\n\nExample:\n```text\n// Insert some text at the cursor position and make it bold.\nconst cursor = DocumentApp.getActiveDocument().getCursor();\nif (cursor) {\n // Attempt to insert text at the cursor position. If the insertion returns\n // null, the cursor's containing element doesn't allow insertions, so show the\n // user an error message.\n const element = cursor.insertText('ಠ‿ಠ');\n if (element) {\n element.setBold(true);\n } else {\n DocumentApp.getUi().alert('Cannot insert text here.');\n }\n} else {\n DocumentApp.getUi().alert('Cannot find a cursor.');\n}\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the text of the active or first tab's footer and logs it to the console.\nconsole.log(doc.getFooter().getText());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the first footnote in the active or first tab's body.\nconst footnote = doc.getFootnotes()[0];\n\n// Logs footnote contents to the console.\nconsole.log(footnote.getFootnoteContents().getText());\n```\n\nExample:\n```text\n// Opens the Docs file by its ID. If you created your script from within\n// a Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('123abc');\n\n// Gets the text of the active or first tab's header and logs it to the console.\nconsole.log(doc.getHeader().getText());\n```\n\nExample:\n```text\n// Display a dialog box that tells the user how many elements are included in\n// the selection.\nconst selection = DocumentApp.getActiveDocument().getSelection();\nif (selection) {\n const elements = selection.getRangeElements();\n DocumentApp.getUi().alert(`Number of selected elements: ${elements.length}`);\n} else {\n DocumentApp.getUi().alert('Nothing is selected.');\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\n\n// Send out the link to open the document.\nMailApp.sendEmail('<email-address>', doc.getName(), doc.getUrl());\n```\n\nExample:\n```text\n// Append a paragraph to the active tab, then place the user's cursor after the\n// first word of the new paragraph.\nconst doc = DocumentApp.getActiveDocument();\nconst paragraph = doc.getBody().appendParagraph('My new paragraph.');\nconst position = doc.newPosition(paragraph.getChild(0), 2);\ndoc.setCursor(position);\n```\n\nExample:\n```text\n// Change the user's selection to a range that includes every table in the\n// active tab.\nconst doc = DocumentApp.getActiveDocument();\nconst rangeBuilder = doc.newRange();\nconst tables = doc.getBody().getTables();\nfor (let i = 0; i < tables.length; i++) {\n rangeBuilder.addElement(tables[i]);\n}\ndoc.setSelection(rangeBuilder.build());\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\n\n// Sets the user's selected tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst tab = doc.setActiveTab('123abc');\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\n\n// Append a paragraph, then place the user's cursor after the first word of the\n// new paragraph.\nconst paragraph = documentTab.getBody().appendParagraph('My new paragraph.');\nconst position = documentTab.newPosition(paragraph.getChild(0), 2);\ndoc.setCursor(position);\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\n\n// Change the user's selection to a range that includes every table in the\n// document.\nconst rangeBuilder = documentTab.newRange();\nconst tables = documentTab.getBody().getTables();\nfor (let i = 0; i < tables.length; i++) {\n rangeBuilder.addElement(tables[i]);\n}\ndoc.setSelection(rangeBuilder.build());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.318Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":22,"totalLines":287,"estimatedTokens":2211}}1087{"id":"doc-tasks_service_apps_script_google_for_developers-3fb0fbe2","source":"documentation","title":"Tasks Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/service_tasks","text":"Example:\n```text\n/**\n * Lists the titles and IDs of tasksList.\n * @see https://developers.google.com/tasks/reference/rest/v1/tasklists/list\n */\nfunction listTaskLists() {\n try {\n // Returns all the authenticated user's task lists.\n const taskLists = Tasks.Tasklists.list();\n // If taskLists are available then print all tasklists.\n if (!taskLists.items) {\n console.log(\"No task lists found.\");\n return;\n }\n // Print the tasklist title and tasklist id.\n for (let i = 0; i < taskLists.items.length; i++) {\n const taskList = taskLists.items[i];\n console.log(\n 'Task list with title \"%s\" and ID \"%s\" was found.',\n taskList.title,\n taskList.id,\n );\n }\n } catch (err) {\n // TODO (developer) - Handle exception from Task API\n console.log(\"Failed with an error %s \", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Lists task items for a provided tasklist ID.\n * @param {string} taskListId The tasklist ID.\n * @see https://developers.google.com/tasks/reference/rest/v1/tasks/list\n */\nfunction listTasks(taskListId) {\n try {\n // List the task items of specified tasklist using taskList id.\n const tasks = Tasks.Tasks.list(taskListId);\n // If tasks are available then print all task of given tasklists.\n if (!tasks.items) {\n console.log(\"No tasks found.\");\n return;\n }\n // Print the task title and task id of specified tasklist.\n for (let i = 0; i < tasks.items.length; i++) {\n const task = tasks.items[i];\n console.log(\n 'Task with title \"%s\" and ID \"%s\" was found.',\n task.title,\n task.id,\n );\n }\n } catch (err) {\n // TODO (developer) - Handle exception from Task API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Adds a task to a tasklist.\n * @param {string} taskListId The tasklist to add to.\n * @see https://developers.google.com/tasks/reference/rest/v1/tasks/insert\n */\nfunction addTask(taskListId) {\n // Task details with title and notes for inserting new task\n let task = {\n title: \"Pick up dry cleaning\",\n notes: \"Remember to get this done!\",\n };\n try {\n // Call insert method with taskDetails and taskListId to insert Task to specified tasklist.\n task = Tasks.Tasks.insert(task, taskListId);\n // Print the Task ID of created task.\n console.log('Task with ID \"%s\" was created.', task.id);\n } catch (err) {\n // TODO (developer) - Handle exception from Tasks.insert() of Task API\n console.log(\"Failed with an error %s\", err.message);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.319Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":89,"estimatedTokens":646}}1088{"id":"doc-class_listitem_apps_script_google_for_developers-4ccd95cb","source":"documentation","title":"Class ListItem | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_documentapp_listitem","text":"Example:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Append a new list item to the body.\nconst item1 = body.appendListItem('Item 1');\n\n// Log the new list item's list ID.\nLogger.log(item1.getListId());\n\n// Append a table after the list item.\nbody.appendTable([['Cell 1', 'Cell 2']]);\n\n// Append a second list item with the same list ID. The two items are treated as\n// the same list, despite not being consecutive.\nconst item2 = body.appendListItem('Item 2');\nitem2.setListId(item1);\n```\n\nExample:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Insert two paragraphs separated by a paragraph containing an\n// horizontal rule.\nbody.insertParagraph(0, 'An editAsText sample.');\nbody.insertHorizontalRule(0);\nbody.insertParagraph(0, 'An example.');\n\n// Delete \" sample.\\n\\n An\" removing the horizontal rule in the process.\nbody.editAsText().deleteText(14, 25);\n```\n\nExample:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Define the search parameters.\n\nlet searchResult = null;\n\n// Search until the paragraph is found.\nwhile (\n (searchResult = body.findElement(\n DocumentApp.ElementType.PARAGRAPH,\n searchResult,\n ))) {\n const par = searchResult.getElement().asParagraph();\n if (par.getHeading() === DocumentApp.ParagraphHeading.HEADING1) {\n // Found one, update and stop.\n par.setText('This is the first header.');\n break;\n }\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Append a styled paragraph.\nconst par = body.appendParagraph('A bold, italicized paragraph.');\npar.setBold(true);\npar.setItalic(true);\n\n// Retrieve the paragraph's attributes.\nconst atts = par.getAttributes();\n\n// Log the paragraph attributes.\nfor (const att in atts) {\n Logger.log(`${att}:${atts[att]}`);\n}\n```\n\nExample:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Obtain the first element in the tab.\nconst firstChild = body.getChild(0);\n\n// If it's a paragraph, set its contents.\nif (firstChild.getType() === DocumentApp.ElementType.PARAGRAPH) {\n firstChild.asParagraph().setText('This is the first paragraph.');\n}\n```\n\nExample:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Log the number of elements in the tab.\nLogger.log(`There are ${body.getNumChildren()} elements in the tab's body.`);\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Obtain the first element in the active tab's body.\n\nconst firstChild = body.getChild(0);\n\n// Use getType() to determine the element's type.\nif (firstChild.getType() === DocumentApp.ElementType.PARAGRAPH) {\n Logger.log('The first element is a paragraph.');\n} else {\n Logger.log('The first element is not a paragraph.');\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Example 1: Merge paragraphs\n// Append two paragraphs to the document's active tab.\nconst par1 = body.appendParagraph('Paragraph 1.');\nconst par2 = body.appendParagraph('Paragraph 2.');\n// Merge the newly added paragraphs into a single paragraph.\npar2.merge();\n\n// Example 2: Merge table cells\n// Create a two-dimensional array containing the table's cell contents.\nconst cells = [\n ['Row 1, Cell 1', 'Row 1, Cell 2'],\n ['Row 2, Cell 1', 'Row 2, Cell 2'],\n];\n// Build a table from the array.\nconst table = body.appendTable(cells);\n// Get the first row in the table.\nconst row = table.getRow(0);\n// Get the two cells in this row.\nconst cell1 = row.getCell(0);\nconst cell2 = row.getCell(1);\n// Merge the current cell into its preceding sibling element.\nconst merged = cell2.merge();\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Remove all images in the active tab's body.\nconst imgs = body.getImages();\nfor (let i = 0; i < imgs.length; i++) {\n imgs[i].removeFromParent();\n}\n```\n\nExample:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Clear the text surrounding \"Apps Script\", with or without text.\nbody.replaceText('^.*Apps ?Script.*$', 'Apps Script');\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Define a custom paragraph style.\nconst style = {};\nstyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =\n DocumentApp.HorizontalAlignment.RIGHT;\nstyle[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';\nstyle[DocumentApp.Attribute.FONT_SIZE] = 18;\nstyle[DocumentApp.Attribute.BOLD] = true;\n\n// Append a plain paragraph.\nconst par = body.appendParagraph('A paragraph with custom style.');\n\n// Apply the custom style.\npar.setAttributes(style);\n```\n\nExample:\n```text\n// Make the entire first paragraph in the active tab be superscript.\nconst documentTab =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab();\nconst text = documentTab.getBody().getParagraphs()[0].editAsText();\ntext.setTextAlignment(DocumentApp.TextAlignment.SUPERSCRIPT);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.327Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":203,"estimatedTokens":1379}}1089{"id":"doc-class_calendareventseries_apps_script_google_for-128c1eea","source":"documentation","title":"Class CalendarEventSeries | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_calendareventseries","text":"Example:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Adds an email notification for 15 minutes before the event.\nevent.addEmailReminder(15);\n```\n\nExample:\n```text\n// Example 1: Add a guest to one event\nfunction addAttendeeToEvent() {\n // Replace the below values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to add\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar containing\n // event\n const eventId = '123abc'; // ID of event instance\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n const event = calendar.getEventById(eventId);\n if (event === null) {\n // Event not found\n console.log('Event not found', eventId);\n return;\n }\n event.addGuest(attendeeEmail);\n}\n\n// Example 2: Add a guest to all events on a calendar within a specified\n// timeframe\nfunction addAttendeeToAllEvents() {\n // Replace the following values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to add\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar with the\n // events\n const startDate =\n new Date('YYYY-MM-DD'); // The first date to add the guest to the events\n const endDate =\n new Date('YYYY-MM-DD'); // The last date to add the guest to the events\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n // Get the events within the specified timeframe\n const calEvents = calendar.getEvents(startDate, endDate);\n console.log(calEvents.length); // Checks how many events are found\n // Loop through all events and add the attendee to each of them\n for (let i = 0; i < calEvents.length; i++) {\n const event = calEvents[i];\n event.addGuest(attendeeEmail);\n }\n}\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Adds a pop-up notification for 15 minutes before the event.\nevent.addPopupReminder(15);\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Determines whether people can add themselves as guests to the event and logs\n// it.\nconsole.log(event.anyoneCanAddSelf());\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Gets the color of the calendar event and logs it.\nconst eventColor = event.getColor();\nconsole.log(eventColor);\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Gets a list of the creators of the event and logs it.\nconsole.log(event.getCreators());\n```\n\nExample:\n```text\n// Opens the calendar by using its ID.\n// To get the user's default calendar use CalendarApp.getDefault() instead.\n// TODO(developer): Replace the calendar ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 8:10 AM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 08:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the date that the\n // event was created and logs it.\n const eventCreated = event.getDateCreated();\n console.log(eventCreated);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// To get the user's default calendar use CalendarApp.getDefault() instead.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 4:00 PM and 5:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 16:00:00'),\n new Date('Feb 04, 2023 17:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the description of the\n // event.\n event.setDescription('Important meeting');\n\n // Gets the description of the event and logs it.\n const description = event.getDescription();\n console.log(description);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:00 PM and 6:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 15:00:00'),\n new Date('Feb 04, 2023 18:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, adds email reminders for\n // the user to be sent at 4 and 7 minutes before the event.\n event.addEmailReminder(4);\n event.addEmailReminder(7);\n\n // Gets the minute values for all email reminders that are set up for the user\n // for this event and logs it.\n const emailReminder = event.getEmailReminders();\n console.log(emailReminder);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the default calendar and logs all out-of-office events for the current day.\nconst calendar = CalendarApp.getDefaultCalendar();\nconst events = calendar.getEventsForDay(new Date());\nconsole.log(events.filter(e => e.getEventType() === CalendarApp.EventType.OUT_OF_OFFICE));\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Gets a guest by email address.\nconst guestEmailId = event.getGuestByEmail('alex@example.com');\n\n// If the email address corresponds to an event guest, logs the email address.\nif (guestEmailId) {\n console.log(guestEmailId.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Adds two guests to the event by using their email addresses.\nevent.addGuest('alex@example.com');\nevent.addGuest('cruz@example.com');\n\n// Gets the guests list for the event.\nconst guestList = event.getGuestList();\n\n// Loops through the list to get all the guests and logs their email addresses.\nfor (const guest of guestList) {\n console.log(guest.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Gets the guests list for the event, including the owner of the event.\nconst guestList = event.getGuestList(true);\n\n// Loops through the list to get all the guests and logs it.\nfor (const guest of guestList) {\n console.log(guest.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 5th, 2023 that takes place\n// between 9:00 AM and 9:25 AM.\n// For an event series, use calendar.getEventSeriesById('abc123456@google.com');\n// and replace the series ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 05, 2023 09:00:00'),\n new Date('Jan 05, 2023 09:25:00'),\n )[0];\n\n// Gets the ID of the event and logs it.\nconsole.log(event.getId());\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:00 PM and 5:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:00:00'),\n new Date('Feb 01, 2023 17:00:00'),\n )[0];\n\n// Gets the date the event was last updated and logs it.\nconst eventUpdatedDate = event.getLastUpdated();\nconsole.log(eventUpdatedDate);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the location of the\n // event to Mumbai.\n event.setLocation('Mumbai');\n\n // Gets the location of the event and logs it.\n const eventLocation = event.getLocation();\n console.log(eventLocation);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the event status of\n // the effective user and logs it.\n const myStatus = event.getMyStatus();\n console.log(myStatus.toString());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 4:00 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 16:00:00'),\n new Date('Feb 25,2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the ID of the calendar\n // where the event was originally created and logs it.\n const calendarId = event.getOriginalCalendarId();\n console.log(calendarId);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:05 PM and 5:35 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 17:05:00'),\n new Date('Feb 04, 2023 17:35:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, adds two pop-up reminders\n // to the event. The first reminder pops up 5 minutes before the event starts\n // and the second reminder pops up 3 minutes before the event starts.\n event.addPopupReminder(3);\n event.addPopupReminder(5);\n\n // Gets the minute values for all pop-up reminders for the event and logs it.\n const popUpReminder = event.getPopupReminders();\n console.log(popUpReminder);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@example.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, logs the title of the\n // event.\n console.log(event.getTitle());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Gets the first event from the default calendar for today.\nconst today = new Date();\nconst event = CalendarApp.getDefaultCalendar().getEventsForDay(today)[0];\n// Gets the event's transparency and logs it.\nconst transparency = event.getTransparency();\nLogger.log(transparency);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the visibility of the\n // event and logs it.\n const eventVisibility = event.getVisibility();\n console.log(eventVisibility.toString());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether guests\n // can invite other guests and logs it.\n console.log(event.guestsCanInviteOthers());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // guests can't modify it.\n event.setGuestsCanModify(false);\n\n // Determines whether guests can modify the event and logs it.\n console.log(event.guestsCanModify());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether guests\n // can see other guests and logs it.\n console.log(event.guestsCanSeeGuests());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether you're\n // the owner of the event and logs it.\n console.log(event.isOwnedByMe());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 1,2023 16:10:00'),\n new Date('Feb 1,2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, removes all reminders from\n // the event.\n event.removeAllReminders();\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Example 1: Remove a guest from one event\nfunction removeGuestFromEvent() {\n // Replace the below values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to remove\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar containing\n // event\n const eventId = '123abc'; // ID of event instance\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n const event = calendar.getEventById(eventId);\n if (event === null) {\n // Event not found\n console.log('Event not found', eventId);\n return;\n }\n event.removeGuest(attendeeEmail);\n}\n\n// Example 2: Remove a guest from all events on a calendar within a specified\n// timeframe\nfunction removeGuestFromAllEvents() {\n // Replace the following values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to remove\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar with the\n // events\n const startDate = new Date(\n 'YYYY-MM-DD'); // The first date to remove the guest from the events\n const endDate = new Date(\n 'YYYY-MM-DD'); // The last date to remove the attendee from the events\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n // Get the events within the specified timeframe\n const calEvents = calendar.getEvents(startDate, endDate);\n console.log(calEvents.length); // Checks how many events are found\n // Loop through all events and remove the attendee from each of them\n for (let i = 0; i < calEvents.length; i++) {\n const event = calEvents[i];\n event.removeGuest(attendeeEmail);\n }\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 1, 2023 16:10:00'),\n new Date('Feb 1, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, resets the reminders using\n // the calendar's default settings.\n event.resetRemindersToDefault();\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 15th, 2023 that takes\n// place between 3:30 PM and 4:30 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 15, 2023 15:30:00'),\n new Date('Feb 15, 2023 16:30:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // non-guests can't add themselves to the event.\n event.setAnyoneCanAddSelf(false);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the color of the\n // calendar event to green.\n event.setColor(CalendarApp.EventColor.GREEN);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:05 PM and 5:35 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 17:05:00'),\n new Date('Feb 04, 2023 17:35:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the description of the\n // event to 'Meeting.'\n event.setDescription('Meeting');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own. You must have edit access to\n// the calendar.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // guests can invite other guests.\n event.setGuestsCanInviteOthers(true);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the location of the\n // event to Noida.\n event.setLocation('Noida');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event status for\n // the current user to maybe.\n event.setMyStatus(CalendarApp.GuestStatus.MAYBE);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Sets the events in a series to take place every Wednesday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().getEventSeriesById(\n '123456789@example.com',\n);\nconst startDate = new Date('January 2, 2013 03:00:00 PM EST');\nconst recurrence = CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014'));\neventSeries.setRecurrence(recurrence, startDate);\n```\n\nExample:\n```text\n// Sets the events in a series to take place from 3pm to 4pm every Tuesday and\n// Thursday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().getEventSeriesById(\n '123456789@example.com',\n);\nconst startTime = new Date('January 1, 2013 03:00:00 PM EST');\nconst endTime = new Date('January 1, 2013 04:00:00 PM EST');\nconst recurrence =\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekdays(\n [CalendarApp.Weekday.TUESDAY, CalendarApp.Weekday.THURSDAY])\n .until(new Date('January 1, 2014'));\neventSeries.setRecurrence(recurrence, startTime, endTime);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@example.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, changes its title to\n // Event1.\n event.setTitle('Event1');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n// Sets the event's transparency to TRANSPARENT.\nevent.setTransparency(CalendarApp.EventTransparency.TRANSPARENT);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.330Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":39,"totalLines":1005,"estimatedTokens":8315}}1090{"id":"doc-google_url_shortener_links_will_no_longer_be_ava-605a6387","source":"documentation","title":"Google URL Shortener links will no longer be available [updated] - Google Developers Blog","url":"https://developers.google.com/apps-script/service_urlshortener","text":"Community/Events Learn Blog YouTube Search\n\nCommunity/Events Learn Blog YouTube\n\nGoogle URL Shortener links will no longer be available [updated] JULY 18, 2024 Sumit Chandel Developer Relations Engineer Eldhose Mathokkil Babu Software Engineer Share Facebook Twitter LinkedIn Mail Updated August 1, we previously announced discontinuing support for all goo.gl URLs after August 25, 2025, we've adjusted our approach in order to preserve actively used links.We understand these links are embedded in countless documents, videos, posts and more, and we appreciate the input received.Nine months ago, we redirected URLs that showed no activity in late 2024 to a message specifying that the link would be deactivated in August, and these are the only links targeted to be deactivated. If you get a message that states, “This link will no longer work in the near future”, the link won't work after August 25 and we recommend transitioning to another URL shortener if you haven’t already.All other goo.gl links will be preserved and will continue to function as normal. To check if your link will be retained, visit the link today. If your link redirects you without a message, it will continue to work.In 2018, we announced the deprecation and transition of Google URL Shortener because of the changes we’ve seen in how people find content on the internet, and the number of new popular URL shortening services that emerged in that time. This meant that we no longer accepted new URLs to shorten but that we would continue serving existing URLs.Over time, these existing URLs saw less and less traffic as the years went on - in fact more than 99% of them had no activity in the last month.As such, we will be turning off Google URL Shortener. Please read on below to understand more about how this may impact you.Who is impacted?Any developers using links built with the Google URL Shortener in the form https://goo.gl/* will be impacted, and these URLs will no longer return a response after August 25th, 2025. We recommend transitioning these links to another URL shortener provider.Note that goo.gl links generated via Google apps (such as Maps sharing) will continue to function.What to expectStarting August 23, 2024, goo.gl links will start displaying an interstitial page for a percentage of existing links notifying your users that the link will no longer be supported after August 25th, 2025 prior to navigating to the original target page. Interstitial page shown for some goo.gl links starting on August 23, 2024 Over time the percentage of links that will show the interstitial page will increase until the shutdown date. This interstitial page should help you track and adjust any affected links that you will need to transition as part of this change. We will continue to display this interstitial page until the shutdown date after which all links served will return a 404 response.Note that the interstitial page may cause disruptions in the current flow of your goo.gl links. For example, if you are using other 302 redirects, the interstitial page may prevent the redirect flow from completing correctly. If you’ve embedded social metadata in your destination page, the interstitial page will likely cause these to no longer show up where the initial link is displayed. For this reason, we advise transitioning these links as soon as possible.Note: In the event the interstitial page is disrupting your use cases, you can suppress it by adding the query param “si=1” to existing goo.gl links.We understand the transition away from using goo.gl short links may cause some inconvenience. If you have any questions or concerns, please reach out to us at Firebase Support. Thank you for using the service and we hope you join us in moving forward into new and innovative ways for navigating web and app experiences. posted Announcements Best Practices Learn Previous Next Related Posts Mobile Web Announcements Solutions Introducing Source C++ Library for C2PA Content Credentials from Google AUG. 13, 2026 AI Cloud Announcements Best Practices Build zero-trust AI agents with Google's Agent Development Kit AUG. 17, 2026 Web AI How-To Guides Announcements Mastering Edge AI on Raspberry Pi with LiteRT and Gemma AUG. 11, 2026\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.331Z","totalSectionsIncluded":3,"totalCodeBlocksIncluded":0,"totalLines":7,"estimatedTokens":1063}}1091{"id":"doc-answer_questions_based_on_chat_conversations_wit-a2212df7","source":"documentation","title":"Answer questions based on Chat conversations with a Gemini AI Chat app | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/samples/tutorial-ai-knowledge-assistant","text":"Example:\n```text\ngcloud projects create PROJECT_ID\n```\n\nExample:\n```text\ngcloud billing accounts list\n```\n\nExample:\n```text\ngcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID\n```\n\nExample:\n```text\ngcloud config set project PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable chat.googleapis.com \\\naiplatform.googleapis.com \\\ncloudfunctions.googleapis.com \\\nfirestore.googleapis.com \\\ncloudbuild.googleapis.com \\\npubsub.googleapis.com \\\nworkspaceevents.googleapis.com \\\neventarc.googleapis.com \\\nrun.googleapis.com\n```\n\nExample:\n```text\nhttps://REGION-PROJECT_ID.cloudfunctions.net/app/oauth2\n```\n\nExample:\n```text\ngcloud pubsub topics create events-api\n```\n\nExample:\n```text\ngcloud pubsub topics add-iam-policy-binding events-api \\\n--member='serviceAccount:chat-api-push@system.gserviceaccount.com' \\\n--role='roles/pubsub.publisher'\n```\n\nExample:\n```text\ngcloud firestore databases create \\\n--location=LOCATION \\\n--type=firestore-native\n```\n\nExample:\n```text\ngit clone https://github.com/googleworkspace/add-ons-samples.git\n```\n\nExample:\n```text\ncd add-ons-samples/node/chat/ai-knowledge-assistant\n```\n\nExample:\n```text\ngcloud functions deploy app \\\n--gen2 \\\n--region=REGION \\\n--runtime=nodejs20 \\\n--source=. \\\n--entry-point=app \\\n--trigger-http \\\n--allow-unauthenticated\n```\n\nExample:\n```text\ngcloud functions deploy eventsApp \\\n--gen2 \\\n--region=REGION \\\n--runtime=nodejs20 \\\n--source=. \\\n--entry-point=eventsApp \\\n--trigger-topic=events-api\n```\n\nExample:\n```text\ngcloud functions describe app\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.334Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":96,"estimatedTokens":386}}1092{"id":"doc-class_contact_apps_script_google_for_developers-cd82accc","source":"documentation","title":"Class Contact | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_contact","text":"Example:\n```text\n{@code\n// The code below retrieves a contact named \"John Doe\" and adds the email\n// address \"j.doe@example.com\" to the ContactsApp.Field.HOME_EMAIL label.\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and adds the address\n// \"123 Main St, Some City, NY 10011\" with the ContactsApp.Field.WORK_ADDRESS\n// label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst address = contacts[0].addAddress(\n ContactsApp.Field.WORK_ADDRESS,\n '123 Main St, Some City, NY 10011',\n);\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and adds the company\n// \"Google\" and the job title \"Product Manager\".\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst url = contacts[0].addCompany('Google', 'Product Manager');\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and adds the custom field\n// ContactsApp.ExtendedField.HOBBY with the value \"hiking\".\n// Note that ContactsApp.ExtendedField.HOBBY is not the same as a custom field\n// named 'HOBBY'.\nconst contacts = ContactsApp.getContactsByName('John Doe');\ncontacts[0].addCustomField(ContactsApp.ExtendedField.HOBBY, 'hiking');\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and adds a\n// ContactsApp.ExtendedField.BIRTHDAY with the value \"April 19, 1950\".\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst birthday = contacts[0].addDate(\n ContactsApp.Field.BIRTHDAY,\n ContactsApp.Month.APRIL,\n 19,\n 1950,\n);\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and adds the email\n// address \"j.doe@example.com\" to the ContactsApp.Field.HOME_EMAIL label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst emailField = contacts[0].addEmail(\n ContactsApp.Field.HOME_EMAIL,\n 'j.doe@example.com',\n);\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and adds the IM address\n// \"ChatWithJohn\" with the ContactsApp.Field.AIM label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst email = contacts[0].addIM(ContactsApp.Field.AIM, 'ChatWithJohn');\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and adds the phone number\n// \"212-555-1234\" with the ContactsApp.Field.WORK_PHONE label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst phone = contacts[0].addPhone(\n ContactsApp.Field.WORK_PHONE,\n '212-555-1234',\n);\n```\n\nExample:\n```text\n// The code below creates a new contact and then adds it to the contact group\n// named \"Work Friends\"\nlet contact = ContactsApp.createContact('John', 'Doe', 'john.doe@example.com');\nconst group = ContactsApp.getContactGroup('Work Friends');\ncontact = contact.addToGroup(group);\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and adds the URL\n// \"http://www.example.com\" with the ContactsApp.Field.WORK_WEBSITE label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst url = contacts[0].addUrl(\n ContactsApp.Field.WORK_WEBSITE,\n 'http://www.example.com',\n);\n```\n\nExample:\n```text\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n contacts[i].deleteContact();\n}\n```\n\nExample:\n```text\n// The code below logs the addresses of all the contacts whose names contain\n// \"John Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n Logger.log(contacts[i].getAddresses());\n}\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and logs the addresses\n// associated with that contact that are in the ContactsApp.Field.WORK_ADDRESS\n// label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst addresses = contacts[0].getAddresses(ContactsApp.Field.WORK_ADDRESS);\nfor (const i in addresses) {\n Logger.log(addresses[i].getAddress());\n}\n```\n\nExample:\n```text\n// The code below logs the company names of all the contacts whose names contain\n// \"John Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n const companies = contacts[i].getCompanies();\n for (const j in companies) {\n Logger.log(companies[j].getCompanyName());\n }\n}\n```\n\nExample:\n```text\n// The code below gets a contact named \"John Doe\" and retrieves all the contact\n// groups that the contact belongs to\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst groups = contacts[0].getContactGroups();\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and logs the custom\n// fields associated with that contact\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst fields = contacts[0].getCustomFields();\nfor (const i in fields) {\n Logger.log(fields[i].getValue());\n}\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and logs the custom\n// fields associated with that contact that are in the\n// ContactsApp.ExtendedField.HOBBY label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst hobbies = contacts[0].getCustomFields(ContactsApp.ExtendedField.HOBBY);\nfor (const i in hobbies) {\n Logger.log(hobbies[i].getValue());\n}\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and logs the label of the\n// date associated with that contact\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst dates = contacts[0].getDates();\nfor (const i in dates) {\n Logger.log(dates[i].getLabel());\n}\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and logs the day of the\n// month associated with that contact that are in the ContactsApp.Field.BIRTHDAY\n// label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst birthdays = contacts[0].getDates(ContactsApp.Field.BIRTHDAY);\nfor (const i in birthdays) {\n Logger.log(birthdays[i].getDay());\n}\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and logs the email\n// addresses associated with that contact\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst emails = contacts[0].getEmails();\nfor (const i in emails) {\n Logger.log(emails[i].getAddress());\n}\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and logs the email\n// addresses associated with that contact that are in the\n// ContactsApp.Field.HOME_EMAIL label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst emails = contacts[0].getEmails(ContactsApp.Field.HOME_EMAIL);\nfor (const i in emails) {\n Logger.log(emails[i].getAddress());\n}\n```\n\nExample:\n```text\n// The code below logs the family name of all the contacts whose names contain\n// \"John\"\nconst contacts = ContactsApp.getContactsByName('John');\nfor (const i in contacts) {\n Logger.log(contacts[i].getFamilyName());\n}\n```\n\nExample:\n```text\n// The code below logs the full name of all the contacts whose names contain\n// \"John\"\nconst contacts = ContactsApp.getContactsByName('John');\nfor (const i in contacts) {\n Logger.log(contacts[i].getFullName());\n}\n```\n\nExample:\n```text\n// The code below logs the given name of all the contacts whose names contain\n// \"Smith\"\nconst contacts = ContactsApp.getContactsByName('Smith');\nfor (const i in contacts) {\n Logger.log(contacts[i].getGivenName());\n}\n```\n\nExample:\n```text\n// The code below logs the IM addresses of all the contacts whose names contain\n// \"John Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n Logger.log(contacts[i].getIMs());\n}\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and logs the IM addresses\n// associated with that contact that are in the ContactsApp.Field.GOOGLE_TALK\n// label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst imAddresses = contacts[0].getIMs(ContactsApp.Field.GOOGLE_TALK);\nfor (const i in imAddresses) {\n Logger.log(imAddresses[i].getAddress());\n}\n```\n\nExample:\n```text\nconst contact = ContactsApp.createContact(\n 'John',\n 'Doe',\n 'john.doe@example.com',\n);\nconst id = contact.getId();\n```\n\nExample:\n```text\n// The code below logs the initials of all the contacts whose names contain\n// \"John Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n Logger.log(contacts[i].getInitials());\n}\n```\n\nExample:\n```text\n// The code below logs the last updated date of all the contacts whose names\n// contain \"John Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n Logger.log(contacts[i].getLastUpdated());\n}\n```\n\nExample:\n```text\n// The code below logs the maiden name of all the contacts whose names contain\n// \"Jane\"\nconst contacts = ContactsApp.getContactsByName('Jane');\nfor (const i in contacts) {\n Logger.log(contacts[i].getMaidenName());\n}\n```\n\nExample:\n```text\n// The code below logs the middle name of all the contacts whose names contain\n// \"Smith\"\nconst contacts = ContactsApp.getContactsByName('Smith');\nfor (const i in contacts) {\n Logger.log(contacts[i].getMiddleName());\n}\n```\n\nExample:\n```text\n// The code below logs the nickname of all the contacts whose names contain\n// \"John Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n Logger.log(contacts[i].getNickname());\n}\n```\n\nExample:\n```text\n// The code below logs the notes of all the contacts whose names contain \"John\n// Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n Logger.log(contacts[i].getNotes());\n}\n```\n\nExample:\n```text\n// The code below logs the phone numbers of all the contacts whose names contain\n// \"John Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n Logger.log(contacts[i].getPhones());\n}\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and logs the phone\n// numbers associated with that contact that are in the\n// ContactsApp.Field.WORK_PHONE label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst phones = contacts[0].getPhones(ContactsApp.Field.WORK_PHONE);\nfor (const i in phones) {\n Logger.log(phones[i].getPhoneNumber());\n}\n```\n\nExample:\n```text\n// The code below logs the prefix of all the contacts whose names contain \"John\n// Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n Logger.log(contacts[i].getPrefix());\n}\n```\n\nExample:\n```text\n// The code below logs the primary email address of all the contacts whose names\n// contain \"John Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n Logger.log(contacts[i].getPrimaryEmail());\n}\n```\n\nExample:\n```text\n// The code below logs the short name of all the contacts whose names contain\n// \"Johnathan\"\nconst contacts = ContactsApp.getContactsByName('Johnathan');\nfor (const i in contacts) {\n Logger.log(contacts[i].getShortName());\n}\n```\n\nExample:\n```text\n// The code below logs the suffix of all the contacts whose names contain \"John\n// Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n Logger.log(contacts[i].getSuffix());\n}\n```\n\nExample:\n```text\n// The code below logs the URLs of all the contacts whose names contain \"John\n// Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n Logger.log(contacts[i].getUrls());\n}\n```\n\nExample:\n```text\n// The code below retrieves a contact named \"John Doe\" and logs the URLs\n// associated with that contact that are in the ContactsApp.Field.WORK_WEBSITE\n// label.\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst urls = contacts[0].getUrls(ContactsApp.Field.WORK_WEBSITE);\nfor (const i in urls) {\n Logger.log(urls[i].getAddress());\n}\n```\n\nExample:\n```text\n// The code below gets all the contacts named \"John Doe\" and then removes each\n// of them from the \"Work Friends\" contact group\nconst contacts = ContactsApp.getContactsByName('John Doe');\nconst group = ContactsApp.getContactGroup('Work Friends');\nfor (const i in contacts) {\n contacts[i] = contacts[i].removeFromGroup(group);\n}\n```\n\nExample:\n```text\n// The code below changes the family name of all the contacts whose names are\n// \"John Doe\" to \"Doe-Smith\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n contacts[i].setFamilyName('Doe-Smith');\n}\n```\n\nExample:\n```text\n// The code below changes the full name of all the contacts whose names are\n// \"John Doe\" to \"Johnny Doe\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n contacts[i].setFullName('Johnny Doe');\n}\n```\n\nExample:\n```text\n// The code below changes the given name of all the contacts whose names are\n// \"John Doe\" to \"Johnny\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n contacts[i].setGivenName('Johnny');\n}\n```\n\nExample:\n```text\n// The code below sets the initials of all the contacts whose names are\n// \"Johnathan Doe\" to \"JD\"\nconst contacts = ContactsApp.getContactsByName('Johnathan Doe');\nfor (const i in contacts) {\n contacts[i].setInitials('JD');\n}\n```\n\nExample:\n```text\n// The code below changes the maiden name of all the contacts whose names are\n// \"Jane Doe\" to \"Smith\"\nconst contacts = ContactsApp.getContactsByName('Jane Doe');\nfor (const i in contacts) {\n contacts[i].setMaidenName('Smith');\n}\n```\n\nExample:\n```text\n// The code below changes the middle name of all the contacts whose names are\n// \"John Doe\" to \"Danger\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n contacts[i].setMiddleName('Danger');\n}\n```\n\nExample:\n```text\n// The code below changes the nickname of all the contacts whose names are \"John\n// Doe\" to \"JohnnyD\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n contacts[i].setNickname('JohnnyD');\n}\n```\n\nExample:\n```text\n// The code below sets the notes of all the contacts whose names are \"John Doe\"\n// to \"Met him at the hackathon\"\nconst contacts = ContactsApp.getContactsByName('John Doe');\nfor (const i in contacts) {\n contacts[i].setNotes('Met him at the hackathon');\n}\n```\n\nExample:\n```text\n// The code below sets the prefix of all the contacts whose names are \"Johnathan\n// Doe\" to \"Mr\"\nconst contacts = ContactsApp.getContactsByName('Johnathan Doe');\nfor (const i in contacts) {\n contacts[i].setPrefix('Mr');\n}\n```\n\nExample:\n```text\n// The code below changes the short name of all the contacts whose names are\n// \"Johnathan Doe\" to \"John\"\nconst contacts = ContactsApp.getContactsByName('Johnathan Doe');\nfor (const i in contacts) {\n contacts[i].setShortName('John');\n}\n```\n\nExample:\n```text\n// The code below sets the suffix of all the contacts whose names are \"Johnathan\n// Doe\" to \"Jr\"\nconst contacts = ContactsApp.getContactsByName('Johnathan Doe');\nfor (const i in contacts) {\n contacts[i].setSuffix('Jr');\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.335Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":53,"totalLines":548,"estimatedTokens":3733}}1093{"id":"doc-class_calendarevent_apps_script_google_for_devel-0c314671","source":"documentation","title":"Class CalendarEvent | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_calendarevent","text":"Example:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Adds an email notification for 15 minutes before the event.\nevent.addEmailReminder(15);\n```\n\nExample:\n```text\n// Example 1: Add a guest to one event\nfunction addAttendeeToEvent() {\n // Replace the below values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to add\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar containing\n // event\n const eventId = '123abc'; // ID of event instance\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n const event = calendar.getEventById(eventId);\n if (event === null) {\n // Event not found\n console.log('Event not found', eventId);\n return;\n }\n event.addGuest(attendeeEmail);\n}\n\n// Example 2: Add a guest to all events on a calendar within a specified\n// timeframe\nfunction addAttendeeToAllEvents() {\n // Replace the following values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to add\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar with the\n // events\n const startDate =\n new Date('YYYY-MM-DD'); // The first date to add the guest to the events\n const endDate =\n new Date('YYYY-MM-DD'); // The last date to add the guest to the events\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n // Get the events within the specified timeframe\n const calEvents = calendar.getEvents(startDate, endDate);\n console.log(calEvents.length); // Checks how many events are found\n // Loop through all events and add the attendee to each of them\n for (let i = 0; i < calEvents.length; i++) {\n const event = calEvents[i];\n event.addGuest(attendeeEmail);\n }\n}\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Adds a pop-up notification for 15 minutes before the event.\nevent.addPopupReminder(15);\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Determines whether people can add themselves as guests to the event and logs\n// it.\nconsole.log(event.anyoneCanAddSelf());\n```\n\nExample:\n```text\n// Gets an event by its ID.\n// TODO(developer): Replace the string with the ID of the event that you want to\n// delete.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Deletes the event.\nevent.deleteEvent();\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Creates an event named 'My all-day event' for May 16, 2023.\nconst event = calendar.createAllDayEvent(\n 'My all-day event',\n new Date('May 16, 2023'),\n);\n\n// Gets the event's end date and logs it.\nconst endDate = event.getAllDayEndDate();\nconsole.log(endDate);\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Creates an event named 'My all-day event' for May 16, 2023.\nconst event = calendar.createAllDayEvent(\n 'My all-day event',\n new Date('May 16, 2023'),\n);\n\n// Gets the event's start date and logs it.\nconst startDate = event.getAllDayStartDate();\nconsole.log(startDate);\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Gets the color of the calendar event and logs it.\nconst eventColor = event.getColor();\nconsole.log(eventColor);\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n\n// Gets a list of the creators of the event and logs it.\nconsole.log(event.getCreators());\n```\n\nExample:\n```text\n// Opens the calendar by using its ID.\n// To get the user's default calendar use CalendarApp.getDefault() instead.\n// TODO(developer): Replace the calendar ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 8:10 AM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 08:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the date that the\n // event was created and logs it.\n const eventCreated = event.getDateCreated();\n console.log(eventCreated);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// To get the user's default calendar use CalendarApp.getDefault() instead.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 4:00 PM and 5:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 16:00:00'),\n new Date('Feb 04, 2023 17:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the description of the\n // event.\n event.setDescription('Important meeting');\n\n // Gets the description of the event and logs it.\n const description = event.getDescription();\n console.log(description);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:00 PM and 6:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 15:00:00'),\n new Date('Feb 04, 2023 18:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, adds email reminders for\n // the user to be sent at 4 and 7 minutes before the event.\n event.addEmailReminder(4);\n event.addEmailReminder(7);\n\n // Gets the minute values for all email reminders that are set up for the user\n // for this event and logs it.\n const emailReminder = event.getEmailReminders();\n console.log(emailReminder);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:00 PM and 5:00 PM.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:00:00'),\n new Date('Feb 01, 2023 17:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the date and time at\n // which the event ends and logs it.\n console.log(event.getEndTime());\n} else {\n // If no event exists within the given time frame, logs that info to the\n // console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 18th, 2023 that takes\n// place between 1:00 PM and 2:00 PM.\nconst event = calendar.getEvents(\n new Date('Feb 18, 2023 13:00:00'),\n new Date('Feb 18, 2023 14:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the event series for\n // the event and sets the color to pale green.\n event.getEventSeries().setColor(CalendarApp.EventColor.PALE_GREEN);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the default calendar and logs all out-of-office events for the current day.\nconst calendar = CalendarApp.getDefaultCalendar();\nconst events = calendar.getEventsForDay(new Date());\nconsole.log(events.filter(e => e.getEventType() === CalendarApp.EventType.OUT_OF_OFFICE));\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Gets a guest by email address.\nconst guestEmailId = event.getGuestByEmail('alex@example.com');\n\n// If the email address corresponds to an event guest, logs the email address.\nif (guestEmailId) {\n console.log(guestEmailId.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Adds two guests to the event by using their email addresses.\nevent.addGuest('alex@example.com');\nevent.addGuest('cruz@example.com');\n\n// Gets the guests list for the event.\nconst guestList = event.getGuestList();\n\n// Loops through the list to get all the guests and logs their email addresses.\nfor (const guest of guestList) {\n console.log(guest.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 5:00 PM and 5:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 17:00:00'),\n new Date('Feb 25,2023 17:25:00'),\n )[0];\n\n// Gets the guests list for the event, including the owner of the event.\nconst guestList = event.getGuestList(true);\n\n// Loops through the list to get all the guests and logs it.\nfor (const guest of guestList) {\n console.log(guest.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 5th, 2023 that takes place\n// between 9:00 AM and 9:25 AM.\n// For an event series, use calendar.getEventSeriesById('abc123456@google.com');\n// and replace the series ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 05, 2023 09:00:00'),\n new Date('Jan 05, 2023 09:25:00'),\n )[0];\n\n// Gets the ID of the event and logs it.\nconsole.log(event.getId());\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:00 PM and 5:00 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:00:00'),\n new Date('Feb 01, 2023 17:00:00'),\n )[0];\n\n// Gets the date the event was last updated and logs it.\nconst eventUpdatedDate = event.getLastUpdated();\nconsole.log(eventUpdatedDate);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the location of the\n // event to Mumbai.\n event.setLocation('Mumbai');\n\n // Gets the location of the event and logs it.\n const eventLocation = event.getLocation();\n console.log(eventLocation);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the event status of\n // the effective user and logs it.\n const myStatus = event.getMyStatus();\n console.log(myStatus.toString());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 25th, 2023 that takes\n// place between 4:00 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 25,2023 16:00:00'),\n new Date('Feb 25,2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the ID of the calendar\n // where the event was originally created and logs it.\n const calendarId = event.getOriginalCalendarId();\n console.log(calendarId);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:05 PM and 5:35 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 17:05:00'),\n new Date('Feb 04, 2023 17:35:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, adds two pop-up reminders\n // to the event. The first reminder pops up 5 minutes before the event starts\n // and the second reminder pops up 3 minutes before the event starts.\n event.addPopupReminder(3);\n event.addPopupReminder(5);\n\n // Gets the minute values for all pop-up reminders for the event and logs it.\n const popUpReminder = event.getPopupReminders();\n console.log(popUpReminder);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\n// Gets the date and time at which this calendar event begins and logs it.\nconst startTime = event.getStartTime();\nconsole.log(startTime);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@example.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, logs the title of the\n // event.\n console.log(event.getTitle());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Gets the first event from the default calendar for today.\nconst today = new Date();\nconst event = CalendarApp.getDefaultCalendar().getEventsForDay(today)[0];\n// Gets the event's transparency and logs it.\nconst transparency = event.getTransparency();\nLogger.log(transparency);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, gets the visibility of the\n // event and logs it.\n const eventVisibility = event.getVisibility();\n console.log(eventVisibility.toString());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether guests\n // can invite other guests and logs it.\n console.log(event.guestsCanInviteOthers());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // guests can't modify it.\n event.setGuestsCanModify(false);\n\n // Determines whether guests can modify the event and logs it.\n console.log(event.guestsCanModify());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether guests\n // can see other guests and logs it.\n console.log(event.guestsCanSeeGuests());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\n// Determines whether this event is an all-day event and logs it.\nconsole.log(event.isAllDayEvent());\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether you're\n // the owner of the event and logs it.\n console.log(event.isOwnedByMe());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have view access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for Januart 31st, 2023 that takes\n// place between 9:00 AM and 10:00 AM.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:00:00'),\n new Date('Jan 31, 2023 10:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, determines whether the\n // event is part of an event series and logs it.\n console.log(event.isRecurringEvent());\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 1,2023 16:10:00'),\n new Date('Feb 1,2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, removes all reminders from\n // the event.\n event.removeAllReminders();\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Example 1: Remove a guest from one event\nfunction removeGuestFromEvent() {\n // Replace the below values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to remove\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar containing\n // event\n const eventId = '123abc'; // ID of event instance\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n const event = calendar.getEventById(eventId);\n if (event === null) {\n // Event not found\n console.log('Event not found', eventId);\n return;\n }\n event.removeGuest(attendeeEmail);\n}\n\n// Example 2: Remove a guest from all events on a calendar within a specified\n// timeframe\nfunction removeGuestFromAllEvents() {\n // Replace the following values with your own\n const attendeeEmail =\n 'user@example.com'; // Email address of the person you need to remove\n const calendarId =\n 'calendar_123@group.calendar.google.com'; // ID of calendar with the\n // events\n const startDate = new Date(\n 'YYYY-MM-DD'); // The first date to remove the guest from the events\n const endDate = new Date(\n 'YYYY-MM-DD'); // The last date to remove the attendee from the events\n\n const calendar = CalendarApp.getCalendarById(calendarId);\n if (calendar === null) {\n // Calendar not found\n console.log('Calendar not found', calendarId);\n return;\n }\n // Get the events within the specified timeframe\n const calEvents = calendar.getEvents(startDate, endDate);\n console.log(calEvents.length); // Checks how many events are found\n // Loop through all events and remove the attendee from each of them\n for (let i = 0; i < calEvents.length; i++) {\n const event = calEvents[i];\n event.removeGuest(attendeeEmail);\n }\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 1, 2023 16:10:00'),\n new Date('Feb 1, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, resets the reminders using\n // the calendar's default settings.\n event.resetRemindersToDefault();\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 17th, 2023 that takes\n// place between 4:00 PM and 5:00 PM.\nconst event = calendar.getEvents(\n new Date('Feb 17, 2023 16:00:00'),\n new Date('Feb 17, 2023 17:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the date of the event\n // and updates it to an all-day event.\n event.setAllDayDate(new Date('Feb 17, 2023'));\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 18th, 2023 that takes\n// place between 4:00 PM and 5:00 PM.\nconst event = calendar.getEvents(\n new Date('Feb 18, 2023 16:00:00'),\n new Date('Feb 18, 2023 17:00:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event to be an\n // all-day event from Feb 18th, 2023 until Feb 25th, 2023. Applying this\n // method changes a regular event into an all-day event.\n event.setAllDayDates(new Date('Feb 18, 2023'), new Date('Feb 25, 2023'));\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 15th, 2023 that takes\n// place between 3:30 PM and 4:30 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 15, 2023 15:30:00'),\n new Date('Feb 15, 2023 16:30:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // non-guests can't add themselves to the event.\n event.setAnyoneCanAddSelf(false);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the color of the\n // calendar event to green.\n event.setColor(CalendarApp.EventColor.GREEN);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 4th, 2023 that takes\n// place between 5:05 PM and 5:35 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 04, 2023 17:05:00'),\n new Date('Feb 04, 2023 17:35:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the description of the\n // event to 'Meeting.'\n event.setDescription('Meeting');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own. You must have edit access to\n// the calendar.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 9:35 AM and 9:40 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 09:35:00'),\n new Date('Feb 01, 2023 09:40:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event so that\n // guests can invite other guests.\n event.setGuestsCanInviteOthers(true);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the location of the\n // event to Noida.\n event.setLocation('Noida');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for February 1st, 2023 that takes\n// place between 4:10 PM and 4:25 PM. For an event series, use\n// calendar.getEventSeriesById('abc123456@google.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Feb 01, 2023 16:10:00'),\n new Date('Feb 01, 2023 16:25:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, sets the event status for\n // the current user to maybe.\n event.setMyStatus(CalendarApp.GuestStatus.MAYBE);\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Declares a start time of 11:00 AM on February 20th, 2023 and an end time of\n// 12:00 PM on February 20th, 2023.\nconst startTime = new Date('Feb 20,2023 11:00:00');\nconst endTime = new Date('Feb 20, 2023 12:00:00');\n\n// Creates an all-day event on February 20th, 2023.\nconst event = calendar.createAllDayEvent('Meeting', new Date('Feb 20,2023'));\n\n// Updates the all-day event to a regular event by setting a start and end time\n// for the event.\nevent.setTime(startTime, endTime);\n```\n\nExample:\n```text\n// Opens the calendar by its ID. You must have edit access to the calendar.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Gets the first event from the calendar for January 31st, 2023 that takes\n// place between 9:05 AM and 9:15 AM. For an event series, use\n// calendar.getEventSeriesById('abc123456@example.com'); and replace the series\n// ID with your own.\nconst event = calendar.getEvents(\n new Date('Jan 31, 2023 09:05:00'),\n new Date('Jan 31, 2023 09:15:00'),\n )[0];\n\nif (event) {\n // If an event exists within the given time frame, changes its title to\n // Event1.\n event.setTitle('Event1');\n} else {\n // If no event exists within the given time frame, logs that information to\n // the console.\n console.log('No events exist for the specified range');\n}\n```\n\nExample:\n```text\n// Gets an event by its ID. For an event series, use getEventSeriesById(iCalId)\n// instead.\n// TODO(developer): Replace the string with the event ID that you want to get.\nconst event = CalendarApp.getEventById('abc123456');\n// Sets the event's transparency to TRANSPARENT.\nevent.setTransparency(CalendarApp.EventTransparency.TRANSPARENT);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.341Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":48,"totalLines":1209,"estimatedTokens":9810}}1094{"id":"doc-manage_learning_goals_google_classroom_google_fo-fe92429d","source":"documentation","title":"Manage learning goals | Google Classroom | Google for Developers","url":"https://developers.google.com/workspace/classroom/guides/manage-learning-goals","text":"Example:\n```text\nservice = build(\"classroom\", \"v1\", credentials=creds)\ncourse = {\n \"learning_standard_settings\": {\n # Each Course can have up to 5 learning standard sets.\n \"standard_sets\": [\n # Construct a GUID for the NYS Next Gen Math Standards top-level node.\n # This is a top-level non-leaf node, so it only has a Document ID.\n {\n \"case_guid\": {\n \"case_document_id\": \"c649d172-d7cb-11e8-824f-0242ac160002\"\n }\n },\n # Construct a GUID for the AAS ELA21.AAS.K Standard node.\n # This is a mid-level non-leaf node, so it has both Document and Item IDs.\n {\n \"case_guid\": {\n \"case_document_id\": \"bfc264b3-b4d1-4780-84ff-26b12e6945a6\",\n \"case_item_id\": \"ada2cc30-7d6d-494e-8c6b-c998bae98f0b\",\n }\n },\n ]\n }\n}\n\ncourse = (\n service.courses()\n .patch(\n id=course_id,\n updateMask=\"learningStandardSettings\",\n body=course,\n # Specify the preview version while the feature is in Developer Preview.\n # Learning standards and goals are supported in V1_20260316_PREVIEW and\n # later.\n previewVersion=\"V1_20260316_PREVIEW\")\n .execute()\n)\nprint(f\"Course updated: {course.get('name')}\")\n```\n\nExample:\n```text\n{\n \"id\": \"123456789\",\n \"name\": \"LSS test class\",\n \"learning_standard_settings\": {\n \"standard_sets\": [{\n \"case_guid\": {\n \"case_document_id\": \"c649d172-d7cb-11e8-824f-0242ac160002\"\n },\n \"document_title\": \"New York State Next Generation Mathematics Learning Standards\",\n \"source_data_availability_status\": \"LEARNING_STANDARD_AVAILABLE\"\n },\n {\n \"case_guid\": {\n \"case_document_id\": \"bfc264b3-b4d1-4780-84ff-26b12e6945a6\",\n \"case_item_id\": \"d52217c0-1c5b-4d92-9664-d0792944f3fe\"\n },\n \"document_title\": \"AAS English Language Arts (2021)\",\n \"standard_title\": \"With prompting and support, actively listen and speak.\",\n \"source_data_availability_status\": \"LEARNING_STANDARD_AVAILABLE\"\n }]\n }\n}\n```\n\nExample:\n```text\n# Create a CourseWork assignment with two attached learning goals.\nservice = build(\"classroom\", \"v1\", credentials=creds)\ncoursework = {\n \"title\": \"Ant colonies\",\n \"description\": \"Read the article about ant colonies and complete the quiz.\",\n \"materials\": [\n {\"link\": {\"url\": \"http://example.com/ant-colonies\"}},\n {\"link\": {\"url\": \"http://example.com/ant-quiz\"}},\n ],\n \"workType\": \"ASSIGNMENT\",\n \"state\": \"PUBLISHED\",\n # Each CourseWork can have up to 10 learning goals.\n \"learning_goals\": [\n {\n \"learning_standard_info\": {\n # Construct a GUID for the ELA21.AAS.11.11c Standard node.\n # This is a non-root node, so it has both Document and Item IDs.\n \"case_guid\": {\n \"case_document_id\": \"bfc264b3-b4d1-4780-84ff-26b12e6945a6\",\n \"case_item_id\": \"6d922f1f-b580-42a1-b6d2-5ee5b39c561d\",\n }\n }\n },\n {\n \"learning_standard_info\": {\n # Construct a GUID for the ELA21.AAS.11.22a Standard node.\n # This is a non-root node, so it has both Document and Item IDs.\n \"case_guid\": {\n \"case_document_id\": \"bfc264b3-b4d1-4780-84ff-26b12e6945a6\",\n \"case_item_id\": \"a684609a-c3ec-42da-8b7f-7a325b31ea4a\",\n }\n }\n },\n ],\n}\n\ncoursework = (\n service.courses()\n .courseWork()\n .create(\n courseId=course_id,\n body=coursework,\n # Specify the preview version while the feature is in Developer Preview.\n # Learning standards and goals are supported in V1_20260316_PREVIEW and\n # later.\n previewVersion=\"V1_20260316_PREVIEW\")\n .execute()\n)\nprint(f\"Assignment created with ID {coursework.get('id')}\")\n```\n\nExample:\n```text\n# Create a new Rubric with a learning goal attached to each criterion.\nservice = build(\"classroom\", \"v1\", credentials=creds)\nbody = {\n \"criteria\": [\n {\n \"title\": \"Argument\",\n \"description\": \"How well structured your argument is.\",\n \"levels\": [\n {\n \"title\": \"Convincing\",\n \"description\": \"A compelling case is made.\",\n \"points\": 30,\n },\n {\n \"title\": \"Passable\",\n \"description\": \"Missing some evidence.\",\n \"points\": 20,\n },\n {\n \"title\": \"Needs Work\",\n \"description\": \"Not enough strong evidence.\",\n \"points\": 0,\n },\n ],\n # Each Criterion can have one learning goal.\n \"learning_goal\": {\n \"learning_standard_info\": {\n # Construct a GUID for the ELA21.AAS.11.11c Standard node.\n # This is a non-root node, so it has both Document and Item IDs.\n \"case_guid\": {\n \"case_document_id\": \"bfc264b3-b4d1-4780-84ff-26b12e6945a6\",\n \"case_item_id\": \"6d922f1f-b580-42a1-b6d2-5ee5b39c561d\",\n }\n }\n },\n },\n {\n \"title\": \"Spelling\",\n \"description\": \"How well you spelled all the words.\",\n \"levels\": [\n {\"title\": \"Perfect\", \"description\": \"No mistakes.\", \"points\": 20},\n {\"title\": \"Great\", \"description\": \"A mistake or two.\", \"points\": 15},\n {\"title\": \"Needs Work\", \"description\": \"Many mistakes.\", \"points\": 5},\n ],\n # Each Criterion can have one learning goal.\n \"learning_goal\": {\n \"learning_standard_info\": {\n # Construct a GUID for the ELA21.AAS.11.22a Standard node.\n # This is a non-root node, so it has both Document and Item IDs.\n \"case_guid\": {\n \"case_document_id\": \"bfc264b3-b4d1-4780-84ff-26b12e6945a6\",\n \"case_item_id\": \"a684609a-c3ec-42da-8b7f-7a325b31ea4a\",\n }\n }\n },\n },\n ]\n}\n\nrubric = (\n service.courses()\n .courseWork()\n .rubrics()\n .create(\n courseId=course_id,\n courseWorkId=coursework_id,\n body=body,\n # Specify the preview version while the feature is in Developer Preview.\n # Learning standards and goals are supported in V1_20260316_PREVIEW and\n # later.\n previewVersion=\"V1_20260316_PREVIEW\")\n .execute()\n)\nprint(f\"Rubric created with ID {rubric.get('id')}\")\n```\n\nExample:\n```text\n{\n \"course_id\": \"123456789\",\n \"id\": \"987654321\",\n \"title\": \"Test ELA Assignment\",\n \"state\": \"PUBLISHED\",\n \"learning_goals\": [\n {\n \"learning_standard_info\": {\n \"standard_code\": \"ELA21.AAS.11.11c\",\n \"document_title\": \"AAS English Language Arts (2021)\",\n \"case_guid\": {\n \"case_document_id\": \"bfc264b3-b4d1-4780-84ff-26b12e6945a6\",\n \"case_item_id\": \"6d922f1f-b580-42a1-b6d2-5ee5b39c561d\",\n },\n \"source_data_availability_status\": \"LEARNING_STANDARD_AVAILABLE\"\n },\n \"title\": \"Compose argumentative texts by stating a topic, providing reasons that support the argument, and providing an appropriate conclusion related to the topic.\"\n },\n {\n \"learning_standard_info\": {\n \"standard_code\": \"ELA21.AAS.11.22a\",\n \"document_title\": \"AAS English Language Arts (2021)\",\n \"case_guid\": {\n \"case_document_id\": \"bfc264b3-b4d1-4780-84ff-26b12e6945a6\",\n \"case_item_id\": \"a684609a-c3ec-42da-8b7f-7a325b31ea4a\",\n },\n \"source_data_availability_status\": \"LEARNING_STANDARD_AVAILABLE\"\n },\n \"title\": \"Identify a sentence that uses correct capitalization (i.e., beginning of sentence, names, cities, states, countries, towns, titles, days, months).\"\n }]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.342Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":234,"estimatedTokens":2032}}1095{"id":"doc-class_gmailapp_apps_script_google_for_developers-3bd728b4","source":"documentation","title":"Class GmailApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_gmailapp","text":"Example:\n```text\n// The code below creates a draft email with the current date and time.\nconst now = new Date();\nGmailApp.createDraft(\n 'mike@example.com',\n 'current time',\n `The time is: ${now.toString()}`,\n);\n```\n\nExample:\n```text\n// Create a draft email with a file from Google Drive attached as a PDF.\nconst file = DriveApp.getFileById('1234567890abcdefghijklmnopqrstuvwxyz');\nGmailApp.createDraft(\n 'mike@example.com',\n 'Attachment example',\n 'Please see attached file.',\n {\n attachments: [file.getAs(MimeType.PDF)],\n name: 'Automatic Emailer Script',\n },\n);\n```\n\nExample:\n```text\n// Creates the label @FOO and logs label: FOO\nLogger.log(`label: ${GmailApp.createLabel('FOO')}`);\n```\n\nExample:\n```text\n// Have to get the label by name first\nconst label = GmailApp.getUserLabelByName('FOO');\nGmailApp.deleteLabel(label);\n```\n\nExample:\n```text\n// Log the aliases for this Gmail account and send an email as the first one.\nconst me = Session.getActiveUser().getEmail();\nconst aliases = GmailApp.getAliases();\nLogger.log(aliases);\nif (aliases.length > 0) {\n GmailApp.sendEmail(me, 'From an alias', 'A message from an alias!', {\n from: aliases[0],\n });\n} else {\n GmailApp.sendEmail(me, 'No aliases found', 'You have no aliases.');\n}\n```\n\nExample:\n```text\n// Get the first draft message in your drafts folder\nconst draft = GmailApp.getDrafts()[0];\n// Get its ID\nconst draftId = draft.getId();\n// Now fetch the same draft using that ID.\nconst draftById = GmailApp.getDraft(draftId);\n// Should always log true as they should be the same message\nLogger.log(\n draft.getMessage().getSubject() === draftById.getMessage().getSubject(),\n);\n```\n\nExample:\n```text\n// Logs the number of draft messages\nconst drafts = GmailApp.getDraftMessages();\nLogger.log(drafts.length);\n```\n\nExample:\n```text\nconst drafts = GmailApp.getDrafts();\nfor (let i = 0; i < drafts.length; i++) {\n Logger.log(drafts[i].getId());\n}\n```\n\nExample:\n```text\n// Log the subject lines of your Inbox\nconst threads = GmailApp.getInboxThreads();\nfor (let i = 0; i < threads.length; i++) {\n Logger.log(threads[i].getFirstMessageSubject());\n}\n```\n\nExample:\n```text\n// Log the subject lines of up to the first 50 emails in your Inbox\nconst threads = GmailApp.getInboxThreads(0, 50);\nfor (let i = 0; i < threads.length; i++) {\n Logger.log(threads[i].getFirstMessageSubject());\n}\n```\n\nExample:\n```text\nLogger.log(`Messages unread in inbox: ${GmailApp.getInboxUnreadCount()}`);\n```\n\nExample:\n```text\n// Get the first message in the first thread of your inbox\nconst message = GmailApp.getInboxThreads(0, 1)[0].getMessages()[0];\n// Get its ID\nconst messageId = message.getId();\n// Now fetch the same message using that ID.\nconst messageById = GmailApp.getMessageById(messageId);\n// Should always log true as they should be the same message\nLogger.log(message.getSubject() === messageById.getSubject());\n```\n\nExample:\n```text\n// Log all the subject lines in the first thread of your inbox\nconst thread = GmailApp.getInboxThreads(0, 1)[0];\nconst messages = GmailApp.getMessagesForThread(thread);\nfor (let i = 0; i < messages.length; i++) {\n Logger.log(`subject: ${messages[i].getSubject()}`);\n}\n```\n\nExample:\n```text\n// Log the subject lines of all messages in the first two threads of your inbox\nconst thread = GmailApp.getInboxThreads(0, 2);\nconst messages = GmailApp.getMessagesForThreads(thread);\nfor (let i = 0; i < messages.length; i++) {\n for (let j = 0; j < messages[i].length; j++) {\n Logger.log(`subject: ${messages[i][j].getSubject()}`);\n }\n}\n```\n\nExample:\n```text\nLogger.log(\n `# of messages in your Priority Inbox: ${\n GmailApp.getPriorityInboxThreads().length}`,\n);\n```\n\nExample:\n```text\n// Will log some number 2 or less\nLogger.log(\n `# of messages in your Priority Inbox: ${\n GmailApp.getPriorityInboxThreads(0, 2).length}`,\n);\n```\n\nExample:\n```text\nLogger.log(\n `Number of unread emails in your Priority Inbox : ${\n GmailApp.getPriorityInboxUnreadCount()}`,\n);\n```\n\nExample:\n```text\nLogger.log(`# of total spam threads: ${GmailApp.getSpamThreads().length}`);\n```\n\nExample:\n```text\n// Will log a number at most 5\nLogger.log(`# of total spam threads: ${GmailApp.getSpamThreads(0, 5).length}`);\n```\n\nExample:\n```text\n// Unless you actually read stuff in your spam folder, this should be the same\n// as the number of messages in your spam folder.\nLogger.log(`# unread threads that are spam: ${GmailApp.getSpamUnreadCount()}`);\n```\n\nExample:\n```text\n// Logs the number of starred threads\nLogger.log(`# Starred threads: ${GmailApp.getStarredThreads().length}`);\n```\n\nExample:\n```text\n// Logs the number of starred threads to a maximum of 5\nLogger.log(`# Starred threads: ${GmailApp.getStarredThreads(0, 5).length}`);\n```\n\nExample:\n```text\nLogger.log(`# unread and starred: ${GmailApp.getStarredUnreadCount()}`);\n```\n\nExample:\n```text\n// Gets the first inbox thread.\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\n// Gets the same thread by ID.\nconst threadById = GmailApp.getThreadById(firstThread.getId());\n// Verifies that they are the same.\nconsole.log(\n firstThread.getFirstMessageSubject() ===\n threadById.getFirstMessageSubject(),\n);\n```\n\nExample:\n```text\nLogger.log(`# of total trash threads: ${GmailApp.getTrashThreads().length}`);\n```\n\nExample:\n```text\n// Will log a number at most 5\nLogger.log(\n `# of total trash threads: ${GmailApp.getTrashThreads(0, 5).length}`,\n);\n```\n\nExample:\n```text\nconst labelObject = GmailApp.getUserLabelByName('myLabel');\n```\n\nExample:\n```text\n// Logs all of the names of your labels\nconst labels = GmailApp.getUserLabels();\nfor (let i = 0; i < labels.length; i++) {\n Logger.log(`label: ${labels[i].getName()}`);\n}\n```\n\nExample:\n```text\n// Mark the first message in the first thread of your inbox as read\nconst message = GmailApp.getInboxThreads(0, 1)[0].getMessages()[0];\nGmailApp.markMessageRead(message);\n```\n\nExample:\n```text\n// Mark the first message in the first thread of your inbox as unread\nconst message = GmailApp.getInboxThreads(0, 1)[0].getMessages()[0];\nGmailApp.markMessageUnread(message);\n```\n\nExample:\n```text\n// Mark first three messages in the first inbox thread as read.\n// Assumes that the first inbox thread has 3 messages in it.\nconst threadMessages = GmailApp.getInboxThreads(0, 1)[0].getMessages();\nconst messages = [threadMessages[0], threadMessages[1], threadMessages[2]];\nGmailApp.markMessagesRead(messages);\n```\n\nExample:\n```text\n// Mark first three messages in the first inbox thread as unread.\n// Assumes that the first inbox thread has 3 messages in it\nconst threadMessages = GmailApp.getInboxThreads(0, 1)[0].getMessages();\nconst messages = [threadMessages[0], threadMessages[1], threadMessages[2]];\nGmailApp.markMessagesUnread(messages);\n```\n\nExample:\n```text\n// Marks first inbox thread as important\nconst thread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.markThreadImportant(thread);\n```\n\nExample:\n```text\n// Marks first inbox thread as read\nconst thread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.markThreadRead(thread);\n```\n\nExample:\n```text\n// Marks first inbox thread as unimportant\nconst thread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.markThreadUnimportant(thread);\n```\n\nExample:\n```text\n// Marks first inbox thread as unread\nconst thread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.markThreadUnread(thread);\n```\n\nExample:\n```text\n// Marks first two threads in inbox as important\nconst threads = GmailApp.getInboxThreads(0, 2);\nGmailApp.markThreadsImportant(threads);\n```\n\nExample:\n```text\n// Marks first two threads in inbox as read\nconst threads = GmailApp.getInboxThreads(0, 2);\nGmailApp.markThreadsRead(threads);\n```\n\nExample:\n```text\n// Marks first two threads in inbox as unimportant\nconst threads = GmailApp.getInboxThreads(0, 2);\nGmailApp.markThreadsUnimportant(threads);\n```\n\nExample:\n```text\n// Marks first two threads in inbox as unread\nconst threads = GmailApp.getInboxThreads(0, 2);\nGmailApp.markThreadsUnread(threads);\n```\n\nExample:\n```text\n// Move the first message in your inbox to trash\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst firstMessage = firstThread.getMessages()[0];\nGmailApp.moveMessageToTrash(firstMessage);\n```\n\nExample:\n```text\n// Move first two messages in your inbox to trash\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst messages = firstThread.getMessages();\nconst toDelete = [messages[0], messages[1]];\nGmailApp.moveMessagesToTrash(toDelete);\n```\n\nExample:\n```text\n// Archive the first thread in your inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.moveThreadToArchive(firstThread);\n```\n\nExample:\n```text\n// Find a thread not already in your inbox\nconst thread = GmailApp.search('-in:inbox')[0]; // Get the first one\nGmailApp.moveThreadToInbox(thread);\n```\n\nExample:\n```text\n// Tag first thread in inbox as spam\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.moveThreadToSpam(firstThread);\n```\n\nExample:\n```text\n// Move first thread in inbox to trash\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nGmailApp.moveThreadToTrash(firstThread);\n```\n\nExample:\n```text\n// Move first two threads in your inbox to the archive\nconst firstTwoThreads = GmailApp.getInboxThreads(0, 2);\nGmailApp.moveThreadsToArchive(firstTwoThreads);\n```\n\nExample:\n```text\n// Find two threads not already in your inbox\nconst firstTwoThreads = GmailApp.search('-in:inbox', 0, 2);\nGmailApp.moveThreadsToInbox(firstTwoThreads);\n```\n\nExample:\n```text\n// Move first two threads in your inbox to spam\nconst firstTwoThreads = GmailApp.getInboxThreads(0, 2);\nGmailApp.moveThreadsToSpam(firstTwoThreads);\n```\n\nExample:\n```text\n// Move first two threads in your inbox to trash\nconst firstTwoThreads = GmailApp.getInboxThreads(0, 2);\nGmailApp.moveThreadsToTrash(firstTwoThreads);\n```\n\nExample:\n```text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst firstMessage = firstThread.getMessages()[0];\n// ...Do something that may take a while here....\nGmailApp.refreshMessage(firstMessage);\n// ...Do more stuff with firstMessage...\n```\n\nExample:\n```text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst coupleOfMessages = firstThread.getMessages().slice(0, 2);\n// ...Do something that may take a while here....\nGmailApp.refreshMessages(coupleOfMessages);\n// ...Do more stuff with coupleOfMessages...\n```\n\nExample:\n```text\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\n// ...Do something that may take a while here....\nGmailApp.refreshThread(firstThread);\n// ... Do more stuff with the thread ...\n```\n\nExample:\n```text\nconst threads = GmailApp.getInboxThreads(0, 3);\n// ...Do something that may take a while here....\nGmailApp.refreshThreads(threads);\n// ... Do more stuff with threads ...\n```\n\nExample:\n```text\n// Find starred messages with subject IMPORTANT\nconst threads = GmailApp.search('is:starred subject:\"IMPORTANT\"');\n```\n\nExample:\n```text\n// Find starred messages with subject IMPORTANT and return second batch of 10.\n// Assumes there are at least 11 of them, otherwise this will return an empty\n// array.\nconst threads = GmailApp.search('is:starred subject:\"IMPORTANT\"', 10, 10);\n```\n\nExample:\n```text\n// The code below will send an email with the current date and time.\nconst now = new Date();\nGmailApp.sendEmail(\n 'mike@example.com',\n 'current time',\n `The time is: ${now.toString()}`,\n);\n```\n\nExample:\n```text\n// Send an email with a file from Google Drive attached as a PDF.\nconst file = DriveApp.getFileById('1234567890abcdefghijklmnopqrstuvwxyz');\nGmailApp.sendEmail(\n 'mike@example.com',\n 'Attachment example',\n 'Please see the attached file.',\n {\n attachments: [file.getAs(MimeType.PDF)],\n name: 'Automatic Emailer Script',\n },\n);\n```\n\nExample:\n```text\nfunction handleAddonActionEvent(e) {\n GmailApp.setCurrentMessageAccessToken(e.messageMetadata.accessToken);\n const mailMessage = GmailApp.getMessageById(e.messageMetadata.messageId);\n // Do something with mailMessage\n}\n```\n\nExample:\n```text\n// Stars the first message in the first thread in your inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nGmailApp.starMessage(message);\n```\n\nExample:\n```text\n// Stars the first three messages in the first thread in your inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst coupleOfMessages = firstThread.getMessages().slice(0, 3);\nGmailApp.starMessages(coupleOfMessages);\n```\n\nExample:\n```text\n// Unstars the first message in the first thread in your inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst message = firstThread.getMessages()[0];\nGmailApp.unstarMessage(message);\n```\n\nExample:\n```text\n// Unstars the first three messages in the first thread in your inbox\nconst firstThread = GmailApp.getInboxThreads(0, 1)[0];\nconst coupleOfMessages = firstThread.getMessages().slice(0, 3);\nGmailApp.unstarMessages(coupleOfMessages);\n```\n\nExample:\n```text\nconst threads = GmailApp.getChatThreads();\nLogger.log(`# of chat threads: ${threads.length}`);\n```\n\nExample:\n```text\n// Get first 50 chat threads\nconst threads = GmailApp.getChatThreads(0, 50);\n// Will log no more than 50.0\nLogger.log(threads.length);\nLogger.log(threads[0].getFirstMessageSubject());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.346Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":65,"totalLines":532,"estimatedTokens":3315}}1096{"id":"doc-preview_links_from_google_books_with_smart_chips-281449b5","source":"documentation","title":"Preview links from Google Books with smart chips | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/samples/preview-links-google-books","text":"Example:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/workspace.linkpreview\",\n \"https://www.googleapis.com/auth/script.external_request\"\n ],\n \"urlFetchWhitelist\": [\"https://www.googleapis.com/books/v1/volumes/\"],\n \"addOns\": {\n \"common\": {\n \"name\": \"Preview Books Add-on\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/library-icon.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#dd4b39\"\n }\n },\n \"docs\": {\n \"linkPreviewTriggers\": [\n {\n \"runFunction\": \"bookLinkPreview\",\n \"patterns\": [\n {\n \"hostPattern\": \"*.google.*\",\n \"pathPrefix\": \"books\"\n },\n {\n \"hostPattern\": \"*.google.*\",\n \"pathPrefix\": \"books/edition\"\n }\n ],\n \"labelText\": \"Book\",\n \"logoUrl\": \"https://developers.google.com/workspace/add-ons/images/book-icon.png\",\n \"localizedLabelText\": {\n \"es\": \"Libros\"\n }\n }\n ]\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2025 Google LLC\n *\n * 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 * http://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.\n */\n\nfunction getBook(id) {\n const apiKey = \"YOUR_API_KEY\"; // Replace with your API key\n const apiEndpoint = `https://www.googleapis.com/books/v1/volumes/${id}?key=${apiKey}&country=US`;\n const response = UrlFetchApp.fetch(apiEndpoint);\n return JSON.parse(response);\n}\n\nfunction bookLinkPreview(event) {\n if (event.docs.matchedUrl.url) {\n const segments = event.docs.matchedUrl.url.split(\"/\");\n const volumeID = segments[segments.length - 1];\n\n const bookData = getBook(volumeID);\n const bookTitle = bookData.volumeInfo.title;\n const bookDescription = bookData.volumeInfo.description;\n const bookImage = bookData.volumeInfo.imageLinks.small;\n const bookAuthors = bookData.volumeInfo.authors;\n const bookPageCount = bookData.volumeInfo.pageCount;\n\n const previewHeader = CardService.newCardHeader()\n .setSubtitle(`By ${bookAuthors}`)\n .setTitle(bookTitle);\n\n const previewPages = CardService.newDecoratedText()\n .setTopLabel(\"Page count\")\n .setText(bookPageCount);\n\n const previewDescription = CardService.newDecoratedText()\n .setTopLabel(\"About this book\")\n .setText(bookDescription)\n .setWrapText(true);\n\n const previewImage = CardService.newImage()\n .setAltText(\"Image of book cover\")\n .setImageUrl(bookImage);\n\n const buttonBook = CardService.newTextButton()\n .setText(\"View book\")\n .setOpenLink(CardService.newOpenLink().setUrl(event.docs.matchedUrl.url));\n\n const cardSectionBook = CardService.newCardSection()\n .addWidget(previewImage)\n .addWidget(previewPages)\n .addWidget(CardService.newDivider())\n .addWidget(previewDescription)\n .addWidget(buttonBook);\n\n return CardService.newCardBuilder()\n .setHeader(previewHeader)\n .addSection(cardSectionBook)\n .build();\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.346Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":119,"estimatedTokens":904}}1097{"id":"doc-class_calendarapp_apps_script_google_for_develop-36bb6e3c","source":"documentation","title":"Class CalendarApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/class_calendarapp","text":"Example:\n```text\n// Creates an all-day event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the Woodstock festival (August 15th to 17th) and\n// logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Woodstock Festival',\n new Date('August 15, 1969'),\n new Date('August 18, 1969'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the Woodstock festival (August 15th to 17th) and\n// logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Woodstock Festival',\n new Date('August 15, 1969'),\n new Date('August 18, 1969'),\n {location: 'Bethel, White Lake, New York, U.S.', sendInvites: true},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an all-day event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createAllDayEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969'),\n {location: 'The Moon'},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a no-meetings day, taking place every Wednesday\n// in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries(\n 'No Meetings',\n new Date('January 2, 2013 03:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014')),\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a no-meetings day, taking place every Wednesday\n// in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries(\n 'No Meetings',\n new Date('January 2, 2013 03:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014')),\n {guests: 'everyone@example.com'},\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates a new calendar named \"Travel Plans\".\nconst calendar = CalendarApp.createCalendar('Travel Plans');\nLogger.log(\n 'Created the calendar \"%s\", with the ID \"%s\".',\n calendar.getName(),\n calendar.getId(),\n);\n```\n\nExample:\n```text\n// Creates a new calendar named \"Travel Plans\" with a description and color.\nconst calendar = CalendarApp.createCalendar('Travel Plans', {\n description: 'A calendar to plan my travel schedule.',\n color: CalendarApp.Color.BLUE,\n});\nLogger.log(\n 'Created the calendar \"%s\", with the ID \"%s\".',\n calendar.getName(),\n calendar.getId(),\n);\n```\n\nExample:\n```text\n// Creates an event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:00:00 UTC'),\n new Date('July 21, 1969 21:00:00 UTC'),\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event for the moon landing and logs the ID.\nconst event = CalendarApp.getDefaultCalendar().createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:00:00 UTC'),\n new Date('July 20, 1969 21:00:00 UTC'),\n {location: 'The Moon'},\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates a new event and logs its ID.\nconst event = CalendarApp.getDefaultCalendar().createEventFromDescription(\n 'Lunch with Mary, Friday at 1PM',\n);\nLogger.log(`Event ID: ${event.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a team meeting, taking place every Tuesday and\n// Thursday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createEventSeries(\n 'Team Meeting',\n new Date('January 1, 2013 03:00:00 PM EST'),\n new Date('January 1, 2013 04:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekdays(\n [CalendarApp.Weekday.TUESDAY, CalendarApp.Weekday.THURSDAY])\n .until(new Date('January 1, 2014')),\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Creates an event series for a team meeting, taking place every Tuesday and\n// Thursday in 2013.\nconst eventSeries = CalendarApp.getDefaultCalendar().createEventSeries(\n 'Team Meeting',\n new Date('January 1, 2013 03:00:00 PM EST'),\n new Date('January 1, 2013 04:00:00 PM EST'),\n CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekdays(\n [CalendarApp.Weekday.TUESDAY, CalendarApp.Weekday.THURSDAY])\n .until(new Date('January 1, 2014')),\n {location: 'Conference Room'},\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Determines how many calendars the user can access.\nconst calendars = CalendarApp.getAllCalendars();\nLogger.log(\n 'This user owns or is subscribed to %s calendars.',\n calendars.length,\n);\n```\n\nExample:\n```text\n// Determines how many calendars the user owns.\nconst calendars = CalendarApp.getAllOwnedCalendars();\nLogger.log('This user owns %s calendars.', calendars.length);\n```\n\nExample:\n```text\n// Gets the public calendar \"US Holidays\" by ID.\nconst calendar = CalendarApp.getCalendarById(\n 'en.usa#holiday@group.v.calendar.google.com',\n);\nLogger.log('The calendar is named \"%s\".', calendar.getName());\n```\n\nExample:\n```text\n// Gets the public calendar named \"US Holidays\".\nconst calendars = CalendarApp.getCalendarsByName('US Holidays');\nLogger.log('Found %s matching calendars.', calendars.length);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the color of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getColor() instead.\nconst calendarColor = calendar.getColor();\nconsole.log(calendarColor);\n```\n\nExample:\n```text\n// Determines the time zone of the user's default calendar.\nconst calendar = CalendarApp.getDefaultCalendar();\nLogger.log(\n 'My default calendar is set to the time zone \"%s\".',\n calendar.getTimeZone(),\n);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Sets the description of the calendar to 'Test description.'\ncalendar.setDescription('Test description');\n\n// Gets the description of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getDescription() instead.\nconst description = calendar.getDescription();\nconsole.log(description);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Creates an event for the moon landing.\nconst event = calendar.createEvent(\n 'Apollo 11 Landing',\n new Date('July 20, 1969 20:05:00 UTC'),\n new Date('July 20, 1969 20:17:00 UTC'),\n);\n\n// Gets the calendar event ID and logs it to the console.\nconst iCalId = event.getId();\nconsole.log(iCalId);\n\n// Gets the event by its ID and logs the title of the event to the console.\n// For the default calendar, you can use CalendarApp.getEventById(iCalId)\n// instead.\nconst myEvent = calendar.getEventById(iCalId);\nconsole.log(myEvent.getTitle());\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Creates an event series for a daily team meeting from 1 PM to 2 PM.\n// The series adds the daily event from January 1, 2023 through December 31,\n// 2023.\nconst eventSeries = calendar.createEventSeries(\n 'Team meeting',\n new Date('Jan 1, 2023 13:00:00'),\n new Date('Jan 1, 2023 14:00:00'),\n CalendarApp.newRecurrence().addDailyRule().until(new Date('Jan 1, 2024')),\n);\n\n// Gets the ID of the event series.\nconst iCalId = eventSeries.getId();\n\n// Gets the event series by its ID and logs the series title to the console.\n// For the default calendar, you can use CalendarApp.getEventSeriesById(iCalId)\n// instead.\nconsole.log(calendar.getEventSeriesById(iCalId).getTitle());\n```\n\nExample:\n```text\n// Determines how many events are happening in the next two hours.\nconst now = new Date();\nconst twoHoursFromNow = new Date(now.getTime() + 2 * 60 * 60 * 1000);\nconst events = CalendarApp.getDefaultCalendar().getEvents(now, twoHoursFromNow);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening in the next two hours that contain\n// the term \"meeting\".\nconst now = new Date();\nconst twoHoursFromNow = new Date(now.getTime() + 2 * 60 * 60 * 1000);\nconst events = CalendarApp.getDefaultCalendar().getEvents(\n now,\n twoHoursFromNow,\n {search: 'meeting'},\n);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening today.\nconst today = new Date();\nconst events = CalendarApp.getDefaultCalendar().getEventsForDay(today);\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Determines how many events are happening today and contain the term\n// \"meeting\".\nconst today = new Date();\nconst events = CalendarApp.getDefaultCalendar().getEventsForDay(today, {\n search: 'meeting',\n});\nLogger.log(`Number of events: ${events.length}`);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// To get the user's default calendar, use CalendarApp.getDefaultCalendar().\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the ID of the calendar and logs it to the console.\nconst calendarId = calendar.getId();\nconsole.log(calendarId);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the name of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getName() instead.\nconst calendarName = calendar.getName();\nconsole.log(calendarName);\n```\n\nExample:\n```text\n// Gets a (non-existent) private calendar by ID.\nconst calendar = CalendarApp.getOwnedCalendarById(\n '123456789@group.calendar.google.com',\n);\nLogger.log('The calendar is named \"%s\".', calendar.getName());\n```\n\nExample:\n```text\n// Gets a private calendar named \"Travel Plans\".\nconst calendars = CalendarApp.getOwnedCalendarsByName('Travel Plans');\nLogger.log('Found %s matching calendars.', calendars.length);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Gets the time zone of the calendar and logs it to the console.\n// For the default calendar, you can use CalendarApp.getTimeZone() instead.\nconst timeZone = calendar.getTimeZone();\nconsole.log(timeZone);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Determines whether the calendar is hidden in the user interface and logs it\n// to the console. For the default calendar, you can use CalendarApp.isHidden()\n// instead.\nconst isHidden = calendar.isHidden();\nconsole.log(isHidden);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Determines whether the calendar is the default calendar for\n// the effective user and logs it to the console.\n// For the default calendar, you can use CalendarApp.isMyPrimaryCalendar()\n// instead.\nconst isMyPrimaryCalendar = calendar.isMyPrimaryCalendar();\nconsole.log(isMyPrimaryCalendar);\n```\n\nExample:\n```text\n// Gets a calendar by its ID. To get the user's default calendar, use\n// CalendarApp.getDefault() instead.\n// TODO(developer): Replace the ID with the calendar ID that you want to use.\nconst calendar = CalendarApp.getCalendarById(\n 'abc123456@group.calendar.google.com',\n);\n\n// Determines whether the calendar is owned by you and logs it.\nconsole.log(calendar.isOwnedByMe());\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Determines whether the calendar's events are displayed in the user interface\n// and logs it.\nconsole.log(calendar.isSelected());\n```\n\nExample:\n```text\n// Creates an event series for a no-meetings day, taking place every Wednesday\n// in 2013.\nconst recurrence = CalendarApp.newRecurrence()\n .addWeeklyRule()\n .onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)\n .until(new Date('January 1, 2014'));\nconst eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries(\n 'No Meetings',\n new Date('January 2, 2013 03:00:00 PM EST'),\n recurrence,\n);\nLogger.log(`Event Series ID: ${eventSeries.getId()}`);\n```\n\nExample:\n```text\n// Opens the calendar by its ID.\n// TODO(developer): Replace the ID with your own.\nconst calendar = CalendarApp.getCalendarById('222larabrown@gmail.com');\n\n// Sets the color of the calendar to pink using the Calendar Color enum.\n// For the default calendar, you can use CalendarApp.setColor() instead.\ncalendar.setColor(CalendarApp.Color.PINK);\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the description of the calendar.\n// TODO(developer): Update the string with the description that you want to use.\ncalendar.setDescription('Updated calendar description.');\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the name of the calendar.\n// TODO(developer): Update the string with the name that you want to use.\ncalendar.setName('Example calendar name');\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Selects the calendar so that its events are displayed in the user interface.\n// To unselect the calendar, set the parameter to false.\ncalendar.setSelected(true);\n```\n\nExample:\n```text\n// Gets the user's default calendar. To get a different calendar,\n// use getCalendarById() instead.\nconst calendar = CalendarApp.getDefaultCalendar();\n\n// Sets the time zone of the calendar to America/New York (US/Eastern) time.\ncalendar.setTimeZone('America/New_York');\n```\n\nExample:\n```text\n// Subscribe to the calendar \"US Holidays\".\nconst calendar = CalendarApp.subscribeToCalendar(\n 'en.usa#holiday@group.v.calendar.google.com',\n);\nLogger.log('Subscribed to the calendar \"%s\".', calendar.getName());\n```\n\nExample:\n```text\n// Subscribe to the calendar \"US Holidays\", and set it to the color blue.\nconst calendar = CalendarApp.subscribeToCalendar(\n 'en.usa#holiday@group.v.calendar.google.com',\n {color: CalendarApp.Color.BLUE},\n);\nLogger.log('Subscribed to the calendar \"%s\".', calendar.getName());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.350Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":43,"totalLines":523,"estimatedTokens":3891}}1098{"id":"doc-charts_service_apps_script_google_for_developers-fcc97196","source":"documentation","title":"Charts Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/service_charts","text":"Example:\n```text\nfunction doGet() {\n var data = Charts.newDataTable()\n .addColumn(Charts.ColumnType.STRING, 'Month')\n .addColumn(Charts.ColumnType.NUMBER, 'In Store')\n .addColumn(Charts.ColumnType.NUMBER, 'Online')\n .addRow(['January', 10, 1])\n .addRow(['February', 12, 1])\n .addRow(['March', 20, 2])\n .addRow(['April', 25, 3])\n .addRow(['May', 30, 4])\n .build();\n\n var chart = Charts.newAreaChart()\n .setDataTable(data)\n .setStacked()\n .setRange(0, 40)\n .setTitle('Sales per Month')\n .build();\n\n var htmlOutput = HtmlService.createHtmlOutput().setTitle('My Chart');\n var imageData = Utilities.base64Encode(chart.getAs('image/png').getBytes());\n var imageUrl = \"data:image/png;base64,\" + encodeURI(imageData);\n htmlOutput.append(\"Render chart server side: <br/>\");\n htmlOutput.append(\"<img border=\\\"1\\\" src=\\\"\" + imageUrl + \"\\\">\");\n return htmlOutput;\n\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.352Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":32,"estimatedTokens":238}}1099{"id":"doc-work_with_comments_and_suggestions_google_docs_g-ebc59813","source":"documentation","title":"Work with comments and suggestions | Google Docs | Google for Developers","url":"https://developers.google.com/workspace/docs/api/how-tos/suggestions","text":"Example:\n```text\n{\n \"tabs\": [\n {\n \"documentTab\": {\n \"body\": {\n \"content\": [\n {\n \"startIndex\": 1,\n \"endIndex\": 31,\n \"paragraph\": {\n \"elements\": [\n {\n \"startIndex\": 1,\n \"endIndex\": 31,\n \"textRun\": {\n \"content\": \"Text preceding the suggestion\\n\",\n \"textStyle\": {}\n }\n }\n ],\n \"paragraphStyle\": {\n \"namedStyleType\": \"NORMAL_TEXT\",\n \"direction\": \"LEFT_TO_RIGHT\"\n }\n }\n },\n {\n \"startIndex\": 31,\n \"endIndex\": 51,\n \"paragraph\": {\n \"elements\": [\n {\n \"startIndex\": 31,\n \"endIndex\": 50,\n \"textRun\": {\n \"content\": \"Suggested insertion\",\n \"suggestedInsertionIds\": [\n \"suggest.vcti8ewm4mww\"\n ],\n \"textStyle\": {}\n }\n },\n {\n \"startIndex\": 50,\n \"endIndex\": 51,\n \"textRun\": {\n \"content\": \"\\n\",\n \"textStyle\": {}\n }\n }\n ],\n \"paragraphStyle\": {\n \"namedStyleType\": \"NORMAL_TEXT\",\n \"direction\": \"LEFT_TO_RIGHT\"\n }\n }\n },\n {\n \"startIndex\": 51,\n \"endIndex\": 81,\n \"paragraph\": {\n \"elements\": [\n {\n \"startIndex\": 51,\n \"endIndex\": 81,\n \"textRun\": {\n \"content\": \"Text following the suggestion\\n\",\n \"textStyle\": {}\n }\n }\n ],\n \"paragraphStyle\": {\n \"namedStyleType\": \"NORMAL_TEXT\",\n \"direction\": \"LEFT_TO_RIGHT\"\n }\n }\n }\n ]\n }\n }\n }\n ]\n},\n```\n\nExample:\n```text\n{\n \"tabs\": [\n {\n \"documentTab\": {\n \"body\": {\n \"content\": [\n {\n \"startIndex\": 1,\n \"endIndex\": 31,\n \"paragraph\": {\n \"elements\": [\n {\n \"startIndex\": 1,\n \"endIndex\": 31,\n \"textRun\": {\n \"content\": \"Text preceding the suggestion\\n\",\n \"textStyle\": {}\n }\n }\n ],\n \"paragraphStyle\": {\n \"namedStyleType\": \"NORMAL_TEXT\",\n \"direction\": \"LEFT_TO_RIGHT\"\n }\n }\n },\n {\n \"startIndex\": 31,\n \"endIndex\": 32,\n \"paragraph\": {\n \"elements\": [\n {\n \"startIndex\": 31,\n \"endIndex\": 32,\n \"textRun\": {\n \"content\": \"\\n\",\n \"textStyle\": {}\n }\n }\n ],\n \"paragraphStyle\": {\n \"namedStyleType\": \"NORMAL_TEXT\",\n \"direction\": \"LEFT_TO_RIGHT\"\n }\n }\n },\n {\n \"startIndex\": 32,\n \"endIndex\": 62,\n \"paragraph\": {\n \"elements\": [\n {\n \"startIndex\": 32,\n \"endIndex\": 62,\n \"textRun\": {\n \"content\": \"Text following the suggestion\\n\",\n \"textStyle\": {}\n }\n }\n ],\n \"paragraphStyle\": {\n \"namedStyleType\": \"NORMAL_TEXT\",\n \"direction\": \"LEFT_TO_RIGHT\"\n }\n }\n }\n ]\n }\n }\n }\n ]\n},\n```\n\nExample:\n```text\nfinal string SUGGEST_MODE = \"PREVIEW_WITHOUT_SUGGESTIONS\";\nDocument doc =\n service\n .documents()\n .get(DOCUMENT_ID)\n .setIncludeTabsContent(true)\n .setSuggestionsViewMode(SUGGEST_MODE)\n .execute();\n```\n\nExample:\n```text\nSUGGEST_MODE = \"PREVIEW_WITHOUT_SUGGESTIONS\"\nresult = (\n service.documents()\n .get(\n documentId=DOCUMENT_ID,\n includeTabsContent=True,\n suggestionsViewMode=SUGGEST_MODE,\n )\n .execute()\n)\n```\n\nExample:\n```text\n[01] \"paragraph\": {\n[02] \"elements\": [\n[03] {\n[04] \"endIndex\": 106,\n[05] \"startIndex\": 82,\n[06] \"textRun\": {\n[07] \"content\": \"Some text that does not \",\n[08] \"textStyle\": {}\n[09] }\n[10] },\n[11] {\n[12] \"endIndex\": 115,\n[13] \"startIndex\": 106,\n[14] \"textRun\": {\n[15] \"content\": \"initially\",\n[16] \"suggestedTextStyleChanges\": {\n[17] \"suggest.xymysbs9zldp\": {\n[18] \"textStyle\": {\n[19] \"backgroundColor\": {},\n[20] \"baselineOffset\": \"NONE\",\n[21] \"bold\": true,\n[22] \"fontSize\": {\n[23] \"magnitude\": 11,\n[24] \"unit\": \"PT\"\n[25] },\n[26] \"foregroundColor\": {\n[27] \"color\": {\n[28] \"rgbColor\": {}\n[29] }\n[30] },\n[31] \"italic\": false,\n[32] \"smallCaps\": false,\n[33] \"strikethrough\": false,\n[34] \"underline\": false\n[35] },\n[36] \"textStyleSuggestionState\": {\n[37] \"boldSuggested\": true,\n[38] \"weightedFontFamilySuggested\": true\n[39] }\n[40] }\n[41] },\n[42] \"textStyle\": {\n[43] \"italic\": true\n[44] }\n[45] }\n[46] },\n[47] {\n[48] \"endIndex\": 143,\n[49] \"startIndex\": 115,\n[50] \"textRun\": {\n[51] \"content\": \" contain any boldface text.\\n\",\n[52] \"textStyle\": {}\n[53] }\n[54] }\n[55] ],\n[56] \"paragraphStyle\": {\n[57] \"direction\": \"LEFT_TO_RIGHT\",\n[58] \"namedStyleType\": \"NORMAL_TEXT\"\n[59] }\n[60] }\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"insertComment\": {\n \"content\": \"This is a comment added via the API.\",\n \"range\": {\n \"startIndex\": 10,\n \"endIndex\": 25\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"insertComment\": {\n \"content\": \"Please review this paragraph.\",\n \"assigneeEmailAddress\": \"user@example.com\",\n \"range\": {\n \"startIndex\": 10,\n \"endIndex\": 25\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"addCommentReply\": {\n \"commentId\": \"comment_thread_id\",\n \"post\": {\n \"content\": \"Replying to the comment thread.\"\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"addCommentReply\": {\n \"commentId\": \"comment_thread_id\",\n \"post\": {\n \"commentAction\": \"RESOLVE\"\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"addCommentReply\": {\n \"commentId\": \"comment_thread_id\",\n \"post\": {\n \"content\": \"Replying to the comment thread.\",\n \"assigneeEmail\": \"user@example.com\"\n }\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"updateCommentPost\": {\n \"commentId\": \"comment_thread_id\",\n \"postId\": \"post_id\",\n \"content\": \"This is the updated comment text.\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"deleteComment\": {\n \"commentId\": \"comment_thread_id\"\n }\n }\n ]\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"insertText\": {\n \"text\": \"suggested insertion text\",\n \"location\": {\n \"index\": 1\n }\n }\n }\n ],\n \"writeControl\": {\n \"writeMode\": \"SUGGEST\"\n }\n}\n```\n\nExample:\n```text\n{\n \"requests\": [\n {\n \"acceptSuggestion\": {\n \"suggestionId\": \"suggestion_thread_id\"\n }\n }\n ]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.353Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":398,"estimatedTokens":1926}}1100{"id":"doc-extend_the_compose_ui_with_compose_actions_googl-788c1406","source":"documentation","title":"Extend the compose UI with compose actions | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/apps-script/add-ons/gmail/extending-compose-ui","text":"Example:\n```text\n/**\n * Compose trigger function that fires when the compose UI is\n * requested. Builds and returns a compose UI for inserting images.\n *\n * @param {event} e The compose trigger event object. Not used in\n * this example.\n * @return {Card[]}\n */\nfunction getComposeUI(e) {\n return [buildComposeCard()];\n}\n\n/**\n * Build a card to display interactive buttons to allow the user to\n * update the subject, and To, Cc, Bcc recipients.\n *\n * @return {Card}\n */\nfunction buildComposeCard() {\n\n var card = CardService.newCardBuilder();\n var cardSection = CardService.newCardSection().setHeader('Update email');\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update subject')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('applyUpdateSubjectAction')));\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update To recipients')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('updateToRecipients')));\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update Cc recipients')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('updateCcRecipients')));\n cardSection.addWidget(\n CardService.newTextButton()\n .setText('Update Bcc recipients')\n .setOnClickAction(CardService.newAction()\n .setFunctionName('updateBccRecipients')));\n return card.addSection(cardSection).build();\n}\n\n/**\n * Updates the subject field of the current email when the user clicks\n * on \"Update subject\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateSubjectAction() {\n // Get the new subject field of the email.\n // This function is not shown in this example.\n var subject = getSubject();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftSubjectAction(CardService.newUpdateDraftSubjectAction()\n .addUpdateSubject(subject))\n .build();\n return response;\n}\n\n/**\n * Updates the To recipients of the current email when the user clicks\n * on \"Update To recipients\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateToRecipientsAction() {\n // Get the new To recipients of the email.\n // This function is not shown in this example.\n var toRecipients = getToRecipients();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftToRecipientsAction(CardService.newUpdateDraftToRecipientsAction()\n .addUpdateToRecipients(toRecipients))\n .build();\n return response;\n}\n\n/**\n * Updates the Cc recipients of the current email when the user clicks\n * on \"Update Cc recipients\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateCcRecipientsAction() {\n // Get the new Cc recipients of the email.\n // This function is not shown in this example.\n var ccRecipients = getCcRecipients();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftCcRecipientsAction(CardService.newUpdateDraftCcRecipientsAction()\n .addUpdateToRecipients(ccRecipients))\n .build();\n return response;\n}\n\n/**\n * Updates the Bcc recipients of the current email when the user clicks\n * on \"Update Bcc recipients\" in the compose UI.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @return {UpdateDraftActionResponse}\n */\nfunction applyUpdateBccRecipientsAction() {\n // Get the new Bcc recipients of the email.\n // This function is not shown in this example.\n var bccRecipients = getBccRecipients();\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftBccRecipientsAction(CardService.newUpdateDraftBccRecipientsAction()\n .addUpdateToRecipients(bccRecipients))\n .build();\n return response;\n}\n```\n\nExample:\n```text\n/**\n * Compose trigger function that fires when the compose UI is\n * requested. Builds and returns a compose UI for inserting images.\n *\n * @param {event} e The compose trigger event object. Not used in\n * this example.\n * @return {Card[]}\n */\nfunction getInsertImageComposeUI(e) {\n return [buildImageComposeCard()];\n}\n\n/**\n * Build a card to display images from a third-party source.\n *\n * @return {Card}\n */\nfunction buildImageComposeCard() {\n // Get a short list of image URLs to display in the UI.\n // This function is not shown in this example.\n var imageUrls = getImageUrls();\n\n var card = CardService.newCardBuilder();\n var cardSection = CardService.newCardSection().setHeader('My Images');\n for (var i = 0; i < imageUrls.length; i++) {\n var imageUrl = imageUrls[i];\n cardSection.addWidget(\n CardService.newImage()\n .setImageUrl(imageUrl)\n .setOnClickAction(CardService.newAction()\n .setFunctionName('applyInsertImageAction')\n .setParameters({'url' : imageUrl})));\n }\n return card.addSection(cardSection).build();\n}\n\n/**\n * Adds an image to the current draft email when the image is clicked\n * in the compose UI. The image is inserted at the current cursor\n * location. If any content of the email draft is currently selected,\n * it is deleted and replaced with the image.\n *\n * Note: This is not the compose action that builds a compose UI, but\n * rather an action taken when the user interacts with the compose UI.\n *\n * @param {event} e The incoming event object.\n * @return {UpdateDraftActionResponse}\n */\nfunction applyInsertImageAction(e) {\n var imageUrl = e.parameters.url;\n var imageHtmlContent = '<img style=\\\"display: block\\\" src=\\\"'\n + imageUrl + '\\\"/>';\n var response = CardService.newUpdateDraftActionResponseBuilder()\n .setUpdateDraftBodyAction(CardService.newUpdateDraftBodyAction()\n .addUpdateContent(\n imageHtmlContent,\n CardService.ContentType.MUTABLE_HTML)\n .setUpdateType(\n CardService.UpdateDraftBodyType.IN_PLACE_INSERT))\n .build();\n return response;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.357Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":195,"estimatedTokens":1649}}1101{"id":"doc-manage_projects_with_google_chat_vertex_ai_and_f-ad69b08f","source":"documentation","title":"Manage projects with Google Chat, Vertex AI, and Firestore | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/samples/tutorial-project-management","text":"Example:\n```text\ngcloud projects create PROJECT_ID\n```\n\nExample:\n```text\ngcloud billing accounts list\n```\n\nExample:\n```text\ngcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID\n```\n\nExample:\n```text\ngcloud config set project PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable chat.googleapis.com \\\naiplatform.googleapis.com \\\ncloudfunctions.googleapis.com \\\nfirestore.googleapis.com \\\ncloudbuild.googleapis.com \\\npubsub.googleapis.com \\\nrun.googleapis.com\n```\n\nExample:\n```text\ngcloud firestore databases create \\\n--location=LOCATION \\\n--type=firestore-native\n```\n\nExample:\n```text\ngit clone https://github.com/googleworkspace/add-ons-samples.git\n```\n\nExample:\n```text\ncd add-ons-samples/node/chat/project-management-app\n```\n\nExample:\n```text\ngcloud functions deploy project-management-tutorial \\\n--gen2 \\\n--region=REGION \\\n--runtime=nodejs20 \\\n--source=. \\\n--entry-point=projectManagementChatApp \\\n--trigger-http \\\n--allow-unauthenticated\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.360Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":61,"estimatedTokens":247}}1102{"id":"doc-respond_to_incidents_with_google_chat_vertex_ai_-db4d7ebc","source":"documentation","title":"Respond to incidents with Google Chat, Vertex AI, Apps Script, and user authentication | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/samples/tutorial-incident-response-user-auth","text":"Example:\n```text\ngcloud projects create PROJECT_ID\n```\n\nExample:\n```text\ngcloud billing accounts list\n```\n\nExample:\n```text\ngcloud billing projects link PROJECT_ID --billing-account=BILLING_ACCOUNT_ID\n```\n\nExample:\n```text\ngcloud config set project PROJECT_ID\n```\n\nExample:\n```text\ngcloud services enable chat.googleapis.com docs.googleapis.com admin.googleapis.com aiplatform.googleapis.com\n```\n\nExample:\n```text\nconst PROJECT_ID = 'replace-with-your-project-id';\nconst VERTEX_AI_LOCATION_ID = 'us-central1';\nconst CLOSE_INCIDENT_COMMAND_ID = 1;\nconst MODEL_ID = 'gemini-2.5-flash-lite';\n```\n\nExample:\n```text\n/**\n * Responds to a MESSAGE event in Google Chat.\n * \n * It always responds with a simple \"Hello\" text message.\n *\n * @param {Object} event the event object from Google Chat\n */\nfunction onMessage(event) {\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: \"Hello from Incident Response app!\"\n }}}}};\n}\n\n/**\n * Responds to an APP_COMMAND event in Google Chat.\n *\n * @param {Object} event the event object from Google Chat\n */\nfunction onAppCommand(event) {\n if (event.chat.appCommandPayload.appCommandMetadata.appCommandId != CLOSE_INCIDENT_COMMAND_ID) {\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: \"Command not recognized. Use the quick command `Close incident` to close the incident managed by this space.\"\n }}}}};\n }\n return { action: { navigations: [{ pushCard: { sections: [{\n header: \"Close Incident\",\n widgets: [{\n textInput: {\n label: \"Please describe the incident resolution\",\n type: \"MULTIPLE_LINE\",\n name: \"description\"\n }\n }, {\n buttonList: { buttons: [{\n text: \"Close Incident\",\n onClick: { action: { function: \"closeIncident\" }}\n }]}\n }]\n }]}}]}};\n}\n\n/**\n * Responds to a BUTTON_CLICKED event in Google Chat from Close Incident dialog.\n *\n * @param {Object} event the event object from Google Chat\n */\nfunction closeIncident(event) {\n if (event.chat.buttonClickedPayload.isDialogEvent) {\n if (event.chat.buttonClickedPayload.dialogEventType == 'SUBMIT_DIALOG') {\n return processSubmitDialog_(event);\n }\n return { action: { navigations: [{ endNavigation: {\n action: \"CLOSE_DIALOG\" }\n }]}};\n }\n}\n\n/**\n * Responds to a BUTTON_CLICKED event in Google Chat from Close Incident dialog submission.\n *\n * It creates a Doc with a summary of the incident information and posts a message\n * to the space with a link to the Doc.\n *\n * @param {Object} event the event object from Google Chat\n */\nfunction processSubmitDialog_(event) {\n const resolution = event.commonEventObject.formInputs.description.stringInputs.value[0];\n const space = event.chat.buttonClickedPayload.space;\n const chatHistory = concatenateAllSpaceMessages_(space.name);\n const chatSummary = summarizeChatHistory_(chatHistory);\n const docUrl = createDoc_(space.displayName, resolution, chatHistory, chatSummary);\n return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {\n text: `Incident closed with the following resolution: ${resolution}\\n\\nHere is the automatically generated post-mortem:\\n${docUrl}`\n }}}}};\n}\n\n/**\n * Lists all the messages in the Chat space, then concatenate all of them into\n * a single text containing the full Chat history.\n *\n * For simplicity for this demo, it only fetches the first 100 messages.\n *\n * @return {string} a text containing all the messages in the space in the format:\n * Sender's name: Message\n */\nfunction concatenateAllSpaceMessages_(spaceName) {\n // Call Chat API method spaces.messages.list\n const response = Chat.Spaces.Messages.list(spaceName, { 'pageSize': 100 });\n const messages = response.messages;\n // Fetch the display names of the message senders and returns a text\n // concatenating all the messages.\n let userMap = new Map();\n return messages\n .map(message => `${getUserDisplayName_(userMap, message.sender.name)}: ${message.text}`)\n .join('\\n');\n}\n\n/**\n * Obtains the display name of a user by using the Admin Directory API.\n *\n * The fetched display name is cached in the provided map, so we only call the API\n * once per user.\n *\n * If the user does not have a display name, then the full name is used.\n *\n * @param {Map} userMap a map containing the display names previously fetched\n * @param {string} userName the resource name of the user\n * @return {string} the user's display name\n */\nfunction getUserDisplayName_(userMap, userName) {\n if (userMap.has(userName)) {\n return userMap.get(userName);\n }\n let displayName = 'Unknown User';\n try {\n const user = AdminDirectory.Users.get(\n userName.replace(\"users/\", \"\"),\n { projection: 'BASIC', viewType: 'domain_public' });\n displayName = user.name.displayName ? user.name.displayName : user.name.fullName;\n } catch (e) {\n // Ignore error if the API call fails (for example, because it's an\n // out-of-domain user or Chat app) and just use 'Unknown User'.\n }\n userMap.set(userName, displayName);\n return displayName;\n}\n```\n\nExample:\n```text\n/**\n * Handles an incident by creating a chat space with the provided title and members, and posting a message.\n * All the actions are done using user credentials.\n *\n * @param {Object} formData - The data submitted by the user. It should contain the fields:\n * - title: The display name of the chat space.\n * - description: The description of the incident.\n * - users: A comma-separated string of user emails to be added to the space.\n * @return {string} The resource name of the new space.\n */\nfunction handleIncident(formData) {\n const users = formData.users.trim().length > 0 ? formData.users.split(',') : [];\n const spaceName = setUpSpace_(formData.title, users);\n addAppToSpace_(spaceName);\n createMessage_(spaceName, formData.description);\n return spaceName;\n}\n\n/**\n * Creates a chat space.\n *\n * @return {string} the resource name of the new space.\n */\nfunction setUpSpace_(displayName, users) {\n const memberships = users.map(email => ({\n member: {\n name: `users/${email}`,\n type: \"HUMAN\"\n }\n }));\n const request = {\n space: {\n displayName: displayName,\n spaceType: \"SPACE\"\n },\n memberships: memberships\n };\n // Call Chat API method spaces.setup\n const space = Chat.Spaces.setup(request);\n return space.name;\n}\n\n/**\n * Adds this Chat app to the space.\n *\n * @return {string} the resource name of the new membership.\n */\nfunction addAppToSpace_(spaceName) {\n const request = {\n member: {\n name: \"users/app\",\n type: \"BOT\"\n }\n };\n // Call Chat API method spaces.members.create\n const membership = Chat.Spaces.Members.create(request, spaceName);\n return membership.name;\n}\n\n/**\n * Creates a chat message.\n *\n * @param {string} spaceName - The resource name of the space.\n * @param {string} message - The text to be posted.\n * @return {string} the resource name of the new message.\n */\nfunction createMessage_(spaceName, message) {\n const request = {\n text: message\n };\n // Call Chat API method spaces.messages.create\n const result = Chat.Spaces.Messages.create(request, spaceName);\n return result.name;\n}\n```\n\nExample:\n```text\n/**\n * Creates a Doc in the user's Google Drive and writes a summary of the incident information to it.\n *\n * @param {string} title The title of the incident\n * @param {string} resolution Incident resolution described by the user\n * @param {string} chatHistory The whole Chat history be included in the document\n * @param {string} chatSummary A summary of the Chat conversation to be included in the document\n * @return {string} the URL of the created Doc\n */\nfunction createDoc_(title, resolution, chatHistory, chatSummary) {\n let doc = DocumentApp.create(title);\n let body = doc.getBody();\n body.appendParagraph(`Post-Mortem: ${title}`).setHeading(DocumentApp.ParagraphHeading.TITLE);\n body.appendParagraph(\"Resolution\").setHeading(DocumentApp.ParagraphHeading.HEADING1);\n body.appendParagraph(resolution);\n body.appendParagraph(\"Summary of the conversation\").setHeading(DocumentApp.ParagraphHeading.HEADING1);\n body.appendParagraph(chatSummary);\n body.appendParagraph(\"Full Chat history\").setHeading(DocumentApp.ParagraphHeading.HEADING1);\n body.appendParagraph(chatHistory);\n return doc.getUrl();\n}\n```\n\nExample:\n```text\n/**\n * Summarizes a Chat conversation using the Vertex AI text prediction API.\n *\n * @param {string} chatHistory The Chat history that will be summarized.\n * @return {string} The content from the text prediction response.\n */\nfunction summarizeChatHistory_(chatHistory) {\n const API_ENDPOINT = `https://${VERTEX_AI_LOCATION_ID}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${VERTEX_AI_LOCATION_ID}/publishers/google/models/${MODEL_ID}:generateContent`;\n const prompt =\n \"Summarize the following conversation between Engineers resolving an incident\"\n + \" in a few sentences. Use only the information from the conversation.\\n\\n\"\n + chatHistory;\n // Get the access token.\n const accessToken = ScriptApp.getOAuthToken();\n\n const headers = {\n 'Authorization': 'Bearer ' + accessToken,\n 'Content-Type': 'application/json',\n };\n const payload = {\n 'contents': {\n 'role': 'user',\n 'parts' : [\n {\n 'text': prompt\n }\n ]\n }\n }\n const options = {\n 'method': 'post',\n 'headers': headers,\n 'payload': JSON.stringify(payload),\n 'muteHttpExceptions': true,\n };\n try {\n const response = UrlFetchApp.fetch(API_ENDPOINT, options);\n const responseCode = response.getResponseCode();\n const responseText = response.getContentText();\n\n if (responseCode === 200) {\n const jsonResponse = JSON.parse(responseText);\n console.log(jsonResponse)\n if (jsonResponse.candidates && jsonResponse.candidates.length > 0) {\n return jsonResponse.candidates[0].content.parts[0].text; // Access the summarized text\n } else {\n return \"No summary found in response.\";\n }\n\n } else {\n console.error(\"Vertex AI API Error:\", responseCode, responseText);\n return `Error: ${responseCode} - ${responseText}`;\n }\n } catch (e) {\n console.error(\"UrlFetchApp Error:\", e);\n return \"Error: \" + e.toString();\n }\n}\n```\n\nExample:\n```text\n/**\n * Serves the web page from Index.html.\n */\nfunction doGet() {\n return HtmlService\n .createTemplateFromFile('Index')\n .evaluate();\n}\n\n/**\n * Serves the web content from the specified filename.\n */\nfunction include(filename) {\n return HtmlService\n .createHtmlOutputFromFile(filename)\n .getContent();\n}\n\n/**\n * Returns the email address of the user running the script.\n */\nfunction getUserEmail() {\n return Session.getActiveUser().getEmail();\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet'>\n <?!= include('Stylesheet'); ?>\n </head>\n <body>\n <div class=\"container\">\n <div class=\"content\">\n <h1>Incident Manager</h1>\n <form id=\"incident-form\" onsubmit=\"handleFormSubmit(this)\">\n <div id=\"form\">\n <p>\n <label for=\"title\">Incident title</label><br/>\n <input type=\"text\" name=\"title\" id=\"title\" />\n </p>\n <p>\n <label for=\"users\">Incident responders</label><br/>\n <small>\n Please enter a comma-separated list of email addresses of the users\n that should be added to the space.\n Do not include <?= getUserEmail() ?> as it will be added automatically.\n </small><br/>\n <input type=\"text\" name=\"users\" id=\"users\" />\n </p>\n <p>\n <label for=\"description\">Initial message</label></br>\n <small>This message will be posted after the space is created.</small><br/>\n <textarea name=\"description\" id=\"description\"></textarea>\n </p>\n <p class=\"text-center\">\n <input type=\"submit\" value=\"CREATE CHAT SPACE\" />\n </p>\n </div>\n <div id=\"output\" class=\"hidden\"></div>\n <div id=\"clear\" class=\"hidden\">\n <input type=\"reset\" value=\"CREATE ANOTHER INCIDENT\" onclick=\"onReset()\" />\n </div>\n </form>\n </div>\n </div>\n <?!= include('JavaScript'); ?>\n </body>\n</html>\n```\n\nExample:\n```text\n<script>\n var formDiv = document.getElementById('form');\n var outputDiv = document.getElementById('output');\n var clearDiv = document.getElementById('clear');\n\n function handleFormSubmit(formObject) {\n event.preventDefault();\n outputDiv.innerHTML = 'Please wait while we create the space...';\n hide(formDiv);\n show(outputDiv);\n google.script.run\n .withSuccessHandler(updateOutput)\n .withFailureHandler(onFailure)\n .handleIncident(formObject);\n }\n\n function updateOutput(response) {\n var spaceId = response.replace('spaces/', '');\n outputDiv.innerHTML =\n '<p>Space created!</p><p><a href=\"https://mail.google.com/chat/#chat/space/'\n + spaceId\n + '\" target=\"_blank\">Open space</a></p>';\n show(outputDiv);\n show(clearDiv);\n }\n\n function onFailure(error) {\n outputDiv.innerHTML = 'ERROR: ' + error.message;\n outputDiv.classList.add('error');\n show(outputDiv);\n show(clearDiv);\n }\n\n function onReset() {\n outputDiv.innerHTML = '';\n outputDiv.classList.remove('error');\n show(formDiv);\n hide(outputDiv);\n hide(clearDiv);\n }\n\n function hide(element) {\n element.classList.add('hidden');\n }\n\n function show(element) {\n element.classList.remove('hidden');\n }\n</script>\n```\n\nExample:\n```text\n<style>\n * {\n box-sizing: border-box;\n }\n body {\n font-family: Roboto, Arial, Helvetica, sans-serif;\n }\n div.container {\n display: flex;\n justify-content: center;\n align-items: center;\n position: absolute;\n top: 0; bottom: 0; left: 0; right: 0;\n }\n div.content {\n width: 80%;\n max-width: 1000px;\n padding: 1rem;\n border: 1px solid #999;\n border-radius: 0.25rem;\n box-shadow: 0 2px 2px 0 rgba(66, 66, 66, 0.08), 0 2px 4px 2px rgba(66, 66, 66, 0.16);\n }\n h1 {\n text-align: center;\n padding-bottom: 1rem;\n margin: 0 -1rem 1rem -1rem;\n border-bottom: 1px solid #999;\n }\n #output {\n text-align: center;\n min-height: 250px;\n }\n div#clear {\n text-align: center;\n padding-top: 1rem;\n margin: 1rem -1rem 0 -1rem;\n border-top: 1px solid #999;\n }\n input[type=text], textarea {\n width: 100%;\n padding: 1rem 0.5rem;\n margin: 0.5rem 0;\n border: 0;\n border-bottom: 1px solid #999;\n background-color: #f0f0f0;\n }\n textarea {\n height: 5rem;\n }\n small {\n color: #999;\n }\n input[type=submit], input[type=reset] {\n padding: 1rem;\n border: none;\n background-color: #6200ee;\n color: #fff;\n border-radius: 0.25rem;\n width: 25%;\n }\n .hidden {\n display: none;\n }\n .text-center {\n text-align: center;\n }\n .error {\n color: red;\n }\n</style>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.361Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":533,"estimatedTokens":3805}}1103{"id":"doc-work_with_tabs_google_docs_google_for_developers-8c8147f5","source":"documentation","title":"Work with tabs | Google Docs | Google for Developers","url":"https://developers.google.com/docs/api/how-tos/tabs","text":"Example:\n```text\n/** Prints all text contents from all tabs in the document. */\nstatic void printAllText(Docs service, String documentId) throws IOException {\n // Fetch the document with all of the tabs populated, including any nested\n // child tabs.\n Document doc =\n service.documents().get(<var>DOCUMENT_ID</var>).setIncludeTabsContent(true).execute();\n List<Tab> allTabs = getAllTabs(doc);\n\n // Print the content from each tab in the document.\n for (Tab tab: allTabs) {\n // Get the DocumentTab from the generic Tab.\n DocumentTab documentTab = tab.getDocumentTab();\n System.out.println(\n readStructuralElements(documentTab.getBody().getContent()));\n }\n}\n\n/**\n * Returns a flat list of all tabs in the document in the order they would\n * appear in the UI (top-down ordering). Includes all child tabs.\n */\nprivate List<Tab> getAllTabs(Document doc) {\n List<Tab> allTabs = new ArrayList<>();\n // Iterate over all tabs and recursively add any child tabs to generate a\n // flat list of Tabs.\n for (Tab tab: doc.getTabs()) {\n addCurrentAndChildTabs(tab, allTabs);\n }\n return allTabs;\n}\n\n/**\n * Adds the provided tab to the list of all tabs, and recurses through and\n * adds all child tabs.\n */\nprivate void addCurrentAndChildTabs(Tab tab, List<Tab> allTabs) {\n allTabs.add(tab);\n for (Tab tab: tab.getChildTabs()) {\n addCurrentAndChildTabs(tab, allTabs);\n }\n}\n\n/**\n * Recurses through a list of Structural Elements to read a document's text\n * where text may be in nested elements.\n *\n * <p>For a code sample, see\n * <a href=\"https://developers.google.com/workspace/docs/api/samples/extract-text\">Extract\n * the text from a document</a>.\n */\nprivate static String readStructuralElements(List<StructuralElement> elements) {\n ...\n}\n```\n\nExample:\n```text\n/** Prints all text contents from the first tab in the document. */\nstatic void printAllText(Docs service, String documentId) throws IOException {\n // Fetch the document with all of the tabs populated, including any nested\n // child tabs.\n Document doc =\n service.documents().get(<var>DOCUMENT_ID</var>).setIncludeTabsContent(true).execute();\n List<Tab> allTabs = getAllTabs(doc);\n\n // Print the content from the first tab in the document.\n Tab firstTab = allTabs.get(0);\n // Get the DocumentTab from the generic Tab.\n DocumentTab documentTab = firstTab.getDocumentTab();\n System.out.println(\n readStructuralElements(documentTab.getBody().getContent()));\n}\n```\n\nExample:\n```text\n/** Inserts text into the first tab of the document. */\nstatic void insertTextInFirstTab(Docs service, String documentId)\n throws IOException {\n // Get the first tab's ID.\n Document doc =\n service.documents().get(<var>DOCUMENT_ID</var>).setIncludeTabsContent(true).execute();\n Tab firstTab = doc.getTabs().get(0);\n String tabId = firstTab.getTabProperties().getTabId();\n\n List<Request>requests = new ArrayList<>();\n requests.add(new Request().setInsertText(\n new InsertTextRequest().setText(text).setLocation(new Location()\n // Set the tab ID.\n .setTabId(tabId)\n .setIndex(25))));\n\n BatchUpdateDocumentRequest body =\n new BatchUpdateDocumentRequest().setRequests(requests);\n BatchUpdateDocumentResponse response =\n docsService.documents().batchUpdate(<var>DOCUMENT_ID</var>, body).execute();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.362Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":102,"estimatedTokens":878}}1104{"id":"doc-show_progress_bars_in_a_google_slides_presentati-5d66f7be","source":"documentation","title":"Show progress bars in a Google Slides presentation | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/editors/slides/quickstart/progress-bar","text":"Example:\n```text\n/**\n * @OnlyCurrentDoc Adds progress bars to a presentation.\n */\nconst BAR_ID = \"PROGRESS_BAR_ID\";\nconst BAR_HEIGHT = 10; // px\n\n/**\n * Runs when the add-on is installed.\n * @param {object} e The event parameter for a simple onInstall trigger. To\n * determine which authorization mode (ScriptApp.AuthMode) the trigger is\n * running in, inspect e.authMode. (In practice, onInstall triggers always\n * run in AuthMode.FULL, but onOpen triggers may be AuthMode.LIMITED or\n * AuthMode.NONE.)\n */\nfunction onInstall(e) {\n onOpen();\n}\n\n/**\n * Trigger for opening a presentation.\n * @param {object} e The onOpen event.\n */\nfunction onOpen(e) {\n SlidesApp.getUi()\n .createAddonMenu()\n .addItem(\"Show progress bar\", \"createBars\")\n .addItem(\"Hide progress bar\", \"deleteBars\")\n .addToUi();\n}\n\n/**\n * Create a rectangle on every slide with different bar widths.\n */\nfunction createBars() {\n deleteBars(); // Delete any existing progress bars\n const presentation = SlidesApp.getActivePresentation();\n const slides = presentation.getSlides();\n for (let i = 0; i < slides.length; ++i) {\n const ratioComplete = i / (slides.length - 1);\n const x = 0;\n const y = presentation.getPageHeight() - BAR_HEIGHT;\n const barWidth = presentation.getPageWidth() * ratioComplete;\n if (barWidth > 0) {\n const bar = slides[i].insertShape(\n SlidesApp.ShapeType.RECTANGLE,\n x,\n y,\n barWidth,\n BAR_HEIGHT,\n );\n bar.getBorder().setTransparent();\n bar.setLinkUrl(BAR_ID);\n }\n }\n}\n\n/**\n * Deletes all progress bar rectangles.\n */\nfunction deleteBars() {\n const presentation = SlidesApp.getActivePresentation();\n const slides = presentation.getSlides();\n for (let i = 0; i < slides.length; ++i) {\n const elements = slides[i].getPageElements();\n for (const el of elements) {\n if (\n el.getPageElementType() === SlidesApp.PageElementType.SHAPE &&\n el.asShape().getLink() &&\n el.asShape().getLink().getUrl() === BAR_ID\n ) {\n el.remove();\n }\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.363Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":80,"estimatedTokens":526}}1105{"id":"doc-analyze_feedback_sentiment_using_the_google_clou-987e96d7","source":"documentation","title":"Analyze feedback sentiment using the Google Cloud Natural Language API | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/feedback-sentiment-analysis","text":"Example:\n```text\nconst myApiKey = 'YOUR_API_KEY'; // Replace with your API key.\n```\n\nExample:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/feedback-sentiment-analysis\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Sets API key for accessing Cloud Natural Language API.\nconst myApiKey = \"YOUR_API_KEY\"; // Replace with your API key.\n\n// Matches column names in Review Data sheet to variables.\nconst COLUMN_NAME = {\n COMMENTS: \"comments\",\n ENTITY: \"entity_sentiment\",\n ID: \"id\",\n};\n\n/**\n * Creates a Demo menu in Google Spreadsheets.\n */\nfunction onOpen() {\n SpreadsheetApp.getUi()\n .createMenu(\"Sentiment Tools\")\n .addItem(\"Mark entities and sentiment\", \"markEntitySentiment\")\n .addToUi();\n}\n\n/**\n * Analyzes entities and sentiment for each comment in\n * Review Data sheet and copies results into the\n * Entity Sentiment Data sheet.\n */\nfunction markEntitySentiment() {\n // Sets variables for \"Review Data\" sheet\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const dataSheet = ss.getSheetByName(\"Review Data\");\n const rows = dataSheet.getDataRange();\n const numRows = rows.getNumRows();\n const values = rows.getValues();\n const headerRow = values[0];\n\n // Checks to see if \"Entity Sentiment Data\" sheet is present, and\n // if not, creates a new sheet and sets the header row.\n const entitySheet = ss.getSheetByName(\"Entity Sentiment Data\");\n if (entitySheet == null) {\n ss.insertSheet(\"Entity Sentiment Data\");\n const entitySheet = ss.getSheetByName(\"Entity Sentiment Data\");\n const esHeaderRange = entitySheet.getRange(1, 1, 1, 6);\n const esHeader = [\n [\n \"Review ID\",\n \"Entity\",\n \"Salience\",\n \"Sentiment Score\",\n \"Sentiment Magnitude\",\n \"Number of mentions\",\n ],\n ];\n esHeaderRange.setValues(esHeader);\n }\n\n // Finds the column index for comments, language_detected,\n // and comments_english columns.\n const textColumnIdx = headerRow.indexOf(COLUMN_NAME.COMMENTS);\n const entityColumnIdx = headerRow.indexOf(COLUMN_NAME.ENTITY);\n const idColumnIdx = headerRow.indexOf(COLUMN_NAME.ID);\n if (entityColumnIdx === -1) {\n Browser.msgBox(\n `Error: Could not find the column named ${COLUMN_NAME.ENTITY}. Please create an empty column with header \"entity_sentiment\" on the Review Data tab.`,\n );\n return; // bail\n }\n\n ss.toast(\"Analyzing entities and sentiment...\");\n for (let i = 0; i < numRows; ++i) {\n const value = values[i];\n const commentEnCellVal = value[textColumnIdx];\n const entityCellVal = value[entityColumnIdx];\n const reviewId = value[idColumnIdx];\n\n // Calls retrieveEntitySentiment function for each row that has a comment\n // and also an empty entity_sentiment cell value.\n if (commentEnCellVal && !entityCellVal) {\n const nlData = retrieveEntitySentiment(commentEnCellVal);\n // Pastes each entity and sentiment score into Entity Sentiment Data sheet.\n const newValues = [];\n for (let entity in nlData.entities) {\n entity = nlData.entities[entity];\n const row = [\n reviewId,\n entity.name,\n entity.salience,\n entity.sentiment.score,\n entity.sentiment.magnitude,\n entity.mentions.length,\n ];\n newValues.push(row);\n }\n if (newValues.length) {\n entitySheet\n .getRange(\n entitySheet.getLastRow() + 1,\n 1,\n newValues.length,\n newValues[0].length,\n )\n .setValues(newValues);\n }\n // Pastes \"complete\" into entity_sentiment column to denote completion of NL API call.\n dataSheet.getRange(i + 1, entityColumnIdx + 1).setValue(\"complete\");\n }\n }\n}\n\n/**\n * Calls the Cloud Natural Language API with a string of text to analyze\n * entities and sentiment present in the string.\n * @param {String} the string for entity sentiment analysis\n * @return {Object} the entities and related sentiment present in the string\n */\nfunction retrieveEntitySentiment(line) {\n const apiKey = myApiKey;\n const apiEndpoint = `https://language.googleapis.com/v1/documents:analyzeEntitySentiment?key=${apiKey}`;\n // Creates a JSON request, with text string, language, type and encoding\n const nlData = {\n document: {\n language: \"en-us\",\n type: \"PLAIN_TEXT\",\n content: line,\n },\n encodingType: \"UTF8\",\n };\n // Packages all of the options and the data together for the API call.\n const nlOptions = {\n method: \"post\",\n contentType: \"application/json\",\n payload: JSON.stringify(nlData),\n };\n // Makes the API call.\n const response = UrlFetchApp.fetch(apiEndpoint, nlOptions);\n return JSON.parse(response);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.364Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":164,"estimatedTokens":1326}}1106{"id":"doc-document_service_apps_script_google_for_develope-3b30c66c","source":"documentation","title":"Document Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/service_document","text":"Example:\n```text\n// Open a document by ID.\nvar doc = DocumentApp.openById('DOCUMENT_ID');\n\n// Create and open a document.\ndoc = DocumentApp.create('Document Name');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.369Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":10,"estimatedTokens":46}}1107{"id":"doc-aggregate_content_from_multiple_documents_apps_s-57e2fddf","source":"documentation","title":"Aggregate content from multiple documents | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/aggregate-document-content","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/aggregate-document-content\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * This file containts the main application functions that import data from\n * summary documents into the body of the main document.\n */\n\n// Application constants\nconst APP_TITLE = \"Document summary importer\"; // Application name\nconst PROJECT_FOLDER_NAME = \"Project statuses\"; // Drive folder for the source files.\n\n// Below are the parameters used to identify which content to import from the source documents\n// and which content has already been imported.\nconst FIND_TEXT_KEYWORDS = \"Summary\"; // String that must be found in the heading above the table (case insensitive).\nconst APP_STYLE = DocumentApp.ParagraphHeading.HEADING3; // Style that must be applied to heading above the table.\nconst TEXT_COLOR = \"#2e7d32\"; // Color applied to heading after import to avoid duplication.\n\n/**\n * Updates the main document, importing content from the source files.\n * Uses the above parameters to locate content to be imported.\n *\n * Called from menu option.\n */\nfunction performImport() {\n // Gets the folder in Drive associated with this application.\n const folder = getFolderByName_(PROJECT_FOLDER_NAME);\n // Gets the Google Docs files found in the folder.\n const files = getFiles(folder);\n\n // Warns the user if the folder is empty.\n const ui = DocumentApp.getUi();\n if (files.length === 0) {\n const msg = `No files found in the folder '${PROJECT_FOLDER_NAME}'.\n Run '${MENU.SETUP}' | '${MENU.SAMPLES}' from the menu\n if you'd like to create samples files.`;\n ui.alert(APP_TITLE, msg, ui.ButtonSet.OK);\n return;\n }\n\n /** Processes main document */\n // Gets the active document and body section.\n const docTarget = DocumentApp.getActiveDocument();\n const docTargetBody = docTarget.getBody();\n\n // Appends import summary section to the end of the target document.\n // Adds a horizontal line and a header with today's date and a title string.\n docTargetBody.appendHorizontalRule();\n const dateString = Utilities.formatDate(\n new Date(),\n Session.getScriptTimeZone(),\n \"MMMM dd, yyyy\",\n );\n const headingText = `Imported: ${dateString}`;\n docTargetBody.appendParagraph(headingText).setHeading(APP_STYLE);\n // Appends a blank paragraph for spacing.\n docTargetBody.appendParagraph(\" \");\n\n /** Process source documents */\n // Iterates through each source document in the folder.\n // Copies and pastes new updates to the main document.\n const noContentList = [];\n let numUpdates = 0;\n for (const id of files) {\n // Opens source document; get info and body.\n const docOpen = DocumentApp.openById(id);\n const docName = docOpen.getName();\n const docHtml = docOpen.getUrl();\n const docBody = docOpen.getBody();\n\n // Gets summary content from document and returns as object {content:content}\n const content = getContent(docBody);\n\n // Logs if document doesn't contain content to be imported.\n if (!content) {\n noContentList.push(docName);\n continue;\n }\n numUpdates++;\n // Inserts content into the main document.\n // Appends a title/url reference link back to source document.\n docTargetBody\n .appendParagraph(\"\")\n .appendText(`${docName}`)\n .setLinkUrl(docHtml);\n // Appends a single-cell table and pastes the content.\n docTargetBody.appendTable(content);\n docOpen.saveAndClose();\n }\n /** Provides an import summary */\n docTarget.saveAndClose();\n let msg = `Number of documents updated: ${numUpdates}`;\n if (noContentList.length !== 0) {\n msg += \"\\n\\nThe following documents had no updates:\";\n for (const file of noContentList) {\n msg += `\\n ${file}`;\n }\n }\n ui.alert(APP_TITLE, msg, ui.ButtonSet.OK);\n}\n\n/**\n * Updates the main document drawing content from source files.\n * Uses the parameters at the top of this file to locate content to import.\n *\n * Called from performImport().\n */\nfunction getContent(body) {\n // Finds the heading paragraph with matching style, keywords and !color.\n let parValidHeading;\n const searchType = DocumentApp.ElementType.PARAGRAPH;\n const searchHeading = APP_STYLE;\n let searchResult = null;\n\n // Gets and loops through all paragraphs that match the style of APP_STYLE.\n while (true) {\n searchResult = body.findElement(searchType, searchResult);\n if (!searchResult) {\n break;\n }\n\n const par = searchResult.getElement().asParagraph();\n if (par.getHeading() === searchHeading) {\n // If heading style matches, searches for text string (case insensitive).\n const findPos = par.findText(`(?i)${FIND_TEXT_KEYWORDS}`);\n if (findPos !== null) {\n // If text color is green, then the paragraph isn't a new summary to copy.\n if (par.editAsText().getForegroundColor() !== TEXT_COLOR) {\n parValidHeading = par;\n }\n }\n }\n }\n\n if (!parValidHeading) {\n return;\n }\n // Updates the heading color to indicate that the summary has been imported.\n const style = {};\n style[DocumentApp.Attribute.FOREGROUND_COLOR] = TEXT_COLOR;\n parValidHeading.setAttributes(style);\n parValidHeading.appendText(\" [Exported]\");\n\n // Gets the content from the table following the valid heading.\n const elemObj = parValidHeading.getNextSibling().asTable();\n const content = elemObj.copy();\n\n return content;\n}\n\n/**\n * Gets the IDs of the Docs files within the folder that contains source files.\n *\n * Called from function performImport().\n */\nfunction getFiles(folder) {\n // Only gets Docs files.\n const files = folder.getFilesByType(MimeType.GOOGLE_DOCS);\n const docIDs = [];\n while (files.hasNext()) {\n const file = files.next();\n docIDs.push(file.getId());\n }\n return docIDs;\n}\n```\n\nExample:\n```text\n</section>\n<section>\n <h3>Menu.gs</h3>\n```\n\nExample:\n```text\n/**\n * Copyright 2022 Google LLC\n *\n * 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 * http://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.\n */\n\n/**\n * This file contains the functions that build the custom menu.\n */\n// Menu constants for easy access to update.\nconst MENU = {\n NAME: \"Import summaries\",\n IMPORT: \"Import summaries\",\n SETUP: \"Configure\",\n NEW_INSTANCE: \"Setup new instance\",\n TEMPLATE: \"Create starter template\",\n SAMPLES: \"Run demo setup with sample documents\",\n};\n\n/**\n * Creates custom menu when the document is opened.\n */\nfunction onOpen() {\n const ui = DocumentApp.getUi();\n ui.createMenu(MENU.NAME)\n .addItem(MENU.IMPORT, \"performImport\")\n .addSeparator()\n .addSubMenu(\n ui\n .createMenu(MENU.SETUP)\n .addItem(MENU.NEW_INSTANCE, \"setupConfig\")\n .addItem(MENU.TEMPLATE, \"createSampleFile\")\n .addSeparator()\n .addItem(MENU.SAMPLES, \"setupWithSamples\"),\n )\n .addItem(\"About\", \"aboutApp\")\n .addToUi();\n}\n\n/**\n * About box for context and contact.\n * TODO: Personalize\n */\nfunction aboutApp() {\n const msg = `\n ${APP_TITLE}\n Version: 1.0\n Contact: <Developer Email goes here>`;\n\n const ui = DocumentApp.getUi();\n ui.alert(\"About this application\", msg, ui.ButtonSet.OK);\n}\n```\n\nExample:\n```text\n</section>\n<section>\n <h3>Setup.gs</h3>\n```\n\nExample:\n```text\n/**\n * Copyright 2022 Google LLC\n *\n * 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 * http://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.\n */\n\n/**\n * This file contains functions that create the template and sample documents.\n */\n\n/**\n * Runs full setup configuration, with option to include samples.\n *\n * Called from menu & setupWithSamples()\n *\n * @param {boolean} includeSamples - Optional, if true creates samples files. *\n */\nfunction setupConfig(includeSamples) {\n // Gets folder to store documents in.\n const folder = getFolderByName_(PROJECT_FOLDER_NAME);\n\n let msg = `\\nDrive Folder for Documents: '${PROJECT_FOLDER_NAME}'\n \\nURL: \\n${folder.getUrl()}`;\n\n // Creates sample documents for testing.\n // Remove sample document creation and add your own process as needed.\n if (includeSamples) {\n let filesCreated = 0;\n for (const doc of samples.documents) {\n filesCreated += createGoogleDoc(doc, folder, true);\n }\n msg += `\\n\\nFiles Created: ${filesCreated}`;\n }\n const ui = DocumentApp.getUi();\n ui.alert(`${APP_TITLE} [Setup]`, msg, ui.ButtonSet.OK);\n}\n\n/**\n * Creates a single document instance in the application folder.\n * Includes import settings already created [Heading | Keywords | Table]\n *\n * Called from menu.\n */\nfunction createSampleFile() {\n // Creates a new Google Docs document.\n const templateName = `[Template] ${APP_TITLE}`;\n const doc = DocumentApp.create(templateName);\n const docId = doc.getId();\n\n const msg = `\\nDocument created: '${templateName}'\n \\nURL: \\n${doc.getUrl()}`;\n\n // Adds template content to the body.\n const body = doc.getBody();\n\n body.setText(templateName);\n body.getParagraphs()[0].setHeading(DocumentApp.ParagraphHeading.TITLE);\n body\n .appendParagraph(\"Description\")\n .setHeading(DocumentApp.ParagraphHeading.HEADING1);\n body.appendParagraph(\"\");\n\n const dateString = Utilities.formatDate(\n new Date(),\n Session.getScriptTimeZone(),\n \"MMMM dd, yyyy\",\n );\n body\n .appendParagraph(`${FIND_TEXT_KEYWORDS} - ${dateString}`)\n .setHeading(APP_STYLE);\n body.appendTable().appendTableRow().appendTableCell(\"TL;DR\");\n body.appendParagraph(\"\");\n\n // Gets folder to store documents in.\n const folder = getFolderByName_(PROJECT_FOLDER_NAME);\n\n // Moves document to application folder.\n DriveApp.getFileById(docId).moveTo(folder);\n\n const ui = DocumentApp.getUi();\n ui.alert(`${APP_TITLE} [Template]`, msg, ui.ButtonSet.OK);\n}\n\n/**\n * Configures application for demonstration by setting it up with sample documents.\n *\n * Called from menu | Calls setupConfig with option set to true.\n */\nfunction setupWithSamples() {\n setupConfig(true);\n}\n\n/**\n * Sample document names and demo content.\n * {object} samples[]\n */\nconst samples = {\n documents: [\n {\n name: \"Project GHI\",\n description: \"Google Workspace Add-on inventory review.\",\n content:\n \"Reviewed all of the currently in-use and proposed Google Workspace Add-ons. Will perform an assessment on how we can reduce overlap, reduce licensing costs, and limit security exposures. \\n\\nNext week's goal is to report findings back to the Corp Ops team.\",\n },\n {\n name: \"Project DEF\",\n description: \"Improve IT networks within the main corporate building.\",\n content:\n \"Primarily focused on 2nd thru 5th floors in the main corporate building evaluating the network infrastructure. Benchmarking tests were performed and results are being analyzed. \\n\\nWill submit all findings, analysis, and recommendations next week for committee review.\",\n },\n {\n name: \"Project ABC\",\n description:\n \"Assess existing Google Chromebook inventory and recommend upgrades where necessary.\",\n content:\n \"Concluded a pilot program with the Customer Service department to perform inventory and update inventory records with Chromebook hardware, Chrome OS versions, and installed apps. \\n\\nScheduling a work plan and seeking necessary go-forward approvals for next week.\",\n },\n ],\n common:\n 'This sample document is configured to work with the Import summaries custom menu. For the import to work, the source documents used must contain a specific keyword (currently set to \"Summary\"). The keyword must reside in a paragraph with a set style (currently set to \"Heading 3\") that is directly followed by a single-cell table. The table contains the contents to be imported into the primary document.\\n\\nWhile those rules might seem precise, it\\'s how the application programmatically determines what content is meant to be imported and what can be ignored. Once a summary has been imported, the script updates the heading font to a new color (currently set to Green, hex \\'#2e7d32\\') to ensure the app ignores it in future imports. You can change these settings in the Apps Script code.',\n};\n\n/**\n * Creates a sample document in application folder.\n * Includes import settings already created [Heading | Keywords | Table].\n * Inserts demo data from samples[].\n *\n * Called from menu.\n */\nfunction createGoogleDoc(document, folder, duplicate) {\n // Checks for duplicates.\n if (!duplicate) {\n // Doesn't create file of same name if one already exists.\n if (folder.getFilesByName(document.name).hasNext()) {\n return 0; // File not created.\n }\n }\n\n // Creates a new Google Docs document.\n const doc = DocumentApp.create(document.name).setName(document.name);\n const docId = doc.getId();\n\n // Adds boilerplate content to the body.\n const body = doc.getBody();\n\n body.setText(document.name);\n body.getParagraphs()[0].setHeading(DocumentApp.ParagraphHeading.TITLE);\n body\n .appendParagraph(\"Description\")\n .setHeading(DocumentApp.ParagraphHeading.HEADING1);\n body.appendParagraph(document.description);\n body\n .appendParagraph(\"Usage Instructions\")\n .setHeading(DocumentApp.ParagraphHeading.HEADING1);\n body.appendParagraph(samples.common);\n\n const dateString = Utilities.formatDate(\n new Date(),\n Session.getScriptTimeZone(),\n \"MMMM dd, yyyy\",\n );\n body\n .appendParagraph(`${FIND_TEXT_KEYWORDS} - ${dateString}`)\n .setHeading(APP_STYLE);\n body.appendTable().appendTableRow().appendTableCell(document.content);\n body.appendParagraph(\"\");\n\n // Moves document to application folder.\n DriveApp.getFileById(docId).moveTo(folder);\n\n // Returns if successfully created.\n return 1;\n}\n```\n\nExample:\n```text\n</section>\n<section>\n <h3>Utilities.gs</h3>\n```\n\nExample:\n```text\n/**\n * Copyright 2022 Google LLC\n *\n * 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 * http://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.\n */\n\n/**\n * This file contains common utility functions.\n */\n\n/**\n * Returns a Drive folder located in same folder that the application document is located.\n * Checks if the folder exists and returns that folder, or creates new one if not found.\n *\n * @param {string} folderName - Name of the Drive folder.\n * @return {object} Google Drive folder\n */\nfunction getFolderByName_(folderName) {\n // Gets the Drive folder where the current document is located.\n const docId = DocumentApp.getActiveDocument().getId();\n const parentFolder = DriveApp.getFileById(docId).getParents().next();\n\n // Iterates subfolders to check if folder already exists.\n const subFolders = parentFolder.getFolders();\n while (subFolders.hasNext()) {\n const folder = subFolders.next();\n\n // Returns the existing folder if found.\n if (folder.getName() === folderName) {\n return folder;\n }\n }\n // Creates a new folder if one doesn't already exist.\n return parentFolder\n .createFolder(folderName)\n .setDescription(\n `Created by ${APP_TITLE} application to store documents to process`,\n );\n}\n\n/**\n * Test function to run getFolderByName_.\n * @logs details of created Google Drive folder.\n */\nfunction test_getFolderByName() {\n // Gets the folder in Drive associated with this application.\n const folder = getFolderByName_(PROJECT_FOLDER_NAME);\n\n console.log(\n `Name: ${folder.getName()}\\rID: ${folder.getId()}\\rURL:${folder.getUrl()}\\rDescription: ${folder.getDescription()}`,\n );\n // Uncomment the following to automatically delete the test folder.\n // folder.setTrashed(true);\n}\n```\n\nExample:\n```text\n</section>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.370Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":533,"estimatedTokens":4347}}1108{"id":"doc-calculate_a_tiered_pricing_discount_apps_script_-116d8b5a","source":"documentation","title":"Calculate a tiered pricing discount | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/custom-functions/tier-pricing","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/custom-functions/tier-pricing\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Calculates the tiered pricing discount.\n *\n * You must provide a value to calculate its discount. The value can be a string or a reference\n * to a cell that contains a string.\n * You must provide a data table range, for example, $B$4:$D$7, that includes the\n * tier start, end, and percent columns. If your table has headers, don't include\n * the headers in the range.\n *\n * @param {string} value The value to calculate the discount for, which can be a string or a\n * reference to a cell that contains a string.\n * @param {string} table The tier table data range using A1 notation.\n * @return number The total discount amount for the value.\n * @customfunction\n *\n */\nfunction tierPrice(value, table) {\n let total = 0;\n // Creates an array for each row of the table and loops through each array.\n for (const [start, end, percent] of table) {\n // Checks if the value is less than the starting value of the tier. If it is less, the loop stops.\n if (value < start) {\n break;\n }\n // Calculates the portion of the value to be multiplied by the tier's percent value.\n const amount = Math.min(value, end) - start;\n // Multiplies the amount by the tier's percent value and adds the product to the total.\n total += amount * percent;\n }\n return total;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.371Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":55,"estimatedTokens":506}}1109{"id":"doc-language_service_apps_script_google_for_develope-0649c60d","source":"documentation","title":"Language Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/language","text":"Example:\n```text\n// The code below will write 'Esta es una prueba' to the log.\nvar spanish = LanguageApp.translate('This is a test', 'en', 'es');\nLogger.log(spanish);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.372Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":8,"estimatedTokens":46}}1110{"id":"doc-create_a_sign_up_for_sessions_at_a_conference_ap-73250c9c","source":"documentation","title":"Create a sign-up for sessions at a conference | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/event-session-signup","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/event-session-signup\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Inserts a custom menu when the spreadsheet opens.\n */\nfunction onOpen() {\n SpreadsheetApp.getUi()\n .createMenu(\"Conference\")\n .addItem(\"Set up conference\", \"setUpConference_\")\n .addToUi();\n}\n\n/**\n * Uses the conference data in the spreadsheet to create\n * Google Calendar events, a Google Form, and a trigger that allows the script\n * to react to form responses.\n */\nfunction setUpConference_() {\n const scriptProperties = PropertiesService.getScriptProperties();\n if (scriptProperties.getProperty(\"calId\")) {\n Browser.msgBox(\n \"Your conference is already set up. Look in Google Drive for your\" +\n \" sign-up form!\",\n );\n return;\n }\n const ss = SpreadsheetApp.getActive();\n const sheet = ss.getSheetByName(\"Conference Setup\");\n const range = sheet.getDataRange();\n const values = range.getValues();\n setUpCalendar_(values, range);\n setUpForm_(ss, values);\n ScriptApp.newTrigger(\"onFormSubmit\")\n .forSpreadsheet(ss)\n .onFormSubmit()\n .create();\n}\n\n/**\n * Creates a Google Calendar with events for each conference session in the\n * spreadsheet, then writes the event IDs to the spreadsheet for future use.\n * @param {Array<string[]>} values Cell values for the spreadsheet range.\n * @param {Range} range A spreadsheet range that contains conference data.\n */\nfunction setUpCalendar_(values, range) {\n const cal = CalendarApp.createCalendar(\"Conference Calendar\");\n // Start at 1 to skip the header row.\n for (let i = 1; i < values.length; i++) {\n const session = values[i];\n const title = session[0];\n const start = joinDateAndTime_(session[1], session[2]);\n const end = joinDateAndTime_(session[1], session[3]);\n const options = { location: session[4], sendInvites: true };\n const event = cal\n .createEvent(title, start, end, options)\n .setGuestsCanSeeGuests(false);\n session[5] = event.getId();\n }\n range.setValues(values);\n\n // Stores the ID for the Calendar, which is needed to retrieve events by ID.\n const scriptProperties = PropertiesService.getScriptProperties();\n scriptProperties.setProperty(\"calId\", cal.getId());\n}\n\n/**\n * Creates a single Date object from separate date and time cells.\n *\n * @param {Date} date A Date object from which to extract the date.\n * @param {Date} time A Date object from which to extract the time.\n * @return {Date} A Date object representing the combined date and time.\n */\nfunction joinDateAndTime_(date_, time) {\n const processedDate = new Date(date_);\n processedDate.setHours(time.getHours());\n processedDate.setMinutes(time.getMinutes());\n return processedDate;\n}\n\n/**\n * Creates a Google Form that allows respondents to select which conference\n * sessions they would like to attend, grouped by date and start time in the\n * caller's time zone.\n *\n * @param {Spreadsheet} ss The spreadsheet that contains the conference data.\n * @param {Array<String[]>} values Cell values for the spreadsheet range.\n */\nfunction setUpForm_(ss, values) {\n // Group the sessions by date and time so that they can be passed to the form.\n const schedule = {};\n // Start at 1 to skip the header row.\n for (let i = 1; i < values.length; i++) {\n const session = values[i];\n const day = session[1].toLocaleDateString();\n const time = session[2].toLocaleTimeString();\n if (!schedule[day]) {\n schedule[day] = {};\n }\n if (!schedule[day][time]) {\n schedule[day][time] = [];\n }\n schedule[day][time].push(session[0]);\n }\n\n // Creates the form and adds a multiple-choice question for each timeslot.\n const form = FormApp.create(\"Conference Form\");\n form.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());\n form.addTextItem().setTitle(\"Name\").setRequired(true);\n form.addTextItem().setTitle(\"Email\").setRequired(true);\n for (const day of Object.keys(schedule)) {\n form.addSectionHeaderItem().setTitle(`Sessions for ${day}`);\n for (const time of Object.keys(schedule[day])) {\n form\n .addMultipleChoiceItem()\n .setTitle(`${time} ${day}`)\n .setChoiceValues(schedule[day][time]);\n }\n }\n}\n\n/**\n * Sends out calendar invitations and a\n * personalized Google Docs itinerary after a user responds to the form.\n *\n * @param {Object} e The event parameter for form submission to a spreadsheet;\n * see https://developers.google.com/apps-script/understanding_events\n */\nfunction onFormSubmit(e) {\n const user = {\n name: e.namedValues.Name[0],\n email: e.namedValues.Email[0],\n };\n\n // Grab the session data again so that we can match it to the user's choices.\n const response = [];\n const values = SpreadsheetApp.getActive()\n .getSheetByName(\"Conference Setup\")\n .getDataRange()\n .getValues();\n for (let i = 1; i < values.length; i++) {\n const session = values[i];\n const title = session[0];\n const day = session[1].toLocaleDateString();\n const time = session[2].toLocaleTimeString();\n const timeslot = `${time} ${day}`;\n\n // For every selection in the response, find the matching timeslot and title\n // in the spreadsheet and add the session data to the response array.\n if (e.namedValues[timeslot] && e.namedValues[timeslot] === title) {\n response.push(session);\n }\n }\n sendInvites_(user, response);\n sendDoc_(user, response);\n}\n\n/**\n * Add the user as a guest for every session he or she selected.\n * @param {object} user An object that contains the user's name and email.\n * @param {Array<String[]>} response An array of data for the user's session choices.\n */\nfunction sendInvites_(user, response) {\n const id = ScriptProperties.getProperty(\"calId\");\n const cal = CalendarApp.getCalendarById(id);\n for (let i = 0; i < response.length; i++) {\n cal.getEventSeriesById(response[i][5]).addGuest(user.email);\n }\n}\n\n/**\n * Creates and shares a personalized Google Doc that shows the user's itinerary.\n * @param {object} user An object that contains the user's name and email.\n * @param {Array<string[]>} response An array of data for the user's session choices.\n */\nfunction sendDoc_(user, response) {\n const doc = DocumentApp.create(\n `Conference Itinerary for ${user.name}`,\n ).addEditor(user.email);\n const body = doc.getBody();\n let table = [[\"Session\", \"Date\", \"Time\", \"Location\"]];\n for (let i = 0; i < response.length; i++) {\n table.push([\n response[i][0],\n response[i][1].toLocaleDateString(),\n response[i][2].toLocaleTimeString(),\n response[i][4],\n ]);\n }\n body\n .insertParagraph(0, doc.getName())\n .setHeading(DocumentApp.ParagraphHeading.HEADING1);\n table = body.appendTable(table);\n table.getRow(0).editAsText().setBold(true);\n doc.saveAndClose();\n\n // Emails a link to the Doc as well as a PDF copy.\n MailApp.sendEmail({\n to: user.email,\n subject: doc.getName(),\n body: `Thanks for registering! Here's your itinerary: ${doc.getUrl()}`,\n attachments: doc.getAs(MimeType.PDF),\n });\n}\n\n/**\n * Removes the calId script property so that the 'setUpConference_()' can be run again.\n */\nfunction resetProperties() {\n const scriptProperties = PropertiesService.getScriptProperties();\n scriptProperties.deleteAllProperties();\n}\n```\n\nExample:\n```text\n</section>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.372Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":238,"estimatedTokens":1983}}1111{"id":"doc-library_quickstart_apps_script_google_for_develo-102dcb27","source":"documentation","title":"Library quickstart | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/quickstart/library","text":"Example:\n```text\n/**\n * Removes duplicate rows from the current sheet.\n */\nfunction removeDuplicates() {\n const sheet = SpreadsheetApp.getActiveSheet();\n const data = sheet.getDataRange().getValues();\n const uniqueData = {};\n for (const row of data) {\n const key = row.join();\n uniqueData[key] = uniqueData[key] || row;\n }\n sheet.clearContents();\n const newData = Object.values(uniqueData);\n sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);\n}\n```\n\nExample:\n```text\nfunction runLibrary() {\n Removeduplicaterows.removeDuplicates();\n}\n```\n\nExample:\n```text\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst data = sheet.getDataRange().getValues();\n```\n\nExample:\n```text\nconst newData = Object.values(uniqueData);\n```\n\nExample:\n```text\nuniqueData[key] = uniqueData[key] || row;\n```\n\nExample:\n```text\nsheet.clearContents();\nconst newData = Object.values(uniqueData);\nsheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);\n```\n\nExample:\n```text\nif(row.join() == newData[j].join()){\n duplicate = true;\n }\n```\n\nExample:\n```text\nif(row[0] == newData[j][0] && row[1] == newData[j][1]){\n duplicate = true;\n }\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.373Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":64,"estimatedTokens":299}}1112{"id":"doc-create_a_mail_merge_with_gmail_google_sheets_app-c1c00321","source":"documentation","title":"Create a mail merge with Gmail & Google Sheets | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/mail-merge","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/mail-merge\n\n/*\nCopyright 2022 Martin Hawksey\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * @OnlyCurrentDoc\n */\n\n/**\n * Change these to match the column names you are using for email\n * recipient addresses and email sent column.\n */\nconst RECIPIENT_COL = \"Recipient\";\nconst EMAIL_SENT_COL = \"Email Sent\";\n\n/**\n * Creates the menu item \"Mail Merge\" for user to run scripts on drop-down.\n */\nfunction onOpen() {\n const ui = SpreadsheetApp.getUi();\n ui.createMenu(\"Mail Merge\").addItem(\"Send Emails\", \"sendEmails\").addToUi();\n}\n\n/**\n * Sends emails from sheet data.\n * @param {string} subjectLine (optional) for the email draft message\n * @param {Sheet} sheet to read data from\n */\nfunction sendEmails(subjectLine, sheet = SpreadsheetApp.getActiveSheet()) {\n // option to skip browser prompt if you want to use this code in other projects\n let processedSubjectLine = subjectLine;\n if (!processedSubjectLine) {\n processedSubjectLine = Browser.inputBox(\n \"Mail Merge\",\n \"Type or copy/paste the subject line of the Gmail \" +\n \"draft message you would like to mail merge with:\",\n Browser.Buttons.OK_CANCEL,\n );\n\n if (processedSubjectLine === \"cancel\" || processedSubjectLine === \"\") {\n // If no subject line, finishes up\n return;\n }\n }\n\n // Gets the draft Gmail message to use as a template\n const emailTemplate = getGmailTemplateFromDrafts_(processedSubjectLine);\n\n // Gets the data from the passed sheet\n const dataRange = sheet.getDataRange();\n // Fetches displayed values for each row in the Range HT Andrew Roberts\n // https://mashe.hawksey.info/2020/04/a-bulk-email-mail-merge-with-gmail-and-google-sheets-solution-evolution-using-v8/#comment-187490\n // @see https://developers.google.com/apps-script/reference/spreadsheet/range#getdisplayvalues\n const data = dataRange.getDisplayValues();\n\n // Assumes row 1 contains our column headings\n const heads = data.shift();\n\n // Gets the index of the column named 'Email Status' (Assumes header names are unique)\n // @see http://ramblings.mcpher.com/Home/excelquirks/gooscript/arrayfunctions\n const emailSentColIdx = heads.indexOf(EMAIL_SENT_COL);\n\n // Converts 2d array into an object array\n // See https://stackoverflow.com/a/22917499/1027723\n // For a pretty version, see https://mashe.hawksey.info/?p=17869/#comment-184945\n const obj = data.map((r) =>\n heads.reduce((o, k, i) => {\n o[k] = r[i] || \"\";\n return o;\n }, {}),\n );\n\n // Creates an array to record sent emails\n const out = [];\n\n // Loops through all the rows of data\n obj.forEach((row, rowIdx) => {\n // Only sends emails if email_sent cell is blank and not hidden by a filter\n if (row[EMAIL_SENT_COL] === \"\") {\n try {\n const msgObj = fillInTemplateFromObject_(emailTemplate.message, row);\n\n // See https://developers.google.com/apps-script/reference/gmail/gmail-app#sendEmail(String,String,String,Object)\n // If you need to send emails with unicode/emoji characters change GmailApp for MailApp\n // Uncomment advanced parameters as needed (see docs for limitations)\n GmailApp.sendEmail(row[RECIPIENT_COL], msgObj.subject, msgObj.text, {\n htmlBody: msgObj.html,\n // bcc: 'a.bcc@email.com',\n // cc: 'a.cc@email.com',\n // from: 'an.alias@email.com',\n // name: 'name of the sender',\n // replyTo: 'a.reply@email.com',\n // noReply: true, // if the email should be sent from a generic no-reply email address (not available to gmail.com users)\n attachments: emailTemplate.attachments,\n inlineImages: emailTemplate.inlineImages,\n });\n // Edits cell to record email sent date\n out.push([new Date()]);\n } catch (e) {\n // modify cell to record error\n out.push([e.message]);\n }\n } else {\n out.push([row[EMAIL_SENT_COL]]);\n }\n });\n\n // Updates the sheet with new data\n sheet.getRange(2, emailSentColIdx + 1, out.length).setValues(out);\n\n /**\n * Get a Gmail draft message by matching the subject line.\n * @param {string} subject_line to search for draft message\n * @return {object} containing the subject, plain and html message body and attachments\n */\n function getGmailTemplateFromDrafts_(subject_line) {\n try {\n // get drafts\n const drafts = GmailApp.getDrafts();\n // filter the drafts that match subject line\n const draft = drafts.filter(subjectFilter_(subject_line))[0];\n // get the message object\n const msg = draft.getMessage();\n\n // Handles inline images and attachments so they can be included in the merge\n // Based on https://stackoverflow.com/a/65813881/1027723\n // Gets all attachments and inline image attachments\n const allInlineImages = draft.getMessage().getAttachments({\n includeInlineImages: true,\n includeAttachments: false,\n });\n const attachments = draft\n .getMessage()\n .getAttachments({ includeInlineImages: false });\n const htmlBody = msg.getBody();\n\n // Creates an inline image object with the image name as key\n // (can't rely on image index as array based on insert order)\n const img_obj = allInlineImages.reduce((obj, i) => {\n obj[i.getName()] = i;\n return obj;\n }, {});\n\n //Regexp searches for all img string positions with cid\n const imgexp = /<img.*?src=\"cid:(.*?)\".*?alt=\"(.*?)\"[^\\>]+>/g;\n const matches = [...htmlBody.matchAll(imgexp)];\n\n //Initiates the allInlineImages object\n const inlineImagesObj = {};\n for (const match of matches) {\n inlineImagesObj[match[1]] = img_obj[match[2]];\n }\n\n return {\n message: {\n subject: subject_line,\n text: msg.getPlainBody(),\n html: htmlBody,\n },\n attachments: attachments,\n inlineImages: inlineImagesObj,\n };\n } catch (e) {\n throw new Error(\"Oops - can't find Gmail draft\");\n }\n\n /**\n * Filter draft objects with the matching subject linemessage by matching the subject line.\n * @param {string} subject_line to search for draft message\n * @return {object} GmailDraft object\n */\n function subjectFilter_(subject_line) {\n return (element) => {\n if (element.getMessage().getSubject() === subject_line) {\n return element;\n }\n };\n }\n }\n\n /**\n * Fill template string with data object\n * @see https://stackoverflow.com/a/378000/1027723\n * @param {string} template string containing {{}} markers which are replaced with data\n * @param {object} data object used to replace {{}} markers\n * @return {object} message replaced with data\n */\n function fillInTemplateFromObject_(template, data) {\n // We have two templates one for plain text and the html body\n // Stringifing the object means we can do a global replace\n let template_string = JSON.stringify(template);\n\n // Token replacement\n template_string = template_string.replace(/{{[^{}]+}}/g, (key) => {\n return escapeData_(data[key.replace(/[{}]+/g, \"\")] || \"\");\n });\n return JSON.parse(template_string);\n }\n\n /**\n * Escape cell data to make JSON safe\n * @see https://stackoverflow.com/a/9204218/1027723\n * @param {string} str to escape JSON special characters from\n * @return {string} escaped string\n */\n function escapeData_(str) {\n return str\n .replace(/[\\\\]/g, \"\\\\\\\\\")\n .replace(/[\\\"]/g, '\\\\\"')\n .replace(/[\\/]/g, \"\\\\/\")\n .replace(/[\\b]/g, \"\\\\b\")\n .replace(/[\\f]/g, \"\\\\f\")\n .replace(/[\\n]/g, \"\\\\n\")\n .replace(/[\\r]/g, \"\\\\r\")\n .replace(/[\\t]/g, \"\\\\t\");\n }\n}\n```\n\nExample:\n```text\nGmailApp.sendEmail(row[RECIPIENT_COL], msgObj.subject, msgObj.text, {\n htmlBody: msgObj.html,\n bcc: 'bcc@example.com',\n cc: 'cc@example.com',\n from: 'from.alias@example.com',\n name: 'name of the sender',\n replyTo: 'reply@example.com',\n // noReply: true, // if the email should be sent from a generic no-reply email address (not available to gmail.com users)\n```\n\nExample:\n```text\nGmailApp.sendEmail(row[RECIPIENT_COL], msgObj.subject, msgObj.text, {\n```\n\nExample:\n```text\nMailApp.sendEmail(row[RECIPIENT_COL], msgObj.subject, msgObj.text, {\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.374Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":259,"estimatedTokens":2232}}1113{"id":"doc-automation_quickstart_apps_script_google_for_dev-6570f4ea","source":"documentation","title":"Automation quickstart | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/quickstart/automation","text":"Example:\n```text\n/**\n * Creates a Google Doc and sends an email to the current user with a link to the doc.\n */\nfunction createAndSendDocument() {\n try {\n // Create a new Google Doc named 'Hello, world!'\n const doc = DocumentApp.create(\"Hello, world!\");\n\n // Access the body of the document, then add a paragraph.\n doc\n .getBody()\n .appendParagraph(\"This document was created by Google Apps Script.\");\n\n // Get the URL of the document.\n const url = doc.getUrl();\n\n // Get the email address of the active user - that's you.\n const email = Session.getActiveUser().getEmail();\n\n // Get the name of the document to use as an email subject line.\n const subject = doc.getName();\n\n // Append a new string to the \"url\" variable to use as an email body.\n const body = `Link to your doc: ${url}`;\n\n // Send yourself an email with a link to the document.\n GmailApp.sendEmail(email, subject, body);\n } catch (err) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", err.message);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.375Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":37,"estimatedTokens":270}}1114{"id":"doc-calculate_driving_distance_convert_meters_to_mil-e360577a","source":"documentation","title":"Calculate driving distance & convert meters to miles | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/custom-functions/calculate-driving-distance","text":"Example:\n```text\n/**\n * @OnlyCurrentDoc Limits the script to only accessing the current sheet.\n */\n\n/**\n * A special function that runs when the spreadsheet is open, used to add a\n * custom menu to the spreadsheet.\n */\nfunction onOpen() {\n try {\n const spreadsheet = SpreadsheetApp.getActive();\n const menuItems = [\n { name: \"Prepare sheet...\", functionName: \"prepareSheet_\" },\n { name: \"Generate step-by-step...\", functionName: \"generateStepByStep_\" },\n ];\n spreadsheet.addMenu(\"Directions\", menuItems);\n } catch (e) {\n // TODO (Developer) - Handle Exception\n console.log(`Failed with error: %s${e.error}`);\n }\n}\n\n/**\n * A custom function that converts meters to miles.\n *\n * @param {Number} meters The distance in meters.\n * @return {Number} The distance in miles.\n */\nfunction metersToMiles(meters) {\n if (typeof meters !== \"number\") {\n return null;\n }\n return (meters / 1000) * 0.621371;\n}\n\n/**\n * A custom function that gets the driving distance between two addresses.\n *\n * @param {String} origin The starting address.\n * @param {String} destination The ending address.\n * @return {Number} The distance in meters.\n */\nfunction drivingDistance(origin, destination) {\n const directions = getDirections_(origin, destination);\n return directions.routes[0].legs[0].distance.value;\n}\n\n/**\n * A function that adds headers and some initial data to the spreadsheet.\n */\nfunction prepareSheet_() {\n try {\n const sheet = SpreadsheetApp.getActiveSheet().setName(\"Settings\");\n const headers = [\n \"Start Address\",\n \"End Address\",\n \"Driving Distance (meters)\",\n \"Driving Distance (miles)\",\n ];\n const initialData = [\n \"350 5th Ave, New York, NY 10118\",\n \"405 Lexington Ave, New York, NY 10174\",\n ];\n sheet.getRange(\"A1:D1\").setValues([headers]).setFontWeight(\"bold\");\n sheet.getRange(\"A2:B2\").setValues([initialData]);\n sheet.setFrozenRows(1);\n sheet.autoResizeColumns(1, 4);\n } catch (e) {\n // TODO (Developer) - Handle Exception\n console.log(`Failed with error: %s${e.error}`);\n }\n}\n\n/**\n * Creates a new sheet containing step-by-step directions between the two\n * addresses on the \"Settings\" sheet that the user selected.\n */\nfunction generateStepByStep_() {\n try {\n const spreadsheet = SpreadsheetApp.getActive();\n const settingsSheet = spreadsheet.getSheetByName(\"Settings\");\n settingsSheet.activate();\n\n // Prompt the user for a row number.\n const selectedRow = Browser.inputBox(\n \"Generate step-by-step\",\n \"Please enter the row number of\" +\n \" the\" +\n \" addresses to use\" +\n ' (for example, \"2\"):',\n Browser.Buttons.OK_CANCEL,\n );\n if (selectedRow === \"cancel\") {\n return;\n }\n const rowNumber = Number(selectedRow);\n if (\n Number.isNaN(rowNumber) ||\n rowNumber < 2 ||\n rowNumber > settingsSheet.getLastRow()\n ) {\n Browser.msgBox(\n \"Error\",\n Utilities.formatString('Row \"%s\" is not valid.', selectedRow),\n Browser.Buttons.OK,\n );\n return;\n }\n\n // Retrieve the addresses in that row.\n const row = settingsSheet.getRange(rowNumber, 1, 1, 2);\n const rowValues = row.getValues();\n const origin = rowValues[0][0];\n const destination = rowValues[0][1];\n if (!origin || !destination) {\n Browser.msgBox(\n \"Error\",\n \"Row does not contain two addresses.\",\n Browser.Buttons.OK,\n );\n return;\n }\n\n // Get the raw directions information.\n const directions = getDirections_(origin, destination);\n\n // Create a new sheet and append the steps in the directions.\n const sheetName = `Driving Directions for Row ${rowNumber}`;\n let directionsSheet = spreadsheet.getSheetByName(sheetName);\n if (directionsSheet) {\n directionsSheet.clear();\n directionsSheet.activate();\n } else {\n directionsSheet = spreadsheet.insertSheet(\n sheetName,\n spreadsheet.getNumSheets(),\n );\n }\n const sheetTitle = Utilities.formatString(\n \"Driving Directions from %s to %s\",\n origin,\n destination,\n );\n const headers = [\n [sheetTitle, \"\", \"\"],\n [\"Step\", \"Distance (Meters)\", \"Distance (Miles)\"],\n ];\n const newRows = [];\n for (const step of directions.routes[0].legs[0].steps) {\n // Remove HTML tags from the instructions.\n const instructions = step.html_instructions\n .replace(/<br>|<div.*?>/g, \"\\n\")\n .replace(/<.*?>/g, \"\");\n newRows.push([instructions, step.distance.value]);\n }\n directionsSheet.getRange(1, 1, headers.length, 3).setValues(headers);\n directionsSheet\n .getRange(headers.length + 1, 1, newRows.length, 2)\n .setValues(newRows);\n directionsSheet\n .getRange(headers.length + 1, 3, newRows.length, 1)\n .setFormulaR1C1(\"=METERSTOMILES(R[0]C[-1])\");\n\n // Format the new sheet.\n directionsSheet.getRange(\"A1:C1\").merge().setBackground(\"#ddddee\");\n directionsSheet.getRange(\"A1:2\").setFontWeight(\"bold\");\n directionsSheet.setColumnWidth(1, 500);\n directionsSheet.getRange(\"B2:C\").setVerticalAlignment(\"top\");\n directionsSheet.getRange(\"C2:C\").setNumberFormat(\"0.00\");\n const stepsRange = directionsSheet\n .getDataRange()\n .offset(2, 0, directionsSheet.getLastRow() - 2);\n setAlternatingRowBackgroundColors_(stepsRange, \"#ffffff\", \"#eeeeee\");\n directionsSheet.setFrozenRows(2);\n SpreadsheetApp.flush();\n } catch (e) {\n // TODO (Developer) - Handle Exception\n console.log(`Failed with error: %s${e.error}`);\n }\n}\n\n/**\n * Sets the background colors for alternating rows within the range.\n * @param {Range} range The range to change the background colors of.\n * @param {string} oddColor The color to apply to odd rows (relative to the\n * start of the range).\n * @param {string} evenColor The color to apply to even rows (relative to the\n * start of the range).\n */\nfunction setAlternatingRowBackgroundColors_(range, oddColor, evenColor) {\n const backgrounds = [];\n for (let row = 1; row <= range.getNumRows(); row++) {\n const rowBackgrounds = [];\n for (let column = 1; column <= range.getNumColumns(); column++) {\n if (row % 2 === 0) {\n rowBackgrounds.push(evenColor);\n } else {\n rowBackgrounds.push(oddColor);\n }\n }\n backgrounds.push(rowBackgrounds);\n }\n range.setBackgrounds(backgrounds);\n}\n\n/**\n * A shared helper function used to obtain the full set of directions\n * information between two addresses. Uses the Apps Script Maps Service.\n *\n * @param {String} origin The starting address.\n * @param {String} destination The ending address.\n * @return {Object} The directions response object.\n */\nfunction getDirections_(origin, destination) {\n const directionFinder = Maps.newDirectionFinder();\n directionFinder.setOrigin(origin);\n directionFinder.setDestination(destination);\n const directions = directionFinder.getDirections();\n if (directions.status !== \"OK\") {\n throw directions.error_message;\n }\n return directions;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.375Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":228,"estimatedTokens":1767}}1115{"id":"doc-clean_up_data_in_a_google_sheets_spreadsheet_goo-a8857acc","source":"documentation","title":"Clean up data in a Google Sheets spreadsheet | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/samples/clean-sheet","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/add-ons/clean-sheet\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Application Constants\nconst APP_TITLE = \"Clean sheet\";\n\n/**\n * Identifies and deletes empty rows in selected range of active sheet.\n *\n * Cells that contain space characters are treated as non-empty.\n * The entire row, including the cells outside of the selected range,\n * must be empty to be deleted.\n *\n * Called from menu option.\n */\nfunction deleteEmptyRows() {\n const sheet = SpreadsheetApp.getActiveSheet();\n\n // Gets active selection and dimensions.\n const activeRange = sheet.getActiveRange();\n const rowCount = activeRange.getHeight();\n const firstActiveRow = activeRange.getRow();\n const columnCount = sheet.getMaxColumns();\n\n // Tests that the selection is a valid range.\n if (rowCount < 1) {\n showMessage(\"Select a valid range.\");\n return;\n }\n // Tests active range isn't too large to process. Enforces limit set to 10k.\n if (rowCount > 10000) {\n showMessage(\n \"Selected range too large. Select up to 10,000 rows at one time.\",\n );\n return;\n }\n\n // Utilizes an array of values for efficient processing to determine blank rows.\n const activeRangeValues = sheet\n .getRange(firstActiveRow, 1, rowCount, columnCount)\n .getValues();\n\n // Checks if array is all empty values.\n const valueFilter = (value) => value !== \"\";\n const isRowEmpty = (row) => {\n return row.filter(valueFilter).length === 0;\n };\n\n // Maps the range values as an object with value (to test) and corresponding row index (with offset from selection).\n const rowsToDelete = activeRangeValues\n .map((row, index) => ({ row, offset: index + activeRange.getRowIndex() }))\n .filter((item) => isRowEmpty(item.row)) // Test to filter out non-empty rows.\n .map((item) => item.offset); //Remap to include just the row indexes that will be removed.\n\n // Combines a sorted, ascending list of indexes into a set of ranges capturing consecutive values as start/end ranges.\n // Combines sequential empty rows for faster processing.\n const rangesToDelete = rowsToDelete.reduce((ranges, index) => {\n const currentRange = ranges[ranges.length - 1];\n if (currentRange && index === currentRange[1] + 1) {\n currentRange[1] = index;\n return ranges;\n }\n ranges.push([index, index]);\n return ranges;\n }, []);\n\n // Sends a list of row indexes to be deleted to the console.\n console.log(rangesToDelete);\n\n // Deletes the rows using REVERSE order to ensure proper indexing is used.\n for (const [start, end] of rangesToDelete.reverse()) {\n sheet.deleteRows(start, end - start + 1);\n }\n SpreadsheetApp.flush();\n}\n\n/**\n * Removes blank columns in a selected range.\n *\n * Cells containing Space characters are treated as non-empty.\n * The entire column, including cells outside of the selected range,\n * must be empty to be deleted.\n *\n * Called from menu option.\n */\nfunction deleteEmptyColumns() {\n const sheet = SpreadsheetApp.getActiveSheet();\n\n // Gets active selection and dimensions.\n const activeRange = sheet.getActiveRange();\n const rowCountMax = sheet.getMaxRows();\n const columnWidth = activeRange.getWidth();\n const firstActiveColumn = activeRange.getColumn();\n\n // Tests that the selection is a valid range.\n if (columnWidth < 1) {\n showMessage(\"Select a valid range.\");\n return;\n }\n // Tests active range is not too large to process. Enforces limit set to 1k.\n if (columnWidth > 1000) {\n showMessage(\n \"Selected range too large. Select up to 10,000 rows at one time.\",\n );\n return;\n }\n\n // Utilizes an array of values for efficient processing to determine blank columns.\n const activeRangeValues = sheet\n .getRange(1, firstActiveColumn, rowCountMax, columnWidth)\n .getValues();\n\n // Transposes the array of range values so it can be processed in order of columns.\n const activeRangeValuesTransposed = activeRangeValues[0].map((_, colIndex) =>\n activeRangeValues.map((row) => row[colIndex]),\n );\n\n // Checks if array is all empty values.\n const valueFilter = (value) => value !== \"\";\n const isColumnEmpty = (column) => {\n return column.filter(valueFilter).length === 0;\n };\n\n // Maps the range values as an object with value (to test) and corresponding column index (with offset from selection).\n const columnsToDelete = activeRangeValuesTransposed\n .map((column, index) => ({ column, offset: index + firstActiveColumn }))\n .filter((item) => isColumnEmpty(item.column)) // Test to filter out non-empty rows.\n .map((item) => item.offset); //Remap to include just the column indexes that will be removed.\n\n // Combines a sorted, ascending list of indexes into a set of ranges capturing consecutive values as start/end ranges.\n // Combines sequential empty columns for faster processing.\n const rangesToDelete = columnsToDelete.reduce((ranges, index) => {\n const currentRange = ranges[ranges.length - 1];\n if (currentRange && index === currentRange[1] + 1) {\n currentRange[1] = index;\n return ranges;\n }\n ranges.push([index, index]);\n return ranges;\n }, []);\n\n // Sends a list of column indexes to be deleted to the console.\n console.log(rangesToDelete);\n\n // Deletes the columns using REVERSE order to ensure proper indexing is used.\n for (const [start, end] of rangesToDelete.reverse()) {\n sheet.deleteColumns(start, end - start + 1);\n }\n SpreadsheetApp.flush();\n}\n\n/**\n * Trims all of the unused rows and columns outside of selected data range.\n *\n * Called from menu option.\n */\nfunction cropSheet() {\n const dataRange = SpreadsheetApp.getActiveSheet().getDataRange();\n const sheet = dataRange.getSheet();\n\n let numRows = dataRange.getNumRows();\n let numColumns = dataRange.getNumColumns();\n\n const maxRows = sheet.getMaxRows();\n const maxColumns = sheet.getMaxColumns();\n\n const numFrozenRows = sheet.getFrozenRows();\n const numFrozenColumns = sheet.getFrozenColumns();\n\n // If last data row is less than maximium row, then deletes rows after the last data row.\n if (numRows < maxRows) {\n numRows = Math.max(numRows, numFrozenRows + 1); // Don't crop empty frozen rows.\n sheet.deleteRows(numRows + 1, maxRows - numRows);\n }\n\n // If last data column is less than maximium column, then deletes columns after the last data column.\n if (numColumns < maxColumns) {\n numColumns = Math.max(numColumns, numFrozenColumns + 1); // Don't crop empty frozen columns.\n sheet.deleteColumns(numColumns + 1, maxColumns - numColumns);\n }\n}\n\n/**\n * Copies value of active cell to the blank cells beneath it.\n * Stops at last row of the sheet's data range if only blank cells are encountered.\n *\n * Called from menu option.\n */\nfunction fillDownData() {\n const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\n\n // Gets sheet's active cell and confirms it's not empty.\n const activeCell = sheet.getActiveCell();\n const activeCellValue = activeCell.getValue();\n\n if (!activeCellValue) {\n showMessage(\"The active cell is empty. Nothing to fill.\");\n return;\n }\n\n // Gets coordinates of active cell.\n const column = activeCell.getColumn();\n const row = activeCell.getRow();\n\n // Gets entire data range of the sheet.\n const dataRange = sheet.getDataRange();\n const dataRangeRows = dataRange.getNumRows();\n\n // Gets trimmed range starting from active cell to the end of sheet data range.\n const searchRange = dataRange.offset(\n row - 1,\n column - 1,\n dataRangeRows - row + 1,\n 1,\n );\n const searchValues = searchRange.getDisplayValues();\n\n // Find the number of empty rows below the active cell.\n let i = 1; // Start at 1 to skip the ActiveCell.\n while (searchValues[i] && searchValues[i][0] === \"\") {\n i++;\n }\n\n // If blanks exist, fill the range with values.\n if (i > 1) {\n const fillRange = searchRange.offset(0, 0, i, 1).setValue(activeCellValue);\n //sheet.setActiveRange(fillRange) // Uncomment to test affected range.\n } else {\n showMessage(\"There are no empty cells below the Active Cell to fill.\");\n }\n}\n\n/**\n * A helper function to display messages to user.\n *\n * @param {string} message - Message to be displayed.\n * @param {string} caller - {Optional} text to append to title.\n */\nfunction showMessage(message, caller) {\n // Sets the title using the APP_TITLE variable; adds optional caller string.\n let title = APP_TITLE;\n if (caller != null) {\n title += ` : ${caller}`;\n }\n\n const ui = SpreadsheetApp.getUi();\n ui.alert(title, message, ui.ButtonSet.OK);\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2022 Google LLC\n *\n * 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 * http://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.\n */\n\n/**\n * Creates a menu entry in the Google Sheets Extensions menu when the document is opened.\n *\n * @param {object} e The event parameter for a simple onOpen trigger.\n */\nfunction onOpen(e) {\n // Builds a menu that displays under the Extensions menu in Sheets.\n const menu = SpreadsheetApp.getUi().createAddonMenu();\n\n menu\n .addItem(\"Delete blank rows (from selected rows only)\", \"deleteEmptyRows\")\n .addItem(\n \"Delete blank columns (from selected columns only)\",\n \"deleteEmptyColumns\",\n )\n .addItem(\"Crop sheet to data range\", \"cropSheet\")\n .addSeparator()\n .addItem(\"Fill in blank rows below\", \"fillDownData\")\n .addSeparator()\n .addItem(\"About\", \"aboutApp\")\n .addToUi();\n}\n\n/**\n * Runs when the add-on is installed; calls onOpen() to ensure menu creation and\n * any other initializion work is done immediately. This method is only used by\n * the desktop add-on and is never called by the mobile version.\n *\n * @param {object} e The event parameter for a simple onInstall trigger.\n */\nfunction onInstall(e) {\n onOpen(e);\n}\n\n/**\n * About box for context and developer contact information.\n * TODO: Personalize\n */\nfunction aboutApp() {\n const msg = `\n Name: ${APP_TITLE}\n Version: 1.0\n Contact: <Developer Email Goes Here>`;\n\n const ui = SpreadsheetApp.getUi();\n ui.alert(\"About this application\", msg, ui.ButtonSet.OK);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.377Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":336,"estimatedTokens":2781}}1116{"id":"doc-send_personalized_appreciation_certificates_to_e-0d155551","source":"documentation","title":"Send personalized appreciation certificates to employees | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/employee-certificate","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/employee-certificate\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nconst slideTemplateId = \"PRESENTATION_ID\";\nconst tempFolderId = \"FOLDER_ID\"; // Create an empty folder in Google Drive\n\n/**\n * Creates a custom menu \"Appreciation\" in the spreadsheet\n * with drop-down options to create and send certificates\n */\nfunction onOpen() {\n const ui = SpreadsheetApp.getUi();\n ui.createMenu(\"Appreciation\")\n .addItem(\"Create certificates\", \"createCertificates\")\n .addSeparator()\n .addItem(\"Send certificates\", \"sendCertificates\")\n .addToUi();\n}\n\n/**\n * Creates a personalized certificate for each employee\n * and stores every individual Slides doc on Google Drive\n */\nfunction createCertificates() {\n // Load the Google Slide template file\n const template = DriveApp.getFileById(slideTemplateId);\n\n // Get all employee data from the spreadsheet and identify the headers\n const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\n const values = sheet.getDataRange().getValues();\n const headers = values[0];\n const empNameIndex = headers.indexOf(\"Employee Name\");\n const dateIndex = headers.indexOf(\"Date\");\n const managerNameIndex = headers.indexOf(\"Manager Name\");\n const titleIndex = headers.indexOf(\"Title\");\n const compNameIndex = headers.indexOf(\"Company Name\");\n const empEmailIndex = headers.indexOf(\"Employee Email\");\n const empSlideIndex = headers.indexOf(\"Employee Slide\");\n const statusIndex = headers.indexOf(\"Status\");\n\n // Iterate through each row to capture individual details\n for (let i = 1; i < values.length; i++) {\n const rowData = values[i];\n const empName = rowData[empNameIndex];\n const date = rowData[dateIndex];\n const managerName = rowData[managerNameIndex];\n const title = rowData[titleIndex];\n const compName = rowData[compNameIndex];\n\n // Make a copy of the Slide template and rename it with employee name\n const tempFolder = DriveApp.getFolderById(tempFolderId);\n const empSlideId = template.makeCopy(tempFolder).setName(empName).getId();\n const empSlide = SlidesApp.openById(empSlideId).getSlides()[0];\n\n // Replace placeholder values with actual employee related details\n empSlide.replaceAllText(\"Employee Name\", empName);\n empSlide.replaceAllText(\n \"Date\",\n `Date: ${Utilities.formatDate(\n date,\n Session.getScriptTimeZone(),\n \"MMMM dd, yyyy\",\n )}`,\n );\n empSlide.replaceAllText(\"Your Name\", managerName);\n empSlide.replaceAllText(\"Title\", title);\n empSlide.replaceAllText(\"Company Name\", compName);\n\n // Update the spreadsheet with the new Slide Id and status\n sheet.getRange(i + 1, empSlideIndex + 1).setValue(empSlideId);\n sheet.getRange(i + 1, statusIndex + 1).setValue(\"CREATED\");\n SpreadsheetApp.flush();\n }\n}\n\n/**\n * Send an email to each individual employee\n * with a PDF attachment of their appreciation certificate\n */\nfunction sendCertificates() {\n // Get all employee data from the spreadsheet and identify the headers\n const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();\n const values = sheet.getDataRange().getValues();\n const headers = values[0];\n const empNameIndex = headers.indexOf(\"Employee Name\");\n const dateIndex = headers.indexOf(\"Date\");\n const managerNameIndex = headers.indexOf(\"Manager Name\");\n const titleIndex = headers.indexOf(\"Title\");\n const compNameIndex = headers.indexOf(\"Company Name\");\n const empEmailIndex = headers.indexOf(\"Employee Email\");\n const empSlideIndex = headers.indexOf(\"Employee Slide\");\n const statusIndex = headers.indexOf(\"Status\");\n\n // Iterate through each row to capture individual details\n for (let i = 1; i < values.length; i++) {\n const rowData = values[i];\n const empName = rowData[empNameIndex];\n const date = rowData[dateIndex];\n const managerName = rowData[managerNameIndex];\n const title = rowData[titleIndex];\n const compName = rowData[compNameIndex];\n const empSlideId = rowData[empSlideIndex];\n const empEmail = rowData[empEmailIndex];\n\n // Load the employee's personalized Google Slide file\n const attachment = DriveApp.getFileById(empSlideId);\n\n // Setup the required parameters and send them the email\n const senderName = \"CertBot\";\n const subject = `${empName}, you're awesome!`;\n const body = `Please find your employee appreciation certificate attached.\\n\\n${compName} team`;\n GmailApp.sendEmail(empEmail, subject, body, {\n attachments: [attachment.getAs(MimeType.PDF)],\n name: senderName,\n });\n\n // Update the spreadsheet with email status\n sheet.getRange(i + 1, statusIndex + 1).setValue(\"SENT\");\n SpreadsheetApp.flush();\n }\n}\n```\n\nExample:\n```text\n</section>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.378Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":147,"estimatedTokens":1345}}1117{"id":"doc-get_stock_price_drop_alerts_apps_script_google_f-d30772a3","source":"documentation","title":"Get stock price drop alerts | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/tax-loss-harvest-alerts","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/tax-loss-harvest-alerts\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Checks for losses in the sheet.\n */\nfunction checkLosses() {\n // Pulls data from the spreadsheet\n const sheet =\n SpreadsheetApp.getActiveSpreadsheet().getSheetByName(\"Calculations\");\n const source = sheet.getRange(\"A:G\");\n const data = source.getValues();\n\n //Prepares the email alert content\n let message = \"Stocks: <br><br>\";\n\n let send_message = false;\n\n console.log(\"starting loop\");\n\n //Loops through the cells in the spreadsheet to find cells where the stock fell below purchase price\n let n = 0;\n for (const i in data) {\n //Skips the first row\n if (n++ === 0) continue;\n\n //Loads the current row\n const row = data[i];\n\n console.log(row[1]);\n console.log(row[6]);\n\n //Once at the end of the list, exits the loop\n if (row[1] === \"\") break;\n\n //If value is below purchase price, adds stock ticker and difference to list of tax loss opportunities\n if (row[6] < 0) {\n message += `${row[1]}: ${(Number.parseFloat(row[6].toString()) * 100).toFixed(2).toString()}%<br>`;\n send_message = true;\n }\n }\n if (!send_message) return;\n\n MailApp.sendEmail({\n to: SpreadsheetApp.getActiveSpreadsheet().getOwner().getEmail(),\n subject: \"Tax-loss harvest\",\n htmlBody: message,\n });\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.378Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":70,"estimatedTokens":499}}1118{"id":"doc-collect_review_timesheets_from_employees_apps_sc-78d7d54b","source":"documentation","title":"Collect & review timesheets from employees | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/timesheets","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/timesheets\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Global variables representing the index of certain columns.\nconst COLUMN_NUMBER = {\n EMAIL: 2,\n HOURS_START: 4,\n HOURS_END: 8,\n HOURLY_PAY: 9,\n TOTAL_HOURS: 10,\n CALC_PAY: 11,\n APPROVAL: 12,\n NOTIFY: 13,\n};\n\n// Global variables:\nconst APPROVED_EMAIL_SUBJECT = \"Weekly Timesheet APPROVED\";\nconst REJECTED_EMAIL_SUBJECT = \"Weekly Timesheet NOT APPROVED\";\nconst APPROVED_EMAIL_MESSAGE = \"Your timesheet has been approved.\";\nconst REJECTED_EMAIL_MESSAGE = \"Your timesheet has not been approved.\";\n\n/**\n * Creates the menu item \"Timesheets\" for user to run scripts on drop-down.\n */\nfunction onOpen() {\n const ui = SpreadsheetApp.getUi();\n ui.createMenu(\"Timesheets\")\n .addItem(\"Form setup\", \"setUpForm\")\n .addItem(\"Column setup\", \"columnSetup\")\n .addItem(\"Notify employees\", \"checkApprovedStatusToNotify\")\n .addToUi();\n}\n\n/**\n * Adds \"WEEKLY PAY\" column with calculated values using array formulas.\n * Adds an \"APPROVAL\" column at the end of the sheet, containing\n * drop-down menus to either approve/disapprove employee timesheets.\n * Adds a \"NOTIFIED STATUS\" column indicating whether or not an\n * employee has yet been e mailed.\n */\nfunction columnSetup() {\n const sheet = SpreadsheetApp.getActiveSheet();\n const lastCol = sheet.getLastColumn();\n const lastRow = sheet.getLastRow();\n const frozenRows = sheet.getFrozenRows();\n const beginningRow = frozenRows + 1;\n const numRows = lastRow - frozenRows;\n\n // Calls helper functions to add new columns.\n addCalculatePayColumn(sheet, beginningRow);\n addApprovalColumn(sheet, beginningRow, numRows);\n addNotifiedColumn(sheet, beginningRow, numRows);\n}\n\n/**\n * Adds TOTAL HOURS and CALCULATE PAY columns and automatically calculates\n * every employee's weekly pay.\n *\n * @param {Object} sheet Spreadsheet object of current sheet.\n * @param {Integer} beginningRow Index of beginning row.\n */\nfunction addCalculatePayColumn(sheet, beginningRow) {\n sheet.insertColumnAfter(COLUMN_NUMBER.HOURLY_PAY);\n sheet.getRange(1, COLUMN_NUMBER.TOTAL_HOURS).setValue(\"TOTAL HOURS\");\n sheet.getRange(1, COLUMN_NUMBER.CALC_PAY).setValue(\"WEEKLY PAY\");\n\n // Calculates weekly total hours.\n sheet\n .getRange(beginningRow, COLUMN_NUMBER.TOTAL_HOURS)\n .setFormula(\"=ArrayFormula(D2:D+E2:E+F2:F+G2:G+H2:H)\");\n // Calculates weekly pay.\n sheet\n .getRange(beginningRow, COLUMN_NUMBER.CALC_PAY)\n .setFormula(\"=ArrayFormula(I2:I * J2:J)\");\n}\n\n/**\n * Adds an APPROVAL column allowing managers to approve/\n * disapprove of each employee's timesheet.\n *\n * @param {Object} sheet Spreadsheet object of current sheet.\n * @param {Integer} beginningRow Index of beginning row.\n * @param {Integer} numRows Number of rows currently in use.\n */\nfunction addApprovalColumn(sheet, beginningRow, numRows) {\n sheet.insertColumnAfter(COLUMN_NUMBER.CALC_PAY);\n sheet.getRange(1, COLUMN_NUMBER.APPROVAL).setValue(\"APPROVAL\");\n\n // Make sure approval column is all drop-down menus.\n const approvalColumnRange = sheet.getRange(\n beginningRow,\n COLUMN_NUMBER.APPROVAL,\n numRows,\n 1,\n );\n const dropdownValues = [\"APPROVED\", \"NOT APPROVED\", \"IN PROGRESS\"];\n const rule = SpreadsheetApp.newDataValidation()\n .requireValueInList(dropdownValues)\n .build();\n approvalColumnRange.setDataValidation(rule);\n approvalColumnRange.setValue(\"IN PROGRESS\");\n}\n\n/**\n * Adds a NOTIFIED column allowing managers to see which employees\n * have/have not yet been notified of their approval status.\n *\n * @param {Object} sheet Spreadsheet object of current sheet.\n * @param {Integer} beginningRow Index of beginning row.\n * @param {Integer} numRows Number of rows currently in use.\n */\nfunction addNotifiedColumn(sheet, beginningRow, numRows) {\n sheet.insertColumnAfter(COLUMN_NUMBER.APPROVAL); // global\n sheet.getRange(1, COLUMN_NUMBER.APPROVAL + 1).setValue(\"NOTIFIED STATUS\");\n\n // Make sure notified column is all drop-down menus.\n const notifiedColumnRange = sheet.getRange(\n beginningRow,\n COLUMN_NUMBER.APPROVAL + 1,\n numRows,\n 1,\n );\n const dropdownValues = [\"NOTIFIED\", \"PENDING\"];\n const rule = SpreadsheetApp.newDataValidation()\n .requireValueInList(dropdownValues)\n .build();\n notifiedColumnRange.setDataValidation(rule);\n notifiedColumnRange.setValue(\"PENDING\");\n}\n\n/**\n * Sets the notification status to NOTIFIED for employees\n * who have received a notification email.\n *\n * @param {Object} sheet Current Spreadsheet.\n * @param {Object} notifiedValues Array of notified values.\n * @param {Integer} i Current status in the for loop.\n * @parma {Integer} beginningRow Row where iterations began.\n */\nfunction updateNotifiedStatus(sheet, notifiedValues, i, beginningRow) {\n // Update notification status.\n notifiedValues[i][0] = \"NOTIFIED\";\n sheet.getRange(i + beginningRow, COLUMN_NUMBER.NOTIFY).setValue(\"NOTIFIED\");\n}\n\n/**\n * Checks the approval status of every employee, and calls helper functions\n * to notify employees via email & update their notification status.\n */\nfunction checkApprovedStatusToNotify() {\n const sheet = SpreadsheetApp.getActiveSheet();\n const lastRow = sheet.getLastRow();\n const lastCol = sheet.getLastColumn();\n // lastCol here is the NOTIFIED column.\n const frozenRows = sheet.getFrozenRows();\n const beginningRow = frozenRows + 1;\n const numRows = lastRow - frozenRows;\n\n // Gets ranges of email, approval, and notified values for every employee.\n const emailValues = sheet\n .getRange(beginningRow, COLUMN_NUMBER.EMAIL, numRows, 1)\n .getValues();\n const approvalValues = sheet\n .getRange(beginningRow, COLUMN_NUMBER.APPROVAL, lastRow - frozenRows, 1)\n .getValues();\n const notifiedValues = sheet\n .getRange(beginningRow, COLUMN_NUMBER.NOTIFY, numRows, 1)\n .getValues();\n\n // Traverses through employee's row.\n for (let i = 0; i < numRows; i++) {\n // Do not notify twice.\n if (notifiedValues[i][0] === \"NOTIFIED\") {\n continue;\n }\n const emailAddress = emailValues[i][0];\n const approvalValue = approvalValues[i][0];\n\n // Sends notifying emails & update status.\n if (approvalValue === \"IN PROGRESS\") {\n } else if (approvalValue === \"APPROVED\") {\n MailApp.sendEmail(\n emailAddress,\n APPROVED_EMAIL_SUBJECT,\n APPROVED_EMAIL_MESSAGE,\n );\n updateNotifiedStatus(sheet, notifiedValues, i, beginningRow);\n } else if (approvalValue === \"NOT APPROVED\") {\n MailApp.sendEmail(\n emailAddress,\n REJECTED_EMAIL_SUBJECT,\n REJECTED_EMAIL_MESSAGE,\n );\n updateNotifiedStatus(sheet, notifiedValues, i, beginningRow);\n }\n }\n}\n\n/**\n * Set up the Timesheets Responses form, & link the form's trigger to\n * send manager an email when a new request is submitted.\n */\nfunction setUpForm() {\n const sheet = SpreadsheetApp.getActiveSpreadsheet();\n if (sheet.getFormUrl()) {\n const msg = \"Form already exists. Unlink the form and try again.\";\n SpreadsheetApp.getUi().alert(msg);\n return;\n }\n\n // Create the form.\n const form = FormApp.create(\"Weekly Timesheets\")\n .setCollectEmail(true)\n .setDestination(FormApp.DestinationType.SPREADSHEET, sheet.getId())\n .setLimitOneResponsePerUser(false);\n form.addTextItem().setTitle(\"Employee Name:\").setRequired(true);\n form.addTextItem().setTitle(\"Monday Hours:\").setRequired(true);\n form.addTextItem().setTitle(\"Tuesday Hours:\").setRequired(true);\n form.addTextItem().setTitle(\"Wednesday Hours:\").setRequired(true);\n form.addTextItem().setTitle(\"Thursday Hours:\").setRequired(true);\n form.addTextItem().setTitle(\"Friday Hours:\").setRequired(true);\n form.addTextItem().setTitle(\"HourlyWage:\").setRequired(true);\n\n // Set up on form submit trigger.\n ScriptApp.newTrigger(\"onFormSubmit\").forForm(form).onFormSubmit().create();\n}\n\n/**\n * Handle new form submissions to trigger the workflow.\n *\n * @param {Object} event Form submit event\n */\nfunction onFormSubmit(event) {\n const response = getResponsesByName(event.response);\n\n // Load form responses into a new row.\n const row = [\n \"New\",\n \"\",\n response[\"Emoloyee Email:\"],\n response[\"Employee Name:\"],\n response[\"Monday Hours:\"],\n response[\"Tuesday Hours:\"],\n response[\"Wednesday Hours:\"],\n response[\"Thursday Hours:\"],\n response[\"Friday Hours:\"],\n response[\"Hourly Wage:\"],\n ];\n const sheet = SpreadsheetApp.getActiveSpreadsheet();\n sheet.appendRow(row);\n}\n\n/**\n * Converts a form response to an object keyed by the item titles. Allows easier\n * access to response values.\n *\n * @param {FormResponse} response\n * @return {Object} Form values keyed by question title\n */\nfunction getResponsesByName(response) {\n const initialValue = {\n email: response.getRespondentEmail(),\n timestamp: response.getTimestamp(),\n };\n return response.getItemResponses().reduce((obj, itemResponse) => {\n const key = itemResponse.getItem().getTitle();\n obj[key] = itemResponse.getResponse();\n return obj;\n }, initialValue);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.380Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":291,"estimatedTokens":2415}}1119{"id":"doc-summarize_data_from_multiple_sheets_apps_script_-981d96e0","source":"documentation","title":"Summarize data from multiple sheets | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/custom-functions/summarize-sheets-data","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/custom-functions/summarize-sheets-data\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Gets summary data from other sheets. The sheets you want to summarize must have columns with headers that match the names of the columns this function summarizes data from.\n *\n * @return {string} Summary data from other sheets.\n * @customfunction\n */\n\n// The following sheets are ignored. Add additional constants for other sheets that should be ignored.\nconst READ_ME_SHEET_NAME = \"ReadMe\";\nconst PM_SHEET_NAME = \"Summary\";\n\n/**\n * Reads data ranges for each sheet. Filters and counts based on 'Status' columns. To improve performance, the script uses arrays\n * until all summary data is gathered. Then the script writes the summary array starting at the cell of the custom function.\n */\nfunction getSheetsData() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const sheets = ss.getSheets();\n const outputArr = [];\n\n // For each sheet, summarizes the data and pushes to a temporary array.\n for (const s in sheets) {\n // Gets sheet name.\n const sheetNm = sheets[s].getName();\n // Skips ReadMe and Summary sheets.\n if (sheetNm === READ_ME_SHEET_NAME || sheetNm === PM_SHEET_NAME) {\n continue;\n }\n // Gets sheets data.\n const values = sheets[s].getDataRange().getValues();\n // Gets the first row of the sheet which is the header row.\n const headerRowValues = values[0];\n // Finds the columns with the heading names 'Owner Name' and 'Status' and gets the index value of each.\n // Using 'indexOf()' to get the position of each column prevents the script from breaking if the columns change positions in a sheet.\n const columnOwner = headerRowValues.indexOf(\"Owner Name\");\n const columnStatus = headerRowValues.indexOf(\"Status\");\n // Removes header row.\n values.splice(0, 1);\n // Gets the 'Owner Name' column value by retrieving the first data row in the array.\n const owner = values[0][columnOwner];\n // Counts the total number of tasks.\n const taskCnt = values.length;\n // Counts the number of tasks that have the 'Complete' status.\n // If the options you want to count in your spreadsheet differ, update the strings below to match the text of each option.\n // To add more options, copy the line below and update the string to the new text.\n const completeCnt = filterByPosition(\n values,\n \"Complete\",\n columnStatus,\n ).length;\n // Counts the number of tasks that have the 'In-Progress' status.\n const inProgressCnt = filterByPosition(\n values,\n \"In-Progress\",\n columnStatus,\n ).length;\n // Counts the number of tasks that have the 'Scheduled' status.\n const scheduledCnt = filterByPosition(\n values,\n \"Scheduled\",\n columnStatus,\n ).length;\n // Counts the number of tasks that have the 'Overdue' status.\n const overdueCnt = filterByPosition(values, \"Overdue\", columnStatus).length;\n // Builds the output array.\n outputArr.push([\n owner,\n taskCnt,\n completeCnt,\n inProgressCnt,\n scheduledCnt,\n overdueCnt,\n sheetNm,\n ]);\n }\n // Writes the output array.\n return outputArr;\n}\n\n/**\n * Below is a helper function that filters a 2-dimenstional array.\n */\nfunction filterByPosition(array, find, position) {\n return array.filter((innerArray) => innerArray[position] === find);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.381Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":109,"estimatedTokens":1008}}1120{"id":"doc-respond_to_feedback_apps_script_google_for_devel-3abb7424","source":"documentation","title":"Respond to feedback | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/course-feedback-response","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/course-feedback-response\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Creates custom menu for user to run scripts.\n */\nfunction onOpen() {\n const ui = SpreadsheetApp.getUi();\n ui.createMenu(\"Form Reply Tool\")\n .addItem(\"Enable auto draft replies\", \"installTrigger\")\n .addToUi();\n}\n\n/**\n * Installs a trigger on the Spreadsheet for when a Form response is submitted.\n */\nfunction installTrigger() {\n ScriptApp.newTrigger(\"onFormSubmit\")\n .forSpreadsheet(SpreadsheetApp.getActive())\n .onFormSubmit()\n .create();\n}\n\n/**\n * Creates a draft email for every response on a form\n *\n * @param {Object} event - Form submit event\n */\nfunction onFormSubmit(e) {\n const responses = e.namedValues;\n\n // parse form response data\n const timestamp = responses.Timestamp[0];\n const email = responses[\"Email address\"][0].trim();\n\n // create email body\n const emailBody = createEmailBody(responses);\n\n // create draft email\n createDraft(timestamp, email, emailBody);\n}\n\n/**\n * Creates email body and includes feedback from Google Form.\n *\n * @param {string} responses - The form response data\n * @return {string} - The email body as an HTML string\n */\nfunction createEmailBody(responses) {\n // parse form response data\n const name = responses.Name[0].trim();\n const industry = responses[\"What industry do you work in?\"][0];\n const source = responses[\"How did you find out about this course?\"][0];\n const rating =\n responses[\"On a scale of 1 - 5 how would you rate this course?\"][0];\n const productFeedback =\n responses[\"What could be different to make it a 5 rating?\"][0];\n const otherFeedback = responses[\"Any other feedback?\"][0];\n\n // create email body\n const htmlBody = `Hi ${name},<br><br>Thanks for responding to our course feedback questionnaire.<br><br>It's really useful to us to help improve this course.<br><br>Have a great day!<br><br>Thanks,<br>Course Team<br><br>****************************************************************<br><br><i>Your feedback:<br><br>What industry do you work in?<br><br>${industry}<br><br>How did you find out about this course?<br><br>${source}<br><br>On a scale of 1 - 5 how would you rate this course?<br><br>${rating}<br><br>What could be different to make it a 5 rating?<br><br>${productFeedback}<br><br>Any other feedback?<br><br>${otherFeedback}<br><br></i>`;\n\n return htmlBody;\n}\n\n/**\n * Create a draft email with the feedback\n *\n * @param {string} timestamp Timestamp for the form response\n * @param {string} email Email address from the form response\n * @param {string} emailBody The email body as an HTML string\n */\nfunction createDraft(timestamp, email, emailBody) {\n console.log(\"draft email create process started\");\n\n // create subject line\n const subjectLine = `Thanks for your course feedback! ${timestamp}`;\n\n // create draft email\n GmailApp.createDraft(email, subjectLine, \"\", {\n htmlBody: emailBody,\n });\n}\n```\n\nExample:\n```text\n</section>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.381Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":109,"estimatedTokens":905}}1121{"id":"doc-copy_macros_to_other_google_sheets_spreadsheets_-cca4d3db","source":"documentation","title":"Copy macros to other Google Sheets spreadsheets | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/workspace/add-ons/samples/share-macro","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.devsite.corp.google.com/apps-script/add-ons/share-macro\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Uses Apps Script API to copy source Apps Script project\n * to destination Google Spreadsheet container.\n *\n * @param {string} sourceScriptId - Script ID of the source project.\n * @param {string} targetSpreadsheetUrl - URL if the target spreadsheet.\n */\nfunction shareMacro_(sourceScriptId, targetSpreadsheetUrl) {\n // Gets the source project content using the Apps Script API.\n const sourceProject = APPS_SCRIPT_API.get(sourceScriptId);\n const sourceFiles = APPS_SCRIPT_API.getContent(sourceScriptId);\n\n // Opens the target spreadsheet and gets its ID.\n const parentSSId = SpreadsheetApp.openByUrl(targetSpreadsheetUrl).getId();\n\n // Creates an Apps Script project that's bound to the target spreadsheet.\n const targetProjectObj = APPS_SCRIPT_API.create(\n sourceProject.title,\n parentSSId,\n );\n\n // Updates the Apps Script project with the source project content.\n APPS_SCRIPT_API.updateContent(targetProjectObj.scriptId, sourceFiles);\n}\n\n/**\n * Function that encapsulates Apps Script API project manipulation.\n */\nconst APPS_SCRIPT_API = {\n accessToken: ScriptApp.getOAuthToken(),\n\n /* APPS_SCRIPT_API.get\n * Gets Apps Script source project.\n * @param {string} scriptId - Script ID of the source project.\n * @return {Object} - JSON representation of source project.\n */\n get: function (scriptId) {\n const url = `https://script.googleapis.com/v1/projects/${scriptId}`;\n const options = {\n method: \"get\",\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n muteHttpExceptions: true,\n };\n const res = UrlFetchApp.fetch(url, options);\n if (res.getResponseCode() === 200) {\n return JSON.parse(res);\n }\n console.log(\"An error occurred gettting the project details\");\n console.log(res.getResponseCode());\n console.log(res.getContentText());\n console.log(res);\n return false;\n },\n\n /* APPS_SCRIPT_API.create\n * Creates new Apps Script project in the target spreadsheet.\n * @param {string} title - Name of Apps Script project.\n * @param {string} parentId - Internal ID of target spreadsheet.\n * @return {Object} - JSON representation completed project creation.\n */\n create: function (title, parentId) {\n const url = \"https://script.googleapis.com/v1/projects\";\n const options = {\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n muteHttpExceptions: true,\n method: \"POST\",\n payload: { title: title },\n };\n if (parentId) {\n options.payload.parentId = parentId;\n }\n options.payload = JSON.stringify(options.payload);\n let res = UrlFetchApp.fetch(url, options);\n if (res.getResponseCode() === 200) {\n res = JSON.parse(res);\n return res;\n }\n console.log(\"An error occurred while creating the project\");\n console.log(res.getResponseCode());\n console.log(res.getContentText());\n console.log(res);\n return false;\n },\n /* APPS_SCRIPT_API.getContent\n * Gets the content of the source Apps Script project.\n * @param {string} scriptId - Script ID of the source project.\n * @return {Object} - JSON representation of Apps Script project content.\n */\n getContent: function (scriptId) {\n const url = `https://script.googleapis.com/v1/projects/${scriptId}/content`;\n const options = {\n method: \"get\",\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n muteHttpExceptions: true,\n };\n let res = UrlFetchApp.fetch(url, options);\n if (res.getResponseCode() === 200) {\n res = JSON.parse(res);\n return res.files;\n }\n console.log(\n \"An error occurred obtaining the content from the source script\",\n );\n console.log(res.getResponseCode());\n console.log(res.getContentText());\n console.log(res);\n return false;\n },\n\n /* APPS_SCRIPT_API.updateContent\n * Updates (copies) content from source to target Apps Script project.\n * @param {string} scriptId - Script ID of the source project.\n * @param {Object} files - JSON representation of Apps Script project content.\n * @return {boolean} - Result status of the function.\n */\n updateContent: function (scriptId, files) {\n const url = `https://script.googleapis.com/v1/projects/${scriptId}/content`;\n const options = {\n method: \"put\",\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n },\n contentType: \"application/json\",\n payload: JSON.stringify({ files: files }),\n muteHttpExceptions: true,\n };\n const res = UrlFetchApp.fetch(url, options);\n if (res.getResponseCode() === 200) {\n return true;\n }\n console.log(`An error occurred updating content of script ${scriptId}`);\n console.log(res.getResponseCode());\n console.log(res.getContentText());\n console.log(res);\n return false;\n },\n};\n```\n\nExample:\n```text\n/**\n * Copyright 2022 Google LLC\n *\n * 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 * http://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.\n */\n\n// Change application logo here (and in manifest) as desired.\nconst ADDON_LOGO =\n \"https://www.gstatic.com/images/branding/product/2x/apps_script_48dp.png\";\n\n/**\n * Callback function for rendering the main card.\n * @return {CardService.Card} The card to show the user.\n */\nfunction onHomepage(e) {\n return createSelectionCard(e);\n}\n\n/**\n * Builds the primary card interface used to collect user inputs.\n *\n * @param {Object} e - Add-on event object.\n * @param {string} sourceScriptId - Script ID of the source project.\n * @param {string} targetSpreadsheetUrl - URL of the target spreadsheet.\n * @param {string[]} errors - Array of error messages.\n *\n * @return {CardService.Card} The card to show to the user for inputs.\n */\nfunction createSelectionCard(e, sourceScriptId, targetSpreadsheetUrl, errors) {\n // Configures card header.\n const cardHeader = CardService.newCardHeader()\n .setTitle(\"Share macros with other spreadheets!\")\n .setImageUrl(ADDON_LOGO)\n .setImageStyle(CardService.ImageStyle.SQUARE);\n\n // If form errors exist, configures section with error messages.\n let showErrors = false;\n\n if (errors?.length) {\n showErrors = true;\n let msg = errors.reduce((str, err) => `${str}• ${err}<br>`, \"\");\n msg = `<b>Form submission errors:</b><br><font color=\"#ba0000\">${msg}</font>`;\n\n // Builds error message section.\n sectionErrors = CardService.newCardSection().addWidget(\n CardService.newDecoratedText().setText(msg).setWrapText(true),\n );\n }\n\n // Configures source project section.\n const sectionSource = CardService.newCardSection()\n .addWidget(\n CardService.newDecoratedText().setText(\n \"<b>Source macro</b><br>The Apps Script project to copy\",\n ),\n )\n\n .addWidget(\n CardService.newTextInput()\n .setFieldName(\"sourceScriptId\")\n .setValue(sourceScriptId || \"\")\n .setTitle(\"Script ID of the source macro\")\n .setHint(\n \"You must have at least edit permission for the source spreadsheet to access its script project\",\n ),\n )\n\n .addWidget(\n CardService.newTextButton()\n .setText(\"Find the script ID\")\n .setOpenLink(\n CardService.newOpenLink()\n .setUrl(\n \"https://developers.google.com/apps-script/api/samples/execute\",\n )\n .setOpenAs(CardService.OpenAs.FULL_SIZE)\n .setOnClose(CardService.OnClose.NOTHING),\n ),\n );\n\n // Configures target spreadsheet section.\n const sectionTarget = CardService.newCardSection()\n .addWidget(\n CardService.newDecoratedText().setText(\"<b>Target spreadsheet</b>\"),\n )\n\n .addWidget(\n CardService.newTextInput()\n .setFieldName(\"targetSpreadsheetUrl\")\n .setValue(targetSpreadsheetUrl || \"\")\n .setHint(\n \"You must have at least edit permission for the target spreadsheet\",\n )\n .setTitle(\"Target spreadsheet URL\"),\n );\n\n // Configures help section.\n const sectionHelp = CardService.newCardSection()\n .addWidget(\n CardService.newDecoratedText()\n .setText(\n \"<b><font color=#c80000>NOTE: </font></b>\" +\n \"The Apps Script API must be turned on.\",\n )\n .setWrapText(true),\n )\n\n .addWidget(\n CardService.newTextButton()\n .setText(\"Turn on Apps Script API\")\n .setOpenLink(\n CardService.newOpenLink()\n .setUrl(\"https://script.google.com/home/usersettings\")\n .setOpenAs(CardService.OpenAs.FULL_SIZE)\n .setOnClose(CardService.OnClose.NOTHING),\n ),\n );\n\n // Configures card footer with action to copy the macro.\n const cardFooter = CardService.newFixedFooter().setPrimaryButton(\n CardService.newTextButton()\n .setText(\"Share macro\")\n .setOnClickAction(\n CardService.newAction().setFunctionName(\"onClickFunction_\"),\n ),\n );\n\n // Begins building the card.\n const builder = CardService.newCardBuilder().setHeader(cardHeader);\n\n // Adds error section if applicable.\n if (showErrors) {\n builder.addSection(sectionErrors);\n }\n\n // Adds final sections & footer.\n builder\n .addSection(sectionSource)\n .addSection(sectionTarget)\n .addSection(sectionHelp)\n .setFixedFooter(cardFooter);\n\n return builder.build();\n}\n\n/**\n * Action handler that validates user inputs and calls shareMacro_\n * function to copy Apps Script project to target spreadsheet.\n *\n * @param {Object} e - Add-on event object.\n *\n * @return {CardService.Card} Responds with either a success or error card.\n */\nfunction onClickFunction_(e) {\n const sourceScriptId = e.formInput.sourceScriptId;\n const targetSpreadsheetUrl = e.formInput.targetSpreadsheetUrl;\n\n // Validates inputs for errors.\n const errors = [];\n\n // Pushes an error message if the Script ID parameter is missing.\n if (!sourceScriptId) {\n errors.push(\"Missing script ID\");\n } else {\n // Gets the Apps Script project if the Script ID parameter is valid.\n const sourceProject = APPS_SCRIPT_API.get(sourceScriptId);\n if (!sourceProject) {\n // Pushes an error message if the Script ID parameter isn't valid.\n errors.push(\"Invalid script ID\");\n }\n }\n\n // Pushes an error message if the spreadsheet URL is missing.\n if (!targetSpreadsheetUrl) {\n errors.push(\"Missing Spreadsheet URL\");\n } else\n try {\n // Tests for valid spreadsheet URL to get the spreadsheet ID.\n const ssId = SpreadsheetApp.openByUrl(targetSpreadsheetUrl).getId();\n } catch (err) {\n // Pushes an error message if the spreadsheet URL parameter isn't valid.\n errors.push(\"Invalid spreadsheet URL\");\n }\n\n if (errors?.length) {\n // Redisplays form if inputs are missing or invalid.\n return createSelectionCard(e, sourceScriptId, targetSpreadsheetUrl, errors);\n }\n // Calls shareMacro function to copy the project.\n shareMacro_(sourceScriptId, targetSpreadsheetUrl);\n\n // Creates a success card to display to users.\n return buildSuccessCard(e, targetSpreadsheetUrl);\n}\n\n/**\n * Builds success card to inform user & let them open the spreadsheet.\n *\n * @param {Object} e - Add-on event object.\n * @param {string} targetSpreadsheetUrl - URL of the target spreadsheet.\n *\n * @return {CardService.Card} Returns success card.\n */ function buildSuccessCard(e, targetSpreadsheetUrl) {\n // Configures card header.\n const cardHeader = CardService.newCardHeader()\n .setTitle(\"Share macros with other spreadsheets!\")\n .setImageUrl(ADDON_LOGO)\n .setImageStyle(CardService.ImageStyle.SQUARE);\n\n // Configures card body section with success message and open button.\n const sectionBody1 = CardService.newCardSection()\n .addWidget(\n CardService.newTextParagraph().setText(\"Sharing process is complete!\"),\n )\n .addWidget(\n CardService.newTextButton()\n .setText(\"Open spreadsheet\")\n .setOpenLink(\n CardService.newOpenLink()\n .setUrl(targetSpreadsheetUrl)\n .setOpenAs(CardService.OpenAs.FULL_SIZE)\n .setOnClose(CardService.OnClose.RELOAD_ADD_ON),\n ),\n );\n const sectionBody2 = CardService.newCardSection()\n .addWidget(\n CardService.newTextParagraph().setText(\n \"If you don't see the copied project in your target spreadsheet,\" +\n \" make sure you turned on the Apps Script API in the Apps Script dashboard.\",\n ),\n )\n .addWidget(\n CardService.newTextButton()\n .setText(\"Check API\")\n .setOpenLink(\n CardService.newOpenLink()\n .setUrl(\"https://script.google.com/home/usersettings\")\n .setOpenAs(CardService.OpenAs.FULL_SIZE)\n .setOnClose(CardService.OnClose.RELOAD_ADD_ON),\n ),\n );\n\n // Configures the card footer with action to start new process.\n const cardFooter = CardService.newFixedFooter().setPrimaryButton(\n CardService.newTextButton()\n .setText(\"Share another\")\n .setOnClickAction(CardService.newAction().setFunctionName(\"onHomepage\")),\n );\n\n const builder = CardService.newCardBuilder()\n .setHeader(cardHeader)\n .addSection(sectionBody1)\n .addSection(sectionBody2)\n .setFixedFooter(cardFooter);\n\n return builder.build();\n}\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/spreadsheets\",\n \"https://www.googleapis.com/auth/script.external_request\",\n \"https://www.googleapis.com/auth/drive.readonly\",\n \"https://www.googleapis.com/auth/script.projects\"\n ],\n \"urlFetchWhitelist\": [\"https://script.googleapis.com/\"],\n \"addOns\": {\n \"common\": {\n \"name\": \"Share Macro\",\n \"logoUrl\": \"https://www.gstatic.com/images/branding/product/2x/apps_script_48dp.png\",\n \"layoutProperties\": {\n \"primaryColor\": \"#188038\",\n \"secondaryColor\": \"#34a853\"\n },\n \"homepageTrigger\": {\n \"runFunction\": \"onHomepage\"\n }\n },\n \"sheets\": {}\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.383Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":469,"estimatedTokens":3800}}1122{"id":"doc-make_an_agenda_for_meetings_apps_script_google_f-df7720ea","source":"documentation","title":"Make an agenda for meetings | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/agenda-maker","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/agenda-maker\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Checks if the folder for Agenda docs exists, and creates it if it doesn't.\n *\n * @return {*} Drive folder ID for the app.\n */\nfunction checkFolder() {\n const folders = DriveApp.getFoldersByName(\"Agenda Maker - App\");\n // Finds the folder if it exists\n while (folders.hasNext()) {\n const folder = folders.next();\n if (\n folder.getDescription() ===\n \"Apps Script App - Do not change this description\" &&\n folder.getOwner().getEmail() === Session.getActiveUser().getEmail()\n ) {\n return folder.getId();\n }\n }\n // If the folder doesn't exist, creates one\n const folder = DriveApp.createFolder(\"Agenda Maker - App\");\n folder.setDescription(\"Apps Script App - Do not change this description\");\n return folder.getId();\n}\n\n/**\n * Finds the template agenda doc, or creates one if it doesn't exist.\n */\nfunction getTemplateId(folderId) {\n const folder = DriveApp.getFolderById(folderId);\n const files = folder.getFilesByName(\"Agenda TEMPLATE##\");\n\n // If there is a file, returns the ID.\n while (files.hasNext()) {\n const file = files.next();\n return file.getId();\n }\n\n // Otherwise, creates the agenda template.\n // You can adjust the default template here\n const doc = DocumentApp.create(\"Agenda TEMPLATE##\");\n const body = doc.getBody();\n\n body\n .appendParagraph(\"##Attendees##\")\n .setHeading(DocumentApp.ParagraphHeading.HEADING1)\n .editAsText()\n .setBold(true);\n body.appendParagraph(\" \").editAsText().setBold(false);\n\n body\n .appendParagraph(\"Overview\")\n .setHeading(DocumentApp.ParagraphHeading.HEADING1)\n .editAsText()\n .setBold(true);\n body.appendParagraph(\" \");\n body.appendParagraph(\"- Topic 1: \").editAsText().setBold(true);\n body.appendParagraph(\" \").editAsText().setBold(false);\n body.appendParagraph(\"- Topic 2: \").editAsText().setBold(true);\n body.appendParagraph(\" \").editAsText().setBold(false);\n body.appendParagraph(\"- Topic 3: \").editAsText().setBold(true);\n body.appendParagraph(\" \").editAsText().setBold(false);\n\n body\n .appendParagraph(\"Next Steps\")\n .setHeading(DocumentApp.ParagraphHeading.HEADING1)\n .editAsText()\n .setBold(true);\n body.appendParagraph(\"- Takeaway 1: \").editAsText().setBold(true);\n body.appendParagraph(\"- Responsible: \").editAsText().setBold(false);\n body.appendParagraph(\"- Accountable: \");\n body.appendParagraph(\"- Consult: \");\n body.appendParagraph(\"- Inform: \");\n body.appendParagraph(\" \");\n body.appendParagraph(\"- Takeaway 2: \").editAsText().setBold(true);\n body.appendParagraph(\"- Responsible: \").editAsText().setBold(false);\n body.appendParagraph(\"- Accountable: \");\n body.appendParagraph(\"- Consult: \");\n body.appendParagraph(\"- Inform: \");\n body.appendParagraph(\" \");\n body.appendParagraph(\"- Takeaway 3: \").editAsText().setBold(true);\n body.appendParagraph(\"- Responsible: \").editAsText().setBold(false);\n body.appendParagraph(\"- Accountable: \");\n body.appendParagraph(\"- Consult: \");\n body.appendParagraph(\"- Inform: \");\n\n doc.saveAndClose();\n\n folder.addFile(DriveApp.getFileById(doc.getId()));\n\n return doc.getId();\n}\n\n/**\n * When there is a change to the calendar, searches for events that include \"#agenda\"\n * in the decrisption.\n *\n */\nfunction onCalendarChange() {\n // Gets recent events with the #agenda tag\n const now = new Date();\n const events = CalendarApp.getEvents(\n now,\n new Date(now.getTime() + 2 * 60 * 60 * 1000000),\n { search: \"#agenda\" },\n );\n\n const folderId = checkFolder();\n const templateId = getTemplateId(folderId);\n\n const folder = DriveApp.getFolderById(folderId);\n\n // Loops through any events found\n for (i = 0; i < events.length; i++) {\n const event = events[i];\n\n // Confirms whether the event has the #agenda tag\n let description = event.getDescription();\n if (description.search(\"#agenda\") === -1) continue;\n\n // Only works with events created by the owner of this calendar\n if (event.isOwnedByMe()) {\n // Creates a new document from the template for an agenda for this event\n const newDoc = DriveApp.getFileById(templateId).makeCopy();\n newDoc.setName(`Agenda for ${event.getTitle()}`);\n\n const file = DriveApp.getFileById(newDoc.getId());\n folder.addFile(file);\n\n const doc = DocumentApp.openById(newDoc.getId());\n const body = doc.getBody();\n\n // Fills in the template with information about the attendees from the\n // calendar event\n const conf = body.findText(\"##Attendees##\");\n if (conf) {\n const ref = conf.getStartOffset();\n\n for (const i in event.getGuestList()) {\n const guest = event.getGuestList()[i];\n\n body.insertParagraph(ref + 2, guest.getEmail());\n }\n body.replaceText(\"##Attendees##\", \"Attendees\");\n }\n\n // Replaces the tag with a link to the agenda document\n const agendaUrl = `https://docs.google.com/document/d/${newDoc.getId()}`;\n description = description.replace(\n \"#agenda\",\n `<a href=${agendaUrl}>Agenda Doc</a>`,\n );\n event.setDescription(description);\n\n // Invites attendees to the Google doc so they automatically receive access to the agenda\n newDoc.addEditor(newDoc.getOwner());\n\n for (const i in event.getGuestList()) {\n const guest = event.getGuestList()[i];\n\n newDoc.addEditor(guest.getEmail());\n }\n }\n }\n return;\n}\n\n/**\n * Creates an event-driven trigger that fires whenever there's a change to the calendar.\n */\nfunction setUp() {\n const email = Session.getActiveUser().getEmail();\n ScriptApp.newTrigger(\"onCalendarChange\")\n .forUserCalendar(email)\n .onEventUpdated()\n .create();\n}\n```\n\nExample:\n```text\n</section>\n```\n\nExample:\n```text\nfor (let i in event.getGuestList()) {\n let guest = event.getGuestList()[i];\n\n newDoc.addEditor(guest.getEmail());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.384Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":213,"estimatedTokens":1645}}1123{"id":"doc-track_youtube_video_views_comments_apps_script_g-10e834c3","source":"documentation","title":"Track YouTube video views & comments | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/youtube-tracker","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/youtube-tracker\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Sets preferences for email notification. Choose 'Y' to send emails, 'N' to skip emails.\nconst EMAIL_ON = \"Y\";\n\n// Matches column names in Video sheet to variables. If the column names change, update these variables.\nconst COLUMN_NAME = {\n VIDEO: \"Video Link\",\n TITLE: \"Video Title\",\n};\n\n/**\n * Gets YouTube video details and statistics for all\n * video URLs listed in 'Video Link' column in each\n * sheet. Sends email summary, based on preferences above,\n * when videos have new comments or replies.\n */\nfunction markVideos() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();\n\n // Runs through process for each tab in Spreadsheet.\n for (const dataSheet of sheets) {\n const tabName = dataSheet.getName();\n const range = dataSheet.getDataRange();\n const numRows = range.getNumRows();\n const rows = range.getValues();\n const headerRow = rows[0];\n\n // Finds the column indices.\n const videoColumnIdx = headerRow.indexOf(COLUMN_NAME.VIDEO);\n const titleColumnIdx = headerRow.indexOf(COLUMN_NAME.TITLE);\n\n // Creates empty array to collect data for email table.\n const emailContent = [];\n\n // Processes each row in spreadsheet.\n for (let i = 1; i < numRows; ++i) {\n const row = rows[i];\n // Extracts video ID.\n const videoId = extractVideoIdFromUrl(row[videoColumnIdx]);\n // Processes each row that contains a video ID.\n if (!videoId) {\n continue;\n }\n // Calls getVideoDetails function and extracts target data for the video.\n const detailsResponse = getVideoDetails(videoId);\n const title = detailsResponse.items[0].snippet.title;\n const publishDate = detailsResponse.items[0].snippet.publishedAt;\n const publishDateFormatted = new Date(publishDate);\n const views = detailsResponse.items[0].statistics.viewCount;\n const likes = detailsResponse.items[0].statistics.likeCount;\n const comments = detailsResponse.items[0].statistics.commentCount;\n const channel = detailsResponse.items[0].snippet.channelTitle;\n\n // Collects title, publish date, channel, views, comments, likes details and pastes into tab.\n const detailsRow = [\n title,\n publishDateFormatted,\n channel,\n views,\n comments,\n likes,\n ];\n dataSheet\n .getRange(i + 1, titleColumnIdx + 1, 1, 6)\n .setValues([detailsRow]);\n\n // Determines if new count of comments/replies is greater than old count of comments/replies.\n const addlCommentCount = comments - row[titleColumnIdx + 4];\n\n // Adds video title, link, and additional comment count to table if new counts > old counts.\n if (addlCommentCount > 0) {\n const emailRow = [title, row[videoColumnIdx], addlCommentCount];\n emailContent.push(emailRow);\n }\n }\n // Sends notification email if Content is not empty.\n if (emailContent.length > 0 && EMAIL_ON === \"Y\") {\n sendEmailNotificationTemplate(emailContent, tabName);\n }\n }\n}\n\n/**\n * Gets video details for YouTube videos\n * using YouTube advanced service.\n */\nfunction getVideoDetails(videoId) {\n const part = \"snippet,statistics\";\n const response = YouTube.Videos.list(part, { id: videoId });\n return response;\n}\n\n/**\n * Extracts YouTube video ID from url.\n * (h/t https://stackoverflow.com/a/3452617)\n */\nfunction extractVideoIdFromUrl(url) {\n let videoId = url.split(\"v=\")[1];\n const ampersandPosition = videoId.indexOf(\"&\");\n if (ampersandPosition !== -1) {\n videoId = videoId.substring(0, ampersandPosition);\n }\n return videoId;\n}\n\n/**\n * Assembles notification email with table of video details.\n * (h/t https://stackoverflow.com/questions/37863392/making-table-in-google-apps-script-from-array)\n */\nfunction sendEmailNotificationTemplate(content, emailAddress) {\n const template = HtmlService.createTemplateFromFile(\"email\");\n template.content = content;\n const msg = template.evaluate();\n MailApp.sendEmail(\n emailAddress,\n \"New comments or replies on YouTube\",\n msg.getContent(),\n { htmlBody: msg.getContent() },\n );\n}\n```\n\nExample:\n```text\n<!--\n Copyright 2022 Google LLC\n\n 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 http://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.\n-->\n\n<body>\n Hello,<br><br>You have new comments and/or replies on videos: <br><br>\n <table border=\"1\">\n <tr>\n <th>Video Title</th>\n <th>Link</th>\n <th>Number of new replies and comments</th>\n </tr>\n <? for (var i = 0; i < content.length; i++) { ?>\n <tr>\n <? for (var j = 0; j < content[i].length; j++) { ?>\n <td align=\"center\"><?= content[i][j] ?></td>\n <? } ?>\n </tr>\n <? } ?>\n </table>\n</body>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.385Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":181,"estimatedTokens":1479}}1124{"id":"doc-manage_new_employee_equipment_requests_apps_scri-0fd69b31","source":"documentation","title":"Manage new employee equipment requests | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/equipment-requests","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/equipment-requests\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// Update this variable with the email address you want to send equipment requests to.\nconst REQUEST_NOTIFICATION_EMAIL = \"request_intake@example.com\";\n\n// Update the following variables with your own equipment options.\nconst AVAILABLE_LAPTOPS = [\n '15\" high Performance Laptop (OS X)',\n '15\" high Performance Laptop (Windows)',\n '15\" high performance Laptop (Linux)',\n '13\" lightweight laptop (Windows)',\n];\nconst AVAILABLE_DESKTOPS = [\n \"Standard workstation (Windows)\",\n \"Standard workstation (Linux)\",\n \"High performance workstation (Windows)\",\n \"High performance workstation (Linux)\",\n \"Mac Pro (OS X)\",\n];\nconst AVAILABLE_MONITORS = ['Single 27\"', 'Single 32\"', 'Dual 24\"'];\n\n// Form field titles, used for creating the form and as keys when handling\n// responses.\n/**\n * Adds a custom menu to the spreadsheet.\n */\nfunction onOpen() {\n SpreadsheetApp.getUi()\n .createMenu(\"Equipment requests\")\n .addItem(\"Set up\", \"setup_\")\n .addItem(\"Clean up\", \"cleanup_\")\n .addToUi();\n}\n\n/**\n * Creates the form and triggers for the workflow.\n */\nfunction setup_() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n if (ss.getFormUrl()) {\n const msg = \"Form already exists. Unlink the form and try again.\";\n SpreadsheetApp.getUi().alert(msg);\n return;\n }\n const form = FormApp.create(\"Equipment Requests\")\n .setCollectEmail(true)\n .setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId())\n .setLimitOneResponsePerUser(false);\n form.addTextItem().setTitle(\"Employee name\").setRequired(true);\n form.addTextItem().setTitle(\"Desk location\").setRequired(true);\n form.addDateItem().setTitle(\"Due date\").setRequired(true);\n form.addListItem().setTitle(\"Laptop\").setChoiceValues(AVAILABLE_LAPTOPS);\n form.addListItem().setTitle(\"Desktop\").setChoiceValues(AVAILABLE_DESKTOPS);\n form.addListItem().setTitle(\"Monitor\").setChoiceValues(AVAILABLE_MONITORS);\n\n // Hide the raw form responses.\n for (const sheet of ss.getSheets()) {\n if (sheet.getFormUrl() === ss.getFormUrl()) {\n sheet.hideSheet();\n }\n }\n // Start workflow on each form submit\n ScriptApp.newTrigger(\"onFormSubmit_\").forForm(form).onFormSubmit().create();\n // Archive completed items every 5m.\n ScriptApp.newTrigger(\"processCompletedItems_\")\n .timeBased()\n .everyMinutes(5)\n .create();\n}\n\n/**\n * Cleans up the project (stop triggers, form submission, etc.)\n */\nfunction cleanup_() {\n const formUrl = SpreadsheetApp.getActiveSpreadsheet().getFormUrl();\n if (!formUrl) {\n return;\n }\n for (const trigger of ScriptApp.getProjectTriggers()) {\n ScriptApp.deleteTrigger(trigger);\n }\n FormApp.openByUrl(formUrl).deleteAllResponses().setAcceptingResponses(false);\n}\n\n/**\n * Handles new form submissions to trigger the workflow.\n *\n * @param {Object} event - Form submit event\n */\nfunction onFormSubmit_(event) {\n const response = mapResponse_(event.response);\n sendNewEquipmentRequestEmail_(response);\n const equipmentDetails = Utilities.formatString(\n \"%s\\n%s\\n%s\",\n response.Laptop,\n response.Desktop,\n response.Monitor,\n );\n const row = [\n \"New\",\n \"\",\n response[\"Due date\"],\n response[\"Employee name\"],\n response[\"Desk location\"],\n equipmentDetails,\n response.email,\n ];\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const sheet = ss.getSheetByName(\"Pending requests\");\n sheet.appendRow(row);\n}\n\n/**\n * Sweeps completed and cancelled requests, notifying the requestors and archiving them\n * to the completed sheet.\n *\n * @param {Object} event\n */\nfunction processCompletedItems_() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const pending = ss.getSheetByName(\"Pending requests\");\n const completed = ss.getSheetByName(\"Completed requests\");\n const rows = pending.getDataRange().getValues();\n for (let i = rows.length; i >= 2; i--) {\n const row = rows[i - 1];\n const status = row[0];\n if (status === \"Completed\" || status === \"Cancelled\") {\n pending.deleteRow(i);\n completed.appendRow(row);\n console.log(`Deleted row: ${i}`);\n sendEquipmentRequestCompletedEmail_({\n \"Employee name\": row[3],\n \"Desk location\": row[4],\n email: row[6],\n });\n }\n }\n}\n\n/**\n * Sends an email notification that a new equipment request has been submitted.\n *\n * @param {Object} request - Request details\n */\nfunction sendNewEquipmentRequestEmail_(request) {\n const template = HtmlService.createTemplateFromFile(\n \"new-equipment-request.html\",\n );\n template.request = request;\n template.sheetUrl = SpreadsheetApp.getActiveSpreadsheet().getUrl();\n const msg = template.evaluate();\n MailApp.sendEmail({\n to: REQUEST_NOTIFICATION_EMAIL,\n subject: \"New equipment request\",\n htmlBody: msg.getContent(),\n });\n}\n\n/**\n * Sends an email notifying the requestor that the request is complete.\n *\n * @param {Object} request - Request details\n */\nfunction sendEquipmentRequestCompletedEmail_(request) {\n const template = HtmlService.createTemplateFromFile(\"request-complete.html\");\n template.request = request;\n const msg = template.evaluate();\n MailApp.sendEmail({\n to: request.email,\n subject: \"Equipment request completed\",\n htmlBody: msg.getContent(),\n });\n}\n\n/**\n * Converts a form response to an object keyed by the item titles. Allows easier\n * access to response values.\n *\n * @param {FormResponse} response\n * @return {Object} Form values keyed by question title\n */\nfunction mapResponse_(response) {\n const initialValue = {\n email: response.getRespondentEmail(),\n timestamp: response.getTimestamp(),\n };\n return response.getItemResponses().reduce((obj, itemResponse) => {\n const key = itemResponse.getItem().getTitle();\n obj[key] = itemResponse.getResponse();\n return obj;\n }, initialValue);\n}\n```\n\nExample:\n```text\n</section>\n<section>\n <h3>new-equipment-request.html</h3>\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<!--\n Copyright 2022 Google LLC\n\n 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 http://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.\n-->\n\n<html>\n <body>\n <p>\n A new equipment request has been made by <?= request.email ?>.\n </p>\n\n <p>\n Employee name: <?= request['Employee name'] ?><br/>\n Desk location name: <?= request['Desk location'] ?><br/>\n Due date: <?= request['Due date'] ?><br/>\n Laptop model: <?= request['Laptop'] ?><br/>\n Desktop model: <?= request['Desktop'] ?><br/>\n Monitor(s): <?= request['Monitor'] ?><br/>\n </p>\n\n See <a href=\"<?= sheetUrl ?>\">the spreadsheet</a> to take or assign this item.\n </body>\n</html>\n```\n\nExample:\n```text\n</section>\n<section>\n <h3>request-complete.html</h3>\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<!--\n Copyright 2022 Google LLC\n\n 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 http://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.\n-->\n\n<html>\n <body>\n <p>\n An equipment request has been completed.\n </p>\n\n <p>\n Employee name: <?= request['Employee name'] ?><br/>\n Desk location name: <?= request['Desk location'] ?><br/>\n </p>\n </body>\n</html>\n```\n\nExample:\n```text\n</section>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.386Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":305,"estimatedTokens":2172}}1125{"id":"doc-project_management_apps_script_google_for_develo-38f7d4b2","source":"documentation","title":"Project management | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/api/samples/manage","text":"Example:\n```text\nPOST https://scriptmanagement.googleapis.com/v1/projects/\n```\n\nExample:\n```text\n{\n \"title\": \"My Script\"\n}\n```\n\nExample:\n```text\nGET https://scriptmanagement.googleapis.com/v1/projects/scriptId\n```\n\nExample:\n```text\n{\n \"scriptId\": \"scriptId\",\n \"title\": \"My Title\",\n \"parentId\": \"parentId\",\n \"createTime\": \"2017-10-02T15:01:23.045123456Z\",\n \"updateTime\": \"2017-10-02T15:01:23.045123456Z\",\n \"creator\": { \"name\": \"Grant\" },\n \"lastModifyUser\": { \"name\": \"Grant\" },\n}\n```\n\nExample:\n```text\nGET https://scriptmanagement.googleapis.com/v1/projects/scriptId/content\n```\n\nExample:\n```text\n{\n \"scriptId\": \"scriptId\",\n \"files\": [{\n \"name\": \"My Script\",\n \"type\": \"SERVER_JS\",\n \"source\": \"function hello(){\\nconsole.log('Hello world');}\",\n \"lastModifyUser\": {\n \"name\": \"Grant\",\n \"email\": \"grant@example.com\",\n },\n \"createTime\": \"2017-10-02T15:01:23.045123456Z\",\n \"updateTime\": \"2017-10-02T15:01:23.045123456Z\",\n \"functionSet\": {\n \"values\": [\n \"name\": \"helloWorld\"\n ]\n }\n }, {\n \"name\": \"appsscript\",\n \"type\": \"JSON\",\n \"source\": \"{\\\"timeZone\\\":\\\"America/New_York\\\",\\\"exceptionLogging\\\":\\\"CLOUD\\\"}\",\n \"lastModifyUser\": {\n \"name\": \"Grant\",\n \"email\": \"grant@example.com\",\n },\n \"createTime\": \"2017-10-02T15:01:23.045123456Z\",\n \"updateTime\": \"2017-10-02T15:01:23.045123456Z\"\n }]\n}\n```\n\nExample:\n```text\nPUT https://scriptmanagement.googleapis.com/v1/projects/scriptID/content\n```\n\nExample:\n```text\n{\n \"files\": [{\n \"name\": \"index\",\n \"type\": \"HTML\",\n \"source\": \"<html> <header><title>HTML Page</title></header> <body> My HTML </body> </html>\"\n }, {\n \"name\": \"My Script\",\n \"type\": \"SERVER_JS\",\n \"source\": \"function hello(){\\nconsole.log('Hello world');}\",\n }, {\n \"name\": \"appsscript\",\n \"type\": \"JSON\",\n \"source\": \"{\\\"timeZone\\\":\\\"America/New_York\\\",\\\"exceptionLogging\\\":\\\"CLOUD\\\"}\",\n \"lastModifyUser\": {\n \"name\": \"Grant\",\n \"email\": \"grant@example.com\",\n },\n \"createTime\": \"2017-10-02T15:01:23.045123456Z\",\n \"updateTime\": \"2017-10-02T15:01:23.045123456Z\"\n }]\n}\n```\n\nExample:\n```text\n{\n \"scriptId\": \"scriptId\",\n \"files\": [{\n \"name\": \"index\",\n \"type\": \"HTML\",\n \"source\": \"<html> <header><title>HTML Page</title></header> <body> My HTML </body> </html>\",\n \"lastModifyUser\": {\n \"name\": \"Grant\",\n \"email\": \"grant@example.com\",\n },\n \"createTime\": \"2017-10-02T15:01:23.045123456Z\",\n \"updateTime\": \"2017-10-02T15:01:23.045123456Z\"\n }, {\n \"name\": \"My Script\",\n \"type\": \"SERVER_JS\",\n \"source\": \"function hello(){\\nconsole.log('Hello world');}\",\n \"lastModifyUser\": {\n \"name\": \"Grant\",\n \"email\": \"grant@example.com\",\n },\n \"createTime\": \"2017-10-02T15:01:23.045123456Z\",\n \"updateTime\": \"2017-10-02T15:01:23.045123456Z\",\n \"functionSet\": {\n \"values\": [\n \"name\": \"helloWorld\"\n ]\n }\n }, {\n \"name\": \"appsscript\",\n \"type\": \"JSON\",\n \"source\": \"{\\\"timeZone\\\":\\\"America/New_York\\\",\\\"exceptionLogging\\\":\\\"CLOUD\\\"}\",\n \"lastModifyUser\": {\n \"name\": \"Grant\",\n \"email\": \"grant@example.com\",\n },\n \"createTime\": \"2017-10-02T15:01:23.045123456Z\",\n \"updateTime\": \"2017-10-02T15:01:23.045123456Z\"\n }]\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.386Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":142,"estimatedTokens":813}}1126{"id":"doc-import_csv_data_to_a_spreadsheet_apps_script_goo-97a769f8","source":"documentation","title":"Import CSV data to a spreadsheet | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/import-csv-sheets","text":"Example:\n```text\n// To learn more about this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/import-csv-sheets\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * This file contains the main functions that import data from CSV files into a Google Spreadsheet.\n */\n\n// Application constants\nconst APP_TITLE = \"Trigger-driven CSV import [App Script Sample]\"; // Application name\nconst APP_FOLDER = \"[App Script sample] Import CSVs\"; // Application primary folder\nconst SOURCE_FOLDER = \"Inbound CSV Files\"; // Folder for the update files.\nconst PROCESSED_FOLDER = \"Processed CSV Files\"; // Folder to hold processed files.\nconst SHEET_REPORT_NAME = \"Import CSVs\"; // Name of destination spreadsheet.\n\n// Application settings\nconst CSV_HEADER_EXIST = true; // Set to true if CSV files have a header row, false if not.\nconst HANDLER_FUNCTION = \"updateApplicationSheet\"; // Function called by installable trigger to run data processing.\n\n/**\n * Installs a time-driven trigger that runs daily to import CSVs into the main application spreadsheet.\n * Prior to creating a new instance, removes any existing triggers to avoid duplication.\n *\n * Called by setupSample() or run directly setting up the application.\n */\nfunction installTrigger() {\n // Checks for an existing trigger to avoid creating duplicate instances.\n // Removes existing if found.\n const projectTriggers = ScriptApp.getProjectTriggers();\n for (let i = 0; i < projectTriggers.length; i++) {\n if (projectTriggers[i].getHandlerFunction() === HANDLER_FUNCTION) {\n console.log(\n `Existing trigger with Handler Function of '${HANDLER_FUNCTION}' removed.`,\n );\n ScriptApp.deleteTrigger(projectTriggers[i]);\n }\n }\n // Creates the new trigger.\n const newTrigger = ScriptApp.newTrigger(HANDLER_FUNCTION)\n .timeBased()\n .atHour(23) // Runs at 11 PM in the time zone of this script.\n .everyDays(1) // Runs once per day.\n .create();\n console.log(\n `New trigger with Handler Function of '${HANDLER_FUNCTION}' created.`,\n );\n}\n\n/**\n * Handler function called by the trigger created with the \"installTrigger\" function.\n * Run this directly to execute the entire automation process of the application with a trigger.\n *\n * Process: Iterates through CSV files located in the source folder (SOURCE_FOLDER),\n * and appends them to the end of destination spreadsheet (SHEET_REPORT_NAME).\n * Successfully processed CSV files are moved to the processed folder (PROCESSED_FOLDER) to avoid duplication.\n * Sends summary email with status of the import.\n */\nfunction updateApplicationSheet() {\n // Gets application & supporting folders.\n const folderAppPrimary = getApplicationFolder_(APP_FOLDER);\n const folderSource = getFolder_(SOURCE_FOLDER);\n const folderProcessed = getFolder_(PROCESSED_FOLDER);\n\n // Gets the application's destination spreadsheet {Spreadsheet object}\n const objSpreadSheet = getSpreadSheet_(SHEET_REPORT_NAME, folderAppPrimary);\n\n // Creates arrays to track every CSV file, categorized as processed sucessfully or not.\n const filesProcessed = [];\n const filesNotProcessed = [];\n\n // Gets all CSV files found in the source folder.\n const cvsFiles = folderSource.getFilesByType(MimeType.CSV);\n\n // Iterates through each CSV file.\n while (cvsFiles.hasNext()) {\n const csvFile = cvsFiles.next();\n const isSuccess = processCsv_(objSpreadSheet, csvFile);\n\n if (isSuccess) {\n // Moves the processed file to the processed folder to prevent future duplicate data imports.\n csvFile.moveTo(folderProcessed);\n // Logs the successfully processed file to the filesProcessed array.\n filesProcessed.push(csvFile.getName());\n console.log(`Successfully processed: ${csvFile.getName()}`);\n } else {\n // Doesn't move the unsuccesfully processed file so that it can be corrected and reprocessed later.\n // Logs the unsuccessfully processed file to the filesNotProcessed array.\n filesNotProcessed.push(csvFile.getName());\n console.log(`Not processed: ${csvFile.getName()}`);\n }\n }\n\n // Prepares summary email.\n // Gets variables to link to this Apps Script project.\n const scriptId = ScriptApp.getScriptId();\n const scriptUrl = DriveApp.getFileById(scriptId).getUrl();\n const scriptName = DriveApp.getFileById(scriptId).getName();\n\n // Gets variables to link to the main application spreadsheet.\n const sheetUrl = objSpreadSheet.getUrl();\n const sheetName = objSpreadSheet.getName();\n\n // Gets user email and timestamp.\n const emailTo = Session.getEffectiveUser().getEmail();\n const timestamp = Utilities.formatDate(\n new Date(),\n Session.getScriptTimeZone(),\n \"yyyy-MM-dd HH:mm:ss zzzz\",\n );\n\n // Prepares lists and counts of processed CSV files.\n let processedList = \"\";\n const processedCount = filesProcessed.length;\n for (const processed of filesProcessed) {\n processedList += `${processed}<br>`;\n }\n\n const unProcessedCount = filesNotProcessed.length;\n let unProcessedList = \"\";\n for (const unProcessed of filesNotProcessed) {\n unProcessedList += `${unProcessed}\\n`;\n }\n\n // Assembles email body as html.\n const eMailBody = `${APP_TITLE} ran an automated process at ${timestamp}.<br><br><b>Files successfully updated:</b> ${processedCount}<br>${processedList}<br><b>Files not updated:</b> ${unProcessedCount}<br>${unProcessedList}<br><br>View all updates in the Google Sheets spreadsheet <b><a href= \"${sheetUrl}\" target=\\\"_blank\\\">${sheetName}</a></b>.<br><br>*************<br><br>This email was generated by Google Apps Script. To learn more about this application or make changes, open the script project below: <br><a href= \"${scriptUrl}\" target=\\\"_blank\\\">${scriptName}</a>`;\n\n MailApp.sendEmail({\n to: emailTo,\n subject: `Automated email from ${APP_TITLE}`,\n htmlBody: eMailBody,\n });\n console.log(`Email sent to ${emailTo}`);\n}\n\n/**\n * Parses CSV data into an array and appends it after the last row in the destination spreadsheet.\n *\n * @return {boolean} true if the update is successful, false if unexpected errors occur.\n */\nfunction processCsv_(objSpreadSheet, csvFile) {\n try {\n // Gets the first sheet of the destination spreadsheet.\n const sheet = objSpreadSheet.getSheets()[0];\n\n // Parses CSV file into data array.\n const data = Utilities.parseCsv(csvFile.getBlob().getDataAsString());\n\n // Omits header row if application variable CSV_HEADER_EXIST is set to 'true'.\n if (CSV_HEADER_EXIST) {\n data.splice(0, 1);\n }\n // Gets the row and column coordinates for next available range in the spreadsheet.\n const startRow = sheet.getLastRow() + 1;\n const startCol = 1;\n // Determines the incoming data size.\n const numRows = data.length;\n const numColumns = data[0].length;\n\n // Appends data into the sheet.\n sheet.getRange(startRow, startCol, numRows, numColumns).setValues(data);\n return true; // Success.\n } catch {\n return false; // Failure. Checks for CSV data file error.\n }\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2022 Google LLC\n *\n * 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 * http://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.\n */\n\n/**\n * This file contains functions to access headings and data for sample files.\n *\n * Sample data is stored in the variable SAMPLE_DATA.\n */\n\n// Fictitious sample data.\nconst SAMPLE_DATA = {\n headings: [\n \"PropertyName\",\n \"LeaseID\",\n \"LeaseLocation\",\n \"OwnerName\",\n \"SquareFootage\",\n \"RenewDate\",\n \"LastAmount\",\n \"LastPaymentDate\",\n \"Revenue\",\n ],\n csvFiles: [\n {\n name: \"Sample One.CSV\",\n rows: [\n {\n PropertyName: \"The Modern Building\",\n LeaseID: \"271312\",\n LeaseLocation: \"Mountain View CA 94045\",\n OwnerName: \"Yuri\",\n SquareFootage: \"17500\",\n RenewDate: \"12/15/2022\",\n LastAmount: \"100000\",\n LastPaymentDate: \"3/01/2022\",\n Revenue: \"12000\",\n },\n {\n PropertyName: \"Garage @ 45\",\n LeaseID: \"271320\",\n LeaseLocation: \"Mountain View CA 94045\",\n OwnerName: \"Luka\",\n SquareFootage: \"1000\",\n RenewDate: \"6/2/2022\",\n LastAmount: \"50000\",\n LastPaymentDate: \"4/01/2022\",\n Revenue: \"20000\",\n },\n {\n PropertyName: \"Office Park Deluxe\",\n LeaseID: \"271301\",\n LeaseLocation: \"Mountain View CA 94045\",\n OwnerName: \"Sasha\",\n SquareFootage: \"5000\",\n RenewDate: \"6/2/2022\",\n LastAmount: \"25000\",\n LastPaymentDate: \"4/01/2022\",\n Revenue: \"1200\",\n },\n ],\n },\n {\n name: \"Sample Two.CSV\",\n rows: [\n {\n PropertyName: \"Tours Jumelles Minuscules\",\n LeaseID: \"271260\",\n LeaseLocation: \"8 Rue du Nom Fictif 341 Paris\",\n OwnerName: \"Lucian\",\n SquareFootage: \"1000000\",\n RenewDate: \"7/14/2022\",\n LastAmount: \"1250000\",\n LastPaymentDate: \"5/01/2022\",\n Revenue: \"77777\",\n },\n {\n PropertyName: \"Barraca da Praia\",\n LeaseID: \"271281\",\n LeaseLocation: \"Avenida da Pastelaria 1903 Lisbon 1229-076\",\n OwnerName: \"Raha\",\n SquareFootage: \"1000\",\n RenewDate: \"6/2/2022\",\n LastAmount: \"50000\",\n LastPaymentDate: \"4/01/2022\",\n Revenue: \"20000\",\n },\n ],\n },\n {\n name: \"Sample Three.CSV\",\n rows: [\n {\n PropertyName: \"Round Building in the Square\",\n LeaseID: \"371260\",\n LeaseLocation: \"8 Rue du Nom Fictif 341 Paris\",\n OwnerName: \"Charlie\",\n SquareFootage: \"75000\",\n RenewDate: \"8/1/2022\",\n LastAmount: \"250000\",\n LastPaymentDate: \"6/01/2022\",\n Revenue: \"22222\",\n },\n {\n PropertyName: \"Square Building in the Round\",\n LeaseID: \"371281\",\n LeaseLocation: \"Avenida da Pastelaria 1903 Lisbon 1229-076\",\n OwnerName: \"Lee\",\n SquareFootage: \"10000\",\n RenewDate: \"6/2/2022\",\n LastAmount: \"5000\",\n LastPaymentDate: \"4/01/2022\",\n Revenue: \"1800\",\n },\n ],\n },\n ],\n};\n\n/**\n * Returns headings for use in destination spreadsheet and CSV files.\n * @return {string[][]} array of each column heading as string.\n */\nfunction getHeadings() {\n const headings = [[]];\n for (const i in SAMPLE_DATA.headings)\n headings[0].push(SAMPLE_DATA.headings[i]);\n return headings;\n}\n\n/**\n * Returns CSV file names and content to create sample CSV files.\n * @return {object[]} {\"file\": [\"name\",\"csv\"]}\n */\nfunction getCSVFilesData() {\n const files = [];\n\n // Gets headings once - same for all files/rows.\n let csvHeadings = \"\";\n for (const i in SAMPLE_DATA.headings)\n csvHeadings += `${SAMPLE_DATA.headings[i]},`;\n\n // Gets data for each file by rows.\n for (const i in SAMPLE_DATA.csvFiles) {\n let sampleCSV = \"\";\n sampleCSV += csvHeadings;\n const fileName = SAMPLE_DATA.csvFiles[i].name;\n for (const j in SAMPLE_DATA.csvFiles[i].rows) {\n sampleCSV += \"\\n\";\n for (const k in SAMPLE_DATA.csvFiles[i].rows[j]) {\n sampleCSV += `${SAMPLE_DATA.csvFiles[i].rows[j][k]},`;\n }\n }\n files.push({ name: fileName, csv: sampleCSV });\n }\n return files;\n}\n\n/*\n * Checks data functions are working as necessary.\n */\nfunction test_getHeadings() {\n const h = getHeadings();\n console.log(h);\n console.log(h[0].length);\n}\n\nfunction test_getCSVFilesData() {\n const csvFiles = getCSVFilesData();\n console.log(csvFiles);\n\n for (const file of csvFiles) {\n console.log(file.name);\n console.log(file.csv);\n }\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2022 Google LLC\n *\n * 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 * http://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.\n */\n\n/**\n * This file contains functions that set up the folders and sample files used to demo the application.\n *\n * Sample data for the application is stored in the SampleData.gs file.\n */\n\n// Global variables for sample setup.\nconst INCLUDE_SAMPLE_DATA_FILES = true; // Set to true to create sample data files, false to skip.\n\n/**\n * Runs the setup for the sample.\n * 1) Creates the application folder and subfolders for unprocessed/processed CSV files.\n * from global variables APP_FOLDER | SOURCE_FOLDER | PROCESSED_FOLDER\n * 2) Creates the sample Sheets spreadsheet in the application folder.\n * from global variable SHEET_REPORT_NAME\n * 3) Creates CSV files from sample data in the unprocessed files folder.\n * from variable SAMPLE_DATA in SampleData.gs.\n * 4) Creates an installable trigger to run process automatically at a specified time interval.\n */\nfunction setupSample() {\n console.log(`Application setup for: ${APP_TITLE}`);\n\n // Creates application folder.\n const folderAppPrimary = getApplicationFolder_(APP_FOLDER);\n // Creates supporting folders.\n const folderSource = getFolder_(SOURCE_FOLDER);\n const folderProcessed = getFolder_(PROCESSED_FOLDER);\n\n console.log(\n `Application folders: ${folderAppPrimary.getName()}, ${folderSource.getName()}, ${folderProcessed.getName()}`,\n );\n\n if (INCLUDE_SAMPLE_DATA_FILES) {\n // Sets up primary destination spreadsheet\n const sheet = setupPrimarySpreadsheet_(folderAppPrimary);\n\n // Gets the CSV files data - refer to the SampleData.gs file to view.\n const csvFiles = getCSVFilesData();\n\n // Processes each CSV file.\n for (const file of csvFiles) {\n // Creates CSV file in source folder if it doesn't exist.\n if (!fileExists_(file.name, folderSource)) {\n const csvFileId = DriveApp.createFile(\n file.name,\n file.csv,\n MimeType.CSV,\n );\n console.log(`Created Sample CSV: ${file.name}`);\n csvFileId.moveTo(folderSource);\n }\n }\n }\n // Installs (or recreates) project trigger\n installTrigger();\n\n console.log(`Setup completed for: ${APP_TITLE}`);\n}\n\n/**\n *\n */\nfunction setupPrimarySpreadsheet_(folderAppPrimary) {\n // Creates the report destination spreadsheet if doesn't exist.\n if (!fileExists_(SHEET_REPORT_NAME, folderAppPrimary)) {\n // Creates new destination spreadsheet (report) with cell size of 20 x 10.\n const sheet = SpreadsheetApp.create(SHEET_REPORT_NAME, 20, 10);\n\n // Adds the sample data headings.\n const sheetHeadings = getHeadings();\n sheet\n .getSheets()[0]\n .getRange(1, 1, 1, sheetHeadings[0].length)\n .setValues(sheetHeadings);\n SpreadsheetApp.flush();\n // Moves to primary application root folder.\n DriveApp.getFileById(sheet.getId()).moveTo(folderAppPrimary);\n\n console.log(\n `Created file: ${SHEET_REPORT_NAME} In folder: ${folderAppPrimary.getName()}.`,\n );\n return sheet;\n }\n}\n\n/**\n * Moves sample content to Drive trash & uninstalls trigger.\n * This function removes all folders and content related to this application.\n */\nfunction removeSample() {\n getApplicationFolder_(APP_FOLDER).setTrashed(true);\n console.log(\n `'${APP_FOLDER}' contents have been moved to Drive Trash folder.`,\n );\n\n // Removes existing trigger if found.\n const projectTriggers = ScriptApp.getProjectTriggers();\n for (let i = 0; i < projectTriggers.length; i++) {\n if (projectTriggers[i].getHandlerFunction() === HANDLER_FUNCTION) {\n console.log(\n `Existing trigger with handler function of '${HANDLER_FUNCTION}' removed.`,\n );\n ScriptApp.deleteTrigger(projectTriggers[i]);\n }\n }\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2022 Google LLC\n *\n * 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 * http://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.\n */\n\n/**\n * This file contains utility functions that work with application's folder and files.\n */\n\n/**\n * Gets application destination spreadsheet from a given folder\n * Returns new sample version if orignal is not found.\n *\n * @param {string} fileName - Name of the file to test for.\n * @param {object} objFolder - Folder object in which to search.\n * @return {object} Spreadsheet object.\n */\nfunction getSpreadSheet_(fileName, objFolder) {\n const files = objFolder.getFilesByName(fileName);\n\n while (files.hasNext()) {\n const file = files.next();\n const fileId = file.getId();\n\n const existingSpreadsheet = SpreadsheetApp.openById(fileId);\n return existingSpreadsheet;\n }\n\n // If application destination spreadsheet is missing, creates a new sample version.\n const folderAppPrimary = getApplicationFolder_(APP_FOLDER);\n const sampleSheet = setupPrimarySpreadsheet_(folderAppPrimary);\n return sampleSheet;\n}\n\n/**\n * Tests if a file exists within a given folder.\n *\n * @param {string} fileName - Name of the file to test for.\n * @param {object} objFolder - Folder object in which to search.\n * @return {boolean} true if found in folder, false if not.\n */\nfunction fileExists_(fileName, objFolder) {\n const files = objFolder.getFilesByName(fileName);\n\n while (files.hasNext()) {\n const file = files.next();\n console.log(`${file.getName()} already exists.`);\n return true;\n }\n return false;\n}\n\n/**\n * Returns folder named in folderName parameter.\n * Checks if folder already exists, creates it if it doesn't.\n *\n * @param {string} folderName - Name of the Drive folder.\n * @return {object} Google Drive Folder\n */\nfunction getFolder_(folderName) {\n // Gets the primary folder for the application.\n const parentFolder = getApplicationFolder_();\n\n // Iterates subfolders to check if folder already exists.\n const subFolders = parentFolder.getFolders();\n while (subFolders.hasNext()) {\n const folder = subFolders.next();\n\n // Returns the existing folder if found.\n if (folder.getName() === folderName) {\n return folder;\n }\n }\n // Creates a new folder if one doesn't already exist.\n return parentFolder\n .createFolder(folderName)\n .setDescription(`Supporting folder created by ${APP_TITLE}.`);\n}\n\n/**\n * Returns the primary folder as named by the APP_FOLDER variable in the Code.gs file.\n * Checks if folder already exists to avoid duplication.\n * Creates new instance if existing folder not found.\n *\n * @return {object} Google Drive Folder\n */\nfunction getApplicationFolder_() {\n // Gets root folder, currently set to 'My Drive'\n const parentFolder = DriveApp.getRootFolder();\n\n // Iterates through the subfolders to check if folder already exists.\n const subFolders = parentFolder.getFolders();\n while (subFolders.hasNext()) {\n const folder = subFolders.next();\n\n // Returns the existing folder if found.\n if (folder.getName() === APP_FOLDER) {\n return folder;\n }\n }\n // Creates a new folder if one doesn't already exist.\n return parentFolder\n .createFolder(APP_FOLDER)\n .setDescription(`Main application folder created by ${APP_TITLE}.`);\n}\n\n/**\n * Tests getApplicationFolder_ and getFolder_\n * @logs details of created Google Drive folder.\n */\nfunction test_getFolderByName() {\n let folder = getApplicationFolder_();\n console.log(\n `Name: ${folder.getName()}\\rID: ${folder.getId()}\\rURL:${folder.getUrl()}\\rDescription: ${folder.getDescription()}`,\n );\n // Uncomment the following to automatically delete test folder.\n // folder.setTrashed(true);\n\n folder = getFolder_(SOURCE_FOLDER);\n console.log(\n `Name: ${folder.getName()}\\rID: ${folder.getId()}\\rURL:${folder.getUrl()}\\rDescription: ${folder.getDescription()}`,\n );\n // Uncomment the following to automatically delete test folder.\n // folder.setTrashed(true);\n\n folder = getFolder_(PROCESSED_FOLDER);\n console.log(\n `Name: ${folder.getName()}\\rID: ${folder.getId()}\\rURL:${folder.getUrl()}\\rDescription: ${folder.getDescription()}`,\n );\n // Uncomment the following to automatically delete test folder.\n // folder.setTrashed(true);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.388Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":650,"estimatedTokens":5425}}1127{"id":"doc-create_a_sign_up_for_an_offsite_apps_script_goog-d1bf1ca1","source":"documentation","title":"Create a sign-up for an offsite | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/offsite-activity-signup","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/offsite-activity-signup\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nconst NUM_ITEMS_TO_RANK = 5;\nconst ACTIVITIES_PER_PERSON = 2;\nconst NUM_TEST_USERS = 150;\n\n/**\n * Adds custom menu items when opening the sheet.\n */\nfunction onOpen() {\n const menu = SpreadsheetApp.getUi()\n .createMenu(\"Activities\")\n .addItem(\"Create form\", \"buildForm_\")\n .addItem(\"Generate test data\", \"generateTestData_\")\n .addItem(\"Assign activities\", \"assignActivities_\")\n .addToUi();\n}\n\n/**\n * Builds a form based on the \"Activity Schedule\" sheet. The form asks attendees to rank their top\n * N choices of activities, where N is defined by NUM_ITEMS_TO_RANK.\n */\nfunction buildForm_() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n if (ss.getFormUrl()) {\n const msg = \"Form already exists. Unlink the form and try again.\";\n SpreadsheetApp.getUi().alert(msg);\n return;\n }\n const form = FormApp.create(\"Activity Signup\")\n .setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId())\n .setAllowResponseEdits(true)\n .setLimitOneResponsePerUser(true)\n .setCollectEmail(true);\n const sectionHelpText = Utilities.formatString(\n \"Please choose your top %d activities\",\n NUM_ITEMS_TO_RANK,\n );\n form\n .addSectionHeaderItem()\n .setTitle(\"Activity choices\")\n .setHelpText(sectionHelpText);\n\n // Presents activity ranking as a form grid with each activity as a row and rank as a column.\n const rows = loadActivitySchedule_(ss).map(\n (activity) => activity.description,\n );\n const columns = range_(1, NUM_ITEMS_TO_RANK).map((value) =>\n Utilities.formatString(\"%s\", toOrdinal_(value)),\n );\n const gridValidation = FormApp.createGridValidation()\n .setHelpText(\"Select one item per column.\")\n .requireLimitOneResponsePerColumn()\n .build();\n form\n .addGridItem()\n .setColumns(columns)\n .setRows(rows)\n .setValidation(gridValidation);\n\n form\n .addListItem()\n .setTitle(\"Assign other activities if choices are not available?\")\n .setChoiceValues([\"Yes\", \"No\"]);\n}\n\n/**\n * Assigns activities using a random priority/random serial dictatorship approach. The results\n * are then populated into two new sheets, one listing activities per person, the other listing\n * the rosters for each activity.\n *\n * See https://en.wikipedia.org/wiki/Random_serial_dictatorship for additional information.\n */\nfunction assignActivities_() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const activities = loadActivitySchedule_(ss);\n const activityIds = activities.map((activity) => activity.id);\n const attendees = loadAttendeeResponses_(ss, activityIds);\n assignWithRandomPriority_(attendees, activities, 2);\n writeAttendeeAssignments_(ss, attendees);\n writeActivityRosters_(ss, activities);\n}\n\n/**\n * Selects activities via random priority.\n *\n * @param {object[]} attendees - Array of attendees to assign activities to\n * @param {object[]} activities - Array of all available activities\n * @param {number} numActivitiesPerPerson - Maximum number of activities to assign\n */\nfunction assignWithRandomPriority_(\n attendees,\n activities,\n numActivitiesPerPerson,\n) {\n const activitiesById = activities.reduce((obj, activity) => {\n obj[activity.id] = activity;\n return obj;\n }, {});\n for (let i = 0; i < numActivitiesPerPerson; ++i) {\n const randomizedAttendees = shuffleArray_(attendees);\n for (const attendee of randomizedAttendees) {\n makeChoice_(attendee, activitiesById);\n }\n }\n}\n\n/**\n * Attempts to assign an activity for an attendee based on their preferences and current schedule.\n *\n * @param {object} attendee - Attendee looking to join an activity\n * @param {object} activitiesById - Map of all available activities\n */\nfunction makeChoice_(attendee, activitiesById) {\n for (let i = 0; i < attendee.preferences.length; ++i) {\n const activity = activitiesById[attendee.preferences[i]];\n if (!activity) {\n continue;\n }\n const canJoin = checkAvailability_(attendee, activity);\n if (canJoin) {\n attendee.assigned.push(activity);\n activity.roster.push(attendee);\n break;\n }\n }\n}\n\n/**\n * Checks that an activity has capacity and doesn't conflict with previously assigned\n * activities.\n *\n * @param {object} attendee - Attendee looking to join the activity\n * @param {object} activity - Proposed activity\n * @return {boolean} - True if attendee can join the activity\n */\nfunction checkAvailability_(attendee, activity) {\n if (activity.capacity <= activity.roster.length) {\n return false;\n }\n const timesConflict = attendee.assigned.some(\n (assignedActivity) =>\n !(\n assignedActivity.startAt.getTime() > activity.endAt.getTime() ||\n activity.startAt.getTime() > assignedActivity.endAt.getTime()\n ),\n );\n return !timesConflict;\n}\n\n/**\n * Populates a sheet with the assigned activities for each attendee.\n *\n * @param {Spreadsheet} ss - Spreadsheet to write to.\n * @param {object[]} attendees - Array of attendees with their activity assignments\n */\nfunction writeAttendeeAssignments_(ss, attendees) {\n const sheet = findOrCreateSheetByName_(ss, \"Activities by person\");\n sheet.clear();\n sheet.appendRow([\"Email address\", \"Activities\"]);\n sheet.getRange(\"B1:1\").merge();\n const rows = attendees.map((attendee) => {\n // Prefill row to ensure consistent length otherwise\n // can't bulk update the sheet with range.setValues()\n const row = fillArray_([], ACTIVITIES_PER_PERSON + 1, \"\");\n row[0] = attendee.email;\n attendee.assigned.forEach((activity, index) => {\n row[index + 1] = activity.description;\n });\n return row;\n });\n bulkAppendRows_(sheet, rows);\n sheet.setFrozenRows(1);\n sheet.getRange(\"1:1\").setFontWeight(\"bold\");\n sheet.autoResizeColumns(1, sheet.getLastColumn());\n}\n\n/**\n * Populates a sheet with the rosters for each activity.\n *\n * @param {Spreadsheet} ss - Spreadsheet to write to.\n * @param {object[]} activities - Array of activities with their rosters\n */\nfunction writeActivityRosters_(ss, activities) {\n const sheet = findOrCreateSheetByName_(ss, \"Activity rosters\");\n sheet.clear();\n let rows = activities.map((activity) => {\n const roster = activity.roster.map((attendee) => attendee.email);\n return [activity.description].concat(roster);\n });\n // Transpose the data so each activity is a column\n rows = transpose_(rows, \"\");\n bulkAppendRows_(sheet, rows);\n sheet.setFrozenRows(1);\n sheet.getRange(\"1:1\").setFontWeight(\"bold\");\n sheet.autoResizeColumns(1, sheet.getLastColumn());\n}\n\n/**\n * Loads the activity schedule.\n *\n * @param {Spreadsheet} ss - Spreadsheet to load from\n * @return {object[]} Array of available activities.\n */\nfunction loadActivitySchedule_(ss) {\n const timeZone = ss.getSpreadsheetTimeZone();\n const sheet = ss.getSheetByName(\"Activity Schedule\");\n const rows = sheet.getSheetValues(\n sheet.getFrozenRows() + 1,\n 1,\n sheet.getLastRow() - 1,\n sheet.getLastRow(),\n );\n const activities = rows.map((row, index) => {\n const name = row[0];\n const startAt = new Date(row[1]);\n const endAt = new Date(row[2]);\n const capacity = Number.parseInt(row[3]);\n const formattedStartAt = Utilities.formatDate(\n startAt,\n timeZone,\n \"EEE hh:mm a\",\n );\n const formattedEndAt = Utilities.formatDate(endAt, timeZone, \"hh:mm a\");\n const description = Utilities.formatString(\n \"%s (%s-%s)\",\n name,\n formattedStartAt,\n formattedEndAt,\n );\n return {\n id: index,\n name: name,\n description: description,\n capacity: capacity,\n startAt: startAt,\n endAt: endAt,\n roster: [],\n };\n });\n return activities;\n}\n\n/**\n * Loads the attendeee response data.\n *\n * @param {Spreadsheet} ss - Spreadsheet to load from\n * @param {number[]} allActivityIds - Full set of available activity IDs\n * @return {object[]} Array of parsed attendee respones.\n */\nfunction loadAttendeeResponses_(ss, allActivityIds) {\n const sheet = findResponseSheetForForm_(ss);\n\n if (!sheet || sheet.getLastRow() === 1) {\n return undefined;\n }\n\n const rows = sheet.getSheetValues(\n sheet.getFrozenRows() + 1,\n 1,\n sheet.getLastRow() - 1,\n sheet.getLastRow(),\n );\n const attendees = rows.map((row) => {\n const _ = row.shift(); // Ignore timestamp\n const email = row.shift();\n const autoAssign = row.pop();\n // Find ranked items in the response data.\n let preferences = row.reduce((prefs, value, index) => {\n const match = value.match(/(\\d+).*/);\n if (!match) {\n return prefs;\n }\n const rank = Number.parseInt(match[1]) - 1; // Convert ordinal to array index\n prefs[rank] = index;\n return prefs;\n }, []);\n if (autoAssign === \"Yes\") {\n // If auto assigning additional activites, append a randomized list of all the activities.\n // These will then be considered as if the attendee ranked them.\n const additionalChoices = shuffleArray_(allActivityIds);\n preferences = preferences.concat(additionalChoices);\n }\n return {\n email: email,\n preferences: preferences,\n assigned: [],\n };\n });\n return attendees;\n}\n\n/**\n * Simulates a large number of users responding to the form. This enables users to quickly\n * experience the full solution without having to collect sufficient form responses\n * through other means.\n */\nfunction generateTestData_() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const sheet = findResponseSheetForForm_(ss);\n if (!sheet) {\n const msg = \"No response sheet found. Create the form and try again.\";\n SpreadsheetApp.getUi().alert(msg);\n }\n if (sheet.getLastRow() > 1) {\n const msg =\n \"Response sheet is not empty, can not generate test data. \" +\n \"Remove responses and try again.\";\n SpreadsheetApp.getUi().alert(msg);\n return;\n }\n\n const activities = loadActivitySchedule_(ss);\n const choices = fillArray_([], activities.length, \"\");\n for (const value of range_(1, 5)) {\n choices[value] = toOrdinal_(value);\n }\n\n const rows = range_(1, NUM_TEST_USERS).map((value) => {\n const randomizedChoices = shuffleArray_(choices);\n const email = Utilities.formatString(\"person%d@example.com\", value);\n return [new Date(), email].concat(randomizedChoices).concat([\"Yes\"]);\n });\n bulkAppendRows_(sheet, rows);\n}\n\n/**\n * Retrieves a sheet by name, creating it if it doesn't yet exist.\n *\n * @param {Spreadsheet} ss - Containing spreadsheet\n * @Param {string} name - Name of sheet to return\n * @return {Sheet} Sheet instance\n */\nfunction findOrCreateSheetByName_(ss, name) {\n const sheet = ss.getSheetByName(name);\n if (sheet) {\n return sheet;\n }\n return ss.insertSheet(name);\n}\n\n/**\n * Faster version of appending multiple rows via ranges. Requires all rows are equal length.\n *\n * @param {Sheet} sheet - Sheet to append to\n * @param {Array<Array<object>>} rows - Rows to append\n */\nfunction bulkAppendRows_(sheet, rows) {\n const startRow = sheet.getLastRow() + 1;\n const startColumn = 1;\n const numRows = rows.length;\n const numColumns = rows[0].length;\n sheet.getRange(startRow, startColumn, numRows, numColumns).setValues(rows);\n}\n\n/**\n * Copies and randomizes an array.\n *\n * @param {object[]} array - Array to shuffle\n * @return {object[]} randomized copy of the array\n */\nfunction shuffleArray_(array) {\n const clone = array.slice(0);\n for (let i = clone.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n const temp = clone[i];\n clone[i] = clone[j];\n clone[j] = temp;\n }\n return clone;\n}\n\n/**\n * Formats an number as an ordinal.\n *\n * See: https://stackoverflow.com/questions/13627308/add-st-nd-rd-and-th-ordinal-suffix-to-a-number/13627586\n *\n * @param {number} i - Number to format\n * @return {string} Formatted string\n */\nfunction toOrdinal_(i) {\n const j = i % 10;\n const k = i % 100;\n if (j === 1 && k !== 11) {\n return `${i}st`;\n }\n if (j === 2 && k !== 12) {\n return `${i}nd`;\n }\n if (j === 3 && k !== 13) {\n return `${i}rd`;\n }\n return `${i}th`;\n}\n\n/**\n * Locates the sheet containing the form responses.\n *\n * @param {Spreadsheet} ss - Spreadsheet instance to search\n * @return {Sheet} Sheet with form responses, undefined if not found.\n */\nfunction findResponseSheetForForm_(ss) {\n const formUrl = ss.getFormUrl();\n if (!ss || !formUrl) {\n return undefined;\n }\n const sheets = ss.getSheets();\n for (const i in sheets) {\n if (sheets[i].getFormUrl() === formUrl) {\n return sheets[i];\n }\n }\n return undefined;\n}\n\n/**\n * Fills an array with a value ([].fill() not supported in Apps Script).\n *\n * @param {object[]} arr - Array to fill\n * @param {number} length - Number of items to fill.\n * @param {object} value - Value to place at each index.\n * @return {object[]} the array, for chaining purposes\n */\nfunction fillArray_(arr, length, value) {\n for (let i = 0; i < length; ++i) {\n arr[i] = value;\n }\n return arr;\n}\n\n/**\n * Creates and fills an array with numbers in the range [start, end].\n *\n * @param {number} start - First value in the range, inclusive\n * @param {number} end - Last value in the range, inclusive\n * @return {number[]} Array of values representing the range\n */\nfunction range_(start, end) {\n const arr = [start];\n let i = start;\n while (i < end) {\n i += 1;\n arr.push(i);\n }\n return arr;\n}\n\n/**\n * Transposes a matrix/2d array. For cases where the rows are not the same length,\n * `fillValue` is used where no other value would otherwise be present.\n *\n * @param {Array<Array<object>>} arr - 2D array to transpose\n * @param {object} fillValue - Placeholder for undefined values created as a result\n * of the transpose. Only required if rows aren't all of equal length.\n * @return {Array<Array<object>>} New transposed array\n */\nfunction transpose_(arr, fillValue) {\n const transposed = [];\n for (const [rowIndex, row] of arr.entries()) {\n for (const [colIndex, col] of row.entries()) {\n transposed[colIndex] =\n transposed[colIndex] || fillArray_([], arr.length, fillValue);\n transposed[colIndex][rowIndex] = row[colIndex];\n }\n }\n return transposed;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.389Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":489,"estimatedTokens":3703}}1128{"id":"doc-execute_google_apps_script_functions_google_for_-2fba3fd4","source":"documentation","title":"Execute Google Apps Script functions | Google for Developers","url":"https://developers.google.com/apps-script/api/samples/execute","text":"Example:\n```text\nPOST https://script.googleapis.com/v1/scripts/scriptId:run\n```\n\nExample:\n```text\n{\n \"function\": \"listFolderContent\",\n \"parameters\": [\n folderId,\n MAX_SIZE\n ],\n \"devMode\": true\n}\n```\n\nExample:\n```text\n{\n \"response\": {\n \"result\": [\n \"fileTitle1\",\n \"fileTitle2\",\n \"fileTitle3\"\n ]\n },\n}\n```\n\nExample:\n```text\n{\n \"response\": {\n \"error\": {\n \"code\": 3,\n \"message\": \"ScriptError\",\n \"details\": [{\n \"@type\": \"type.googleapis.com/google.apps.script.v1.ExecutionError\",\n \"errorMessage\": \"The script enountered an exeception it could not resolve.\",\n \"errorType\": \"ScriptError\",\n \"scriptStackTraceElements\": [{\n \"function\": \"listFolderContent\",\n \"lineNumber\": 14\n }]\n }]\n }\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.390Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":52,"estimatedTokens":204}}1129{"id":"doc-generate_send_pdfs_from_google_sheets_apps_scrip-adde4544","source":"documentation","title":"Generate & send PDFs from Google Sheets | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/generate-pdfs","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/generate-pdfs\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// TODO: To test this solution, set EMAIL_OVERRIDE to true and set EMAIL_ADDRESS_OVERRIDE to your email address.\nconst EMAIL_OVERRIDE = false;\nconst EMAIL_ADDRESS_OVERRIDE = \"test@example.com\";\n\n// Application constants\nconst APP_TITLE = \"Generate and send PDFs\";\nconst OUTPUT_FOLDER_NAME = \"Customer PDFs\";\nconst DUE_DATE_NUM_DAYS = 15;\n\n// Sheet name constants. Update if you change the names of the sheets.\nconst CUSTOMERS_SHEET_NAME = \"Customers\";\nconst PRODUCTS_SHEET_NAME = \"Products\";\nconst TRANSACTIONS_SHEET_NAME = \"Transactions\";\nconst INVOICES_SHEET_NAME = \"Invoices\";\nconst INVOICE_TEMPLATE_SHEET_NAME = \"Invoice Template\";\n\n// Email constants\nconst EMAIL_SUBJECT = \"Invoice Notification\";\nconst EMAIL_BODY = \"Hello!\\rPlease see the attached PDF document.\";\n\n/**\n * Iterates through the worksheet data populating the template sheet with\n * customer data, then saves each instance as a PDF document.\n *\n * Called by user via custom menu item.\n */\nfunction processDocuments() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const customersSheet = ss.getSheetByName(CUSTOMERS_SHEET_NAME);\n const productsSheet = ss.getSheetByName(PRODUCTS_SHEET_NAME);\n const transactionsSheet = ss.getSheetByName(TRANSACTIONS_SHEET_NAME);\n const invoicesSheet = ss.getSheetByName(INVOICES_SHEET_NAME);\n const invoiceTemplateSheet = ss.getSheetByName(INVOICE_TEMPLATE_SHEET_NAME);\n\n // Gets data from the storage sheets as objects.\n const customers = dataRangeToObject(customersSheet);\n const products = dataRangeToObject(productsSheet);\n const transactions = dataRangeToObject(transactionsSheet);\n\n ss.toast(\"Creating Invoices\", APP_TITLE, 1);\n const invoices = [];\n\n // Iterates for each customer calling createInvoiceForCustomer routine.\n for (const customer of customers) {\n ss.toast(`Creating Invoice for ${customer.customer_name}`, APP_TITLE, 1);\n const invoice = createInvoiceForCustomer(\n customer,\n products,\n transactions,\n invoiceTemplateSheet,\n ss.getId(),\n );\n invoices.push(invoice);\n }\n // Writes invoices data to the sheet.\n invoicesSheet\n .getRange(2, 1, invoices.length, invoices[0].length)\n .setValues(invoices);\n}\n\n/**\n * Processes each customer instance with passed in data parameters.\n *\n * @param {object} customer - Object for the customer\n * @param {object} products - Object for all the products\n * @param {object} transactions - Object for all the transactions\n * @param {object} invoiceTemplateSheet - Object for the invoice template sheet\n * @param {string} ssId - Google Sheet ID\n * Return {array} of instance customer invoice data\n */\nfunction createInvoiceForCustomer(\n customer,\n products,\n transactions,\n templateSheet,\n ssId,\n) {\n const customerTransactions = transactions.filter(\n (transaction) => transaction.customer_name === customer.customer_name,\n );\n\n // Clears existing data from the template.\n clearTemplateSheet();\n\n const lineItems = [];\n let totalAmount = 0;\n for (const lineItem of customerTransactions) {\n const lineItemProduct = products.filter(\n (product) => product.sku_name === lineItem.sku,\n )[0];\n const qty = Number.parseInt(lineItem.licenses);\n const price = Number.parseFloat(lineItemProduct.price).toFixed(2);\n const amount = Number.parseFloat(qty * price).toFixed(2);\n lineItems.push([\n lineItemProduct.sku_name,\n lineItemProduct.sku_description,\n \"\",\n qty,\n price,\n amount,\n ]);\n totalAmount += Number.parseFloat(amount);\n }\n\n // Generates a random invoice number. You can replace with your own document ID method.\n const invoiceNumber = Math.floor(100000 + Math.random() * 900000);\n\n // Calulates dates.\n const todaysDate = new Date().toDateString();\n const dueDate = new Date(\n Date.now() + 1000 * 60 * 60 * 24 * DUE_DATE_NUM_DAYS,\n ).toDateString();\n\n // Sets values in the template.\n templateSheet.getRange(\"B10\").setValue(customer.customer_name);\n templateSheet.getRange(\"B11\").setValue(customer.address);\n templateSheet.getRange(\"F10\").setValue(invoiceNumber);\n templateSheet.getRange(\"F12\").setValue(todaysDate);\n templateSheet.getRange(\"F14\").setValue(dueDate);\n templateSheet.getRange(18, 2, lineItems.length, 6).setValues(lineItems);\n\n // Cleans up and creates PDF.\n SpreadsheetApp.flush();\n Utilities.sleep(500); // Using to offset any potential latency in creating .pdf\n const pdf = createPDF(\n ssId,\n templateSheet,\n `Invoice#${invoiceNumber}-${customer.customer_name}`,\n );\n return [\n invoiceNumber,\n todaysDate,\n customer.customer_name,\n customer.email,\n \"\",\n totalAmount,\n dueDate,\n pdf.getUrl(),\n \"No\",\n ];\n}\n\n/**\n * Resets the template sheet by clearing out customer data.\n * You use this to prepare for the next iteration or to view blank\n * the template for design.\n *\n * Called by createInvoiceForCustomer() or by the user via custom menu item.\n */\nfunction clearTemplateSheet() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const templateSheet = ss.getSheetByName(INVOICE_TEMPLATE_SHEET_NAME);\n // Clears existing data from the template.\n const rngClear = templateSheet\n .getRangeList([\"B10:B11\", \"F10\", \"F12\", \"F14\"])\n .getRanges();\n for (const cell of rngClear) {\n cell.clearContent();\n }\n // This sample only accounts for six rows of data 'B18:G24'. You can extend or make dynamic as necessary.\n templateSheet.getRange(18, 2, 7, 6).clearContent();\n}\n\n/**\n * Creates a PDF for the customer given sheet.\n * @param {string} ssId - Id of the Google Spreadsheet\n * @param {object} sheet - Sheet to be converted as PDF\n * @param {string} pdfName - File name of the PDF being created\n * @return {file object} PDF file as a blob\n */\nfunction createPDF(ssId, sheet, pdfName) {\n const fr = 0;\n const fc = 0;\n const lc = 9;\n const lr = 27;\n const url = `https://docs.google.com/spreadsheets/d/${ssId}/export?format=pdf&size=7&fzr=true&portrait=true&fitw=true&gridlines=false&printtitle=false&top_margin=0.5&bottom_margin=0.25&left_margin=0.5&right_margin=0.5&sheetnames=false&pagenum=UNDEFINED&attachment=true&gid=${sheet.getSheetId()}&r1=${fr}&c1=${fc}&r2=${lr}&c2=${lc}`;\n\n const params = {\n method: \"GET\",\n headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` },\n };\n const blob = UrlFetchApp.fetch(url, params)\n .getBlob()\n .setName(`${pdfName}.pdf`);\n\n // Gets the folder in Drive where the PDFs are stored.\n const folder = getFolderByName_(OUTPUT_FOLDER_NAME);\n\n const pdfFile = folder.createFile(blob);\n return pdfFile;\n}\n\n/**\n * Sends emails with PDF as an attachment.\n * Checks/Sets 'Email Sent' column to 'Yes' to avoid resending.\n *\n * Called by user via custom menu item.\n */\nfunction sendEmails() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n const invoicesSheet = ss.getSheetByName(INVOICES_SHEET_NAME);\n const invoicesData = invoicesSheet\n .getRange(1, 1, invoicesSheet.getLastRow(), invoicesSheet.getLastColumn())\n .getValues();\n const keysI = invoicesData.splice(0, 1)[0];\n const invoices = getObjects(invoicesData, createObjectKeys(keysI));\n ss.toast(\"Emailing Invoices\", APP_TITLE, 1);\n invoices.forEach((invoice, index) => {\n if (invoice.email_sent !== \"Yes\") {\n ss.toast(`Emailing Invoice for ${invoice.customer}`, APP_TITLE, 1);\n\n const fileId = invoice.invoice_link.match(/[-\\w]{25,}(?!.*[-\\w]{25,})/);\n const attachment = DriveApp.getFileById(fileId);\n\n let recipient = invoice.email;\n if (EMAIL_OVERRIDE) {\n recipient = EMAIL_ADDRESS_OVERRIDE;\n }\n\n GmailApp.sendEmail(recipient, EMAIL_SUBJECT, EMAIL_BODY, {\n attachments: [attachment.getAs(MimeType.PDF)],\n name: APP_TITLE,\n });\n invoicesSheet.getRange(index + 2, 9).setValue(\"Yes\");\n }\n });\n}\n\n/**\n * Helper function that turns sheet data range into an object.\n *\n * @param {SpreadsheetApp.Sheet} sheet - Sheet to process\n * Return {object} of a sheet's datarange as an object\n */\nfunction dataRangeToObject(sheet) {\n const dataRange = sheet\n .getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn())\n .getValues();\n const keys = dataRange.splice(0, 1)[0];\n return getObjects(dataRange, createObjectKeys(keys));\n}\n\n/**\n * Utility function for mapping sheet data to objects.\n */\nfunction getObjects(data, keys) {\n const objects = [];\n for (let i = 0; i < data.length; ++i) {\n const object = {};\n let hasData = false;\n for (let j = 0; j < data[i].length; ++j) {\n const cellData = data[i][j];\n if (isCellEmpty(cellData)) {\n continue;\n }\n object[keys[j]] = cellData;\n hasData = true;\n }\n if (hasData) {\n objects.push(object);\n }\n }\n return objects;\n}\n// Creates object keys for column headers.\nfunction createObjectKeys(keys) {\n return keys.map((key) => key.replace(/\\W+/g, \"_\").toLowerCase());\n}\n// Returns true if the cell where cellData was read from is empty.\nfunction isCellEmpty(cellData) {\n return typeof cellData === \"string\" && cellData === \"\";\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2022 Google LLC\n *\n * 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 * http://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.\n */\n\n/**\n * @OnlyCurrentDoc\n *\n * The above comment specifies that this automation will only\n * attempt to read or modify the spreadsheet this script is bound to.\n * The authorization request message presented to users reflects the\n * limited scope.\n */\n\n/**\n * Creates a custom menu in the Google Sheets UI when the document is opened.\n *\n * @param {object} e The event parameter for a simple onOpen trigger.\n */\nfunction onOpen(e) {\n const menu = SpreadsheetApp.getUi().createMenu(APP_TITLE);\n menu\n .addItem(\"Process invoices\", \"processDocuments\")\n .addItem(\"Send emails\", \"sendEmails\")\n .addSeparator()\n .addItem(\"Reset template\", \"clearTemplateSheet\")\n .addToUi();\n}\n```\n\nExample:\n```text\n/**\n * Copyright 2022 Google LLC\n *\n * 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 * http://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.\n */\n\n/**\n * Returns a Google Drive folder in the same location\n * in Drive where the spreadsheet is located. First, it checks if the folder\n * already exists and returns that folder. If the folder doesn't already\n * exist, the script creates a new one. The folder's name is set by the\n * \"OUTPUT_FOLDER_NAME\" variable from the Code.gs file.\n *\n * @param {string} folderName - Name of the Drive folder.\n * @return {object} Google Drive Folder\n */\nfunction getFolderByName_(folderName) {\n // Gets the Drive Folder of where the current spreadsheet is located.\n const ssId = SpreadsheetApp.getActiveSpreadsheet().getId();\n const parentFolder = DriveApp.getFileById(ssId).getParents().next();\n\n // Iterates the subfolders to check if the PDF folder already exists.\n const subFolders = parentFolder.getFolders();\n while (subFolders.hasNext()) {\n const folder = subFolders.next();\n\n // Returns the existing folder if found.\n if (folder.getName() === folderName) {\n return folder;\n }\n }\n // Creates a new folder if one does not already exist.\n return parentFolder\n .createFolder(folderName)\n .setDescription(\n `Created by ${APP_TITLE} application to store PDF output files`,\n );\n}\n\n/**\n * Test function to run getFolderByName_.\n * @prints a Google Drive FolderId.\n */\nfunction test_getFolderByName() {\n // Gets the PDF folder in Drive.\n const folder = getFolderByName_(OUTPUT_FOLDER_NAME);\n\n console.log(\n `Name: ${folder.getName()}\\rID: ${folder.getId()}\\rDescription: ${folder.getDescription()}`,\n );\n // To automatically delete test folder, uncomment the following code:\n // folder.setTrashed(true);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.391Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":406,"estimatedTokens":3330}}1130{"id":"doc-upload_files_to_google_drive_from_google_forms_a-0ae849e5","source":"documentation","title":"Upload files to Google Drive from Google Forms | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/upload-files","text":"Example:\n```text\n// TODO Before you start using this sample, you must run the setUp()\n// function in the Setup.gs file.\n\n// Application constants\nconst APP_TITLE = \"Upload files to Drive from Forms\";\nconst APP_FOLDER_NAME = \"Upload files to Drive (File responses)\";\n\n// Identifies the subfolder form item\nconst APP_SUBFOLDER_ITEM = \"Subfolder\";\nconst APP_SUBFOLDER_NONE = \"<None>\";\n\n/**\n * Gets the file uploads from a form response and moves files to the corresponding subfolder.\n *\n * @param {object} event - Form submit.\n */\nfunction onFormSubmit(e) {\n try {\n // Gets the application root folder.\n let destFolder = getFolder_(APP_FOLDER_NAME);\n\n // Gets all form responses.\n const itemResponses = e.response.getItemResponses();\n\n // Determines the subfolder to route the file to, if any.\n let subFolderName;\n const dest = itemResponses.filter(\n (itemResponse) =>\n itemResponse.getItem().getTitle().toString() === APP_SUBFOLDER_ITEM,\n );\n\n // Gets the destination subfolder name, but ignores if APP_SUBFOLDER_NONE was selected;\n if (dest.length > 0) {\n if (dest[0].getResponse() !== APP_SUBFOLDER_NONE) {\n subFolderName = dest[0].getResponse();\n }\n }\n // Gets the subfolder or creates it if it doesn't exist.\n if (subFolderName !== undefined) {\n destFolder = getSubFolder_(destFolder, subFolderName);\n }\n console.log(`Destination folder to use:\n Name: ${destFolder.getName()}\n ID: ${destFolder.getId()}\n URL: ${destFolder.getUrl()}`);\n\n // Gets the file upload response as an array to allow for multiple files.\n const fileUploads = itemResponses\n .filter(\n (itemResponse) =>\n itemResponse.getItem().getType().toString() === \"FILE_UPLOAD\",\n )\n .map((itemResponse) => itemResponse.getResponse())\n .reduce((a, b) => a.concat(b), []);\n\n // Moves the files to the destination folder.\n if (fileUploads.length > 0) {\n for (const fileId of fileUploads) {\n DriveApp.getFileById(fileId).moveTo(destFolder);\n console.log(`File Copied: ${fileId}`);\n }\n }\n } catch (err) {\n console.log(err);\n }\n}\n\n/**\n * Returns a Drive folder under the passed in objParentFolder parent\n * folder. Checks if folder of same name exists before creating, returning\n * the existing folder or the newly created one if not found.\n *\n * @param {object} objParentFolder - Drive folder as an object.\n * @param {string} subFolderName - Name of subfolder to create/return.\n * @return {object} Drive folder\n */\nfunction getSubFolder_(objParentFolder, subFolderName) {\n // Iterates subfolders of parent folder to check if folder already exists.\n const subFolders = objParentFolder.getFolders();\n while (subFolders.hasNext()) {\n const folder = subFolders.next();\n\n // Returns the existing folder if found.\n if (folder.getName() === subFolderName) {\n return folder;\n }\n }\n // Creates a new folder if one doesn't already exist.\n return objParentFolder\n .createFolder(subFolderName)\n .setDescription(\n `Created by ${APP_TITLE} application to store uploaded Forms files.`,\n );\n}\n```\n\nExample:\n```text\n// TODO You must run the setUp() function before you start using this sample.\n\n/**\n * The setUp() function performs the following:\n * - Creates a Google Drive folder named by the APP_FOLDER_NAME\n * variable in the Code.gs file.\n * - Creates a trigger to handle onFormSubmit events.\n */\nfunction setUp() {\n // Ensures the root destination folder exists.\n const appFolder = getFolder_(APP_FOLDER_NAME);\n if (appFolder !== null) {\n console.log(`Application folder setup.\n Name: ${appFolder.getName()}\n ID: ${appFolder.getId()}\n URL: ${appFolder.getUrl()}`);\n } else {\n console.log(\"Could not setup application folder.\");\n }\n // Calls the function that creates the Forms onSubmit trigger.\n installTrigger_();\n}\n\n/**\n * Returns a folder to store uploaded files in the same location\n * in Drive where the form is located. First, it checks if the folder\n * already exists, and creates it if it doesn't.\n *\n * @param {string} folderName - Name of the Drive folder.\n * @return {object} Google Drive Folder\n */\nfunction getFolder_(folderName) {\n // Gets the Drive folder where the form is located.\n const ssId = FormApp.getActiveForm().getId();\n const parentFolder = DriveApp.getFileById(ssId).getParents().next();\n\n // Iterates through the subfolders to check if folder already exists.\n // The script checks for the folder name specified in the APP_FOLDER_NAME variable.\n const subFolders = parentFolder.getFolders();\n while (subFolders.hasNext()) {\n const folder = subFolders.next();\n\n // Returns the existing folder if found.\n if (folder.getName() === folderName) {\n return folder;\n }\n }\n // Creates a new folder if one doesn't already exist.\n return parentFolder\n .createFolder(folderName)\n .setDescription(\n `Created by ${APP_TITLE} application to store uploaded files.`,\n );\n}\n\n/**\n * Installs trigger to capture onFormSubmit event when a form is submitted.\n * Ensures that the trigger is only installed once.\n * Called by setup().\n */\nfunction installTrigger_() {\n // Ensures existing trigger doesn't already exist.\n const propTriggerId =\n PropertiesService.getScriptProperties().getProperty(\"triggerUniqueId\");\n if (propTriggerId !== null) {\n const triggers = ScriptApp.getProjectTriggers();\n for (const t in triggers) {\n if (triggers[t].getUniqueId() === propTriggerId) {\n console.log(\n `Trigger with the following unique ID already exists: ${propTriggerId}`,\n );\n return;\n }\n }\n }\n // Creates the trigger if one doesn't exist.\n const triggerUniqueId = ScriptApp.newTrigger(\"onFormSubmit\")\n .forForm(FormApp.getActiveForm())\n .onFormSubmit()\n .create()\n .getUniqueId();\n PropertiesService.getScriptProperties().setProperty(\n \"triggerUniqueId\",\n triggerUniqueId,\n );\n console.log(\n `Trigger with the following unique ID was created: ${triggerUniqueId}`,\n );\n}\n\n/**\n * Removes all script properties and triggers for the project.\n * Use primarily to test setup routines.\n */\nfunction removeTriggersAndScriptProperties() {\n PropertiesService.getScriptProperties().deleteAllProperties();\n // Removes all triggers associated with project.\n const triggers = ScriptApp.getProjectTriggers();\n for (const t in triggers) {\n ScriptApp.deleteTrigger(triggers[t]);\n }\n}\n\n/**\n * Removes all form responses to reset the form.\n */\nfunction deleteAllResponses() {\n FormApp.getActiveForm().deleteAllResponses();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.392Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":212,"estimatedTokens":1659}}1131{"id":"doc-class_datavalidation_apps_script_google_for_deve-5acc7706","source":"documentation","title":"Class DataValidation | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/forms/data-validation","text":"Example:\n```text\n// Add a text item to a form and require it to be a number within a range.\n var textItem = form.addTextItem().setTitle('Pick a number between 1 and 100?');\n var textValidation = FormApp.createTextValidation()\n .setHelpText(“Input was not a number between 1 and 100.”)\n .requireNumberBetween(1, 100);\n textItem.setValidation(textValidation);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.393Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":11,"estimatedTokens":95}}1132{"id":"doc-create_events_google_calendar_google_for_develop-968b2175","source":"documentation","title":"Create events | Google Calendar | Google for Developers","url":"https://developers.google.com/calendar/create-events","text":"Example:\n```text\n// Refer to the Go quickstart on how to setup the environment:\n// https://developers.google.com/workspace/calendar/quickstart/go\n// Change the scope to calendar.CalendarScope and delete any stored credentials.\n\nevent := &calendar.Event{\n Summary: \"Google I/O 2015\",\n Location: \"800 Howard St., San Francisco, CA 94103\",\n Description: \"A chance to hear more about Google's developer products.\",\n Start: &calendar.EventDateTime{\n DateTime: \"2015-05-28T09:00:00-07:00\",\n TimeZone: \"America/Los_Angeles\",\n },\n End: &calendar.EventDateTime{\n DateTime: \"2015-05-28T17:00:00-07:00\",\n TimeZone: \"America/Los_Angeles\",\n },\n Recurrence: []string{\"RRULE:FREQ=DAILY;COUNT=2\"},\n Attendees: []*calendar.EventAttendee{\n &calendar.EventAttendee{Email:\"lpage@example.com\"},\n &calendar.EventAttendee{Email:\"sbrin@example.com\"},\n },\n}\n\ncalendarId := \"primary\"\nevent, err = srv.Events.Insert(calendarId, event).Do()\nif err != nil {\n log.Fatalf(\"Unable to create event. %v\\n\", err)\n}\nfmt.Printf(\"Event created: %s\\n\", event.HtmlLink)\n```\n\nExample:\n```text\n// Refer to the Java quickstart on how to setup the environment:\n// https://developers.google.com/workspace/calendar/quickstart/java\n// Change the scope to CalendarScopes.CALENDAR and delete any stored\n// credentials.\n\nEvent event = new Event()\n .setSummary(\"Google I/O 2015\")\n .setLocation(\"800 Howard St., San Francisco, CA 94103\")\n .setDescription(\"A chance to hear more about Google's developer products.\");\n\nDateTime startDateTime = new DateTime(\"2015-05-28T09:00:00-07:00\");\nEventDateTime start = new EventDateTime()\n .setDateTime(startDateTime)\n .setTimeZone(\"America/Los_Angeles\");\nevent.setStart(start);\n\nDateTime endDateTime = new DateTime(\"2015-05-28T17:00:00-07:00\");\nEventDateTime end = new EventDateTime()\n .setDateTime(endDateTime)\n .setTimeZone(\"America/Los_Angeles\");\nevent.setEnd(end);\n\nString[] recurrence = new String[] {\"RRULE:FREQ=DAILY;COUNT=2\"};\nevent.setRecurrence(Arrays.asList(recurrence));\n\nEventAttendee[] attendees = new EventAttendee[] {\n new EventAttendee().setEmail(\"lpage@example.com\"),\n new EventAttendee().setEmail(\"sbrin@example.com\"),\n};\nevent.setAttendees(Arrays.asList(attendees));\n\nEventReminder[] reminderOverrides = new EventReminder[] {\n new EventReminder().setMethod(\"email\").setMinutes(24 * 60),\n new EventReminder().setMethod(\"popup\").setMinutes(10),\n};\nEvent.Reminders reminders = new Event.Reminders()\n .setUseDefault(false)\n .setOverrides(Arrays.asList(reminderOverrides));\nevent.setReminders(reminders);\n\nString calendarId = \"primary\";\nevent = service.events().insert(calendarId, event).execute();\nSystem.out.printf(\"Event created: %s\\n\", event.getHtmlLink());\n```\n\nExample:\n```text\n// Refer to the JavaScript quickstart on how to setup the environment:\n// https://developers.google.com/workspace/calendar/quickstart/js\n// Change the scope to 'https://www.googleapis.com/auth/calendar' and delete any\n// stored credentials.\n\nconst event = {\n 'summary': 'Google I/O 2015',\n 'location': '800 Howard St., San Francisco, CA 94103',\n 'description': 'A chance to hear more about Google\\'s developer products.',\n 'start': {\n 'dateTime': '2015-05-28T09:00:00-07:00',\n 'timeZone': 'America/Los_Angeles'\n },\n 'end': {\n 'dateTime': '2015-05-28T17:00:00-07:00',\n 'timeZone': 'America/Los_Angeles'\n },\n 'recurrence': [\n 'RRULE:FREQ=DAILY;COUNT=2'\n ],\n 'attendees': [\n {'email': 'lpage@example.com'},\n {'email': 'sbrin@example.com'}\n ],\n 'reminders': {\n 'useDefault': false,\n 'overrides': [\n {'method': 'email', 'minutes': 24 * 60},\n {'method': 'popup', 'minutes': 10}\n ]\n }\n};\n\nconst request = gapi.client.calendar.events.insert({\n 'calendarId': 'primary',\n 'resource': event\n});\n\nrequest.execute(function(event) {\n appendPre('Event created: ' + event.htmlLink);\n});\n```\n\nExample:\n```text\n// Refer to the Node.js quickstart on how to setup the environment:\n// https://developers.google.com/workspace/calendar/quickstart/node\n// Change the scope to 'https://www.googleapis.com/auth/calendar' and delete any\n// stored credentials.\n\nconst event = {\n 'summary': 'Google I/O 2015',\n 'location': '800 Howard St., San Francisco, CA 94103',\n 'description': 'A chance to hear more about Google\\'s developer products.',\n 'start': {\n 'dateTime': '2015-05-28T09:00:00-07:00',\n 'timeZone': 'America/Los_Angeles',\n },\n 'end': {\n 'dateTime': '2015-05-28T17:00:00-07:00',\n 'timeZone': 'America/Los_Angeles',\n },\n 'recurrence': [\n 'RRULE:FREQ=DAILY;COUNT=2'\n ],\n 'attendees': [\n {'email': 'lpage@example.com'},\n {'email': 'sbrin@example.com'},\n ],\n 'reminders': {\n 'useDefault': false,\n 'overrides': [\n {'method': 'email', 'minutes': 24 * 60},\n {'method': 'popup', 'minutes': 10},\n ],\n },\n};\n\ncalendar.events.insert({\n auth: auth,\n calendarId: 'primary',\n resource: event,\n}, function(err, event) {\n if (err) {\n console.log('There was an error contacting the Calendar service: ' + err);\n return;\n }\n console.log('Event created: %s', event.htmlLink);\n});\n```\n\nExample:\n```text\n$event = new Google_Service_Calendar_Event(array(\n 'summary' => 'Google I/O 2015',\n 'location' => '800 Howard St., San Francisco, CA 94103',\n 'description' => 'A chance to hear more about Google\\'s developer products.',\n 'start' => array(\n 'dateTime' => '2015-05-28T09:00:00-07:00',\n 'timeZone' => 'America/Los_Angeles',\n ),\n 'end' => array(\n 'dateTime' => '2015-05-28T17:00:00-07:00',\n 'timeZone' => 'America/Los_Angeles',\n ),\n 'recurrence' => array(\n 'RRULE:FREQ=DAILY;COUNT=2'\n ),\n 'attendees' => array(\n array('email' => 'lpage@example.com'),\n array('email' => 'sbrin@example.com'),\n ),\n 'reminders' => array(\n 'useDefault' => FALSE,\n 'overrides' => array(\n array('method' => 'email', 'minutes' => 24 * 60),\n array('method' => 'popup', 'minutes' => 10),\n ),\n ),\n));\n\n$calendarId = 'primary';\n$event = $service->events->insert($calendarId, $event);\nprintf('Event created: %s\\n', $event->htmlLink);\n```\n\nExample:\n```text\n# Refer to the Python quickstart on how to setup the environment:\n# https://developers.google.com/workspace/calendar/quickstart/python\n# Change the scope to 'https://www.googleapis.com/auth/calendar' and delete any\n# stored credentials.\n\nevent = {\n 'summary': 'Google I/O 2015',\n 'location': '800 Howard St., San Francisco, CA 94103',\n 'description': 'A chance to hear more about Google\\'s developer products.',\n 'start': {\n 'dateTime': '2015-05-28T09:00:00-07:00',\n 'timeZone': 'America/Los_Angeles',\n },\n 'end': {\n 'dateTime': '2015-05-28T17:00:00-07:00',\n 'timeZone': 'America/Los_Angeles',\n },\n 'recurrence': [\n 'RRULE:FREQ=DAILY;COUNT=2'\n ],\n 'attendees': [\n {'email': 'lpage@example.com'},\n {'email': 'sbrin@example.com'},\n ],\n 'reminders': {\n 'useDefault': False,\n 'overrides': [\n {'method': 'email', 'minutes': 24 * 60},\n {'method': 'popup', 'minutes': 10},\n ],\n },\n}\n\nevent = service.events().insert(calendarId='primary', body=event).execute()\nprint 'Event created: %s' % (event.get('htmlLink'))\n```\n\nExample:\n```text\nevent = Google::Apis::CalendarV3::Event.new(\n summary: 'Google I/O 2015',\n location: '800 Howard St., San Francisco, CA 94103',\n description: 'A chance to hear more about Google\\'s developer products.',\n start: Google::Apis::CalendarV3::EventDateTime.new(\n date_time: '2015-05-28T09:00:00-07:00',\n time_zone: 'America/Los_Angeles'\n ),\n end: Google::Apis::CalendarV3::EventDateTime.new(\n date_time: '2015-05-28T17:00:00-07:00',\n time_zone: 'America/Los_Angeles'\n ),\n recurrence: [\n 'RRULE:FREQ=DAILY;COUNT=2'\n ],\n attendees: [\n Google::Apis::CalendarV3::EventAttendee.new(\n email: 'lpage@example.com'\n ),\n Google::Apis::CalendarV3::EventAttendee.new(\n email: 'sbrin@example.com'\n )\n ],\n reminders: Google::Apis::CalendarV3::Event::Reminders.new(\n use_default: false,\n overrides: [\n Google::Apis::CalendarV3::EventReminder.new(\n reminder_method: 'email',\n minutes: 24 * 60\n ),\n Google::Apis::CalendarV3::EventReminder.new(\n reminder_method: 'popup',\n minutes: 10\n )\n ]\n )\n)\n\nresult = client.insert_event('primary', event)\nputs \"Event created: #{result.html_link}\"\n```\n\nExample:\n```text\npublic static void addAttachment(Calendar calendarService, Drive driveService, String calendarId,\n String eventId, String fileId) throws IOException {\n File file = driveService.files().get(fileId).execute();\n Event event = calendarService.events().get(calendarId, eventId).execute();\n\n List<EventAttachment> attachments = event.getAttachments();\n if (attachments == null) {\n attachments = new ArrayList<EventAttachment>();\n }\n attachments.add(new EventAttachment()\n .setFileUrl(file.getAlternateLink())\n .setMimeType(file.getMimeType())\n .setTitle(file.getTitle()));\n\n Event changes = new Event()\n .setAttachments(attachments);\n calendarService.events().patch(calendarId, eventId, changes)\n .setSupportsAttachments(true)\n .execute();\n}\n```\n\nExample:\n```text\nfunction addAttachment($calendarService, $driveService, $calendarId, $eventId, $fileId) {\n $file = $driveService->files->get($fileId);\n $event = $calendarService->events->get($calendarId, $eventId);\n $attachments = $event->attachments;\n\n $attachments[] = array(\n 'fileUrl' => $file->alternateLink,\n 'mimeType' => $file->mimeType,\n 'title' => $file->title\n );\n $changes = new Google_Service_Calendar_Event(array(\n 'attachments' => $attachments\n ));\n\n $calendarService->events->patch($calendarId, $eventId, $changes, array(\n 'supportsAttachments' => TRUE\n ));\n}\n```\n\nExample:\n```text\ndef add_attachment(calendarService, driveService, calendarId, eventId, fileId):\n file = driveService.files().get(fileId=fileId).execute()\n event = calendarService.events().get(calendarId=calendarId,\n eventId=eventId).execute()\n\n attachments = event.get('attachments', [])\n attachments.append({\n 'fileUrl': file['alternateLink'],\n 'mimeType': file['mimeType'],\n 'title': file['title']\n })\n\n changes = {\n 'attachments': attachments\n }\n calendarService.events().patch(calendarId=calendarId, eventId=eventId,\n body=changes,\n supportsAttachments=True).execute()\n```\n\nExample:\n```text\nconst solution = event.conferenceData.conferenceSolution;\n\nconst content = document.getElementById(\"content\");\nconst text = document.createTextNode(\"Join \" + solution.name);\nconst icon = document.createElement(\"img\");\nicon.src = solution.iconUri;\n\ncontent.appendChild(icon);\ncontent.appendChild(text);\n```\n\nExample:\n```text\nconst eventPatch = {\n conferenceData: {\n createRequest: {requestId: \"7qxalsvy0e\"}\n }\n};\n\ngapi.client.calendar.events.patch({\n calendarId: \"primary\",\n eventId: \"7cbh8rpc10lrc0ckih9tafss99\",\n resource: eventPatch,\n sendUpdates: \"all\",\n conferenceDataVersion: 1\n}).execute(function(event) {\n console.log(\"Conference created for event: %s\", event.htmlLink);\n});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.394Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":392,"estimatedTokens":2811}}1133{"id":"doc-record_time_activities_in_google_calendar_google-1ecf2d98","source":"documentation","title":"Record time & activities in Google Calendar & Google Sheets | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/calendar-timesheet","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/calendar-timesheet\n\n/*\nCopyright 2022 Jasper Duizendstra\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n/**\n * Runs when the spreadsheet is opened and adds the menu options\n * to the spreadsheet menu\n */\nconst onOpen = () => {\n SpreadsheetApp.getUi()\n .createMenu(\"myTime\")\n .addItem(\"Sync calendar events\", \"run\")\n .addItem(\"Settings\", \"settings\")\n .addToUi();\n};\n\n/**\n * Opens the sidebar\n */\nconst settings = () => {\n const html =\n HtmlService.createHtmlOutputFromFile(\"Page\").setTitle(\"Settings\");\n\n SpreadsheetApp.getUi().showSidebar(html);\n};\n\n/**\n * returns the settings from the script properties\n */\nconst getSettings = () => {\n const settings = {};\n\n // get the current settings\n const savedCalendarSettings = JSON.parse(\n PropertiesService.getScriptProperties().getProperty(\"calendar\") || \"[]\",\n );\n\n // get the primary calendar\n const primaryCalendar = CalendarApp.getAllCalendars()\n .filter((cal) => cal.isMyPrimaryCalendar())\n .map((cal) => ({\n name: \"Primary calendar\",\n id: cal.getId(),\n }));\n\n // get the secondary calendars\n const secundaryCalendars = CalendarApp.getAllCalendars()\n .filter((cal) => cal.isOwnedByMe() && !cal.isMyPrimaryCalendar())\n .map((cal) => ({\n name: cal.getName(),\n id: cal.getId(),\n }));\n\n // the current available calendars\n const availableCalendars = primaryCalendar.concat(secundaryCalendars);\n\n // find any calendars that were removed\n const unavailebleCalendars = [];\n for (const savedCalendarSetting of savedCalendarSettings) {\n if (\n !availableCalendars.find(\n (availableCalendar) => availableCalendar.id === savedCalendarSetting.id,\n )\n ) {\n unavailebleCalendars.push(savedCalendarSetting);\n }\n }\n\n // map the current settings to the available calendars\n const calendarSettings = availableCalendars.map((availableCalendar) => {\n if (\n savedCalendarSettings.find(\n (savedCalendar) => savedCalendar.id === availableCalendar.id,\n )\n ) {\n availableCalendar.sync = true;\n }\n return availableCalendar;\n });\n\n // add the calendar settings to the settings\n settings.calendarSettings = calendarSettings;\n\n const savedFrom =\n PropertiesService.getScriptProperties().getProperty(\"syncFrom\");\n settings.syncFrom = savedFrom;\n\n const savedTo = PropertiesService.getScriptProperties().getProperty(\"syncTo\");\n settings.syncTo = savedTo;\n\n const savedIsUpdateTitle =\n PropertiesService.getScriptProperties().getProperty(\"isUpdateTitle\") ===\n \"true\";\n settings.isUpdateCalendarItemTitle = savedIsUpdateTitle;\n\n const savedIsUseCategoriesAsCalendarItemTitle =\n PropertiesService.getScriptProperties().getProperty(\n \"isUseCategoriesAsCalendarItemTitle\",\n ) === \"true\";\n settings.isUseCategoriesAsCalendarItemTitle =\n savedIsUseCategoriesAsCalendarItemTitle;\n\n const savedIsUpdateDescription =\n PropertiesService.getScriptProperties().getProperty(\n \"isUpdateDescription\",\n ) === \"true\";\n settings.isUpdateCalendarItemDescription = savedIsUpdateDescription;\n\n return settings;\n};\n\n/**\n * Saves the settings from the sidebar\n */\nconst saveSettings = (settings) => {\n PropertiesService.getScriptProperties().setProperty(\n \"calendar\",\n JSON.stringify(settings.calendarSettings),\n );\n PropertiesService.getScriptProperties().setProperty(\n \"syncFrom\",\n settings.syncFrom,\n );\n PropertiesService.getScriptProperties().setProperty(\n \"syncTo\",\n settings.syncTo,\n );\n PropertiesService.getScriptProperties().setProperty(\n \"isUpdateTitle\",\n settings.isUpdateCalendarItemTitle,\n );\n PropertiesService.getScriptProperties().setProperty(\n \"isUseCategoriesAsCalendarItemTitle\",\n settings.isUseCategoriesAsCalendarItemTitle,\n );\n PropertiesService.getScriptProperties().setProperty(\n \"isUpdateDescription\",\n settings.isUpdateCalendarItemDescription,\n );\n return \"Settings saved\";\n};\n\n/**\n * Builds the myTime object and runs the synchronisation\n */\nconst run = () => {\n myTime({\n mainSpreadsheetId: SpreadsheetApp.getActiveSpreadsheet().getId(),\n }).run();\n};\n\n/**\n * The main function used for the synchronisation\n * @param {Object} par The main parameter object.\n * @return {Object} The myTime Object.\n */\nconst myTime = (par) => {\n /**\n * Format the sheet\n */\n const formatSheet = () => {\n // sort decending on start date\n hourSheet.sort(3, false);\n\n // hide the technical columns\n hourSheet.hideColumns(1, 2);\n\n // remove any extra rows\n if (\n hourSheet.getLastRow() > 1 &&\n hourSheet.getLastRow() < hourSheet.getMaxRows()\n ) {\n hourSheet.deleteRows(\n hourSheet.getLastRow() + 1,\n hourSheet.getMaxRows() - hourSheet.getLastRow(),\n );\n }\n\n // set the validation for the customers\n let rule = SpreadsheetApp.newDataValidation()\n .requireValueInRange(categoriesSheet.getRange(\"A2:A\"), true)\n .setAllowInvalid(true)\n .build();\n hourSheet.getRange(\"I2:I\").setDataValidation(rule);\n\n // set the validation for the projects\n rule = SpreadsheetApp.newDataValidation()\n .requireValueInRange(categoriesSheet.getRange(\"B2:B\"), true)\n .setAllowInvalid(true)\n .build();\n hourSheet.getRange(\"J2:J\").setDataValidation(rule);\n\n // set the validation for the tsaks\n rule = SpreadsheetApp.newDataValidation()\n .requireValueInRange(categoriesSheet.getRange(\"C2:C\"), true)\n .setAllowInvalid(true)\n .build();\n hourSheet.getRange(\"K2:K\").setDataValidation(rule);\n\n if (isUseCategoriesAsCalendarItemTitle) {\n hourSheet\n .getRange(\"L2:L\")\n .setFormulaR1C1(\n 'IF(OR(R[0]C[-3]=\"tbd\";R[0]C[-2]=\"tbd\";R[0]C[-1]=\"tbd\");\"\"; CONCATENATE(R[0]C[-3];\"|\";R[0]C[-2];\"|\";R[0]C[-1];\"|\"))',\n );\n }\n // set the hours, month, week and number collumns\n hourSheet\n .getRange(\"P2:P\")\n .setFormulaR1C1('=IF(R[0]C[-12]=\"\";\"\";R[0]C[-12]-R[0]C[-13])');\n hourSheet\n .getRange(\"Q2:Q\")\n .setFormulaR1C1('=IF(R[0]C[-13]=\"\";\"\";month(R[0]C[-13]))');\n hourSheet\n .getRange(\"R2:R\")\n .setFormulaR1C1('=IF(R[0]C[-14]=\"\";\"\";WEEKNUM(R[0]C[-14];2))');\n hourSheet.getRange(\"S2:S\").setFormulaR1C1(\"=R[0]C[-3]\");\n };\n\n /**\n * Activate the synchronisation\n */\n function run() {\n console.log(\"Started processing hours.\");\n\n const processCalendar = (setting) => {\n SpreadsheetApp.flush();\n\n // current calendar info\n const calendarName = setting.name;\n const calendarId = setting.id;\n\n console.log(\n `processing ${calendarName} with the id ${calendarId} from ${syncStartDate} to ${syncEndDate}`,\n );\n\n // get the calendar\n const calendar = CalendarApp.getCalendarById(calendarId);\n\n // get the calendar events and create lookups\n const events = calendar.getEvents(syncStartDate, syncEndDate);\n const eventsLookup = events.reduce((jsn, event) => {\n jsn[event.getId()] = event;\n return jsn;\n }, {});\n\n // get the sheet events and create lookups\n const existingEvents = hourSheet.getDataRange().getValues().slice(1);\n const existingEventsLookUp = existingEvents.reduce((jsn, row, index) => {\n if (row[0] !== calendarId) {\n return jsn;\n }\n jsn[row[1]] = {\n event: row,\n row: index + 2,\n };\n return jsn;\n }, {});\n\n // handle a calendar event\n const handleEvent = (event) => {\n const eventId = event.getId();\n\n // new event\n if (!existingEventsLookUp[eventId]) {\n hourSheet.appendRow([\n calendarId,\n eventId,\n event.getStartTime(),\n event.getEndTime(),\n calendarName,\n event.getCreators().join(\",\"),\n event.getTitle(),\n event.getDescription(),\n event.getTag(\"Client\") || \"tbd\",\n event.getTag(\"Project\") || \"tbd\",\n event.getTag(\"Task\") || \"tbd\",\n isUpdateCalendarItemTitle ? \"\" : event.getTitle(),\n isUpdateCalendarItemDescription ? \"\" : event.getDescription(),\n event\n .getGuestList()\n .map((guest) => guest.getEmail())\n .join(\",\"),\n event.getLocation(),\n undefined,\n undefined,\n undefined,\n undefined,\n ]);\n return true;\n }\n\n // existing event\n const exisitingEvent = existingEventsLookUp[eventId].event;\n const exisitingEventRow = existingEventsLookUp[eventId].row;\n\n if (event.getStartTime() - exisitingEvent[startTimeColumn - 1] !== 0) {\n hourSheet\n .getRange(exisitingEventRow, startTimeColumn)\n .setValue(event.getStartTime());\n }\n\n if (event.getEndTime() - exisitingEvent[endTimeColumn - 1] !== 0) {\n hourSheet\n .getRange(exisitingEventRow, endTimeColumn)\n .setValue(event.getEndTime());\n }\n\n if (\n event.getCreators().join(\",\") !== exisitingEvent[creatorsColumn - 1]\n ) {\n hourSheet\n .getRange(exisitingEventRow, creatorsColumn)\n .setValue(event.getCreators()[0]);\n }\n\n if (\n event\n .getGuestList()\n .map((guest) => guest.getEmail())\n .join(\",\") !== exisitingEvent[guestListColumn - 1]\n ) {\n hourSheet.getRange(exisitingEventRow, guestListColumn).setValue(\n event\n .getGuestList()\n .map((guest) => guest.getEmail())\n .join(\",\"),\n );\n }\n\n if (event.getLocation() !== exisitingEvent[locationColumn - 1]) {\n hourSheet\n .getRange(exisitingEventRow, locationColumn)\n .setValue(event.getLocation());\n }\n\n if (event.getTitle() !== exisitingEvent[titleColumn - 1]) {\n if (!isUpdateCalendarItemTitle) {\n hourSheet\n .getRange(exisitingEventRow, titleColumn)\n .setValue(event.getTitle());\n }\n if (isUpdateCalendarItemTitle) {\n event.setTitle(exisitingEvent[titleColumn - 1]);\n }\n }\n\n if (event.getDescription() !== exisitingEvent[descriptionColumn - 1]) {\n if (!isUpdateCalendarItemDescription) {\n hourSheet\n .getRange(exisitingEventRow, descriptionColumn)\n .setValue(event.getDescription());\n }\n if (isUpdateCalendarItemDescription) {\n event.setDescription(exisitingEvent[descriptionColumn - 1]);\n }\n }\n\n return true;\n };\n\n // process each event for the calendar\n events.every(handleEvent);\n\n // remove any events in the sheet that are not in de calendar\n existingEvents.every((event, index) => {\n if (event[0] !== calendarId) {\n return true;\n }\n\n if (eventsLookup[event[1]]) {\n return true;\n }\n\n if (event[3] < syncStartDate) {\n return true;\n }\n\n hourSheet.getRange(index + 2, 1, 1, 20).clear();\n return true;\n });\n\n return true;\n };\n\n // process the calendars\n settings.calendarSettings\n .filter((calenderSetting) => calenderSetting.sync === true)\n .every(processCalendar);\n\n formatSheet();\n SpreadsheetApp.setActiveSheet(hourSheet);\n\n console.log(\"Finished processing hours.\");\n }\n\n const mainSpreadSheetId = par.mainSpreadsheetId;\n const mainSpreadsheet = SpreadsheetApp.openById(mainSpreadSheetId);\n const hourSheet = mainSpreadsheet.getSheetByName(\"Hours\");\n const categoriesSheet = mainSpreadsheet.getSheetByName(\"Categories\");\n const settings = getSettings();\n\n const syncStartDate = new Date();\n syncStartDate.setDate(syncStartDate.getDate() - Number(settings.syncFrom));\n\n const syncEndDate = new Date();\n syncEndDate.setDate(syncEndDate.getDate() + Number(settings.syncTo));\n\n const isUpdateCalendarItemTitle = settings.isUpdateCalendarItemTitle;\n const isUseCategoriesAsCalendarItemTitle =\n settings.isUseCategoriesAsCalendarItemTitle;\n const isUpdateCalendarItemDescription =\n settings.isUpdateCalendarItemDescription;\n\n const startTimeColumn = 3;\n const endTimeColumn = 4;\n const creatorsColumn = 6;\n const originalTitleColumn = 7;\n const originalDescriptionColumn = 8;\n const clientColumn = 9;\n const projectColumn = 10;\n const taskColumn = 11;\n const titleColumn = 12;\n const descriptionColumn = 13;\n const guestListColumn = 14;\n const locationColumn = 15;\n\n return Object.freeze({\n run: run,\n });\n};\n```\n\nExample:\n```text\n</section>\n<section>\n <h3>Page.html</h3>\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<!--\n Copyright 2022 Google LLC\n\n 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 http://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.\n-->\n\n<html>\n\n<head>\n <link rel=\"stylesheet\" href=\"https://ssl.gstatic.com/docs/script/css/add-ons1.css\">\n <style>\n #main {\n display: none\n }\n\n #categories-as-item-title {\n display: none\n }\n\n #show_title_warning {\n display: none\n }\n\n #show_description_warning {\n display: none\n }\n\n .red {\n color: red;\n }\n\n .branding-below {\n bottom: 56px;\n top: 0;\n }\n\n input[type=number] {\n width: 50px;\n height: 15px;\n }\n </style>\n</head>\n\n<body>\n <div class=\"sidebar branding-below\" id=\"wait\">\n Please wait...\n </div>\n <div class=\"sidebar branding-below\" id=\"main\">\n <div class=\"block\" id=\"checks\">\n <b>Synchronise calendars</b>\n <div>\n <span class=\"error\" id=\"calendar-message\"></span>\n </div>\n </div>\n\n <div class=\"block\">\n <b>Synchronisation period</b>\n <br>Synchronise from the last <input type=\"number\" name=\"sync-from\" id=\"sync-from\"> days\n <br>Synchronise up to the coming <input type=\"number\" name=\"sync-to\" id=\"sync-to\"> days\n </div>\n\n <div class=\"block\">\n <b>Update the calendar items</b><br>\n <input type=\"checkbox\" id=\"is-update-calendar-item-title\">\n <label for=\"is-update-calendar-item-title\">Overwrite the calendar item title</label>\n <span class=\"secondary\" id=\"show_title_warning\">The calendar title will be overwritten with the values in\n title\n column of the sheet</span>\n </div>\n <div id=\"categories-as-item-title\">\n <input type=\"checkbox\" id=\"is-use-categories-as-item-title\">\n <label for=\"is-use-categories-as-item-title\">Use categories as the calendar item title</label>\n </div>\n <div class=\"block\">\n <input type=\"checkbox\" id=\"is-update-calendar-item-description\">\n <label for=\"is-update-calendar-item-description\">Overwrite the calendar item description</label>\n <span class=\"secondary\" id=\"show_description_warning\">The calendar description will be overwritten with the\n values in description column of the sheet</span>\n </div>\n <div class=\"block\">\n <button class=\"blue\" onClick=\"saveSettings()\">Save</button>\n </div>\n <div class=\"block\">\n <span class=\"error\" id=\"generic-error\"></span>\n <span class=\"gray\" id=\"generic-message\"></span>\n </div>\n\n </div>\n <div class=\"sidebar bottom\">\n <span class=\"gray\">\n myTime v1.2.0</span>\n </div>\n</body>\n<script>\n // event handler for categrories\n document.getElementById('is-update-calendar-item-title').addEventListener('change', (event) => {\n if (event.target.checked) {\n document.getElementById('categories-as-item-title').style.display = \"block\";\n document.getElementById('show_title_warning').style.display = \"block\";\n } else {\n document.getElementById('categories-as-item-title').style.display = \"none\";\n document.getElementById('is-use-categories-as-item-title').checked = false;\n document.getElementById('show_title_warning').style.display = \"none\";\n }\n })\n\n document.getElementById('is-update-calendar-item-description').addEventListener('change', (event) => {\n if (event.target.checked) {\n document.getElementById('show_description_warning').style.display = \"block\";\n } else {\n document.getElementById('show_description_warning').style.display = \"none\";\n }\n })\n\n // generic error handler\n const onFailure = (error) => {\n console.debug(error);\n document.getElementById('generic-error').innerHTML = error.message;\n }\n\n // receiving the settings\n const onSuccessGetSettings = (settings) => {\n console.debug(settings);\n\n settings.calendarSettings.forEach((calendar, index) => {\n const div = document.createElement('div');\n\n const check = document.createElement('input');\n check.className = 'calendar-check';\n check.className = 'calendar-check red';\n check.type = 'checkbox';\n check.id = 'calendar' + index;\n check.value = (calendar.id);\n check.name = (calendar.name);\n check.checked = (calendar.sync);\n\n const label = document.createElement('label')\n label.htmlFor = \"calendar\" + index;\n label.appendChild(document.createTextNode(calendar.name));\n if (index == 0) {\n label.className = 'red';\n }\n\n div.appendChild(check);\n div.appendChild(label);\n\n document.getElementById('checks').appendChild(div);\n });\n\n document.getElementById('sync-from').value = settings.syncFrom || 31;\n document.getElementById('sync-to').value = settings.syncTo || 31;\n document.getElementById('is-update-calendar-item-title').checked = settings.isUpdateCalendarItemTitle;\n\n if (settings.isUpdateCalendarItemTitle) {\n document.getElementById('categories-as-item-title').style.display = \"block\";\n document.getElementById('is-use-categories-as-item-title').checked = settings.isUseCategoriesAsCalendarItemTitle;\n document.getElementById('show_title_warning').style.display = \"block\";\n }\n\n if (settings.isUpdateCalendarItemDescription) {\n document.getElementById('is-update-calendar-item-description').checked = settings.isUpdateCalendarItemDescription;\n document.getElementById('show_description_warning').style.display = \"block\";\n }\n document.getElementById('wait').style.display = \"none\";\n document.getElementById('main').style.display = \"block\";\n\n\n }\n\n // receiving the settings saved confirmation\n const onSuccessSaveSettings = (msg) => {\n console.debug(msg);\n document.getElementById('generic-message').innerHTML = msg;\n }\n\n // save the settings\n const saveSettings = () => {\n document.getElementById('generic-message').innerHTML = '';\n const checks = document.getElementsByClassName('calendar-check');\n const calendarSettings = [];\n for (let check of checks) {\n if (!check.checked) {\n continue;\n }\n calendarSettings.push({\n name: check.name,\n id: check.value,\n sync: check.checked\n });\n }\n\n const settings = {};\n settings.calendarSettings = calendarSettings;\n settings.syncFrom = document.getElementById('sync-from').value;\n settings.syncTo = document.getElementById('sync-to').value;\n settings.isUpdateCalendarItemTitle = document.getElementById('is-update-calendar-item-title').checked;\n if (settings.isUpdateCalendarItemTitle) {\n settings.isUseCategoriesAsCalendarItemTitle = document.getElementById('is-use-categories-as-item-title').checked;\n }\n if (!settings.isUpdateCalendarItemTitle) {\n settings.isUseCategoriesAsCalendarItemTitle = false;\n }\n\n settings.isUpdateCalendarItemDescription = document.getElementById('is-update-calendar-item-description').checked;\n console.debug(settings)\n\n google.script.run\n .withFailureHandler(onFailure)\n .withSuccessHandler(onSuccessSaveSettings)\n .saveSettings(settings);\n }\n\n // get the initial settings\n google.script.run\n .withFailureHandler(onFailure)\n .withSuccessHandler(onSuccessGetSettings)\n .getSettings();\n</script>\n\n</html>\n```\n\nExample:\n```text\n</section>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.396Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":698,"estimatedTokens":5465}}1134{"id":"doc-class_gridvalidation_apps_script_google_for_deve-0d2aae70","source":"documentation","title":"Class GridValidation | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/forms/grid-validation","text":"Example:\n```text\n// Add a grid item to a form and require one response per column.\nconst form = FormApp.create('My Form');\nconst gridItem = form.addGridItem();\ngridItem.setTitle('Rate your interests')\n .setRows(['Cars', 'Computers', 'Celebrities'])\n .setColumns(['Boring', 'So-so', 'Interesting']);\nconst gridValidation = FormApp.createGridValidation()\n .setHelpText('Select one item per column.')\n .requireLimitOneResponsePerColumn()\n .build();\ngridItem.setValidation(gridValidation);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.397Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":16,"estimatedTokens":147}}1135{"id":"doc-class_paragraphtextvalidation_apps_script_google-fe485f80","source":"documentation","title":"Class ParagraphTextValidation | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/forms/paragraph-text-validation","text":"Example:\n```text\n// Add a paragraph text item to a form and require the answer to be at least 100\n// characters.\nconst form = FormApp.create('My Form');\nconst paragraphTextItem =\n form.addParagraphTextItem().setTitle('Describe yourself:');\nconst paragraphtextValidation =\n FormApp.createParagraphTextValidation()\n .setHelpText('Answer must be more than 100 characters.')\n .requireTextLengthGreaterThanOrEqualTo(100)\n .build();\nparagraphTextItem.setValidation(paragraphtextValidation);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.398Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":16,"estimatedTokens":133}}1136{"id":"doc-class_textvalidation_apps_script_google_for_deve-72991d5d","source":"documentation","title":"Class TextValidation | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/forms/text-validation","text":"Example:\n```text\n// Add a text item to a form and require it to be a number within a range.\nconst form = FormApp.create('My form');\nconst textItem =\n form.addTextItem().setTitle('Pick a number between 1 and 100?');\nconst textValidation =\n FormApp.createTextValidation()\n .setHelpText('Input was not a number between 1 and 100.')\n .requireNumberBetween(1, 100)\n .build();\ntextItem.setValidation(textValidation);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.400Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":15,"estimatedTokens":114}}1137{"id":"doc-execute_functions_with_the_google_apps_script_ap-25e25c05","source":"documentation","title":"Execute functions with the Google Apps Script API | Google for Developers","url":"https://developers.google.com/apps-script/guides/rest/api","text":"Example:\n```text\nList<String> mylist = (List<String>)(op.getResponse().get(\"result\"));\n```\n\nExample:\n```text\nreturn Utilities.base64Encode(myByteArray); // returns a string.\n```\n\nExample:\n```text\nif (credential.getExpiresInSeconds() <= 360) {\n credential.refreshToken();\n}\n```\n\nExample:\n```text\n/**\n * Return the set of folder names contained in the user's root folder as an\n * object (with folder IDs as keys).\n * @return {Object} A set of folder names keyed by folder ID.\n */\nfunction getFoldersUnderRoot() {\n const root = DriveApp.getRootFolder();\n const folders = root.getFolders();\n const folderSet = {};\n while (folders.hasNext()) {\n const folder = folders.next();\n folderSet[folder.getId()] = folder.getName();\n }\n return folderSet;\n}target.js\n```\n\nExample:\n```text\n/**\n * Create a HttpRequestInitializer from the given one, except set\n * the HTTP read timeout to be longer than the default (to allow\n * called scripts time to execute).\n *\n * @param {HttpRequestInitializer} requestInitializer the initializer\n * to copy and adjust; typically a Credential object.\n * @return an initializer with an extended read timeout.\n */\nprivate static HttpRequestInitializer setHttpTimeout(\n final HttpRequestInitializer requestInitializer) {\n return new HttpRequestInitializer() {\n @Override\n public void initialize(HttpRequest httpRequest) throws IOException {\n requestInitializer.initialize(httpRequest);\n // This allows the API to call (and avoid timing out on)\n // functions that take up to 6 minutes to complete (the maximum\n // allowed script run time), plus a little overhead.\n httpRequest.setReadTimeout(380000);\n }\n };\n}\n\n/**\n * Build and return an authorized Script client service.\n *\n * @param {Credential} credential an authorized Credential object\n * @return an authorized Script client service\n */\npublic static Script getScriptService() throws IOException {\n Credential credential = authorize();\n return new Script.Builder(\n HTTP_TRANSPORT, JSON_FACTORY, setHttpTimeout(credential))\n .setApplicationName(APPLICATION_NAME)\n .build();\n}\n\n/**\n * Interpret an error response returned by the API and return a String\n * summary.\n *\n * @param {Operation} op the Operation returning an error response\n * @return summary of error response, or null if Operation returned no\n * error\n */\npublic static String getScriptError(Operation op) {\n if (op.getError() == null) {\n return null;\n }\n\n // Extract the first (and only) set of error details and cast as a Map.\n // The values of this map are the script's 'errorMessage' and\n // 'errorType', and an array of stack trace elements (which also need to\n // be cast as Maps).\n Map<String, Object> detail = op.getError().getDetails().get(0);\n List<Map<String, Object>> stacktrace =\n (List<Map<String, Object>>) detail.get(\"scriptStackTraceElements\");\n\n java.lang.StringBuilder sb =\n new StringBuilder(\"\\nScript error message: \");\n sb.append(detail.get(\"errorMessage\"));\n sb.append(\"\\nScript error type: \");\n sb.append(detail.get(\"errorType\"));\n\n if (stacktrace != null) {\n // There may not be a stacktrace if the script didn't start\n // executing.\n sb.append(\"\\nScript error stacktrace:\");\n for (Map<String, Object> elem : stacktrace) {\n sb.append(\"\\n \");\n sb.append(elem.get(\"function\"));\n sb.append(\":\");\n sb.append(elem.get(\"lineNumber\"));\n }\n }\n sb.append(\"\\n\");\n return sb.toString();\n}\n\npublic static void main(String[] args) throws IOException {\n // ID of the script to call. Acquire this from the Apps Script editor,\n // under Publish > Deploy as API executable.\n String scriptId = \"ENTER_YOUR_SCRIPT_ID_HERE\";\n Script service = getScriptService();\n\n // Create an execution request object.\n ExecutionRequest request = new ExecutionRequest()\n .setFunction(\"getFoldersUnderRoot\");\n\n try {\n // Make the API request.\n Operation op =\n service.scripts().run(scriptId, request).execute();\n\n // Print results of request.\n if (op.getError() != null) {\n // The API executed, but the script returned an error.\n System.out.println(getScriptError(op));\n } else {\n // The result provided by the API needs to be cast into\n // the correct type, based upon what types the Apps\n // Script function returns. Here, the function returns\n // an Apps Script Object with String keys and values,\n // so must be cast into a Java Map (folderSet).\n Map<String, String> folderSet =\n (Map<String, String>) (op.getResponse().get(\"result\"));\n if (folderSet.size() == 0) {\n System.out.println(\"No folders returned!\");\n } else {\n System.out.println(\"Folders under your root folder:\");\n for (String id : folderSet.keySet()) {\n System.out.printf(\n \"\\t%s (%s)\\n\", folderSet.get(id), id);\n }\n }\n }\n } catch (GoogleJsonResponseException e) {\n // The API encountered a problem before the script was called.\n e.printStackTrace(System.out);\n }\n}Execute.java\n```\n\nExample:\n```text\n/**\n * Load the API and make an API call. Display the results on the screen.\n */\nfunction callScriptFunction() {\n const scriptId = '<ENTER_YOUR_SCRIPT_ID_HERE>';\n\n // Call the Apps Script API run method\n // 'scriptId' is the URL parameter that states what script to run\n // 'resource' describes the run request body (with the function name\n // to execute)\n try {\n gapi.client.script.scripts.run({\n 'scriptId': scriptId,\n 'resource': {\n 'function': 'getFoldersUnderRoot',\n },\n }).then(function(resp) {\n const result = resp.result;\n if (result.error && result.error.status) {\n // The API encountered a problem before the script\n // started executing.\n appendPre('Error calling API:');\n appendPre(JSON.stringify(result, null, 2));\n } else if (result.error) {\n // The API executed, but the script returned an error.\n\n // Extract the first (and only) set of error details.\n // The values of this object are the script's 'errorMessage' and\n // 'errorType', and an array of stack trace elements.\n const error = result.error.details[0];\n appendPre('Script error message: ' + error.errorMessage);\n\n if (error.scriptStackTraceElements) {\n // There may not be a stacktrace if the script didn't start\n // executing.\n appendPre('Script error stacktrace:');\n for (let i = 0; i < error.scriptStackTraceElements.length; i++) {\n const trace = error.scriptStackTraceElements[i];\n appendPre('\\t' + trace.function + ':' + trace.lineNumber);\n }\n }\n } else {\n // The structure of the result will depend upon what the Apps\n // Script function returns. Here, the function returns an Apps\n // Script Object with String keys and values, and so the result\n // is treated as a JavaScript object (folderSet).\n\n const folderSet = result.response.result;\n if (Object.keys(folderSet).length == 0) {\n appendPre('No folders returned!');\n } else {\n appendPre('Folders under your root folder:');\n Object.keys(folderSet).forEach(function(id) {\n appendPre('\\t' + folderSet[id] + ' (' + id + ')');\n });\n }\n }\n });\n } catch (err) {\n document.getElementById('content').innerText = err.message;\n return;\n }\n}\nindex.js\n```\n\nExample:\n```text\nimport {GoogleAuth} from 'google-auth-library';\nimport {google} from 'googleapis';\n\n/**\n * Calls an Apps Script function to list the folders in the user's root Drive folder.\n */\nasync function callAppsScript() {\n // The ID of the Apps Script project to call.\n const scriptId = '1xGOh6wCm7hlIVSVPKm0y_dL-YqetspS5DEVmMzaxd_6AAvI-_u8DSgBT';\n\n // Authenticate with Google and get an authorized client.\n // TODO (developer): Use an appropriate auth mechanism for your app.\n const auth = new GoogleAuth({\n scopes: 'https://www.googleapis.com/auth/drive',\n });\n\n // Create a new Apps Script API client.\n const script = google.script({version: 'v1', auth});\n\n const resp = await script.scripts.run({\n auth,\n requestBody: {\n // The name of the function to call in the Apps Script project.\n function: 'getFoldersUnderRoot',\n },\n scriptId,\n });\n\n if (resp.data.error?.details?.[0]) {\n // The API executed, but the script returned an error.\n // Extract the error details.\n const error = resp.data.error.details[0];\n console.log(`Script error message: ${error.errorMessage}`);\n console.log('Script error stacktrace:');\n\n if (error.scriptStackTraceElements) {\n // Log the stack trace.\n for (let i = 0; i < error.scriptStackTraceElements.length; i++) {\n const trace = error.scriptStackTraceElements[i];\n console.log('\\t%s: %s', trace.function, trace.lineNumber);\n }\n }\n } else {\n // The script executed successfully.\n // The structure of the response depends on the Apps Script function's return value.\n const folderSet = resp.data.response ?? {};\n if (Object.keys(folderSet).length === 0) {\n console.log('No folders returned!');\n } else {\n console.log('Folders under your root folder:');\n Object.keys(folderSet).forEach((id) => {\n console.log('\\t%s (%s)', folderSet[id], id);\n });\n }\n }\n}index.js\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef main():\n \"\"\"Runs the sample.\"\"\"\n # pylint: disable=maybe-no-member\n script_id = \"1VFBDoJFy6yb9z7-luOwRv3fCmeNOzILPnR4QVmR0bGJ7gQ3QMPpCW-yt\"\n\n creds, _ = google.auth.default()\n service = build(\"script\", \"v1\", credentials=creds)\n\n # Create an execution request object.\n request = {\"function\": \"getFoldersUnderRoot\"}\n\n try:\n # Make the API request.\n response = service.scripts().run(scriptId=script_id, body=request).execute()\n if \"error\" in response:\n # The API executed, but the script returned an error.\n # Extract the first (and only) set of error details. The values of\n # this object are the script's 'errorMessage' and 'errorType', and\n # a list of stack trace elements.\n error = response[\"error\"][\"details\"][0]\n print(f\"Script error message: {0}.{format(error['errorMessage'])}\")\n\n if \"scriptStackTraceElements\" in error:\n # There may not be a stacktrace if the script didn't start\n # executing.\n print(\"Script error stacktrace:\")\n for trace in error[\"scriptStackTraceElements\"]:\n print(f\"\\t{0}: {1}.{format(trace['function'], trace['lineNumber'])}\")\n else:\n # The structure of the result depends upon what the Apps Script\n # function returns. Here, the function returns an Apps Script\n # Object with String keys and values, and so the result is\n # treated as a Python dictionary (folder_set).\n folder_set = response[\"response\"].get(\"result\", {})\n if not folder_set:\n print(\"No folders returned!\")\n else:\n print(\"Folders under your root folder:\")\n for folder_id, folder in folder_set.items():\n print(f\"\\t{0} ({1}).{format(folder, folder_id)}\")\n\n except HttpError as error:\n # The API encountered a problem before the script started executing.\n print(f\"An error occurred: {error}\")\n print(error.content)\n\n\nif __name__ == \"__main__\":\n main()execute.py\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.401Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":8,"totalLines":348,"estimatedTokens":2887}}1138{"id":"doc-manage_gmail_filters_google_for_developers-871ea013","source":"documentation","title":"Manage Gmail filters | Google for Developers","url":"https://developers.google.com/gmail/api/guides/filter_settings","text":"Example:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.gmail.Gmail;\nimport com.google.api.services.gmail.GmailScopes;\nimport com.google.api.services.gmail.model.Filter;\nimport com.google.api.services.gmail.model.FilterAction;\nimport com.google.api.services.gmail.model.FilterCriteria;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.util.Arrays;\n\n/* Class to demonstrate the use of Gmail Create Filter API */\npublic class CreateFilter {\n /**\n * Create a new filter.\n *\n * @param labelId - ID of the user label to add\n * @return the created filter id, {@code null} otherwise.\n * @throws IOException - if service account credentials file not found.\n */\n public static String createNewFilter(String labelId) throws IOException {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(GmailScopes.GMAIL_SETTINGS_BASIC,\n GmailScopes.GMAIL_LABELS);\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);\n\n // Create the gmail API client\n Gmail service = new Gmail.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Gmail samples\")\n .build();\n\n try {\n // Filter the mail from sender and archive them(skip the inbox)\n Filter filter = new Filter()\n .setCriteria(new FilterCriteria()\n .setFrom(\"gduser2@workspacesamples.dev\"))\n .setAction(new FilterAction()\n .setAddLabelIds(Arrays.asList(labelId))\n .setRemoveLabelIds(Arrays.asList(\"INBOX\")));\n\n Filter result = service.users().settings().filters().create(\"me\", filter).execute();\n // Prints the new created filter ID\n System.out.println(\"Created filter \" + result.getId());\n return result.getId();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 403) {\n System.err.println(\"Unable to create filter: \" + e.getDetails());\n } else {\n throw e;\n }\n }\n return null;\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef create_filter():\n \"\"\"Create a filter.\n Returns: Draft object, including filter id.\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create gmail api client\n service = build(\"gmail\", \"v1\", credentials=creds)\n\n label_name = \"IMPORTANT\"\n filter_content = {\n \"criteria\": {\"from\": \"gsuder1@workspacesamples.dev\"},\n \"action\": {\n \"addLabelIds\": [label_name],\n \"removeLabelIds\": [\"INBOX\"],\n },\n }\n\n # pylint: disable=E1101\n result = (\n service.users()\n .settings()\n .filters()\n .create(userId=\"me\", body=filter_content)\n .execute()\n )\n print(f'Created filter with id: {result.get(\"id\")}')\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n result = None\n\n return result.get(\"id\")\n\n\nif __name__ == \"__main__\":\n create_filter()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.402Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":121,"estimatedTokens":987}}1139{"id":"doc-migrate_to_iframe_sandbox_mode_apps_script_googl-4f8226d0","source":"documentation","title":"Migrate to IFRAME Sandbox Mode | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/migration/iframe","text":"Example:\n```text\nfunction doGet() {\n var template = HtmlService.createTemplateFromFile('top');\n return template.evaluate();\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <body>\n <div>\n <a href=\"http://google.com\" target=\"_top\">Click Me!</a>\n </div>\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n </head>\n <body>\n <div>\n <a href=\"http://google.com\">Click Me!</a>\n </div>\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <body>\n <!-- Add your HTML content here -->\n </body>\n</html>\n```\n\nExample:\n```text\n<script src=\"https://apis.google.com/js/api.js?onload=onApiLoad\">\n</script>\n```\n\nExample:\n```text\nfunction createPicker(oauthToken) {\n var picker = new google.picker.PickerBuilder()\n .addView(google.picker.ViewId.SPREADSHEETS) // Or a different ViewId\n .setOAuthToken(oauthToken)\n .setDeveloperKey(developerKey)\n .setCallback(pickerCallback)\n .setOrigin(google.script.host.origin) // Note the setOrigin\n .build();\n picker.setVisible(true);\n}\n```\n\nExample:\n```text\n// Prevent forms from submitting.\nfunction preventFormSubmit() {\n var forms = document.querySelectorAll('form');\n for (var i = 0; i < forms.length; i++) {\n forms[i].addEventListener('submit', function(event) {\n event.preventDefault();\n });\n }\n}\nwindow.addEventListener('load', preventFormSubmit);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.403Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":80,"estimatedTokens":355}}1140{"id":"doc-class_checkboxvalidation_apps_script_google_for_-b790a494","source":"documentation","title":"Class CheckboxValidation | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/forms/checkbox-validation","text":"Example:\n```text\n// Add a checkBox item to a form and require exactly two selections.\nconst form = FormApp.create('My Form');\nconst checkBoxItem = form.addCheckboxItem();\ncheckBoxItem.setTitle('What two condiments would you like on your hot dog?');\ncheckBoxItem.setChoices([\n checkBoxItem.createChoice('Ketchup'),\n checkBoxItem.createChoice('Mustard'),\n checkBoxItem.createChoice('Relish'),\n]);\nconst checkBoxValidation = FormApp.createCheckboxValidation()\n .setHelpText('Select two condiments.')\n .requireSelectExactly(2)\n .build();\ncheckBoxItem.setValidation(checkBoxValidation);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.404Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":19,"estimatedTokens":173}}1141{"id":"doc-advanced_sheets_service_apps_script_google_for_d-4f2d2dca","source":"documentation","title":"Advanced Sheets Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/advanced/sheets","text":"Example:\n```text\n/**\n * Read a range (A1:D5) of data values. Logs the values.\n * @param {string} spreadsheetId The spreadsheet ID to read from.\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/get\n */\nfunction readRange(spreadsheetId = yourspreadsheetId) {\n try {\n const response = Sheets.Spreadsheets.Values.get(\n spreadsheetId,\n \"Sheet1!A1:D5\",\n );\n if (response.values) {\n console.log(response.values);\n return;\n }\n console.log(\"Failed to get range of values from spreadsheet\");\n } catch (e) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", e.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Write to multiple, disjoint data ranges.\n * @param {string} spreadsheetId The spreadsheet ID to write to.\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/batchUpdate\n */\nfunction writeToMultipleRanges(spreadsheetId = yourspreadsheetId) {\n // Specify some values to write to the sheet.\n const columnAValues = [[\"Item\", \"Wheel\", \"Door\", \"Engine\"]];\n const rowValues = [\n [\"Cost\", \"Stocked\", \"Ship Date\"],\n [\"$20.50\", \"4\", \"3/1/2016\"],\n ];\n\n const request = {\n valueInputOption: \"USER_ENTERED\",\n data: [\n {\n range: \"Sheet1!A1:A4\",\n majorDimension: \"COLUMNS\",\n values: columnAValues,\n },\n {\n range: \"Sheet1!B1:D2\",\n majorDimension: \"ROWS\",\n values: rowValues,\n },\n ],\n };\n try {\n const response = Sheets.Spreadsheets.Values.batchUpdate(\n request,\n spreadsheetId,\n );\n if (response) {\n console.log(response);\n return;\n }\n console.log(\"response null\");\n } catch (e) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", e.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Add a new sheet with some properties.\n * @param {string} spreadsheetId The spreadsheet ID.\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/batchUpdate\n */\nfunction addSheet(spreadsheetId = yourspreadsheetId) {\n const requests = [\n {\n addSheet: {\n properties: {\n title: \"Deposits\",\n gridProperties: {\n rowCount: 20,\n columnCount: 12,\n },\n tabColor: {\n red: 1.0,\n green: 0.3,\n blue: 0.4,\n },\n },\n },\n },\n ];\n try {\n const response = Sheets.Spreadsheets.batchUpdate(\n { requests: requests },\n spreadsheetId,\n );\n console.log(\n `Created sheet with ID: ${response.replies[0].addSheet.properties.sheetId}`,\n );\n } catch (e) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", e.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Add a pivot table.\n * @param {string} spreadsheetId The spreadsheet ID to add the pivot table to.\n * @param {string} pivotSourceDataSheetId The sheet ID to get the data from.\n * @param {string} destinationSheetId The sheet ID to add the pivot table to.\n * @see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/batchUpdate\n */\nfunction addPivotTable(\n spreadsheetId = yourspreadsheetId,\n pivotSourceDataSheetId = yourpivotSourceDataSheetId,\n destinationSheetId = yourdestinationSheetId,\n) {\n const requests = [\n {\n updateCells: {\n rows: {\n values: [\n {\n pivotTable: {\n source: {\n sheetId: pivotSourceDataSheetId,\n startRowIndex: 0,\n startColumnIndex: 0,\n endRowIndex: 20,\n endColumnIndex: 7,\n },\n rows: [\n {\n sourceColumnOffset: 0,\n showTotals: true,\n sortOrder: \"ASCENDING\",\n valueBucket: {\n buckets: [\n {\n stringValue: \"West\",\n },\n ],\n },\n },\n {\n sourceColumnOffset: 1,\n showTotals: true,\n sortOrder: \"DESCENDING\",\n valueBucket: {},\n },\n ],\n columns: [\n {\n sourceColumnOffset: 4,\n sortOrder: \"ASCENDING\",\n showTotals: true,\n valueBucket: {},\n },\n ],\n values: [\n {\n summarizeFunction: \"SUM\",\n sourceColumnOffset: 3,\n },\n ],\n valueLayout: \"HORIZONTAL\",\n },\n },\n ],\n },\n start: {\n sheetId: destinationSheetId,\n rowIndex: 49,\n columnIndex: 0,\n },\n fields: \"pivotTable\",\n },\n },\n ];\n try {\n const response = Sheets.Spreadsheets.batchUpdate(\n { requests: requests },\n spreadsheetId,\n );\n // The Pivot table will appear anchored to cell A50 of the destination sheet.\n } catch (e) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", e.message);\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.405Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":203,"estimatedTokens":1335}}1142{"id":"doc-manage_aliases_and_signatures_with_the_gmail_api-79e23563","source":"documentation","title":"Manage aliases and signatures with the Gmail API | Google for Developers","url":"https://developers.google.com/gmail/api/guides/alias_and_signature_settings","text":"Example:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.gmail.Gmail;\nimport com.google.api.services.gmail.GmailScopes;\nimport com.google.api.services.gmail.model.ListSendAsResponse;\nimport com.google.api.services.gmail.model.SendAs;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\n\n/* Class to demonstrate the use of Gmail Update Signature API */\npublic class UpdateSignature {\n /**\n * Update the gmail signature.\n *\n * @return the updated signature id , {@code null} otherwise.\n * @throws IOException - if service account credentials file not found.\n */\n public static String updateGmailSignature() throws IOException {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(GmailScopes.GMAIL_SETTINGS_BASIC);\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);\n\n // Create the gmail API client\n Gmail service = new Gmail.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Gmail samples\")\n .build();\n\n try {\n SendAs primaryAlias = null;\n ListSendAsResponse aliases = service.users().settings().sendAs().list(\"me\").execute();\n for (SendAs alias : aliases.getSendAs()) {\n if (alias.getIsPrimary()) {\n primaryAlias = alias;\n break;\n }\n }\n // Updating a new signature\n SendAs aliasSettings = new SendAs().setSignature(\"Automated Signature\");\n SendAs result = service.users().settings().sendAs().patch(\n \"me\",\n primaryAlias.getSendAsEmail(),\n aliasSettings)\n .execute();\n //Prints the updated signature\n System.out.println(\"Updated signature - \" + result.getSignature());\n return result.getSignature();\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 403) {\n System.err.println(\"Unable to update signature: \" + e.getDetails());\n } else {\n throw e;\n }\n }\n return null;\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef update_signature():\n \"\"\"Create and update signature in gmail.\n Returns:Draft object, including updated signature.\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create gmail api client\n service = build(\"gmail\", \"v1\", credentials=creds)\n\n primary_alias = None\n\n # pylint: disable=E1101\n aliases = service.users().settings().sendAs().list(userId=\"me\").execute()\n for alias in aliases.get(\"sendAs\"):\n if alias.get(\"isPrimary\"):\n primary_alias = alias\n break\n\n send_as_configuration = {\n \"displayName\": primary_alias.get(\"sendAsEmail\"),\n \"signature\": \"Automated Signature\",\n }\n\n # pylint: disable=E1101\n result = (\n service.users()\n .settings()\n .sendAs()\n .patch(\n userId=\"me\",\n sendAsEmail=primary_alias.get(\"sendAsEmail\"),\n body=send_as_configuration,\n )\n .execute()\n )\n print(f'Updated signature for: {result.get(\"displayName\")}')\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n result = None\n\n return result.get(\"signature\")\n\n\nif __name__ == \"__main__\":\n update_signature()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.406Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":132,"estimatedTokens":1058}}1143{"id":"doc-manage_email_forwarding_gmail_google_for_develop-a6b26f7d","source":"documentation","title":"Manage email forwarding | Gmail | Google for Developers","url":"https://developers.google.com/gmail/api/guides/forwarding_settings","text":"Example:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.gmail.Gmail;\nimport com.google.api.services.gmail.GmailScopes;\nimport com.google.api.services.gmail.model.AutoForwarding;\nimport com.google.api.services.gmail.model.ForwardingAddress;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\n\n/* Class to demonstrate the use of Gmail Enable Forwarding API */\npublic class EnableForwarding {\n /**\n * Enable the auto-forwarding for an account.\n *\n * @param forwardingEmail - Email address of the recipient whose email will be forwarded.\n * @return forwarding id and metadata, {@code null} otherwise.\n * @throws IOException - if service account credentials file not found.\n */\n public static AutoForwarding enableAutoForwarding(String forwardingEmail) throws IOException {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(GmailScopes.GMAIL_SETTINGS_SHARING);\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);\n\n // Create the gmail API client\n Gmail service = new Gmail.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Gmail samples\")\n .build();\n\n try {\n // Enable auto-forwarding and move forwarded messages to the trash\n ForwardingAddress address = new ForwardingAddress()\n .setForwardingEmail(forwardingEmail);\n ForwardingAddress createAddressResult = service.users().settings().forwardingAddresses()\n .create(\"me\", address).execute();\n if (createAddressResult.getVerificationStatus().equals(\"accepted\")) {\n AutoForwarding autoForwarding = new AutoForwarding()\n .setEnabled(true)\n .setEmailAddress(address.getForwardingEmail())\n .setDisposition(\"trash\");\n autoForwarding =\n service.users().settings().updateAutoForwarding(\"me\", autoForwarding).execute();\n System.out.println(autoForwarding.toPrettyString());\n return autoForwarding;\n }\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 403) {\n System.err.println(\"Unable to enable forwarding: \" + e.getDetails());\n } else {\n throw e;\n }\n }\n return null;\n }\n}\n```\n\nExample:\n```text\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\n\n\ndef enable_forwarding():\n \"\"\"Enable email forwarding.\n Returns:Draft object, including forwarding id and result meta data.\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create gmail api client\n service = build(\"gmail\", \"v1\", credentials=creds)\n\n address = {\"forwardingEmail\": \"gduser1@workspacesamples.dev\"}\n\n # pylint: disable=E1101\n result = (\n service.users()\n .settings()\n .forwardingAddresses()\n .create(userId=\"me\", body=address)\n .execute()\n )\n if result.get(\"verificationStatus\") == \"accepted\":\n body = {\n \"emailAddress\": result.get(\"forwardingEmail\"),\n \"enabled\": True,\n \"disposition\": \"trash\",\n }\n # pylint: disable=E1101\n result = (\n service.users()\n .settings()\n .updateAutoForwarding(userId=\"me\", body=body)\n .execute()\n )\n print(f\"Forwarding is enabled : {result}\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n result = None\n\n return result\n\n\nif __name__ == \"__main__\":\n enable_forwarding()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.406Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":127,"estimatedTokens":1099}}1144{"id":"doc-advanced_slides_service_apps_script_google_for_d-7d4d0d51","source":"documentation","title":"Advanced Slides Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/advanced/slides","text":"Example:\n```text\n/**\n * Create a new presentation.\n * @return {string} presentation Id.\n * @see https://developers.google.com/slides/api/reference/rest/v1/presentations/create\n */\nfunction createPresentation() {\n try {\n const presentation = Slides.Presentations.create({\n title: \"MyNewPresentation\",\n });\n console.log(`Created presentation with ID: ${presentation.presentationId}`);\n return presentation.presentationId;\n } catch (e) {\n // TODO (developer) - Handle exception\n console.log(\"Failed with error %s\", e.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Create a new slide.\n * @param {string} presentationId The presentation to add the slide to.\n * @return {Object} slide\n * @see https://developers.google.com/slides/api/reference/rest/v1/presentations/batchUpdate\n */\nfunction createSlide(presentationId) {\n // You can specify the ID to use for the slide, as long as it's unique.\n const pageId = Utilities.getUuid();\n\n const requests = [\n {\n createSlide: {\n objectId: pageId,\n insertionIndex: 1,\n slideLayoutReference: {\n predefinedLayout: \"TITLE_AND_TWO_COLUMNS\",\n },\n },\n },\n ];\n try {\n const slide = Slides.Presentations.batchUpdate(\n { requests: requests },\n presentationId,\n );\n console.log(\n `Created Slide with ID: ${slide.replies[0].createSlide.objectId}`,\n );\n return slide;\n } catch (e) {\n // TODO (developer) - Handle Exception\n console.log(\"Failed with error %s\", e.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Read page element IDs.\n * @param {string} presentationId The presentation to read from.\n * @param {string} pageId The page to read from.\n * @return {Object} response\n * @see https://developers.google.com/slides/api/reference/rest/v1/presentations.pages/get\n */\nfunction readPageElementIds(presentationId, pageId) {\n // You can use a field mask to limit the data the API retrieves\n // in a get request, or what fields are updated in an batchUpdate.\n try {\n const response = Slides.Presentations.Pages.get(presentationId, pageId, {\n fields: \"pageElements.objectId\",\n });\n console.log(response);\n return response;\n } catch (e) {\n // TODO (developer) - Handle Exception\n console.log(\"Failed with error %s\", e.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Add a new text box with text to a page.\n * @param {string} presentationId The presentation ID.\n * @param {string} pageId The page ID.\n * @return {Object} response\n * @see https://developers.google.com/slides/api/reference/rest/v1/presentations/batchUpdate\n */\nfunction addTextBox(presentationId, pageId) {\n // You can specify the ID to use for elements you create,\n // as long as the ID is unique.\n const pageElementId = Utilities.getUuid();\n\n const requests = [\n {\n createShape: {\n objectId: pageElementId,\n shapeType: \"TEXT_BOX\",\n elementProperties: {\n pageObjectId: pageId,\n size: {\n width: {\n magnitude: 150,\n unit: \"PT\",\n },\n height: {\n magnitude: 50,\n unit: \"PT\",\n },\n },\n transform: {\n scaleX: 1,\n scaleY: 1,\n translateX: 200,\n translateY: 100,\n unit: \"PT\",\n },\n },\n },\n },\n {\n insertText: {\n objectId: pageElementId,\n text: \"My Added Text Box\",\n insertionIndex: 0,\n },\n },\n ];\n try {\n const response = Slides.Presentations.batchUpdate(\n { requests: requests },\n presentationId,\n );\n console.log(\n `Created Textbox with ID: ${response.replies[0].createShape.objectId}`,\n );\n return response;\n } catch (e) {\n // TODO (developer) - Handle Exception\n console.log(\"Failed with error %s\", e.message);\n }\n}\n```\n\nExample:\n```text\n/**\n * Format the text in a shape.\n * @param {string} presentationId The presentation ID.\n * @param {string} shapeId The shape ID.\n * @return {Object} replies\n * @see https://developers.google.com/slides/api/reference/rest/v1/presentations/batchUpdate\n */\nfunction formatShapeText(presentationId, shapeId) {\n const requests = [\n {\n updateTextStyle: {\n objectId: shapeId,\n fields: \"foregroundColor,bold,italic,fontFamily,fontSize,underline\",\n style: {\n foregroundColor: {\n opaqueColor: {\n themeColor: \"ACCENT5\",\n },\n },\n bold: true,\n italic: true,\n underline: true,\n fontFamily: \"Corsiva\",\n fontSize: {\n magnitude: 18,\n unit: \"PT\",\n },\n },\n textRange: {\n type: \"ALL\",\n },\n },\n },\n ];\n try {\n const response = Slides.Presentations.batchUpdate(\n { requests: requests },\n presentationId,\n );\n return response.replies;\n } catch (e) {\n // TODO (developer) - Handle Exception\n console.log(\"Failed with error %s\", e.message);\n }\n}\n```\n\nExample:\n```text\nvar titles = [\"slide 1\", \"slide 2\"];\nfor (var i = 0; i < titles.length; i++) {\n Slides.Presentations.batchUpdate(preso, {\n requests: [{\n createSlide: ...\n }]\n });\n}\n```\n\nExample:\n```text\nvar requests = [];\nvar titles = [\"slide 1\", \"slide 2\"];\nfor (var i = 0; i < titles.length; i++) {\n requests.push({ createSlide: ... });\n}\n\nSlides.Presentations.batchUpdate(preso, {\n requests: requests\n});\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.408Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":225,"estimatedTokens":1368}}1145{"id":"doc-enum_sandboxmode_apps_script_google_for_develope-364ce8fb","source":"documentation","title":"Enum SandboxMode | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/html/sandbox-mode","text":"Example:\n```text\n<!-- Read the sandbox mode (in a client-side script). -->\n<script>\n alert(google.script.sandbox.mode);\n</script>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.409Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":9,"estimatedTokens":37}}1146{"id":"doc-configure_push_notifications_in_gmail_api_google-b3f4e2a9","source":"documentation","title":"Configure push notifications in Gmail API | Google for Developers","url":"https://developers.google.com/gmail/api/guides/push","text":"Example:\n```text\nPOST \"https://www.googleapis.com/gmail/v1/users/me/watch\"\nContent-type: application/json\n\n{\n topicName: \"projects/myproject/topics/mytopic\",\n labelIds: [\"INBOX\"],\n labelFilterBehavior: \"INCLUDE\",\n}\n```\n\nExample:\n```text\nrequest = {\n 'labelIds': ['INBOX'],\n 'topicName': 'projects/myproject/topics/mytopic',\n 'labelFilterBehavior': 'INCLUDE'\n}\ngmail.users().watch(userId='me', body=request).execute()\n```\n\nExample:\n```text\nPOST https://yourserver.example.com/yourUrl\nContent-type: application/json\n\n{\n message:\n {\n // This is the actual notification data, as Base64URL-encoded JSON.\n data: \"eyJlbWFpbEFkZHJlc3MiOiAidXNlckBleGFtcGxlLmNvbSIsICJoaXN0b3J5SWQiOiAiMTIzNDU2Nzg5MCJ9\",\n\n // This is a Cloud Pub/Sub message id, unrelated to Gmail messages.\n \"messageId\": \"2070443601311540\",\n\n // This is the publish time of the message.\n \"publishTime\": \"2021-02-26T19:13:55.749Z\",\n }\n\n subscription: \"projects/myproject/subscriptions/mysubscription\"\n}\n```\n\nExample:\n```text\n{\"emailAddress\": \"user@example.com\", \"historyId\": \"9876543210\"}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.409Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":50,"estimatedTokens":273}}1147{"id":"doc-class_namedrange_apps_script_google_for_develope-4573a51c","source":"documentation","title":"Class NamedRange | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/named-range","text":"Example:\n```text\n// The code below deletes all the named ranges in the spreadsheet.\nconst namedRanges = SpreadsheetApp.getActive().getNamedRanges();\nfor (let i = 0; i < namedRanges.length; i++) {\n namedRanges[i].remove();\n}\n```\n\nExample:\n```text\n// The code below updates the name for the first named range.\nconst namedRanges = SpreadsheetApp.getActiveSpreadsheet().getNamedRanges();\nif (namedRanges.length > 1) {\n namedRanges[0].setName('UpdatedNamedRange');\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.410Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":19,"estimatedTokens":121}}1148{"id":"doc-github_googleworkspace_apps_script_oauth2_an_oau-9e8ba27f","source":"documentation","title":"GitHub - googleworkspace/apps-script-oauth2: An OAuth2 library for Google Apps Script. · GitHub","url":"https://developers.google.com/apps-script/migration/oauth-config","text":"Example:\n```text\nhttps://script.google.com/macros/d/{SCRIPT ID}/usercallback\n```\n\nExample:\n```text\n/**\n * Logs the redirect URI to register.\n */\nfunction logRedirectUri() {\n var service = getService_();\n Logger.log(service.getRedirectUri());\n}\n```\n\nExample:\n```text\nfunction getDriveService_() {\n // Create a new service with the given name. The name will be used when\n // persisting the authorized token, so ensure it is unique within the\n // scope of the property store.\n return OAuth2.createService('drive')\n\n // Set the endpoint URLs, which are the same for all Google services.\n .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')\n .setTokenUrl('https://accounts.google.com/o/oauth2/token')\n\n // Set the client ID and secret, from the Google Developers Console.\n .setClientId('...')\n .setClientSecret('...')\n\n // Set the name of the callback function in the script referenced\n // above that should be invoked to complete the OAuth flow.\n .setCallbackFunction('authCallback')\n\n // Set the property store where authorized tokens should be persisted.\n .setPropertyStore(PropertiesService.getUserProperties())\n\n // Set the scopes to request (space-separated for Google services).\n .setScope('https://www.googleapis.com/auth/drive')\n\n // Below are Google-specific OAuth2 parameters.\n\n // Sets the login hint, which will prevent the account chooser screen\n // from being shown to users logged in with multiple accounts.\n .setParam('login_hint', Session.getEffectiveUser().getEmail())\n\n // Requests offline access.\n .setParam('access_type', 'offline')\n\n // Consent prompt is required to ensure a refresh token is always\n // returned when requesting offline access.\n .setParam('prompt', 'consent');\n}\n```\n\nExample:\n```text\nfunction showSidebar() {\n var driveService = getDriveService_();\n if (!driveService.hasAccess()) {\n var authorizationUrl = driveService.getAuthorizationUrl();\n var template = HtmlService.createTemplate(\n '<a href=\"<?= authorizationUrl ?>\" target=\"_blank\">Authorize</a>. ' +\n 'Reopen the sidebar when the authorization is complete.');\n template.authorizationUrl = authorizationUrl;\n var page = template.evaluate();\n DocumentApp.getUi().showSidebar(page);\n } else {\n // ...\n }\n}\n```\n\nExample:\n```text\nfunction authCallback(request) {\n var driveService = getDriveService_();\n var isAuthorized = driveService.handleCallback(request);\n if (isAuthorized) {\n return HtmlService.createHtmlOutput('Success! You can close this tab.');\n } else {\n return HtmlService.createHtmlOutput('Denied. You can close this tab');\n }\n}\n```\n\nExample:\n```text\nfunction makeRequest() {\n var driveService = getDriveService_();\n var response = UrlFetchApp.fetch('https://www.googleapis.com/drive/v2/files?maxResults=10', {\n headers: {\n Authorization: 'Bearer ' + driveService.getAccessToken()\n }\n });\n // ...\n}\n```\n\nExample:\n```text\nfunction logout() {\n var service = getDriveService_()\n service.reset();\n}\n```\n\nExample:\n```text\nreturn OAuth2.createService('Foo')\n .setPropertyStore(PropertiesService.getUserProperties())\n // ...\n```\n\nExample:\n```text\nreturn OAuth2.createService('Foo')\n .setPropertyStore(PropertiesService.getUserProperties())\n .setCache(CacheService.getUserCache())\n // ...\n```\n\nExample:\n```text\nreturn OAuth2.createService('Foo')\n .setPropertyStore(PropertiesService.getUserProperties())\n .setCache(CacheService.getUserCache())\n .setLock(LockService.getUserLock())\n // ...\n```\n\nExample:\n```text\n.setTokenHeaders({\n 'Authorization': 'Basic ' + Utilities.base64Encode(CLIENT_ID + ':' + CLIENT_SECRET)\n});\n```\n\nExample:\n```text\n// Set the handler for modifying the access token request payload:\n.setTokenPayloadHandler(myTokenHandler)\n```\n\nExample:\n```text\nfunction authCallback(request) {\n var service = getService_();\n var authorized = service.handleCallback(request);\n if (authorized) {\n // Gets the authorized account ID from the scope string. Assumes the\n // application is configured to work with single accounts. Has the format\n // \"harvest:{ACCOUNT_ID}\".\n var scope = request.parameter['scope'];\n var accountId = scope.split(':')[1];\n // Save the account ID in the service's storage.\n service.getStorage().setValue('Harvest-Account-Id', accountId);\n return HtmlService.createHtmlOutput('Success!');\n } else {\n return HtmlService.createHtmlOutput('Denied.');\n }\n}\n```\n\nExample:\n```text\nif (service.hasAccess()) {\n // Retrieve the account ID from storage.\n var accountId = service.getStorage().getValue('Harvest-Account-Id');\n var url = 'https://api.harvestapp.com/v2/users/me';\n var response = UrlFetchApp.fetch(url, {\n headers: {\n 'Authorization': 'Bearer ' + service.getAccessToken(),\n 'User-Agent': 'Apps Script Sample',\n 'Harvest-Account-Id': accountId\n }\n });\n```\n\nExample:\n```text\nvar authorizationUrl = getService_().getAuthorizationUrl({\n // Pass the additional parameter \"lang\" with the value \"fr\".\n lang: 'fr'\n});\n```\n\nExample:\n```text\nfunction authCallback(request) {\n var lang = request.parameter.lang;\n // ...\n}\n```\n\nExample:\n```text\nfunction run() {\n var gitHubService = getGitHubService_();\n var mediumService = getMediumService_();\n // ...\n}\n\nfunction getGitHubService_() {\n return OAuth2.createService('GitHub')\n // GitHub settings ...\n}\n\nfunction getMediumService_() {\n return OAuth2.createService('Medium')\n // Medium settings ...\n}\n```\n\nExample:\n```text\nfunction run() {\n var copyFromService = getGitHubService_('from');\n var copyToService = getGitHubService_('to');\n // ...\n}\n\nfunction getGitHubService_(label) {\n return OAuth2.createService('GitHub_' + label)\n // GitHub settings ...\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.412Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":18,"totalLines":231,"estimatedTokens":1463}}1149{"id":"doc-class_htmlservice_apps_script_google_for_develop-469cb6e6","source":"documentation","title":"Class HtmlService | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/html/html-service","text":"Example:\n```text\nconst output = HtmlService.createHtmlOutput();\n```\n\nExample:\n```text\nfunction createFromBlob(blob) {\n const output = HtmlService.createHtmlOutput(blob);\n return output;\n}\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello world!</b>');\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutputFromFile('myPage');\n```\n\nExample:\n```text\nfunction createFromBlob(blob) {\n const template = HtmlService.createTemplate(blob);\n const output = template.evaluate();\n return output;\n}\n```\n\nExample:\n```text\nconst template = HtmlService.createTemplate(\n '<b>The time is <?= new Date() ?></b>',\n);\n```\n\nExample:\n```text\nconst template = HtmlService.createTemplateFromFile('myTemplate');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.414Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":7,"totalLines":45,"estimatedTokens":189}}1150{"id":"doc-class_driveapp_apps_script_google_for_developers-9227b98c","source":"documentation","title":"Class DriveApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/drive/drive-app","text":"Example:\n```text\n// Logs the name of every file in the user's Drive.\nconst files = DriveApp.getFiles();\nwhile (files.hasNext()) {\n const file = files.next();\n console.log(file.getName());\n}\n```\n\nExample:\n```text\n// Continues getting a list of all 'Untitled document' files in the user's\n// Drive. Creates a file iterator named 'previousIterator'.\nconst previousIterator = DriveApp.getFilesByName('Untitled document');\n\n// Gets continuation token from the previous file iterator.\nconst continuationToken = previousIterator.getContinuationToken();\n\n// Creates a new iterator using the continuation token from the previous file\n// iterator.\nconst newIterator = DriveApp.continueFileIterator(continuationToken);\n\n// Resumes the file iteration using a continuation token from 'firstIterator'\n// and logs the file name.\nif (newIterator.hasNext()) {\n const file = newIterator.next();\n console.log(file.getName());\n}\n```\n\nExample:\n```text\n// Continues getting a list of all folders in user's Drive.\n// Creates a folder iterator named 'previousIterator'.\nconst previousIterator = DriveApp.getFolders();\n\n// Gets continuation token from the previous folder iterator.\nconst continuationToken = previousIterator.getContinuationToken();\n\n// Creates a new iterator using the continuation token from the previous folder\n// iterator.\nconst newIterator = DriveApp.continueFolderIterator(continuationToken);\n\n// Resumes the folder iteration using a continuation token from the previous\n// iterator and logs the folder name.\nif (newIterator.hasNext()) {\n const folder = newIterator.next();\n console.log(folder.getName());\n}\n```\n\nExample:\n```text\n// Create a text file with the content \"Hello, world!\"\nDriveApp.createFile('New Text File', 'Hello, world!');\n```\n\nExample:\n```text\n// Create an HTML file with the content \"Hello, world!\"\nDriveApp.createFile('New HTML File', '<b>Hello, world!</b>', MimeType.HTML);\n```\n\nExample:\n```text\n// Creates shortcuts for all folders in the user's drive that have a specific\n// name.\n// TODO(developer): Replace 'Test-Folder' with a valid folder name in your\n// drive.\nconst folders = DriveApp.getFoldersByName('Test-Folder');\n\n// Iterates through all folders named 'Test-Folder'.\nwhile (folders.hasNext()) {\n const folder = folders.next();\n\n // Creates a shortcut to the provided Drive item ID and resource key, and\n // returns it.\n DriveApp.createShortcutForTargetIdAndResourceKey(\n folder.getId(),\n folder.getResourceKey(),\n );\n}\n```\n\nExample:\n```text\n// Enables enforceSingleParent behavior for all calls affecting item parents.\nDriveApp.enforceSingleParent(true);\n```\n\nExample:\n```text\n// Gets a list of all files in Google Drive with the given name.\n// TODO(developer): Replace 'Test' with your file name.\nconst files = DriveApp.getFilesByName('Test');\n\nif (files.hasNext()) {\n // Gets the ID of each file in the list.\n const fileId = files.next().getId();\n\n // Gets the file name using its ID and logs it to the console.\n console.log(DriveApp.getFileById(fileId).getName());\n}\n```\n\nExample:\n```text\n// Gets a list of all files in Drive with the given name.\n// TODO(developer): Replace 'Test' with your file name.\nconst files = DriveApp.getFilesByName('Test');\nif (files.hasNext()) {\n // Gets the first file in the list.\n const file = files.next();\n\n // Gets the ID and resource key.\n const key = file.getResourceKey();\n const id = file.getId();\n\n // Logs the file name to the console using its ID and resource key.\n console.log(DriveApp.getFileByIdAndResourceKey(id, key).getName());\n}\n```\n\nExample:\n```text\n// Gets the user's My Drive folder and logs its name to the console.\nconsole.log(DriveApp.getRootFolder().getName());\n\n// Logs the Drive owner's name to the console.\nconsole.log(DriveApp.getRootFolder().getOwner().getName());\n```\n\nExample:\n```text\n// Gets the number of bytes the user can store in Drive and logs it to the\n// console.\nconsole.log(DriveApp.getStorageLimit());\n```\n\nExample:\n```text\n// Gets the number of bytes the user is currently storing in Drive and logs it\n// to the console.\nconsole.log(DriveApp.getStorageUsed());\n```\n\nExample:\n```text\n// Gets a list of all the files in the trash of the user's Drive.\nconst trashFiles = DriveApp.getTrashedFiles();\n\n// Logs the trash file names to the console.\nwhile (trashFiles.hasNext()) {\n const file = trashFiles.next();\n console.log(file.getName());\n}\n```\n\nExample:\n```text\n// Gets a collection of all the folders in the trash of the user's Drive.\nconst trashFolders = DriveApp.getTrashedFolders();\n\n// Logs the trash folder names to the console.\nwhile (trashFolders.hasNext()) {\n const folder = trashFolders.next();\n console.log(folder.getName());\n}\n```\n\nExample:\n```text\n// Logs the name of every file in the user's Drive that modified after February 28,\n// 2022 whose name contains \"untitled.\"\"\nconst files = DriveApp.searchFiles(\n 'modifiedDate > \"2022-02-28\" and title contains \"untitled\"');\nwhile (files.hasNext()) {\n const file = files.next();\n console.log(file.getName());\n}\n```\n\nExample:\n```text\n// Logs the name of every folder in the user's Drive that you own and is starred.\nconst folders = DriveApp.searchFolders('starred = true and \"me\" in owners');\nwhile (folders.hasNext()) {\n const folder = folders.next();\n console.log(folder.getName());\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.416Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":16,"totalLines":194,"estimatedTokens":1328}}1151{"id":"doc-class_protection_apps_script_google_for_develope-73ad0103","source":"documentation","title":"Class Protection | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/protection","text":"Example:\n```text\n// Protect range A1:B10, then remove all other users from the list of editors.\nconst ss = SpreadsheetApp.getActive();\nconst range = ss.getRange('A1:B10');\nconst protection = range.protect().setDescription('Sample protected range');\n\n// Ensure the current user is an editor before removing others. Otherwise, if\n// the user's edit permission comes from a group, the script throws an exception\n// upon removing the group.\nconst me = Session.getEffectiveUser();\nprotection.addEditor(me);\nprotection.removeEditors(protection.getEditors());\nif (protection.canDomainEdit()) {\n protection.setDomainEdit(false);\n}\n```\n\nExample:\n```text\n// Remove all range protections in the spreadsheet that the user has permission\n// to edit.\nconst ss = SpreadsheetApp.getActive();\nconst protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);\nfor (let i = 0; i < protections.length; i++) {\n const protection = protections[i];\n if (protection.canEdit()) {\n protection.remove();\n }\n}\n```\n\nExample:\n```text\n// Protect the active sheet, then remove all other users from the list of\n// editors.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst protection = sheet.protect().setDescription('Sample protected sheet');\n\n// Ensure the current user is an editor before removing others. Otherwise, if\n// the user's edit permission comes from a group, the script throws an exception\n// upon removing the group.\nconst me = Session.getEffectiveUser();\nprotection.addEditor(me);\nprotection.removeEditors(protection.getEditors());\nif (protection.canDomainEdit()) {\n protection.setDomainEdit(false);\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Adds an editor to the spreadsheet using an email address.\n// TODO(developer): Replace the email address with a valid email.\nss.addEditor('cloudysanfrancisco@gmail.com');\n\n// Gets a sheet by its name and protects it.\nconst sheet = ss.getSheetByName('Sheet1');\nconst sampleProtectedSheet = sheet.protect();\n\n// Adds an editor of the protected sheet using an email address.\n// TODO(developer): Replace the email address with a valid email.\nsampleProtectedSheet.addEditor('cloudysanfrancisco@gmail.com');\n\n// Gets the editors of the protected sheet.\nconst editors = sampleProtectedSheet.getEditors();\n\n// Logs the editors' email addresses to the console.\nfor (const editor of editors) {\n console.log(editor.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Protects the sheet.\nconst sampleProtectedSheet = sheet.protect();\n\n// Adds the active user as an editor of the protected sheet.\nsampleProtectedSheet.addEditor(Session.getActiveUser());\n\n// Gets the editors of the protected sheet.\nconst editors = sampleProtectedSheet.getEditors();\n\n// Logs the editors' email addresses to the console.\nfor (const editor of editors) {\n console.log(editor.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Creates variables for the email addresses to add as editors.\n// TODO(developer): Replace the email addresses with valid ones.\nconst TEST_EMAIL_1 = 'cloudysanfrancisco@gmail.com';\nconst TEST_EMAIL_2 = 'baklavainthebalkans@gmail.com';\n\n// Protects the sheet.\nconst sampleProtectedSheet = sheet.protect();\n\n// Adds editors to the protected sheet using the email address variables.\nsampleProtectedSheet.addEditors([TEST_EMAIL_1, TEST_EMAIL_2]);\n\n// Gets the editors of the protected sheet.\nconst editors = sampleProtectedSheet.getEditors();\n\n// Logs the editors' email addresses to the console.\nfor (const editor of editors) {\n console.log(editor.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Protects the sheet.\nconst sampleProtectedSheet = sheet.protect();\n\n// Logs whether domain users have permission to edit the protected sheet to the\n// console.\nconsole.log(sampleProtectedSheet.canDomainEdit());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Protects the sheet and sets the description.\nconst sampleProtectedSheet =\n sheet.protect().setDescription('Sample sheet is protected');\n\n// Gets the description of the protected sheet and logs it to the console.\nconst sampleProtectedSheetDescription = sampleProtectedSheet.getDescription();\nconsole.log(sampleProtectedSheetDescription);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Protects the sheet.\nconst sampleProtectedSheet = sheet.protect();\n\n// Gets the type of the protected area.\nconst protectionType = sampleProtectedSheet.getProtectionType();\n\n// Logs 'SHEET'to the console since the type of the protected area is a sheet.\nconsole.log(protectionType.toString());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Gets the range 'A1:B10' of Sheet1.\nconst range = sheet.getRange('A1:B10');\n\n// Makes cells A1:B10 a protected range.\nconst sampleProtectedRange = range.protect();\n\n// Gets the protected ranges on the sheet.\nconst protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE);\n\n// Logs the A1 notation of the first protected range on the sheet.\nconsole.log(protections[0].getRange().getA1Notation());\n```\n\nExample:\n```text\n// Protect a named range in a spreadsheet and log the name of the protected\n// range.\nconst ss = SpreadsheetApp.getActive();\nconst range = ss.getRange('A1:B10');\nconst protection = range.protect();\nss.setNamedRange('Test', range); // Create a named range.\nprotection.setRangeName(\n 'Test'); // Associate the protection with the named range.\nLogger.log(\n protection.getRangeName()); // Verify the name of the protected range.\n```\n\nExample:\n```text\n// Unprotect cells E2:F5 in addition to any other unprotected ranges in the\n// protected sheet.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst protection = sheet.protect();\nconst unprotected = protection.getUnprotectedRanges();\nunprotected.push(sheet.getRange('E2:F5'));\nprotection.setUnprotectedRanges(unprotected);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Protects the sheet.\nconst sampleProtectedSheet = sheet.protect();\n\n// Sets the warning status for the protected sheet as true.\nsampleProtectedSheet.setWarningOnly(true);\n\nconst protectedSheetWarningStatus = sampleProtectedSheet.isWarningOnly();\n\n// Logs the warning status of the protected sheet to the console.\nconsole.log(protectedSheetWarningStatus);\n```\n\nExample:\n```text\n// Remove sheet protection from the active sheet, if the user has permission to\n// edit it.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst protection = sheet.getProtections(SpreadsheetApp.ProtectionType.SHEET)[0];\nif (protection?.canEdit()) {\n protection.remove();\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Creates a variable for an email address.\n// TODO(developer): Replace the email address with a valid one.\nconst TEST_EMAIL = 'baklavainthebalkans@gmail.com';\n\n// Protects the sheet.\nconst sampleProtectedSheet = sheet.protect();\n\n// Adds an editor to the protected sheet using the email address variable.\nsampleProtectedSheet.addEditor(TEST_EMAIL);\n\n// Removes the editor from the protected sheet using the email address variable.\nsampleProtectedSheet.removeEditor(TEST_EMAIL);\n\n// Gets the editors of the protected sheet.\nconst editors = sampleProtectedSheet.getEditors();\n\n// Logs the editors' email addresses to the console.\nfor (const editor of editors) {\n console.log(editor.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets a sheet by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Protects the sheet.\nconst sampleProtectedSheet = sheet.protect();\n\n// Removes the active user from the editors of the protected sheet.\nsampleProtectedSheet.removeEditor(Session.getActiveUser());\n\n// Gets the editors of the protected sheet.\nconst editors = sampleProtectedSheet.getEditors();\n\n// Logs the editors' email addresses to the console.\nfor (const editor of editors) {\n console.log(editor.getEmail());\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets the sheet 'Sheet1' by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Protects the sheet.\nconst sampleProtectedSheet = sheet.protect();\n\n// Sets the sheet description to 'Sheet1 is protected.'\nsampleProtectedSheet.setDescription('Sheet1 is protected');\n\n// Gets the description of the protected sheet.\nconst sampleProtectedSheetDescription = sampleProtectedSheet.getDescription();\n\n// Logs the description of the protected sheet to the console.\nconsole.log(sampleProtectedSheetDescription);\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Protects cells A1:D10 on Sheet1.\nconst sheet = ss.getSheetByName('Sheet1');\nconst protectedRange = sheet.getRange('A1:D10').protect();\n\n// Logs the current protected range, A1:D10.\nconsole.log(protectedRange.getRange().getA1Notation());\n\n// Creates a named range for cells E1:J10 called 'NewRange.'\nconst newRange = sheet.getRange('E1:J10');\nss.setNamedRange('NewRange', newRange);\nconst namedRange = ss.getNamedRanges()[0];\n\n// Updates the protected range to the named range, 'NewRange.'\n// This updates the protected range on Sheet1 from A1:D10 to E1:J10.\nprotectedRange.setNamedRange(namedRange);\n\n// Logs the updated protected range to the console.\nconsole.log(protectedRange.getRange().getA1Notation());\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Protects cells A1:D10 on Sheet1 of the spreadsheet.\nconst sheet = ss.getSheetByName('Sheet1');\nconst protectedRange = sheet.getRange('A1:D10').protect();\n\n// Logs the original protected range, A1:D10, to the console.\nconsole.log(protectedRange.getRange().getA1Notation());\n\n// Gets the range E1:J10.\nconst newRange = sheet.getRange('E1:J10');\n\n// Updates the protected range to E1:J10.\nprotectedRange.setRange(newRange);\n\n// Logs the updated protected range to the console.\nconsole.log(protectedRange.getRange().getA1Notation());\n```\n\nExample:\n```text\n// Protect the active sheet except B2:C5, then remove all other users from the\n// list of editors.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst protection = sheet.protect().setDescription('Sample protected sheet');\nconst unprotected = sheet.getRange('B2:C5');\nprotection.setUnprotectedRanges([unprotected]);\n\n// Ensure the current user is an editor before removing others. Otherwise, if\n// the user's edit permission comes from a group, the script throws an exception\n// upon removing the group.\nconst me = Session.getEffectiveUser();\nprotection.addEditor(me);\nprotection.removeEditors(protection.getEditors());\nif (protection.canDomainEdit()) {\n protection.setDomainEdit(false);\n}\n```\n\nExample:\n```text\n// Opens the spreadsheet file by its URL. If you created your script from within\n// a Google Sheets file, you can use SpreadsheetApp.getActiveSpreadsheet()\n// instead.\n// TODO(developer): Replace the URL with your own.\nconst ss = SpreadsheetApp.openByUrl(\n 'https://docs.google.com/spreadsheets/d/abc123456/edit',\n);\n\n// Gets the sheet 'Sheet1' by its name.\nconst sheet = ss.getSheetByName('Sheet1');\n\n// Protects the sheet and sets the protection to warning-based.\nconst sampleProtectedSheet = sheet.protect().setWarningOnly(true);\n\n// Logs whether the protected sheet is warning-based to the console.\nconsole.log(sampleProtectedSheet.isWarningOnly());\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.419Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":21,"totalLines":482,"estimatedTokens":3876}}1152{"id":"doc-manage_vacation_settings_with_the_gmail_api_goog-75b4ed86","source":"documentation","title":"Manage vacation settings with the Gmail API | Google for Developers","url":"https://developers.google.com/gmail/api/guides/vacation_settings","text":"Example:\n```text\nimport com.google.api.client.googleapis.json.GoogleJsonError;\nimport com.google.api.client.googleapis.json.GoogleJsonResponseException;\nimport com.google.api.client.http.HttpRequestInitializer;\nimport com.google.api.client.http.javanet.NetHttpTransport;\nimport com.google.api.client.json.gson.GsonFactory;\nimport com.google.api.services.gmail.Gmail;\nimport com.google.api.services.gmail.GmailScopes;\nimport com.google.api.services.gmail.model.VacationSettings;\nimport com.google.auth.http.HttpCredentialsAdapter;\nimport com.google.auth.oauth2.GoogleCredentials;\nimport java.io.IOException;\nimport java.time.LocalDateTime;\nimport java.time.ZoneOffset;\nimport java.time.ZonedDateTime;\n\n/* Class to demonstrate the use of Gmail Enable Auto Reply API*/\npublic class EnableAutoReply {\n /**\n * Enables the auto reply\n *\n * @return the reply message and response metadata.\n * @throws IOException - if service account credentials file not found.\n */\n public static VacationSettings autoReply() throws IOException {\n /* Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity for\n guides on implementing OAuth2 for your application. */\n GoogleCredentials credentials = GoogleCredentials.getApplicationDefault()\n .createScoped(GmailScopes.GMAIL_SETTINGS_BASIC);\n HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);\n\n // Create the gmail API client\n Gmail service = new Gmail.Builder(new NetHttpTransport(),\n GsonFactory.getDefaultInstance(),\n requestInitializer)\n .setApplicationName(\"Gmail samples\")\n .build();\n\n try {\n // Enable auto reply by restricting domain with start time and end time\n VacationSettings vacationSettings = new VacationSettings()\n .setEnableAutoReply(true)\n .setResponseBodyHtml(\n \"I am on vacation and will reply when I am back in the office. Thanks!\")\n .setRestrictToDomain(true)\n .setStartTime(LocalDateTime.now()\n .toEpochSecond(ZoneOffset.from(ZonedDateTime.now())) * 1000)\n .setEndTime(LocalDateTime.now().plusDays(7)\n .toEpochSecond(ZoneOffset.from(ZonedDateTime.now())) * 1000);\n\n VacationSettings response = service.users().settings()\n .updateVacation(\"me\", vacationSettings).execute();\n // Prints the auto-reply response body\n System.out.println(\"Enabled auto reply with message : \" + response.getResponseBodyHtml());\n return response;\n } catch (GoogleJsonResponseException e) {\n // TODO(developer) - handle error appropriately\n GoogleJsonError error = e.getDetails();\n if (error.getCode() == 403) {\n System.err.println(\"Unable to enable auto reply: \" + e.getDetails());\n } else {\n throw e;\n }\n }\n return null;\n }\n}\n```\n\nExample:\n```text\nfrom datetime import datetime, timedelta\n\nimport google.auth\nfrom googleapiclient.discovery import build\nfrom googleapiclient.errors import HttpError\nfrom numpy import long\n\n\ndef enable_auto_reply():\n \"\"\"Enable auto reply.\n Returns:Draft object, including reply message and response meta data.\n\n Load pre-authorized user credentials from the environment.\n TODO(developer) - See https://developers.google.com/identity\n for guides on implementing OAuth2 for the application.\n \"\"\"\n creds, _ = google.auth.default()\n\n try:\n # create gmail api client\n service = build(\"gmail\", \"v1\", credentials=creds)\n\n epoch = datetime.utcfromtimestamp(0)\n now = datetime.now()\n start_time = (now - epoch).total_seconds() * 1000\n end_time = (now + timedelta(days=7) - epoch).total_seconds() * 1000\n vacation_settings = {\n \"enableAutoReply\": True,\n \"responseBodyHtml\": (\n \"I am on vacation and will reply when I am \"\n \"back in the office. Thanks!\"\n ),\n \"restrictToDomain\": True,\n \"startTime\": long(start_time),\n \"endTime\": long(end_time),\n }\n\n # pylint: disable=E1101\n response = (\n service.users()\n .settings()\n .updateVacation(userId=\"me\", body=vacation_settings)\n .execute()\n )\n print(f\"Enabled AutoReply with message: {response.get('responseBodyHtml')}\")\n\n except HttpError as error:\n print(f\"An error occurred: {error}\")\n response = None\n\n return response\n\n\nif __name__ == \"__main__\":\n enable_auto_reply()\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.421Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":131,"estimatedTokens":1122}}1153{"id":"doc-html_service_create_and_serve_html_apps_script_g-020b18b0","source":"documentation","title":"HTML Service: Create and Serve HTML | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/html","text":"Example:\n```text\nfunction doGet() {\n return HtmlService.createHtmlOutputFromFile('Index');\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n </head>\n <body>\n Hello, World!\n </body>\n</html>\n```\n\nExample:\n```text\n// Use this code for Google Docs, Slides, Forms, or Sheets.\nfunction onOpen() {\n SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.\n .createMenu('Dialog')\n .addItem('Open', 'openDialog')\n .addToUi();\n}\n\nfunction openDialog() {\n var html = HtmlService.createHtmlOutputFromFile('Index');\n SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.\n .showModalDialog(html, 'Dialog title');\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n </head>\n <body>\n Hello, World!\n <input type=\"button\" value=\"Close\"\n onclick=\"google.script.host.close()\" />\n </body>\n</html>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.422Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":53,"estimatedTokens":231}}1154{"id":"doc-enum_protectiontype_apps_script_google_for_devel-5ea37489","source":"documentation","title":"Enum ProtectionType | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/protection-type","text":"Example:\n```text\n// Remove all range protections in the spreadsheet that the user has permission\n// to edit.\nconst ss = SpreadsheetApp.getActive();\nconst protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);\nfor (const protection of protections) {\n if (protection.canEdit()) {\n protection.remove();\n }\n}\n```\n\nExample:\n```text\n// Removes sheet protection from the active sheet, if the user has permission to\n// edit it.\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst protection = sheet.getProtections(SpreadsheetApp.ProtectionType.SHEET)[0];\nif (protection?.canEdit()) {\n protection.remove();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.423Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":25,"estimatedTokens":160}}1155{"id":"doc-class_text_apps_script_google_for_developers-44b985d5","source":"documentation","title":"Class Text | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/document/text","text":"Example:\n```text\n// Gets the body contents of the active tab.\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Use editAsText to obtain a single text element containing\n// all the characters in the tab.\nconst text = body.editAsText();\n\n// Insert text at the beginning of the tab.\ntext.insertText(0, 'Inserted text.\\n');\n\n// Insert text at the end of the tab.\ntext.appendText('\\nAppended text.');\n\n// Make the first half of the tab blue.\ntext.setForegroundColor(0, text.getText().length / 2, '#00FFFF');\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Adds the text, 'Sample body text,' to the end of the tab body.\nconst text = body.editAsText().appendText('Sample body text');\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Deletes the first 10 characters in the body.\nconst text = body.editAsText().deleteText(0, 9);\n```\n\nExample:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Insert two paragraphs separated by a paragraph containing an\n// horizontal rule.\nbody.insertParagraph(0, 'An editAsText sample.');\nbody.insertHorizontalRule(0);\nbody.insertParagraph(0, 'An example.');\n\n// Delete \" sample.\\n\\n An\" removing the horizontal rule in the process.\nbody.editAsText().deleteText(14, 25);\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Append a styled paragraph.\nconst par = body.appendParagraph('A bold, italicized paragraph.');\npar.setBold(true);\npar.setItalic(true);\n\n// Retrieve the paragraph's attributes.\nconst atts = par.getAttributes();\n\n// Log the paragraph attributes.\nfor (const att in atts) {\n Logger.log(`${att}:${atts[att]}`);\n}\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Declares style attributes.\nconst style = {};\nstyle[DocumentApp.Attribute.BOLD] = true;\nstyle[DocumentApp.Attribute.ITALIC] = true;\nstyle[DocumentApp.Attribute.FONT_SIZE] = 29;\n\n// Sets the style attributes to the tab's body.\nconst text = body.editAsText();\ntext.setAttributes(style);\n\n// Gets the style attributes applied to the eleventh character in the\n// body and logs them to the console.\nconst attributes = text.getAttributes(10);\nconsole.log(attributes);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the background color of the first 3 characters in the body.\nconst text = body.editAsText().setBackgroundColor(0, 2, '#FFC0CB');\n\n// Gets the background color of the first character in the body.\nconst backgroundColor = text.getBackgroundColor(0);\n\n// Logs the background color to the console.\nconsole.log(backgroundColor);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the font of the first 16 characters to Impact.\nconst text = body.editAsText().setFontFamily(0, 15, 'Impact');\n\n// Gets the font family of the 16th character in the tab body.\nconst fontFamily = text.getFontFamily(15);\n\n// Logs the font family to the console.\nconsole.log(fontFamily);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the font size of the first 13 characters to 15.\nconst text = body.editAsText().setFontSize(0, 12, 15);\n\n// Gets the font size of the first character.\nconst fontSize = text.getFontSize(0);\n\n// Logs the font size to the console.\nconsole.log(fontSize);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the foreground color of the first 3 characters in the tab body.\nconst text = body.editAsText().setForegroundColor(0, 2, '#0000FF');\n\n// Gets the foreground color of the first character in the tab body.\nconst foregroundColor = text.getForegroundColor(0);\n\n// Logs the foreground color to the console.\nconsole.log(foregroundColor);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Applies a link to the first 10 characters in the body.\nconst text = body.editAsText().setLinkUrl(0, 9, 'https://www.example.com/');\n\n// Gets the URL of the link from the first character.\nconst link = text.getLinkUrl(0);\n\n// Logs the link URL to the console.\nconsole.log(link);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the text alignment of the tab's body to NORMAL.\nconst text =\n body.editAsText().setTextAlignment(DocumentApp.TextAlignment.NORMAL);\n\n// Gets the text alignment of the ninth character.\nconst alignment = text.getTextAlignment(8);\n\n// Logs the text alignment to the console.\nconsole.log(alignment.toString());\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Gets the text indices at which text formatting changes.\nconst indices = body.editAsText().getTextAttributeIndices();\n\n// Logs the indices to the console.\nconsole.log(indices.toString());\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Obtain the first element in the active tab's body.\n\nconst firstChild = body.getChild(0);\n\n// Use getType() to determine the element's type.\nif (firstChild.getType() === DocumentApp.ElementType.PARAGRAPH) {\n Logger.log('The first element is a paragraph.');\n} else {\n Logger.log('The first element is not a paragraph.');\n}\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Inserts the text, 'Sample inserted text', at the start of the body content.\nconst text = body.editAsText().insertText(0, 'Sample inserted text');\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Bolds the first 4 characters in the tab body.\nconst text = body.editAsText().setBold(0, 3, true);\n\n// Gets whether or not the text is bold.\nconst bold = text.editAsText().isBold(0);\n\n// Logs the text's bold setting to the console\nconsole.log(bold);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the first 13 characters of the tab body to italic.\nconst text = body.editAsText().setItalic(0, 12, true);\n\n// Gets whether the fifth character in the tab body is set to\n// italic and logs it to the console.\nconst italic = text.isItalic(4);\nconsole.log(italic);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the first 17 characters of the tab body to strikethrough.\nconst text = body.editAsText().setStrikethrough(0, 16, true);\n\n// Gets whether the first character in the tab body is set to\n// strikethrough and logs it to the console.\nconst strikethrough = text.isStrikethrough(0);\nconsole.log(strikethrough);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the first 13 characters of the tab body to underline.\nconst text = body.editAsText().setUnderline(0, 12, false);\n\n// Gets whether the first character in the tab body is set to\n// underline and logs it to the console\nconst underline = text.editAsText().isUnderline(0);\nconsole.log(underline);\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Example 1: Merge paragraphs\n// Append two paragraphs to the document's active tab.\nconst par1 = body.appendParagraph('Paragraph 1.');\nconst par2 = body.appendParagraph('Paragraph 2.');\n// Merge the newly added paragraphs into a single paragraph.\npar2.merge();\n\n// Example 2: Merge table cells\n// Create a two-dimensional array containing the table's cell contents.\nconst cells = [\n ['Row 1, Cell 1', 'Row 1, Cell 2'],\n ['Row 2, Cell 1', 'Row 2, Cell 2'],\n];\n// Build a table from the array.\nconst table = body.appendTable(cells);\n// Get the first row in the table.\nconst row = table.getRow(0);\n// Get the two cells in this row.\nconst cell1 = row.getCell(0);\nconst cell2 = row.getCell(1);\n// Merge the current cell into its preceding sibling element.\nconst merged = cell2.merge();\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Remove all images in the active tab's body.\nconst imgs = body.getImages();\nfor (let i = 0; i < imgs.length; i++) {\n imgs[i].removeFromParent();\n}\n```\n\nExample:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Clear the text surrounding \"Apps Script\", with or without text.\nbody.replaceText('^.*Apps ?Script.*$', 'Apps Script');\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Declares style attributes for font size and font family.\nconst style = {};\nstyle[DocumentApp.Attribute.FONT_SIZE] = 20;\nstyle[DocumentApp.Attribute.FONT_FAMILY] = 'Impact';\n\n// Sets the style attributes to the first 9 characters in the tab's body.\nconst text = body.setAttributes(0, 8, style);\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Define a custom paragraph style.\nconst style = {};\nstyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =\n DocumentApp.HorizontalAlignment.RIGHT;\nstyle[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';\nstyle[DocumentApp.Attribute.FONT_SIZE] = 18;\nstyle[DocumentApp.Attribute.BOLD] = true;\n\n// Append a plain paragraph.\nconst par = body.appendParagraph('A paragraph with custom style.');\n\n// Apply the custom style.\npar.setAttributes(style);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the background color of the first 3 characters in the\n// tab body to hex color #0000FF.\nconst text = body.editAsText().setBackgroundColor(0, 2, '#0000FF');\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the first 11 characters in the tab's body to bold.\nconst text = body.editAsText().setBold(0, 10, true);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the font of the first 4 characters in the tab's body to Roboto.\nconst text = body.editAsText().setFontFamily(0, 3, 'Roboto');\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the size of the first 11 characters in the tab's body to 12.\nconst text = body.editAsText().setFontSize(0, 10, 12);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the foreground color of the first 2 characters in the\n// tab's body to hex color #FF0000.\nconst text = body.editAsText().setForegroundColor(0, 1, '#FF0000');\n\n// Gets the foreground color for the second character in the tab's body.\nconst foregroundColor = text.getForegroundColor(1);\n\n// Logs the foreground color to the console.\nconsole.log(foregroundColor);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the first 11 characters in the tab's body to italic.\nconst text = body.editAsText().setItalic(0, 10, true);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Applies a link to the first 11 characters in the body.\nconst text = body.editAsText().setLinkUrl(0, 10, 'https://example.com');\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the first 11 characters in the tab's body to strikethrough.\nconst text = body.editAsText().setStrikethrough(0, 10, true);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Replaces the contents of the body with the text, 'New body text.'\nconst text = body.editAsText().setText('New body text.');\n```\n\nExample:\n```text\n// Make the first character in the first paragraph of the active tab be\n// superscript.\nconst documentTab =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab();\nconst text = documentTab.getBody().getParagraphs()[0].editAsText();\ntext.setTextAlignment(0, 0, DocumentApp.TextAlignment.SUPERSCRIPT);\n```\n\nExample:\n```text\n// Make the entire first paragraph in the active tab be superscript.\nconst documentTab =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab();\nconst text = documentTab.getBody().getParagraphs()[0].editAsText();\ntext.setTextAlignment(DocumentApp.TextAlignment.SUPERSCRIPT);\n```\n\nExample:\n```text\n// Opens the Docs file by its URL. If you created your script from within a\n// Google Docs file, you can use DocumentApp.getActiveDocument() instead.\n// TODO(developer): Replace the URL with your own.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/DOCUMENT_ID/edit',\n);\n\n// Gets the body contents of the tab by its ID.\n// TODO(developer): Replace the ID with your own.\nconst body = doc.getTab('123abc').asDocumentTab().getBody();\n\n// Sets the first 11 characters in the tab's body to underline.\nconst text = body.editAsText().setUnderline(0, 10, true);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.430Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":36,"totalLines":694,"estimatedTokens":5748}}1156{"id":"doc-enum_fontfamily_apps_script_google_for_developer-67e8882e","source":"documentation","title":"Enum FontFamily | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/document/font-family","text":"Example:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Insert a paragraph at the start of the document.\nbody.insertParagraph(0, 'Hello, Apps Script!');\n\n// Set the tab font to Calibri.\nbody.editAsText().setFontFamily(DocumentApp.FontFamily.CALIBRI);\n\n// Set the first paragraph font to Arial.\nbody.getParagraphs()[0].setFontFamily(DocumentApp.FontFamily.ARIAL);\n\n// Set \"Apps Script\" to Comic Sans MS.\nconst text = 'Apps Script';\nconst a = body.getText().indexOf(text);\nconst b = a + text.length - 1;\nbody.editAsText().setFontFamily(a, b, DocumentApp.FontFamily.COMIC_SANS_MS);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.433Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":22,"estimatedTokens":163}}1157{"id":"doc-html_service_communicate_with_server_functions_a-b7929c05","source":"documentation","title":"HTML Service: Communicate with Server Functions | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/html/communication","text":"Example:\n```text\nfunction doGet() {\n return HtmlService.createHtmlOutputFromFile('Index');\n}\n\nfunction doSomething() {\n Logger.log('I was called!');\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <script>\n google.script.run.doSomething();\n </script>\n </head>\n</html>\n```\n\nExample:\n```text\nfunction doGet() {\n return HtmlService.createHtmlOutputFromFile('Index');\n}\n\nfunction getUnreadEmails() {\n return GmailApp.getInboxUnreadCount();\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <script>\n function onSuccess(numUnread) {\n var div = document.getElementById('output');\n div.innerHTML = 'You have ' + numUnread\n + ' unread messages in your Gmail inbox.';\n }\n\n google.script.run.withSuccessHandler(onSuccess)\n .getUnreadEmails();\n </script>\n </head>\n <body>\n <div id=\"output\"></div>\n </body>\n</html>\n```\n\nExample:\n```text\nfunction doGet() {\n return HtmlService.createHtmlOutputFromFile('Index');\n}\n\nfunction getUnreadEmails() {\n // 'got' instead of 'get' throws an error.\n return GmailApp.gotInboxUnreadCount();\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <script>\n function onFailure(error) {\n var div = document.getElementById('output');\n div.innerHTML = \"ERROR: \" + error.message;\n }\n\n google.script.run.withFailureHandler(onFailure)\n .getUnreadEmails();\n </script>\n </head>\n <body>\n <div id=\"output\"></div>\n </body>\n</html>\n```\n\nExample:\n```text\nfunction doGet() {\n return HtmlService.createHtmlOutputFromFile('Index');\n}\n\nfunction getEmail() {\n return Session.getActiveUser().getEmail();\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <script>\n function updateButton(email, button) {\n button.value = 'Clicked by ' + email;\n }\n </script>\n </head>\n <body>\n <input type=\"button\" value=\"Not Clicked\"\n onclick=\"google.script.run\n .withSuccessHandler(updateButton)\n .withUserObject(this)\n .getEmail()\" />\n <input type=\"button\" value=\"Not Clicked\"\n onclick=\"google.script.run\n .withSuccessHandler(updateButton)\n .withUserObject(this)\n .getEmail()\" />\n </body>\n</html>\n```\n\nExample:\n```text\nfunction doGet() {\n return HtmlService.createHtmlOutputFromFile('Index');\n}\n\nfunction processForm(formObject) {\n var formBlob = formObject.myFile;\n var driveFile = DriveApp.createFile(formBlob);\n return driveFile.getUrl();\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <script>\n // Prevent forms from submitting.\n function preventFormSubmit() {\n var forms = document.querySelectorAll('form');\n for (var i = 0; i < forms.length; i++) {\n forms[i].addEventListener('submit', function(event) {\n event.preventDefault();\n });\n }\n }\n window.addEventListener('load', preventFormSubmit);\n\n function handleFormSubmit(formObject) {\n google.script.run.withSuccessHandler(updateUrl).processForm(formObject);\n }\n function updateUrl(url) {\n var div = document.getElementById('output');\n div.innerHTML = '<a href=\"' + url + '\">Got it!</a>';\n }\n </script>\n </head>\n <body>\n <form id=\"myForm\" onsubmit=\"handleFormSubmit(this)\">\n <input name=\"myFile\" type=\"file\" />\n <input type=\"submit\" value=\"Submit\" />\n </form>\n <div id=\"output\"></div>\n </body>\n</html>\n```\n\nExample:\n```text\nvar myRunner = google.script.run.withFailureHandler(onFailure);\nvar myRunner1 = myRunner.withSuccessHandler(onSuccess);\nvar myRunner2 = myRunner.withSuccessHandler(onDifferentSuccess);\n\nmyRunner1.doSomething();\nmyRunner1.doSomethingElse();\nmyRunner2.doSomething();\n```\n\nExample:\n```text\nfunction doGet() {\n return HtmlService.createHtmlOutputFromFile('Index');\n}\n\nfunction getBankBalance() {\n var email = Session.getActiveUser().getEmail()\n return deepSecret_(email);\n}\n\nfunction deepSecret_(email) {\n // Do some secret calculations\n return email + ' has $1,000,000 in the bank.';\n}\n\nvar obj = {\n objectMethod: function() {\n // More secret calculations\n }\n};\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <script>\n function onSuccess(balance) {\n var div = document.getElementById('output');\n div.innerHTML = balance;\n }\n\n google.script.run.withSuccessHandler(onSuccess)\n .getBankBalance();\n </script>\n </head>\n <body>\n <div id=\"output\">No result yet...</div>\n </body>\n</html>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.434Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":13,"totalLines":237,"estimatedTokens":1171}}1158{"id":"doc-css_package_for_editor_add_ons_google_workspace_-c0cd002b","source":"documentation","title":"CSS package for Editor add-ons | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/gsuite/add-ons/guides/css","text":"Example:\n```text\n<link rel=\"stylesheet\" href=\"https://ssl.gstatic.com/docs/script/css/add-ons1.css\">\n```\n\nExample:\n```text\n<h1>Titles and headers</h1>\n<b>Bold text</b>\nNormal text\n<a href=\"\">Links</a>\n<span class=\"current\">Current navigation selection</span>\n<span class=\"error\">Form input errors</span>\n<span class=\"gray\">Gray text</span>\n<span class=\"secondary\">Secondary text</span>\n```\n\nExample:\n```text\n<button class=\"action\">Translate</button>\n```\n\nExample:\n```text\n<button class=\"create\">Create</button>\n```\n\nExample:\n```text\n<button class=\"share\">Share</button>\n```\n\nExample:\n```text\n<div class=\"block form-group\">\n <label for=\"select\">Select</label>\n <select id=\"select\">\n <option selected>Google Docs</option>\n <option>Google Forms</option>\n <option>Google Sheets</option>\n </select>\n</div>\n<div class=\"block form-group\">\n <label for=\"disabled-select\">Disabled select</label>\n <select id=\"disabled-select\" disabled>\n <option selected>Google Docs</option>\n <option>Google Forms</option>\n <option>Google Sheets</option>\n </select>\n</div>\n```\n\nExample:\n```text\n<div class=\"form-group\">\n <label for=\"sampleTextArea\">Label</label>\n <textarea id=\"sampleTextArea\" rows=\"3\"></textarea>\n</div>\n```\n\nExample:\n```text\n<div class=\"inline form-group\">\n <label for=\"city\">City</label>\n <input type=\"text\" id=\"city\" style=\"width: 150px;\">\n</div>\n<div class=\"inline form-group\">\n <label for=\"state\">State</label>\n <input type=\"text\" id=\"state\" style=\"width: 40px;\">\n</div>\n<div class=\"inline form-group\">\n <label for=\"zip-code\">Zip code</label>\n <input type=\"text\" id=\"zip-code\" style=\"width: 65px;\">\n</div>\n```\n\nExample:\n```text\n<style>\n.branding-below {\n bottom: 56px;\n top: 0;\n}\n</style>\n\n<div class=\"sidebar branding-below\">\n <div class=\"block form-group\">\n <label for=\"translated-text\">\n <b>Translation</b></label>\n <textarea id=\"translated-text\" rows=\"15\">\n </textarea>\n </div>\n\n <div class=\"block\">\n <input type=\"checkbox\" id=\"save-prefs\">\n <label for=\"save-prefs\">\n Use these languages by default</label>\n </div>\n\n <div class=\"block\">\n <button class=\"blue\">Translate</button>\n <button>Insert</button>\n </div>\n</div>\n\n<div class=\"sidebar bottom\">\n <span class=\"gray\">\n Translate sample by Google</span>\n</div>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.434Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":112,"estimatedTokens":576}}1159{"id":"doc-class_inlineimage_apps_script_google_for_develop-bd3d6bfd","source":"documentation","title":"Class InlineImage | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/document/inline-image","text":"Example:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Append a styled paragraph.\nconst par = body.appendParagraph('A bold, italicized paragraph.');\npar.setBold(true);\npar.setItalic(true);\n\n// Retrieve the paragraph's attributes.\nconst atts = par.getAttributes();\n\n// Log the paragraph attributes.\nfor (const att in atts) {\n Logger.log(`${att}:${atts[att]}`);\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Obtain the first element in the active tab's body.\n\nconst firstChild = body.getChild(0);\n\n// Use getType() to determine the element's type.\nif (firstChild.getType() === DocumentApp.ElementType.PARAGRAPH) {\n Logger.log('The first element is a paragraph.');\n} else {\n Logger.log('The first element is not a paragraph.');\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Example 1: Merge paragraphs\n// Append two paragraphs to the document's active tab.\nconst par1 = body.appendParagraph('Paragraph 1.');\nconst par2 = body.appendParagraph('Paragraph 2.');\n// Merge the newly added paragraphs into a single paragraph.\npar2.merge();\n\n// Example 2: Merge table cells\n// Create a two-dimensional array containing the table's cell contents.\nconst cells = [\n ['Row 1, Cell 1', 'Row 1, Cell 2'],\n ['Row 2, Cell 1', 'Row 2, Cell 2'],\n];\n// Build a table from the array.\nconst table = body.appendTable(cells);\n// Get the first row in the table.\nconst row = table.getRow(0);\n// Get the two cells in this row.\nconst cell1 = row.getCell(0);\nconst cell2 = row.getCell(1);\n// Merge the current cell into its preceding sibling element.\nconst merged = cell2.merge();\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Remove all images in the active tab's body.\nconst imgs = body.getImages();\nfor (let i = 0; i < imgs.length; i++) {\n imgs[i].removeFromParent();\n}\n```\n\nExample:\n```text\nconst doc = DocumentApp.getActiveDocument();\nconst documentTab = doc.getActiveTab().asDocumentTab();\nconst body = documentTab.getBody();\n\n// Define a custom paragraph style.\nconst style = {};\nstyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =\n DocumentApp.HorizontalAlignment.RIGHT;\nstyle[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';\nstyle[DocumentApp.Attribute.FONT_SIZE] = 18;\nstyle[DocumentApp.Attribute.BOLD] = true;\n\n// Append a plain paragraph.\nconst par = body.appendParagraph('A paragraph with custom style.');\n\n// Apply the custom style.\npar.setAttributes(style);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.439Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":5,"totalLines":103,"estimatedTokens":707}}1160{"id":"doc-class_htmloutput_apps_script_google_for_develope-b57e6300","source":"documentation","title":"Class HtmlOutput | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/html/html-output","text":"Example:\n```text\nfunction doGet() {\n return HtmlService.createHtmlOutput('<b>Hello, world!</b>');\n}\n```\n\nExample:\n```text\n<meta name=\"apple-mobile-web-app-capable\" content=\"...\"/>\n<meta name=\"google-site-verification\" content=\"...\"/>\n<meta name=\"mobile-web-app-capable\" content=\"...\"/>\n<meta name=\"viewport\" content=\"...\"/>\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.addMetaTag('viewport', 'width=device-width, initial-scale=1');\n```\n\nExample:\n```text\n// Log \"<b>Hello, world!</b><p>Hello again, world.</p>\"\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.append('<p>Hello again, world.</p>');\nLogger.log(output.getContent());\n```\n\nExample:\n```text\n// Log \"<b>Hello, world!</b><p>Hello again, world.</p>\"\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.appendUntrusted('<p>Hello again, world.</p>');\nLogger.log(output.getContent());\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\nconst template = output.asTemplate();\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.clear();\n```\n\nExample:\n```text\n// Log \"<b>Hello, world!</b>\"\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\nLogger.log(output.getContent());\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.setFaviconUrl('http://www.example.com/image.png');\nLogger.log(output.getFaviconUrl());\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.setHeight(200);\nLogger.log(output.getHeight());\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.addMetaTag('viewport', 'width=device-width, initial-scale=1');\n\nconst tags = output.getMetaTags();\nLogger.log(\n '<meta name=\"%s\" content=\"%s\"/>',\n tags[0].getName(),\n tags[0].getContent(),\n);\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\nLogger.log(output.getTitle());\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.setWidth(200);\nLogger.log(output.getWidth());\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput();\noutput.setContent('<b>Hello, world!</b>');\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.setFaviconUrl('http://www.example.com/image.png');\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.setHeight(200);\n```\n\nExample:\n```text\n<!-- Read the sandbox mode (in a client-side script). -->\n<script>\n alert(google.script.sandbox.mode);\n</script>\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.setTitle('My First Page');\n```\n\nExample:\n```text\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.setWidth(200);\n```\n\nExample:\n```text\n// Serve HTML with no X-Frame-Options header (in Apps Script server-side code).\nconst output = HtmlService.createHtmlOutput('<b>Hello, world!</b>');\noutput.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.440Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":20,"totalLines":142,"estimatedTokens":813}}1161{"id":"doc-class_documentapp_apps_script_google_for_develop-920b33ae","source":"documentation","title":"Class DocumentApp | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/document/document-app","text":"Example:\n```text\n// Open a document by ID.\n// TODO(developer): Replace the ID with your own.\nlet doc = DocumentApp.openById('DOCUMENT_ID');\n\n// Create and open a document.\ndoc = DocumentApp.create('Document Name');\n```\n\nExample:\n```text\n// Create and open a new document.\nconst doc = DocumentApp.create('Document Name');\n```\n\nExample:\n```text\n// Get the document to which this script is bound.\nconst doc = DocumentApp.getActiveDocument();\n```\n\nExample:\n```text\n// Add a custom menu to the active document, including a separator and a\n// sub-menu.\nfunction onOpen(e) {\n DocumentApp.getUi()\n .createMenu('My Menu')\n .addItem('My menu item', 'myFunction')\n .addSeparator()\n .addSubMenu(\n DocumentApp.getUi()\n .createMenu('My sub-menu')\n .addItem('One sub-menu item', 'mySecondFunction')\n .addItem('Another sub-menu item', 'myThirdFunction'),\n )\n .addToUi();\n}\n```\n\nExample:\n```text\n// Open a document by ID.\n// TODO(developer): Replace the ID with your own.\nconst doc = DocumentApp.openById('DOCUMENT_ID');\n```\n\nExample:\n```text\n// Open a document by URL.\nconst doc = DocumentApp.openByUrl(\n 'https://docs.google.com/document/d/1234567890abcdefghijklmnopqrstuvwxyz_a1b2c3/edit',\n);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.442Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":57,"estimatedTokens":321}}1162{"id":"doc-enum_paragraphheading_apps_script_google_for_dev-17ea8cb6","source":"documentation","title":"Enum ParagraphHeading | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/document/paragraph-heading","text":"Example:\n```text\nconst body =\n DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();\n\n// Append a paragraph, with heading 1.\nconst par1 = body.appendParagraph('Title');\npar1.setHeading(DocumentApp.ParagraphHeading.HEADING1);\n\n// Append a paragraph, with heading 2.\nconst par2 = body.appendParagraph('SubTitle');\npar2.setHeading(DocumentApp.ParagraphHeading.HEADING2);\n\n// Append a paragraph, with normal heading.\nconst par3 = body.appendParagraph('Text');\npar3.setHeading(DocumentApp.ParagraphHeading.NORMAL);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.443Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":19,"estimatedTokens":138}}1163{"id":"doc-class_datavalidation_apps_script_google_for_deve-c3fbf42b","source":"documentation","title":"Class DataValidation | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/spreadsheet/data-validation","text":"Example:\n```text\n// Log information about the data validation rule for cell A1.\nconst cell = SpreadsheetApp.getActive().getRange('A1');\nconst rule = cell.getDataValidation();\nif (rule != null) {\n const criteria = rule.getCriteriaType();\n const args = rule.getCriteriaValues();\n Logger.log('The data validation rule is %s %s', criteria, args);\n} else {\n Logger.log('The cell does not have a data validation rule.');\n}\n```\n\nExample:\n```text\n// Change existing data validation rules that require a date in 2013 to require\n// a date in 2014.\nconst oldDates = [new Date('1/1/2013'), new Date('12/31/2013')];\nconst newDates = [new Date('1/1/2014'), new Date('12/31/2014')];\nconst sheet = SpreadsheetApp.getActiveSheet();\nconst range = sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns());\nconst rules = range.getDataValidations();\n\nfor (let i = 0; i < rules.length; i++) {\n for (let j = 0; j < rules[i].length; j++) {\n const rule = rules[i][j];\n\n if (rule != null) {\n const criteria = rule.getCriteriaType();\n const args = rule.getCriteriaValues();\n\n if (criteria === SpreadsheetApp.DataValidationCriteria.DATE_BETWEEN &&\n args[0].getTime() === oldDates[0].getTime() &&\n args[1].getTime() === oldDates[1].getTime()) {\n // Create a builder from the existing rule, then change the dates.\n rules[i][j] = rule.copy().withCriteria(criteria, newDates).build();\n }\n }\n }\n}\nrange.setDataValidations(rules);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.444Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":45,"estimatedTokens":372}}1164{"id":"doc-xml_service_apps_script_google_for_developers-6e304950","source":"documentation","title":"XML Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/xml-service","text":"Example:\n```text\n// Log the title and labels for the first page of blog posts on\n// Google's The Keyword blog.\nfunction parseXml() {\n let url = 'https://blog.google/rss/';\n let xml = UrlFetchApp.fetch(url).getContentText();\n let document = XmlService.parse(xml);\n let root = document.getRootElement();\n\n let channel = root.getChild('channel');\n let items = channel.getChildren('item');\n items.forEach(item => {\n let title = item.getChild('title').getText();\n let categories = item.getChildren('category');\n let labels = categories.map(category => category.getText());\n console.log('%s (%s)', title, labels.join(', '));\n });\n}\n\n// Create and log an XML representation of first 10 threads in your Gmail inbox.\nfunction createXml() {\n let root = XmlService.createElement('threads');\n let threads = GmailApp.getInboxThreads()\n threads = threads.slice(0,10); // Just the first 10\n threads.forEach(thread => {\n let child = XmlService.createElement('thread')\n .setAttribute('messageCount', thread.getMessageCount())\n .setAttribute('isUnread', thread.isUnread())\n .setText(thread.getFirstMessageSubject());\n root.addContent(child);\n });\n let document = XmlService.createDocument(root);\n let xml = XmlService.getPrettyFormat().format(document);\n console.log(xml);\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.445Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":39,"estimatedTokens":333}}1165{"id":"doc-go_to_actions_gmail_google_for_developers-d440e5ec","source":"documentation","title":"Go-To Actions | Gmail | Google for Developers","url":"https://developers.google.com/gmail/markup/reference/go-to-action","text":"Example:\n```text\n<script type=\"application/ld+json\">\n{\n \"@context\": \"http://schema.org\",\n \"@type\": \"EmailMessage\",\n \"potentialAction\": {\n \"@type\": \"ViewAction\",\n \"url\": \"https://watch-movies.com/watch?movieId=abc123\",\n \"name\": \"Watch movie\"\n },\n \"description\": \"Watch the 'Avengers' movie online\"\n}\n</script>\n```\n\nExample:\n```text\n<script type=\"application/ld+json\">\n{\n \"@context\": \"http://schema.org\",\n \"@type\": \"EmailMessage\",\n \"description\": \"Watch the 'Avengers' movie online\",\n \"potentialAction\": {\n \"@type\": \"ViewAction\",\n \"url\": \"https://watch-movies.com/watch?movieId=abc123\",\n \"name\": \"Watch movie\"\n },\n \"publisher\": {\n \"@type\": \"Organization\",\n \"name\": \"Google Play\",\n \"url\": \"https://play.google.com\",\n \"url/googlePlus\": \"https://plus.google.com/106886664866983861036\"\n }\n}\n</script>\n```\n\nExample:\n```text\n<script type=\"application/ld+json\">\n{\n \"@context\": \"http://schema.org\",\n \"@type\": \"ParcelDelivery\",\n \"deliveryAddress\": {\n \"@type\": \"PostalAddress\",\n \"streetAddress\": \"24 Willie Mays Plaza\",\n \"addressLocality\": \"San Francisco\",\n \"addressRegion\": \"CA\",\n \"addressCountry\": \"US\",\n \"postalCode\": \"94107\"\n },\n \"expectedArrivalUntil\": \"2013-03-12T12:00:00-08:00\",\n \"carrier\": {\n \"@type\": \"Organization\",\n \"name\": \"FedEx\"\n },\n \"itemShipped\": {\n \"@type\": \"Product\",\n \"name\": \"iPod Mini\"\n },\n \"partOfOrder\": {\n \"@type\": \"Order\",\n \"orderNumber\": \"176057\",\n \"merchant\": {\n \"@type\": \"Organization\",\n \"name\": \"Bob Dole\"\n }\n },\n \"trackingUrl\": \"http://fedex.com/track/1234567890\"\n \"potentialAction\": {\n \"@type\": \"TrackAction\",\n \"target\": \"http://fedex.com/track/1234567890\"\n },\n}\n</script>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.447Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":79,"estimatedTokens":431}}1166{"id":"doc-built_in_google_services_apps_script_google_for_-cc3c0530","source":"documentation","title":"Built-in Google Services | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/built_in_services","text":"Example:\n```text\nGlobalObjectName.methodName(argument1, argument2, ..., argumentN);\n```\n\nExample:\n```text\nGmailApp.sendEmail('claire@example.com', 'Subject line', 'This is the body.');\n```\n\nExample:\n```text\nvar doc = DocumentApp.create('New document');\nvar body = doc.getTab('t.0').asDocumentTab().getBody();\nbody.appendParagraph('New paragraph.');\n\n// Same result as above.\nDocumentApp.create('New document').getTab('t.0').asDocumentTab().getBody()\n .appendParagraph('New paragraph.');\n```\n\nExample:\n```text\n// Creates a folder that anyone on the Internet can read from and write to.\n// (Domain administrators can prohibit this setting for Google Workspace users.)\nvar folder = DriveApp.createFolder('Shared Folder');\nfolder.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.448Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":30,"estimatedTokens":202}}1167{"id":"doc-authorization_for_google_services_apps_script_go-7870113c","source":"documentation","title":"Authorization for Google Services | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/scripts_google_accounts","text":"Example:\n```text\n/**\n * @OnlyCurrentDoc\n */\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.449Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":8,"estimatedTokens":16}}1168{"id":"doc-class_formtriggerbuilder_apps_script_google_for_-e268382a","source":"documentation","title":"Class FormTriggerBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/script/form-trigger-builder","text":"Example:\n```text\nconst form = FormApp.openById('1234567890abcdefghijklmnopqrstuvwxyz');\nScriptApp.newTrigger('myFunction').forForm(form).onFormSubmit().create();\n```\n\nExample:\n```text\nconst form = FormApp.getActiveForm();\nScriptApp.newTrigger('myFunction').forForm(form).onOpen().create();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.450Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":13,"estimatedTokens":77}}1169{"id":"doc-parcel_delivery_gmail_google_for_developers-f84ba523","source":"documentation","title":"Parcel Delivery | Gmail | Google for Developers","url":"https://developers.google.com/gmail/markup/reference/parcel-delivery","text":"Example:\n```text\n<script type=\"application/ld+json\">\n{\n \"@context\": \"http://schema.org\",\n \"@type\": \"ParcelDelivery\",\n \"deliveryAddress\": {\n \"@type\": \"PostalAddress\",\n \"name\": \"Pickup Corner\",\n \"streetAddress\": \"24 Willie Mays Plaza\",\n \"addressLocality\": \"San Francisco\",\n \"addressRegion\": \"CA\",\n \"addressCountry\": \"US\",\n \"postalCode\": \"94107\"\n },\n \"expectedArrivalUntil\": \"2027-03-12T12:00:00-08:00\",\n \"carrier\": {\n \"@type\": \"Organization\",\n \"name\": \"FedEx\"\n },\n \"itemShipped\": {\n \"@type\": \"Product\",\n \"name\": \"Google Chromecast\"\n },\n \"partOfOrder\": {\n \"@type\": \"Order\",\n \"orderNumber\": \"176057\",\n \"merchant\": {\n \"@type\": \"Organization\",\n \"name\": \"Bob Dole\"\n }\n }\n}\n</script>\n```\n\nExample:\n```text\n<script type=\"application/ld+json\">\n{\n \"@context\": \"http://schema.org\",\n \"@type\": \"ParcelDelivery\",\n \"deliveryAddress\": {\n \"@type\": \"PostalAddress\",\n \"name\": \"John Frank\",\n \"streetAddress\": \"24 Willie Mays Plaza\",\n \"addressLocality\": \"San Francisco\",\n \"addressRegion\": \"CA\",\n \"addressCountry\": \"US\",\n \"postalCode\": \"94107\"\n },\n \"originAddress\": {\n \"@type\": \"PostalAddress\",\n \"name\": \"John Frank\",\n \"streetAddress\": \"25 Willie Mays Plaza\",\n \"addressLocality\": \"San Francisco\",\n \"addressRegion\": \"CA\",\n \"addressCountry\": \"US\",\n \"postalCode\": \"94107\"\n },\n \"expectedArrivalFrom\": \"2027-03-10T12:00:00-08:00\",\n \"expectedArrivalUntil\": \"2027-03-12T12:00:00-08:00\",\n \"carrier\": {\n \"@type\": \"Organization\",\n \"name\": \"FedEx\",\n \"url\": \"http://fedex.com/\"\n },\n \"itemShipped\": {\n \"@type\": \"Product\",\n \"name\": \"iPod Mini\",\n \"url\": \"http://apple.com/ipad32gb\",\n \"image\": \"http://apple.com/images/ipad32gb.jpg\",\n \"sku\": \"B00DR0PDNE\",\n \"description\": \"iPod Mini 32Gb White\",\n \"brand\": {\n \"@type\": \"Brand\",\n \"name\": \"Apple\"\n },\n \"color\": \"white\"\n },\n \"trackingNumber\": \"3453291231\",\n \"trackingUrl\": \"http://fedex.com/track/3453291231\",\n \"potentialAction\": {\n \"@type\": \"TrackAction\",\n \"url\": \"http://fedex.com/track/3453291231\"\n },\n \"hasDeliveryMethod\": {\n \"@type\": \"ParcelService\",\n \"name\": \"http://schema.org/ParcelService\"\n },\n \"partOfOrder\": {\n \"@type\": \"Order\",\n \"orderNumber\": \"176057\",\n \"merchant\": {\n \"@type\": \"Organization\",\n \"name\": \"Bob Dole\",\n \"sameAs\": \"http://www.freebase.com/m/0fhkx\"\n },\n \"orderStatus\": \"http://schema.org/OrderInTransit\"\n }\n}\n</script>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.451Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":105,"estimatedTokens":621}}1170{"id":"doc-order_gmail_google_for_developers-1561d7a0","source":"documentation","title":"Order | Gmail | Google for Developers","url":"https://developers.google.com/gmail/markup/reference/order","text":"Example:\n```text\n<script type=\"application/ld+json\">\n{\n \"@context\": \"http://schema.org\",\n \"@type\": \"Order\",\n \"merchant\": {\n \"@type\": \"Organization\",\n \"name\": \"Amazon.com\"\n },\n \"orderNumber\": \"123-4567890-1234567\",\n \"priceCurrency\": \"USD\",\n \"price\": \"29.99\",\n \"acceptedOffer\": {\n \"@type\": \"Offer\",\n \"itemOffered\": {\n \"@type\": \"Product\",\n \"name\": \"Google Chromecast\"\n },\n \"price\": \"29.99\",\n \"priceCurrency\": \"USD\",\n \"eligibleQuantity\": {\n \"@type\": \"QuantitativeValue\",\n \"value\": \"1\"\n }\n }\n}\n</script>\n```\n\nExample:\n```text\n<script type=\"application/ld+json\">\n{\n \"@context\": \"http://schema.org\",\n \"@type\": \"Order\",\n \"merchant\": {\n \"@type\": \"Organization\",\n \"name\": \"Amazon.com\"\n },\n \"orderNumber\": \"123-4567890-1234567\",\n \"orderStatus\": \"http://schema.org/OrderProcessing\",\n \"priceCurrency\": \"USD\",\n \"price\": \"29.99\",\n \"priceSpecification\": {\n \"@type\": \"PriceSpecification\",\n \"validFrom\": \"2027-12-07T23:30:00-08:00\"\n },\n \"acceptedOffer\": {\n \"@type\": \"Offer\",\n \"itemOffered\": {\n \"@type\": \"Product\",\n \"name\": \"Google Chromecast\",\n \"sku\": \"B00DR0PDNE\",\n \"url\": \"http://www.amazon.com/Google-Chromecast-Streaming-Media-Player/dp/B00DR0PDNE/\",\n \"image\": \"http://ecx.images-amazon.com/images/I/811nvG%2BLgML._SY550_.jpg\"\n },\n \"price\": \"29.99\",\n \"priceCurrency\": \"USD\",\n \"eligibleQuantity\": {\n \"@type\": \"QuantitativeValue\",\n \"value\": \"1\"\n }\n },\n \"url\": \"https://www.amazon.ca/gp/css/summary/edit.html/orderID=123-4567890-1234567\",\n \"potentialAction\": {\n \"@type\": \"ViewAction\",\n \"url\": \"https://www.amazon.ca/gp/css/summary/edit.html/orderID=123-4567890-1234567\"\n }\n}\n</script>\n```\n\nExample:\n```text\n<script type=\"application/ld+json\">\n{\n \"@context\": \"http://schema.org\",\n \"@type\": \"Order\",\n \"merchant\": {\n \"@type\": \"Organization\",\n \"name\": \"Amazon.com\"\n },\n \"orderNumber\": \"123-4567890-1234567\",\n \"priceCurrency\": \"USD\",\n \"price\": \"539.00\",\n \"priceSpecification\": {\n \"@type\": \"PriceSpecification\",\n \"validFrom\": \"2027-12-07T23:30:00-08:00\"\n },\n \"acceptedOffer\": [\n {\n \"@type\": \"Offer\",\n \"itemOffered\": {\n \"@type\": \"Product\",\n \"name\": \"Samsung Chromebook\",\n \"sku\": \"B009LL9VDG\",\n \"url\": \"http://www.amazon.com/Samsung-XE303C12-A01US-Chromebook-Wi-Fi-11-6-Inch/dp/B009LL9VDG/\",\n \"image\": \"http://ecx.images-amazon.com/images/I/81H-DO3qX0L._SX522_.jpg\"\n },\n \"price\": \"249.99\",\n \"priceCurrency\": \"USD\",\n \"eligibleQuantity\": {\n \"@type\": \"QuantitativeValue\",\n \"value\": \"2\"\n },\n \"seller\": {\n \"@type\": \"Organization\",\n \"name\": \"Samsung Marketplace Store\"\n }\n },\n {\n \"@type\": \"Offer\",\n \"itemOffered\": {\n \"@type\": \"Product\",\n \"name\": \"Google Chromecast\",\n \"sku\": \"B00DR0PDNE\",\n \"url\": \"http://www.amazon.com/Google-Chromecast-Streaming-Media-Player/dp/B00DR0PDNE/\",\n \"image\": \"http://ecx.images-amazon.com/images/I/811nvG%2BLgML._SY550_.jpg\"\n },\n \"price\": \"29.99\",\n \"priceCurrency\": \"USD\",\n \"eligibleQuantity\": {\n \"@type\": \"QuantitativeValue\",\n \"value\": \"1\"\n },\n \"seller\": {\n \"@type\": \"Organization\",\n \"name\": \"Google Store @ Amazon\"\n }\n }\n ],\n \"url\": \"https://www.amazon.ca/gp/css/summary/edit.html/orderID=123-4567890-1234567\",\n \"potentialAction\": {\n \"@type\": \"ViewAction\",\n \"url\": \"https://www.amazon.ca/gp/css/summary/edit.html/orderID=123-4567890-1234567\"\n },\n \"orderStatus\": \"http://schema.org/OrderProcessing\",\n \"paymentMethod\": {\n \"@type\": \"PaymentMethod\",\n \"name\": \"http://schema.org/CreditCard\"\n },\n \"paymentMethodId\": \"**** **** **** 1234\",\n \"orderDate\": \"2027-11-07T23:30:00-08:00\",\n \"isGift\": \"false\",\n \"discount\": \"0.97\",\n \"discountCurrency\": \"USD\",\n \"customer\": {\n \"@type\": \"Person\",\n \"name\": \"John Smith\"\n },\n \"billingAddress\": {\n \"@type\": \"PostalAddress\",\n \"name\": \"Google\",\n \"streetAddress\": \"1600 Amphitheatre Pkwy\",\n \"addressLocality\": \"Mountain View\",\n \"addressRegion\": \"CA\",\n \"addressCountry\": \"USA\"\n }\n}\n</script>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.453Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":164,"estimatedTokens":1046}}1171{"id":"doc-enum_mimetype_apps_script_google_for_developers-6fcee03d","source":"documentation","title":"Enum MimeType | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/base/mime-type","text":"Example:\n```text\n// Use MimeType enum to log the name of every Google Doc in the user's Drive.\nconst docs = DriveApp.getFilesByType(MimeType.GOOGLE_DOCS);\nwhile (docs.hasNext()) {\n const doc = docs.next();\n Logger.log(doc.getName());\n}\n\n// Use plain string to log the size of every PNG in the user's Drive.\nconst pngs = DriveApp.getFilesByType('image/png');\nwhile (pngs.hasNext()) {\n const png = pngs.next();\n Logger.log(png.getSize());\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.455Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":18,"estimatedTokens":115}}1172{"id":"doc-class_propertiesservice_apps_script_google_for_d-cba077a8","source":"documentation","title":"Class PropertiesService | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/properties/properties-service","text":"Example:\n```text\n// Sets three properties of different types.\nconst documentProperties = PropertiesService.getDocumentProperties();\nconst scriptProperties = PropertiesService.getScriptProperties();\nconst userProperties = PropertiesService.getUserProperties();\n\ndocumentProperties.setProperty('DAYS_TO_FETCH', '5');\nscriptProperties.setProperty(\n 'SERVER_URL',\n 'http://www.example.com/MyWeatherService/',\n);\nuserProperties.setProperty('DISPLAY_UNITS', 'metric');\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.461Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":16,"estimatedTokens":122}}1173{"id":"doc-class_documenttriggerbuilder_apps_script_google_-33db2eae","source":"documentation","title":"Class DocumentTriggerBuilder | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/script/document-trigger-builder","text":"Example:\n```text\nconst document = DocumentApp.getActiveDocument();\nScriptApp.newTrigger('myFunction').forDocument(document).onOpen().create();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.462Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":1,"totalLines":7,"estimatedTokens":40}}1174{"id":"doc-class_cacheservice_apps_script_google_for_develo-80ac5f31","source":"documentation","title":"Class CacheService | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/cache/cache-service","text":"Example:\n```text\n// Gets a cache that is specific to the current document containing the script\nconst cache = CacheService.getDocumentCache();\n```\n\nExample:\n```text\n// Gets a cache that is common to all users of the script\nconst cache = CacheService.getScriptCache();\n```\n\nExample:\n```text\n// Gets a cache that is specific to the current user of the script\nconst cache = CacheService.getUserCache();\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.463Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":19,"estimatedTokens":105}}1175{"id":"doc-dialogs_and_sidebars_in_google_workspace_documen-15f8a827","source":"documentation","title":"Dialogs and sidebars in Google Workspace documents | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/guides/dialogs","text":"Example:\n```text\nfunction onOpen() {\n SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.\n .createMenu(\"Custom Menu\")\n .addItem(\"Show alert\", \"showAlert\")\n .addToUi();\n}\n\nfunction showAlert() {\n const ui = SpreadsheetApp.getUi(); // Same variations.\n\n const result = ui.alert(\n \"Please confirm\",\n \"Are you sure you want to continue?\",\n ui.ButtonSet.YES_NO,\n );\n\n // Process the user's response.\n if (result === ui.Button.YES) {\n // User clicked \"Yes\".\n ui.alert(\"Confirmation received.\");\n } else {\n // User clicked \"No\" or X in the title bar.\n ui.alert(\"Permission denied.\");\n }\n}\n```\n\nExample:\n```text\nfunction onOpen() {\n SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.\n .createMenu(\"Custom Menu\")\n .addItem(\"Show prompt\", \"showPrompt\")\n .addToUi();\n}\n\nfunction showPrompt() {\n const ui = SpreadsheetApp.getUi(); // Same variations.\n\n const result = ui.prompt(\n \"Let's get to know each other!\",\n \"Please enter your name:\",\n ui.ButtonSet.OK_CANCEL,\n );\n\n // Process the user's response.\n const button = result.getSelectedButton();\n const text = result.getResponseText();\n if (button === ui.Button.OK) {\n // User clicked \"OK\".\n ui.alert(\"Your name is \" + text + \".\");\n } else if (button === ui.Button.CANCEL) {\n // User clicked \"Cancel\".\n ui.alert(\"I didn't get your name.\");\n } else if (button === ui.Button.CLOSE) {\n // User clicked X in the title bar.\n ui.alert(\"You closed the dialog.\");\n }\n}\n```\n\nExample:\n```text\nfunction showToast() {\n SpreadsheetApp.getActiveSpreadsheet().toast(\"Task completed successfully.\");\n}\n```\n\nExample:\n```text\nfunction onOpen() {\n SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.\n .createMenu('Custom Menu')\n .addItem('Show dialog', 'showDialog')\n .addToUi();\n}\n\nfunction showDialog() {\n const html = HtmlService.createHtmlOutputFromFile('Page')\n .setWidth(400)\n .setHeight(300);\n SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.\n .showModalDialog(html, 'My custom dialog');\n}\n```\n\nExample:\n```text\nHello, world! <input type=\"button\" value=\"Close\" onclick=\"google.script.host.close()\" />\n```\n\nExample:\n```text\nfunction onOpen() {\n SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.\n .createMenu('Custom Menu')\n .addItem('Show sidebar', 'showSidebar')\n .addToUi();\n}\n\nfunction showSidebar() {\n const html = HtmlService.createHtmlOutputFromFile('Page')\n .setTitle('My custom sidebar');\n SpreadsheetApp.getUi() // Or DocumentApp or SlidesApp or FormApp.\n .showSidebar(html);\n}\n```\n\nExample:\n```text\n/**\n * Creates a custom menu in Google Sheets when the spreadsheet opens.\n */\nfunction onOpen() {\n SpreadsheetApp.getUi()\n .createMenu(\"Picker\")\n .addItem(\"Start\", \"showPicker\")\n .addToUi();\n}\n\n/**\n * Displays an HTML-service dialog in Google Sheets that contains client-side\n * JavaScript code for the Google Picker API.\n */\nfunction showPicker() {\n const html = HtmlService.createHtmlOutputFromFile(\"dialog.html\")\n .setWidth(800)\n .setHeight(600)\n .setSandboxMode(HtmlService.SandboxMode.IFRAME);\n SpreadsheetApp.getUi().showModalDialog(html, \"Select a file\");\n}\n// Ensure the Drive API is enabled.\nif (!Drive) {\n throw new Error(\"Please enable the Drive advanced service.\");\n}\n\n/**\n * Checks that the file can be accessed.\n * @param {string} fileId The ID of the file.\n * @return {Object} The file resource.\n */\nfunction getFile(fileId) {\n return Drive.Files.get(fileId, { fields: \"*\" });\n}\n\n/**\n * Gets the user's OAuth 2.0 access token so that it can be passed to Picker.\n * This technique keeps Picker from needing to show its own authorization\n * dialog, but is only possible if the OAuth scope that Picker needs is\n * available in Apps Script. In this case, the function includes an unused call\n * to a DriveApp method to ensure that Apps Script requests access to all files\n * in the user's Drive.\n *\n * @return {string} The user's OAuth 2.0 access token.\n */\nfunction getOAuthToken() {\n return ScriptApp.getOAuthToken();\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <link\n rel=\"stylesheet\"\n href=\"https://ssl.gstatic.com/docs/script/css/add-ons.css\"\n />\n <style>\n #result {\n display: flex;\n flex-direction: column;\n gap: 0.25em;\n }\n\n pre {\n font-size: x-small;\n max-height: 25vh;\n overflow-y: scroll;\n background: #eeeeee;\n padding: 1em;\n border: 1px solid #cccccc;\n }\n </style>\n <script>\n // TODO: Replace the value for DEVELOPER_KEY with the API key obtained\n // from the Google Developers Console.\n const DEVELOPER_KEY = \"AIza...\";\n // TODO: Replace the value for CLOUD_PROJECT_NUMBER with the project\n // number obtained from the Google Developers Console.\n const CLOUD_PROJECT_NUMBER = \"1234567890\";\n\n let pickerApiLoaded = false;\n let oauthToken;\n\n /**\n * Loads the Google Picker API.\n */\n function onApiLoad() {\n gapi.load(\"picker\", {\n callback: function () {\n pickerApiLoaded = true;\n },\n });\n }\n\n /**\n * Gets the user's OAuth 2.0 access token from the server-side script so that\n * it can be passed to Picker. This technique keeps Picker from needing to\n * show its own authorization dialog, but is only possible if the OAuth scope\n * that Picker needs is available in Apps Script. Otherwise, your Picker code\n * will need to declare its own OAuth scopes.\n */\n function getOAuthToken() {\n google.script.run\n .withSuccessHandler((token) => {\n oauthToken = token;\n createPicker(token);\n })\n .withFailureHandler(showError)\n .getOAuthToken();\n }\n\n /**\n * Creates a Picker that can access the user's spreadsheets. This function\n * uses advanced options to hide the Picker's left navigation panel and\n * default title bar.\n *\n * @param {string} token An OAuth 2.0 access token that lets Picker access the\n * file type specified in the addView call.\n */\n function createPicker(token) {\n document.getElementById(\"result\").innerHTML = \"\";\n\n if (pickerApiLoaded && token) {\n const picker = new google.picker.PickerBuilder()\n // Instruct Picker to display only spreadsheets in Drive. For other\n // views, see https://developers.google.com/picker/reference/picker.viewid\n .addView(\n new google.picker.DocsView(\n google.picker.ViewId.SPREADSHEETS\n ).setOwnedByMe(true)\n )\n // Hide the navigation panel so that Picker fills more of the dialog.\n .enableFeature(google.picker.Feature.NAV_HIDDEN)\n // Hide the title bar since an Apps Script dialog already has a title.\n .hideTitleBar()\n .setOAuthToken(token)\n .setDeveloperKey(DEVELOPER_KEY)\n .setAppId(CLOUD_PROJECT_NUMBER)\n .setCallback(pickerCallback)\n .setOrigin(google.script.host.origin)\n .build();\n picker.setVisible(true);\n } else {\n showError(\"Unable to load the file picker.\");\n }\n }\n\n /**\n * @typedef {Object} PickerResponse\n * @property {string} action\n * @property {PickerDocument[]} docs\n */\n\n /**\n * @typedef {Object} PickerDocument\n * @property {string} id\n * @property {string} name\n * @property {string} mimeType\n * @property {string} url\n * @property {string} lastEditedUtc\n */\n\n /**\n * A callback function that extracts the chosen document's metadata from the\n * response object. For details on the response object, see\n * https://developers.google.com/picker/reference/picker.responseobject\n *\n * @param {PickerResponse} data The response object.\n */\n function pickerCallback(data) {\n const action = data[google.picker.Response.ACTION];\n if (action == google.picker.Action.PICKED) {\n handlePicked(data);\n } else if (action == google.picker.Action.CANCEL) {\n document.getElementById(\"result\").innerHTML = \"Picker canceled.\";\n }\n }\n\n /**\n * Handles `\"PICKED\"` responsed from the Google Picker.\n *\n * @param {PickerResponse} data The response object.\n */\n function handlePicked(data) {\n const doc = data[google.picker.Response.DOCUMENTS][0];\n const id = doc[google.picker.Document.ID];\n\n google.script.run\n .withSuccessHandler((driveFilesGetResponse) => {\n // Render the response from Picker and the Drive.Files.Get API.\n const resultElement = document.getElementById(\"result\");\n resultElement.innerHTML = \"\";\n\n for (const response of [\n {\n title: \"Picker response\",\n content: JSON.stringify(data, null, 2),\n },\n {\n title: \"Drive.Files.Get response\",\n content: JSON.stringify(driveFilesGetResponse, null, 2),\n },\n ]) {\n const titleElement = document.createElement(\"h3\");\n titleElement.appendChild(document.createTextNode(response.title));\n resultElement.appendChild(titleElement);\n\n const contentElement = document.createElement(\"pre\");\n contentElement.appendChild(\n document.createTextNode(response.content)\n );\n resultElement.appendChild(contentElement);\n }\n })\n .withFailureHandler(showError)\n .getFile(data[google.picker.Response.DOCUMENTS][0].id);\n }\n\n /**\n * Displays an error message within the #result element.\n *\n * @param {string} message The error message to display.\n */\n function showError(message) {\n document.getElementById(\"result\").innerHTML = \"Error: \" + message;\n }\n </script>\n </head>\n\n <body>\n <div>\n <button onclick=\"getOAuthToken()\">Select a file</button>\n <div id=\"result\"></div>\n </div>\n <script src=\"https://apis.google.com/js/api.js?onload=onApiLoad\"></script>\n </body>\n</html>\n```\n\nExample:\n```text\n{\n \"timeZone\": \"America/Los_Angeles\",\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\",\n \"oauthScopes\": [\n \"https://www.googleapis.com/auth/script.container.ui\",\n \"https://www.googleapis.com/auth/drive.file\"\n ],\n \"dependencies\": {\n \"enabledAdvancedServices\": [\n {\n \"userSymbol\": \"Drive\",\n \"version\": \"v3\",\n \"serviceId\": \"drive\"\n }\n ]\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.465Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":9,"totalLines":377,"estimatedTokens":2720}}1176{"id":"doc-admin_sdk_reports_service_apps_script_google_for-85883230","source":"documentation","title":"Admin SDK Reports Service | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/advanced/admin-sdk-reports","text":"Example:\n```text\n/**\n * Generates a login activity report for the last week as a spreadsheet. The\n * report includes the time, user, and login result.\n * @see https://developers.google.com/admin-sdk/reports/reference/rest/v1/activities/list\n */\nfunction generateLoginActivityReport() {\n const now = new Date();\n const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);\n const startTime = oneWeekAgo.toISOString();\n const endTime = now.toISOString();\n\n const rows = [];\n let pageToken;\n let page;\n do {\n page = AdminReports.Activities.list(\"all\", \"login\", {\n startTime: startTime,\n endTime: endTime,\n maxResults: 500,\n pageToken: pageToken,\n });\n const items = page.items;\n if (items) {\n for (const item of items) {\n const row = [\n new Date(item.id.time),\n item.actor.email,\n item.events[0].name,\n ];\n rows.push(row);\n }\n }\n pageToken = page.nextPageToken;\n } while (pageToken);\n\n if (rows.length === 0) {\n console.log(\"No results returned.\");\n return;\n }\n const spreadsheet = SpreadsheetApp.create(\"Google Workspace Login Report\");\n const sheet = spreadsheet.getActiveSheet();\n\n // Append the headers.\n const headers = [\"Time\", \"User\", \"Login Result\"];\n sheet.appendRow(headers);\n\n // Append the results.\n sheet.getRange(2, 1, rows.length, headers.length).setValues(rows);\n\n console.log(\"Report spreadsheet created: %s\", spreadsheet.getUrl());\n}\n```\n\nExample:\n```text\n/**\n * Generates a user usage report for this day last week as a spreadsheet. The\n * report includes the date, user, last login time, number of emails received,\n * and number of drive files created.\n * @see https://developers.google.com/admin-sdk/reports/reference/rest/v1/userUsageReport/get\n */\nfunction generateUserUsageReport() {\n const today = new Date();\n const oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);\n const timezone = Session.getScriptTimeZone();\n const date = Utilities.formatDate(oneWeekAgo, timezone, \"yyyy-MM-dd\");\n\n const parameters = [\n \"accounts:last_login_time\",\n \"gmail:num_emails_received\",\n \"drive:num_items_created\",\n ];\n const rows = [];\n let pageToken;\n let page;\n do {\n page = AdminReports.UserUsageReport.get(\"all\", date, {\n parameters: parameters.join(\",\"),\n maxResults: 500,\n pageToken: pageToken,\n });\n if (page.warnings) {\n for (const warning of page.warnings) {\n console.log(warning.message);\n }\n }\n const reports = page.usageReports;\n if (reports) {\n for (const report of reports) {\n const parameterValues = getParameterValues(report.parameters);\n const row = [\n report.date,\n report.entity.userEmail,\n parameterValues[\"accounts:last_login_time\"],\n parameterValues[\"gmail:num_emails_received\"],\n parameterValues[\"drive:num_items_created\"],\n ];\n rows.push(row);\n }\n }\n pageToken = page.nextPageToken;\n } while (pageToken);\n\n if (rows.length === 0) {\n console.log(\"No results returned.\");\n return;\n }\n const spreadsheet = SpreadsheetApp.create(\n \"Google Workspace User Usage Report\",\n );\n const sheet = spreadsheet.getActiveSheet();\n\n // Append the headers.\n const headers = [\n \"Date\",\n \"User\",\n \"Last Login\",\n \"Num Emails Received\",\n \"Num Drive Files Created\",\n ];\n sheet.appendRow(headers);\n\n // Append the results.\n sheet.getRange(2, 1, rows.length, headers.length).setValues(rows);\n\n console.log(\"Report spreadsheet created: %s\", spreadsheet.getUrl());\n}\n\n/**\n * Gets a map of parameter names to values from an array of parameter objects.\n * @param {Array} parameters An array of parameter objects.\n * @return {Object} A map from parameter names to their values.\n */\nfunction getParameterValues(parameters) {\n return parameters.reduce((result, parameter) => {\n const name = parameter.name;\n let value;\n if (parameter.intValue !== undefined) {\n value = parameter.intValue;\n } else if (parameter.stringValue !== undefined) {\n value = parameter.stringValue;\n } else if (parameter.datetimeValue !== undefined) {\n value = new Date(parameter.datetimeValue);\n } else if (parameter.boolValue !== undefined) {\n value = parameter.boolValue;\n }\n result[name] = value;\n return result;\n }, {});\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.466Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":155,"estimatedTokens":1102}}1177{"id":"doc-event_objects_apps_script_google_for_developers-e44f7f55","source":"documentation","title":"Event Objects | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/understanding_events","text":"Example:\n```text\nfunction onEdit(e){\n // Set a comment on the edited cell to indicate when it was changed.\n var range = e.range;\n range.setNote('Last modified: ' + new Date());\n}\n```\n\nExample:\n```text\nLIMITED\n```\n\nExample:\n```text\nSpreadsheet\n```\n\nExample:\n```text\n4034124084959907503\n```\n\nExample:\n```text\namin@example.com\n```\n\nExample:\n```text\nFULL\n```\n\nExample:\n```text\nINSERT_ROW\n```\n\nExample:\n```text\n1234\n```\n\nExample:\n```text\nRange\n```\n\nExample:\n```text\n10\n```\n\nExample:\n```text\n{\n 'First Name': ['Jane'],\n 'Timestamp': ['6/7/2015 20:54:13'],\n 'Last Name': ['Doe']\n}\n```\n\nExample:\n```text\n['2015/05/04 15:00', 'amin@example.com', 'Bob', '27', 'Bill',\n'28', 'Susan', '25']\n```\n\nExample:\n```text\nDocument\n```\n\nExample:\n```text\nPresentation\n```\n\nExample:\n```text\nForm\n```\n\nExample:\n```text\nFormResponse\n```\n\nExample:\n```text\nsusan@example.com\n```\n\nExample:\n```text\n31\n```\n\nExample:\n```text\n7\n```\n\nExample:\n```text\n23\n```\n\nExample:\n```text\n59\n```\n\nExample:\n```text\n12\n```\n\nExample:\n```text\nUTC\n```\n\nExample:\n```text\n52\n```\n\nExample:\n```text\n2015\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.468Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":25,"totalLines":135,"estimatedTokens":268}}1178{"id":"doc-simple_triggers_apps_script_google_for_developer-5aadff48","source":"documentation","title":"Simple Triggers | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/understanding_triggers","text":"Example:\n```text\n/**\n * The event handler triggered when opening the spreadsheet.\n * @param {Event} e The onOpen event.\n * @see https://developers.google.com/apps-script/guides/triggers#onopene\n */\nfunction onOpen(e) {\n // Add a custom menu to the spreadsheet.\n SpreadsheetApp.getUi() // Or DocumentApp, SlidesApp, or FormApp.\n .createMenu(\"Custom Menu\")\n .addItem(\"First item\", \"menuItem1\")\n .addToUi();\n}\n```\n\nExample:\n```text\n/**\n * The event handler triggered when installing the add-on.\n * @param {Event} e The onInstall event.\n * @see https://developers.google.com/apps-script/guides/triggers#oninstalle\n */\nfunction onInstall(e) {\n onOpen(e);\n}\n```\n\nExample:\n```text\n/**\n * The event handler triggered when editing the spreadsheet.\n * @param {Event} e The onEdit event.\n * @see https://developers.google.com/apps-script/guides/triggers#onedite\n */\nfunction onEdit(e) {\n // Set a comment on the edited cell to indicate when it was changed.\n const range = e.range;\n range.setNote(`Last modified: ${new Date()}`);\n}\n```\n\nExample:\n```text\n/**\n * The event handler triggered when the selection changes in the spreadsheet.\n * @param {Event} e The onSelectionChange event.\n * @see https://developers.google.com/apps-script/guides/triggers#onselectionchangee\n */\nfunction onSelectionChange(e) {\n // Set background to red if a single empty cell is selected.\n const range = e.range;\n if (\n range.getNumRows() === 1 &&\n range.getNumColumns() === 1 &&\n range.getCell(1, 1).getValue() === \"\"\n ) {\n range.setBackground(\"red\");\n }\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.469Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":63,"estimatedTokens":394}}1179{"id":"doc-editor_add_on_authorization_google_workspace_add-fbb8368a","source":"documentation","title":"Editor add-on authorization | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/gsuite/add-ons/concepts/addon-authorization","text":"Example:\n```text\nfunction onInstall(e) {\n onOpen(e);\n // Perform additional setup as needed.\n}\n```\n\nExample:\n```text\nfunction onOpen(e) {\n SpreadsheetApp.getUi().createAddonMenu() // Or DocumentApp.\n .addItem('Insert chart', 'insertChart')\n .addItem('Update charts', 'updateCharts')\n .addToUi();\n}\n```\n\nExample:\n```text\nfunction onOpen(e) {\n var menu = SpreadsheetApp.getUi().createAddonMenu(); // Or DocumentApp.\n if (e && e.authMode == ScriptApp.AuthMode.NONE) {\n // Add a normal menu item (works in all authorization modes).\n menu.addItem('Start workflow', 'startWorkflow');\n } else {\n // Add a menu item based on properties (doesn't work in AuthMode.NONE).\n var properties = PropertiesService.getDocumentProperties();\n var workflowStarted = properties.getProperty('workflowStarted');\n if (workflowStarted) {\n menu.addItem('Check workflow status', 'checkWorkflow');\n } else {\n menu.addItem('Start workflow', 'startWorkflow');\n }\n }\n menu.addToUi();\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.471Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":3,"totalLines":40,"estimatedTokens":257}}1180{"id":"doc-class_ui_apps_script_google_for_developers-e90a8786","source":"documentation","title":"Class Ui | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/reference/base/ui","text":"Example:\n```text\n// Display a dialog box with a title, message, input field, and \"Yes\" and \"No\"\n// buttons. The user can also close the dialog by clicking the close button in\n// its title bar.\nconst ui = SpreadsheetApp.getUi();\nconst response = ui.prompt(\n 'Getting to know you',\n 'May I know your name?',\n ui.ButtonSet.YES_NO,\n);\n\n// Process the user's response.\nif (response.getSelectedButton() === ui.Button.YES) {\n Logger.log('The user\\'s name is %s.', response.getResponseText());\n} else if (response.getSelectedButton() === ui.Button.NO) {\n Logger.log('The user didn\\'t want to provide a name.');\n} else {\n Logger.log('The user clicked the close button in the dialog\\'s title bar.');\n}\n```\n\nExample:\n```text\n// Display \"Hello, world\" in a dialog box with an \"OK\" button. The user can also\n// close the dialog by clicking the close button in its title bar.\nSpreadsheetApp.getUi().alert('Hello, world');\n```\n\nExample:\n```text\n// Display a dialog box with a message and \"Yes\" and \"No\" buttons. The user can\n// also close the dialog by clicking the close button in its title bar.\nconst ui = SpreadsheetApp.getUi();\nconst response = ui.alert(\n 'Are you sure you want to continue?',\n ui.ButtonSet.YES_NO,\n);\n\n// Process the user's response.\nif (response === ui.Button.YES) {\n Logger.log('The user clicked \"Yes.\"');\n} else {\n Logger.log(\n 'The user clicked \"No\" or the close button in the dialog\\'s title bar.',\n );\n}\n```\n\nExample:\n```text\n// Display a dialog box with a title, message, and \"Yes\" and \"No\" buttons. The\n// user can also close the dialog by clicking the close button in its title bar.\nconst ui = SpreadsheetApp.getUi();\nconst response = ui.alert(\n 'Confirm',\n 'Are you sure you want to continue?',\n ui.ButtonSet.YES_NO,\n);\n\n// Process the user's response.\nif (response === ui.Button.YES) {\n Logger.log('The user clicked \"Yes.\"');\n} else {\n Logger.log(\n 'The user clicked \"No\" or the close button in the dialog\\'s title bar.',\n );\n}\n```\n\nExample:\n```text\n// Add an item to the add-on menu, under a sub-menu whose name is set\n// automatically.\nfunction onOpen(e) {\n SpreadsheetApp.getUi()\n .createAddonMenu()\n .addItem('Show', 'showSidebar')\n .addToUi();\n}\n```\n\nExample:\n```text\n// Add a custom menu to the active document, including a separator and a\n// sub-menu.\nfunction onOpen(e) {\n SpreadsheetApp.getUi()\n .createMenu('My Menu')\n .addItem('My menu item', 'myFunction')\n .addSeparator()\n .addSubMenu(\n SpreadsheetApp.getUi()\n .createMenu('My sub-menu')\n .addItem('One sub-menu item', 'mySecondFunction')\n .addItem('Another sub-menu item', 'myThirdFunction'),\n )\n .addToUi();\n}\n```\n\nExample:\n```text\n// Display a dialog box with a message, input field, and an \"OK\" button. The\n// user can also close the dialog by clicking the close button in its title bar.\nconst ui = SpreadsheetApp.getUi();\nconst response = ui.prompt('Enter your name:');\n\n// Process the user's response.\nif (response.getSelectedButton() === ui.Button.OK) {\n Logger.log('The user\\'s name is %s.', response.getResponseText());\n} else {\n Logger.log('The user clicked the close button in the dialog\\'s title bar.');\n}\n```\n\nExample:\n```text\n// Display a dialog box with a message, input field, and \"Yes\" and \"No\" buttons.\n// The user can also close the dialog by clicking the close button in its title\n// bar.\nconst ui = SpreadsheetApp.getUi();\nconst response = ui.prompt('May I know your name?', ui.ButtonSet.YES_NO);\n\n// Process the user's response.\nif (response.getSelectedButton() === ui.Button.YES) {\n Logger.log('The user\\'s name is %s.', response.getResponseText());\n} else if (response.getSelectedButton() === ui.Button.NO) {\n Logger.log('The user didn\\'t want to provide a name.');\n} else {\n Logger.log('The user clicked the close button in the dialog\\'s title bar.');\n}\n```\n\nExample:\n```text\n// Display a modal dialog box with custom HtmlService content.\nconst htmlOutput = HtmlService\n .createHtmlOutput(\n '<p>A change of speed, a change of style...</p>',\n )\n .setWidth(250)\n .setHeight(300);\nSpreadsheetApp.getUi().showModalDialog(htmlOutput, 'My add-on');\n```\n\nExample:\n```text\n// Display a modeless dialog box with custom HtmlService content.\nconst htmlOutput = HtmlService\n .createHtmlOutput(\n '<p>A change of speed, a change of style...</p>',\n )\n .setWidth(250)\n .setHeight(300);\nSpreadsheetApp.getUi().showModelessDialog(htmlOutput, 'My add-on');\n```\n\nExample:\n```text\n// Display a sidebar with custom HtmlService content.\nconst htmlOutput = HtmlService\n .createHtmlOutput(\n '<p>A change of speed, a change of style...</p>',\n )\n .setTitle('My add-on');\nSpreadsheetApp.getUi().showSidebar(htmlOutput);\n```\n\nExample:\n```text\n// Display a dialog box with custom HtmlService content.\nconst htmlOutput = HtmlService\n .createHtmlOutput(\n '<p>A change of speed, a change of style...</p>',\n )\n .setTitle('My add-on')\n .setWidth(250)\n .setHeight(300);\nSpreadsheetApp.getUi().showDialog(htmlOutput);\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.475Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":12,"totalLines":183,"estimatedTokens":1381}}1181{"id":"doc-send_emails_about_new_google_forms_submissions_g-8a6f7777","source":"documentation","title":"Send emails about new Google Forms submissions | Google Workspace add-ons | Google for Developers","url":"https://developers.google.com/apps-script/quickstart/forms-add-on","text":"Example:\n```text\n/**\n * @OnlyCurrentDoc\n *\n * The above comment directs Apps Script to limit the scope of file\n * access for this add-on. It specifies that this add-on will only\n * attempt to read or modify the files in which the add-on is used,\n * and not all of the user's files. The authorization request message\n * presented to users will reflect this limited scope.\n */\n\n/**\n * A global constant String holding the title of the add-on. This is\n * used to identify the add-on in the notification emails.\n */\nconst ADDON_TITLE = \"Form Notifications\";\n\n/**\n * A global constant 'notice' text to include with each email\n * notification.\n */\nconst NOTICE =\n \"Form Notifications was created as an sample add-on, and is\" +\n \" meant for\" +\n \"demonstration purposes only. It should not be used for complex or important\" +\n \"workflows. The number of notifications this add-on produces are limited by the\" +\n \"owner's available email quota; it will not send email notifications if the\" +\n \"owner's daily email quota has been exceeded. Collaborators using this add-on on\" +\n \"the same form will be able to adjust the notification settings, but will not be\" +\n \"able to disable the notification triggers set by other collaborators.\";\n\n/**\n * Adds a custom menu to the active form to show the add-on sidebar.\n *\n * @param {object} e The event parameter for a simple onOpen trigger. To\n * determine which authorization mode (ScriptApp.AuthMode) the trigger is\n * running in, inspect e.authMode.\n */\nfunction onOpen(e) {\n try {\n FormApp.getUi()\n .createAddonMenu()\n .addItem(\"Configure notifications\", \"showSidebar\")\n .addItem(\"About\", \"showAbout\")\n .addToUi();\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Runs when the add-on is installed.\n *\n * @param {object} e The event parameter for a simple onInstall trigger. To\n * determine which authorization mode (ScriptApp.AuthMode) the trigger is\n * running in, inspect e.authMode. (In practice, onInstall triggers always\n * run in AuthMode.FULL, but onOpen triggers may be AuthMode.LIMITED or\n * AuthMode.NONE).\n */\nfunction onInstall(e) {\n onOpen(e);\n}\n\n/**\n * Opens a sidebar in the form containing the add-on's user interface for\n * configuring the notifications this add-on will produce.\n */\nfunction showSidebar() {\n try {\n const ui =\n HtmlService.createHtmlOutputFromFile(\"sidebar\").setTitle(\n \"Form Notifications\",\n );\n FormApp.getUi().showSidebar(ui);\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Opens a purely-informational dialog in the form explaining details about\n * this add-on.\n */\nfunction showAbout() {\n try {\n const ui = HtmlService.createHtmlOutputFromFile(\"about\")\n .setWidth(420)\n .setHeight(270);\n FormApp.getUi().showModalDialog(ui, \"About Form Notifications\");\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Save sidebar settings to this form's Properties, and update the onFormSubmit\n * trigger as needed.\n *\n * @param {Object} settings An Object containing key-value\n * pairs to store.\n */\nfunction saveSettings(settings) {\n try {\n PropertiesService.getDocumentProperties().setProperties(settings);\n adjustFormSubmitTrigger();\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Queries the User Properties and adds additional data required to populate\n * the sidebar UI elements.\n *\n * @return {Object} A collection of Property values and\n * related data used to fill the configuration sidebar.\n */\nfunction getSettings() {\n try {\n const settings = PropertiesService.getDocumentProperties().getProperties();\n\n // Use a default email if the creator email hasn't been provided yet.\n if (!settings.creatorEmail) {\n settings.creatorEmail = Session.getEffectiveUser().getEmail();\n }\n\n // Get text field items in the form and compile a list\n // of their titles and IDs.\n const form = FormApp.getActiveForm();\n const textItems = form.getItems(FormApp.ItemType.TEXT);\n\n settings.textItems = [];\n for (let i = 0; i < textItems.length; i++) {\n settings.textItems.push({\n title: textItems[i].getTitle(),\n id: textItems[i].getId(),\n });\n }\n return settings;\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Adjust the onFormSubmit trigger based on user's requests.\n */\nfunction adjustFormSubmitTrigger() {\n try {\n const form = FormApp.getActiveForm();\n const triggers = ScriptApp.getUserTriggers(form);\n const settings = PropertiesService.getDocumentProperties();\n const triggerNeeded =\n settings.getProperty(\"creatorNotify\") === \"true\" ||\n settings.getProperty(\"respondentNotify\") === \"true\";\n\n // Create a new trigger if required; delete existing trigger\n // if it is not needed.\n let existingTrigger = null;\n for (let i = 0; i < triggers.length; i++) {\n if (triggers[i].getEventType() === ScriptApp.EventType.ON_FORM_SUBMIT) {\n existingTrigger = triggers[i];\n break;\n }\n }\n if (triggerNeeded && !existingTrigger) {\n const trigger = ScriptApp.newTrigger(\"respondToFormSubmit\")\n .forForm(form)\n .onFormSubmit()\n .create();\n } else if (!triggerNeeded && existingTrigger) {\n ScriptApp.deleteTrigger(existingTrigger);\n }\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Responds to a form submission event if an onFormSubmit trigger has been\n * enabled.\n *\n * @param {Object} e The event parameter created by a form\n * submission; see\n * https://developers.google.com/apps-script/understanding_events\n */\nfunction respondToFormSubmit(e) {\n try {\n const settings = PropertiesService.getDocumentProperties();\n const authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);\n\n // Check if the actions of the trigger require authorizations that have not\n // been supplied yet -- if so, warn the active user via email (if possible).\n // This check is required when using triggers with add-ons to maintain\n // functional triggers.\n if (\n authInfo.getAuthorizationStatus() ===\n ScriptApp.AuthorizationStatus.REQUIRED\n ) {\n // Re-authorization is required. In this case, the user needs to be alerted\n // that they need to reauthorize; the normal trigger action is not\n // conducted, since authorization needs to be provided first. Send at\n // most one 'Authorization Required' email a day, to avoid spamming users\n // of the add-on.\n sendReauthorizationRequest();\n } else {\n // All required authorizations have been granted, so continue to respond to\n // the trigger event.\n\n // Check if the form creator needs to be notified; if so, construct and\n // send the notification.\n if (settings.getProperty(\"creatorNotify\") === \"true\") {\n sendCreatorNotification();\n }\n\n // Check if the form respondent needs to be notified; if so, construct and\n // send the notification. Be sure to respect the remaining email quota.\n if (\n settings.getProperty(\"respondentNotify\") === \"true\" &&\n MailApp.getRemainingDailyQuota() > 0\n ) {\n sendRespondentNotification(e.response);\n }\n }\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Called when the user needs to reauthorize. Sends the user of the\n * add-on an email explaining the need to reauthorize and provides\n * a link for the user to do so. Capped to send at most one email\n * a day to prevent spamming the users of the add-on.\n */\nfunction sendReauthorizationRequest() {\n try {\n const settings = PropertiesService.getDocumentProperties();\n const authInfo = ScriptApp.getAuthorizationInfo(ScriptApp.AuthMode.FULL);\n const lastAuthEmailDate = settings.getProperty(\"lastAuthEmailDate\");\n const today = new Date().toDateString();\n if (lastAuthEmailDate !== today) {\n if (MailApp.getRemainingDailyQuota() > 0) {\n const template =\n HtmlService.createTemplateFromFile(\"authorizationEmail\");\n template.url = authInfo.getAuthorizationUrl();\n template.notice = NOTICE;\n const message = template.evaluate();\n MailApp.sendEmail(\n Session.getEffectiveUser().getEmail(),\n \"Authorization Required\",\n message.getContent(),\n {\n name: ADDON_TITLE,\n htmlBody: message.getContent(),\n },\n );\n }\n settings.setProperty(\"lastAuthEmailDate\", today);\n }\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Sends out creator notification email(s) if the current number\n * of form responses is an even multiple of the response step\n * setting.\n */\nfunction sendCreatorNotification() {\n try {\n const form = FormApp.getActiveForm();\n const settings = PropertiesService.getDocumentProperties();\n let responseStep = settings.getProperty(\"responseStep\");\n responseStep = responseStep ? Number.parseInt(responseStep) : 10;\n\n // If the total number of form responses is an even multiple of the\n // response step setting, send a notification email(s) to the form\n // creator(s). For example, if the response step is 10, notifications\n // will be sent when there are 10, 20, 30, etc. total form responses\n // received.\n if (form.getResponses().length % responseStep === 0) {\n const addresses = settings.getProperty(\"creatorEmail\").split(\",\");\n if (MailApp.getRemainingDailyQuota() > addresses.length) {\n const template = HtmlService.createTemplateFromFile(\n \"creatorNotification\",\n );\n template.summary = form.getSummaryUrl();\n template.responses = form.getResponses().length;\n template.title = form.getTitle();\n template.responseStep = responseStep;\n template.formUrl = form.getEditUrl();\n template.notice = NOTICE;\n const message = template.evaluate();\n MailApp.sendEmail(\n settings.getProperty(\"creatorEmail\"),\n `${form.getTitle()}: Form submissions detected`,\n message.getContent(),\n {\n name: ADDON_TITLE,\n htmlBody: message.getContent(),\n },\n );\n }\n }\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n\n/**\n * Sends out respondent notification emails.\n *\n * @param {FormResponse} response FormResponse object of the event\n * that triggered this notification\n */\nfunction sendRespondentNotification(response) {\n try {\n const form = FormApp.getActiveForm();\n const settings = PropertiesService.getDocumentProperties();\n const emailId = settings.getProperty(\"respondentEmailItemId\");\n const emailItem = form.getItemById(Number.parseInt(emailId));\n const respondentEmail = response\n .getResponseForItem(emailItem)\n .getResponse();\n if (respondentEmail) {\n const template = HtmlService.createTemplateFromFile(\n \"respondentNotification\",\n );\n template.paragraphs = settings.getProperty(\"responseText\").split(\"\\n\");\n template.notice = NOTICE;\n const message = template.evaluate();\n MailApp.sendEmail(\n respondentEmail,\n settings.getProperty(\"responseSubject\"),\n message.getContent(),\n {\n name: form.getTitle(),\n htmlBody: message.getContent(),\n },\n );\n }\n } catch (e) {\n // TODO (Developer) - Handle exception\n console.log(\"Failed with error: %s\", e.error);\n }\n}\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <link rel=\"stylesheet\" href=\"https://ssl.gstatic.com/docs/script/css/add-ons1.css\">\n <!-- The CSS package above applies Google styling to buttons and other elements. -->\n <style>\n .branding-below {\n bottom: 54px;\n top: 0;\n }\n .branding-text {\n left: 7px;\n position: relative;\n top: 3px;\n }\n .logo {\n vertical-align: middle;\n }\n .width-100 {\n width: 100%;\n box-sizing: border-box;\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n }\n label {\n font-weight: bold;\n }\n #creator-options,\n #respondent-options {\n background-color: #eee;\n border-color: #eee;\n border-width: 5px;\n border-style: solid;\n display: none;\n }\n #creator-email,\n #respondent-email,\n #button-bar,\n #submit-subject {\n margin-bottom: 10px;\n }\n\n #response-step {\n display: inline;\n }\n </style>\n </head>\n <body>\n <div class=\"sidebar branding-below\">\n <form>\n <div class=\"block\">\n <input type=\"checkbox\" id=\"creator-notify\">\n <label for=\"creator-notify\">Notify me</label>\n </div>\n <div class=\"block form-group\" id=\"creator-options\">\n <label for=\"creator-email\">\n My email addresses (comma-separated)\n </label>\n <input type=\"text\" class=\"width-100\" id=\"creator-email\">\n <label for=\"response-step\">Send notifications after every</label>\n <input type=\"number\" id=\"response-step\" value=\"10\"\n min=\"1\" max=\"99999\"> responses (default 10)\n </div>\n\n <div class=\"block\">\n <input type=\"checkbox\" id=\"respondent-notify\">\n <label for=\"respondent-notify\">Notify respondents</label>\n </div>\n <div class=\"block form-group\" id=\"respondent-options\">\n <label for=\"respondent-email\">\n Which question asks for their email?\n </label>\n <select class=\"width-100\" id=\"respondent-email\"></select>\n <label for=\"submit-subject\">\n Notification email subject:\n </label>\n <input type=\"text\" class=\"width-100\" id=\"submit-subject\">\n <label for=\"submit-notice\">Notification email body:</label>\n <textarea rows=\"8\" cols=\"40\" id=\"submit-notice\"\n class=\"width-100\"></textarea>\n </div>\n\n <div class=\"block\" id=\"button-bar\">\n <button class=\"action\" id=\"save-settings\">Save</button>\n </div>\n </form>\n </div>\n\n <div class=\"sidebar bottom\">\n <img alt=\"Add-on logo\" class=\"logo\" width=\"25\"\n src=\"https://g-suite-documentation-images.firebaseapp.com/images/newFormNotificationsicon.png\">\n <span class=\"gray branding-text\">Form Notifications by Google</span>\n </div>\n\n <script src=\"//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js\">\n </script>\n <script>\n /**\n * On document load, assign required handlers to each element,\n * and attempt to load any saved settings.\n */\n $(function() {\n $('#save-settings').click(saveSettingsToServer);\n $('#creator-notify').click(toggleCreatorNotify);\n $('#respondent-notify').click(toggleRespondentNotify);\n $('#response-step').change(validateNumber);\n google.script.run\n .withSuccessHandler(loadSettings)\n .withFailureHandler(showStatus)\n .withUserObject($('#button-bar').get())\n .getSettings();\n });\n\n /**\n * Callback function that populates the notification options using\n * previously saved values.\n *\n * @param {Object} settings The saved settings from the client.\n */\n function loadSettings(settings) {\n $('#creator-email').val(settings.creatorEmail);\n $('#response-step').val(!settings.responseStep ?\n 10 : settings.responseStep);\n $('#submit-subject').val(!settings.responseSubject ?\n 'Thank you for filling out our form!' :\n settings.responseSubject);\n $('#submit-notice').val(!settings.responseText ?\n 'Thank you for responding to our form!' :\n settings.responseText);\n\n if (settings.creatorNotify === 'true') {\n $('#creator-notify').prop('checked', true);\n $('#creator-options').show();\n }\n\n if (settings.respondentNotify === 'true') {\n $('#respondent-notify').prop('checked', true);\n $('#respondent-options').show();\n }\n\n // Fill the respondent email select box with the\n // titles given to the form's text Items. Also include\n // the form Item IDs as values so that they can be\n // easily recovered during the Save operation.\n for (var i = 0; i < settings.textItems.length; i++) {\n var option = $('<option>').attr('value', settings.textItems[i]['id'])\n .text(settings.textItems[i]['title']);\n $('#respondent-email').append(option);\n }\n $('#respondent-email').val(settings.respondentEmailItemId);\n }\n\n /**\n * Toggles the visibility of the form creator notification options.\n */\n function toggleCreatorNotify() {\n $('#status').remove();\n if ($('#creator-notify').is(':checked')) {\n $('#creator-options').show();\n } else {\n $('#creator-options').hide();\n }\n }\n\n /**\n * Toggles the visibility of the form sumbitter notification options.\n */\n function toggleRespondentNotify() {\n $('#status').remove();\n if($('#respondent-notify').is(':checked')) {\n $('#respondent-options').show();\n } else {\n $('#respondent-options').hide();\n }\n }\n\n /**\n * Ensures that the entered step is a number between 1\n * and 99999, inclusive.\n */\n function validateNumber() {\n var value = $('#response-step').val();\n if (!value) {\n $('#response-step').val(10);\n } else if (value < 1) {\n $('#response-step').val(1);\n } else if (value > 99999) {\n $('#response-step').val(99999);\n }\n }\n\n /**\n * Collects the options specified in the add-on sidebar and sends them to\n * be saved as Properties on the server.\n */\n function saveSettingsToServer() {\n this.disabled = true;\n $('#status').remove();\n var creatorNotify = $('#creator-notify').is(':checked');\n var respondentNotify = $('#respondent-notify').is(':checked');\n var settings = {\n 'creatorNotify': creatorNotify,\n 'respondentNotify': respondentNotify\n };\n\n // Only save creator options if notify is turned on\n if (creatorNotify) {\n settings.responseStep = $('#response-step').val();\n settings.creatorEmail = $('#creator-email').val().trim();\n\n // Abort save if entered email is blank\n if (!settings.creatorEmail) {\n showStatus('Enter an owner email', $('#button-bar'));\n this.disabled = false;\n return;\n }\n }\n\n // Only save respondent options if notify is turned on\n if (respondentNotify) {\n settings.respondentEmailItemId = $('#respondent-email').val();\n settings.responseSubject = $('#submit-subject').val();\n settings.responseText = $('#submit-notice').val();\n }\n\n // Save the settings on the server\n google.script.run\n .withSuccessHandler(\n function(msg, element) {\n showStatus('Saved settings', $('#button-bar'));\n element.disabled = false;\n })\n .withFailureHandler(\n function(msg, element) {\n showStatus(msg, $('#button-bar'));\n element.disabled = false;\n })\n .withUserObject(this)\n .saveSettings(settings);\n }\n\n /**\n * Inserts a div that contains an status message after a given element.\n *\n * @param {String} msg The status message to display.\n * @param {Object} element The element after which to display the Status.\n */\n function showStatus(msg, element) {\n var div = $('<div>')\n .attr('id', 'status')\n .attr('class','error')\n .text(msg);\n $(element).after(div);\n }\n </script>\n </body>\n</html>\n```\n\nExample:\n```text\n<!DOCTYPE html>\n<html>\n <head>\n <base target=\"_top\">\n <link rel=\"stylesheet\" href=\"https://ssl.gstatic.com/docs/script/css/add-ons1.css\">\n <!-- The CSS package above applies Google styling to buttons and other elements. -->\n </head>\n <body>\n <div>\n <p>\n <i>Form Notifications</i> was created as an sample add-on, and is meant\n for demonstration purposes only. It should not be used for complex or\n important workflows.\n </p>\n <p>\n The number of notifications this add-on produces are limited by the owner's\n available email quota; it will not send email notifications if the owner's\n daily email quota has been exceeded. Collaborators using this add-on on the\n same form will be able to adjust the notification settings, but will not be\n able to disable the notification triggers set by other collaborators.\n </p>\n </div>\n </body>\n</html>\n```\n\nExample:\n```text\n<p>The Google Forms add-on <i>Form Notifications</i> is set to run automatically\nwhenever a form is submitted. The add-on was recently updated and it needs you\nto re-authorize it to run on your behalf.</p>\n\n<p>The add-on's automatic functions are temporarily disabled until you\nre-authorize the add-on. You can accomplish this by opening one of the forms\nusing the add-on and running the add-on through the menu. Alternatively, you can\nclick this link to approve authorization directly:</p>\n\n<p><a href=\"<?= url ?>\">Click here</a> to re-authorize the add-on.</p>\n\n<p>This notification email will be sent to you at most once per day until the\nadd-on is re-authorized.</p>\n\n<hr>\n\n<p style=\"font-size:80%\">This automatic message was sent to you via the <i>Form\nNotifications</i> add-on for Google Forms.\n<?= notice ?></p>\n```\n\nExample:\n```text\n<p><i>Form Notifications</i> (a Google Forms add-on) has detected that the form\ntitled <a href=\"<?= formUrl?>\"><b><?= title ?></b></a> has received\n<?= responses ?> responses so far.</p>\n\n<p><a href=\"<?= summary ?>\">Summary of form responses</a></p>\n\n<p>You are receiving this email because an editor of this form configured\n<i>Form Notifications</i> to alert you every time this form receives\n<b><?= responseStep ?></b> responses.</p>\n\n<p>To change this setting, or to stop receiving these notifications, have the\nform owner or editors open the form and adjust the <i>Form Notifications</i>\nadd-on configuration via the \"Configure notifications\" menu item.</p>\n\n<hr>\n\n<p style=\"font-size:80%\">This automatic message was sent to you via the <i>Form\nNotifications</i> add-on for Google Forms.\n<?= notice ?></p>\n```\n\nExample:\n```text\n<? for (var i = 0; i < paragraphs.length; i++) { ?>\n <p><?= paragraphs[i] ?></p>\n<? } ?>\n\n<hr>\n\n<p style=\"font-size:80%\">This automatic message was sent to you via the <i>Form\nNotifications</i> add-on for Google Forms.\n<?= notice ?></p>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.476Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":6,"totalLines":710,"estimatedTokens":5861}}1182{"id":"doc-create_a_tournament_bracket_apps_script_google_f-81846edb","source":"documentation","title":"Create a tournament bracket | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/bracket-maker","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/bracket-maker\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nconst RANGE_PLAYER1 = \"FirstPlayer\";\nconst SHEET_PLAYERS = \"Players\";\nconst SHEET_BRACKET = \"Bracket\";\nconst CONNECTOR_WIDTH = 15;\n\n/**\n * Adds a custom menu item to run the script.\n */\nfunction onOpen() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n ss.addMenu(\"Bracket maker\", [\n { name: \"Create bracket\", functionName: \"createBracket\" },\n ]);\n}\n\n/**\n * Creates the brackets based on the data provided on the players.\n */\nfunction createBracket() {\n const ss = SpreadsheetApp.getActiveSpreadsheet();\n let rangePlayers = ss.getRangeByName(RANGE_PLAYER1);\n const sheetControl = ss.getSheetByName(SHEET_PLAYERS);\n const sheetResults = ss.getSheetByName(SHEET_BRACKET);\n\n // Gets the players from column A. Assumes the entire column is filled.\n rangePlayers = rangePlayers.offset(\n 0,\n 0,\n sheetControl.getMaxRows() - rangePlayers.getRowIndex() + 1,\n 1,\n );\n let players = rangePlayers.getValues();\n\n // Figures out how many players there are by skipping the empty cells.\n let numPlayers = 0;\n for (let i = 0; i < players.length; i++) {\n if (!players[i][0] || players[i][0].length === 0) {\n break;\n }\n numPlayers++;\n }\n players = players.slice(0, numPlayers);\n\n // Provides some error checking in case there are too many or too few players/teams.\n if (numPlayers > 64) {\n Browser.msgBox(\n \"Sorry, this script can only create brackets for 64 or fewer players.\",\n );\n return; // Early exit\n }\n\n if (numPlayers < 3) {\n Browser.msgBox(\"Sorry, you must have at least 3 players.\");\n return; // Early exit\n }\n\n // Clears the 'Bracket' sheet and all formatting.\n sheetResults.clear();\n\n let upperPower = Math.ceil(Math.log(numPlayers) / Math.log(2));\n\n // Calculates the number that is a power of 2 and lower than numPlayers.\n const countNodesUpperBound = 2 ** upperPower;\n\n // Calculates the number that is a power of 2 and higher than numPlayers.\n const countNodesLowerBound = countNodesUpperBound / 2;\n\n // Determines the number of nodes that will not show in the 1st level.\n const countNodesHidden = numPlayers - countNodesLowerBound;\n\n // Enters the players for the 1st round.\n const currentPlayer = 0;\n for (let i = 0; i < countNodesLowerBound; i++) {\n if (i < countNodesHidden) {\n // Must be on the first level\n const rng = sheetResults.getRange(i * 4 + 1, 1);\n setBracketItem_(rng, players);\n setBracketItem_(rng.offset(2, 0, 1, 1), players);\n setConnector_(sheetResults, rng.offset(0, 1, 3, 1));\n setBracketItem_(rng.offset(1, 2, 1, 1));\n } else {\n // This player gets a bye.\n setBracketItem_(sheetResults.getRange(i * 4 + 2, 3), players);\n }\n }\n\n // Fills in the rest of the bracket.\n upperPower--;\n for (let i = 0; i < upperPower; i++) {\n const pow1 = 2 ** (i + 1);\n const pow2 = 2 ** (i + 2);\n const pow3 = 2 ** (i + 3);\n for (let j = 0; j < 2 ** (upperPower - i - 1); j++) {\n setBracketItem_(sheetResults.getRange(j * pow3 + pow2, i * 2 + 5));\n setConnector_(\n sheetResults,\n sheetResults.getRange(j * pow3 + pow1, i * 2 + 4, pow2 + 1, 1),\n );\n }\n }\n}\n\n/**\n * Sets the value of an item in the bracket and the color.\n * @param {Range} rng The Spreadsheet Range.\n * @param {string[]} players The list of players.\n */\nfunction setBracketItem_(rng, players) {\n if (players) {\n const rand = Math.ceil(Math.random() * players.length);\n rng.setValue(players.splice(rand - 1, 1)[0][0]);\n }\n rng.setBackgroundColor(\"yellow\");\n}\n\n/**\n * Sets the color and width for connector cells.\n * @param {Sheet} sheet The spreadsheet to setup.\n * @param {Range} rng The spreadsheet range.\n */\nfunction setConnector_(sheet, rng) {\n sheet.setColumnWidth(rng.getColumnIndex(), CONNECTOR_WIDTH);\n rng.setBackgroundColor(\"green\");\n}\n```\n\nExample:\n```text\n</section>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.479Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":153,"estimatedTokens":1145}}1183{"id":"doc-send_curated_content_apps_script_google_for_deve-3f29fb02","source":"documentation","title":"Send curated content | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/samples/automations/content-signup","text":"Example:\n```text\n// To learn how to use this script, refer to the documentation:\n// https://developers.google.com/apps-script/samples/automations/content-signup\n\n/*\nCopyright 2022 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// To use your own template doc, update the below variable with the URL of your own Google Doc template.\n// Make sure you update the sharing settings so that 'anyone' or 'anyone in your organization' can view.\nconst EMAIL_TEMPLATE_DOC_URL =\n \"https://docs.google.com/document/d/1enes74gWsMG3dkK3SFO08apXkr0rcYBd3JHKOb2Nksk/edit?usp=sharing\";\n// Update this variable to customize the email subject.\nconst EMAIL_SUBJECT = \"Hello, here is the content you requested\";\n\n// Update this variable to the content titles and URLs you want to offer. Make sure you update the form so that the content titles listed here match the content titles you list in the form.\nconst topicUrls = {\n \"Google Calendar how-to videos\":\n \"https://www.youtube.com/playlist?list=PLU8ezI8GYqs7IPb_UdmUNKyUCqjzGO9PJ\",\n \"Google Drive how-to videos\":\n \"https://www.youtube.com/playlist?list=PLU8ezI8GYqs7Y5d1cgZm2Obq7leVtLkT4\",\n \"Google Docs how-to videos\":\n \"https://www.youtube.com/playlist?list=PLU8ezI8GYqs4JKwZ-fpBP-zSoWPL8Sit7\",\n \"Google Sheets how-to videos\":\n \"https://www.youtube.com/playlist?list=PLU8ezI8GYqs61ciKpXf_KkV7ZRbRHVG38\",\n};\n\n/**\n * Installs a trigger on the spreadsheet for when someone submits a form.\n */\nfunction installTrigger() {\n ScriptApp.newTrigger(\"onFormSubmit\")\n .forSpreadsheet(SpreadsheetApp.getActive())\n .onFormSubmit()\n .create();\n}\n\n/**\n * Sends a customized email for every form response.\n *\n * @param {Object} event - Form submit event\n */\nfunction onFormSubmit(e) {\n const responses = e.namedValues;\n\n // If the question title is a label, it can be accessed as an object field.\n // If it has spaces or other characters, it can be accessed as a dictionary.\n const timestamp = responses.Timestamp[0];\n const email = responses[\"Email address\"][0].trim();\n const name = responses.Name[0].trim();\n const topicsString = responses.Topics[0].toLowerCase();\n\n // Parse topics of interest into a list (since there are multiple items\n // that are saved in the row as blob of text).\n const topics = Object.keys(topicUrls).filter((topic) => {\n // indexOf searches for the topic in topicsString and returns a non-negative\n // index if the topic is found, or it will return -1 if it's not found.\n return topicsString.indexOf(topic.toLowerCase()) !== -1;\n });\n\n // If there is at least one topic selected, send an email to the recipient.\n let status = \"\";\n if (topics.length > 0) {\n MailApp.sendEmail({\n to: email,\n subject: EMAIL_SUBJECT,\n htmlBody: createEmailBody(name, topics),\n });\n status = \"Sent\";\n } else {\n status = \"No topics selected\";\n }\n\n // Append the status on the spreadsheet to the responses' row.\n const sheet = SpreadsheetApp.getActiveSheet();\n const row = sheet.getActiveRange().getRow();\n const column = e.values.length + 1;\n sheet.getRange(row, column).setValue(status);\n\n console.log(`status=${status}; responses=${JSON.stringify(responses)}`);\n}\n\n/**\n * Creates email body and includes the links based on topic.\n *\n * @param {string} recipient - The recipient's email address.\n * @param {string[]} topics - List of topics to include in the email body.\n * @return {string} - The email body as an HTML string.\n */\nfunction createEmailBody(name, topics) {\n let topicsHtml = topics\n .map((topic) => {\n const url = topicUrls[topic];\n return `<li><a href=\"${url}\">${topic}</a></li>`;\n })\n .join(\"\");\n topicsHtml = `<ul>${topicsHtml}</ul>`;\n\n // Make sure to update the emailTemplateDocId at the top.\n const docId = DocumentApp.openByUrl(EMAIL_TEMPLATE_DOC_URL).getId();\n let emailBody = docToHtml(docId);\n emailBody = emailBody.replace(/{{NAME}}/g, name);\n emailBody = emailBody.replace(/{{TOPICS}}/g, topicsHtml);\n return emailBody;\n}\n\n/**\n * Downloads a Google Doc as an HTML string.\n *\n * @param {string} docId - The ID of a Google Doc to fetch content from.\n * @return {string} The Google Doc rendered as an HTML string.\n */\nfunction docToHtml(docId) {\n // Downloads a Google Doc as an HTML string.\n const url = `https://docs.google.com/feeds/download/documents/export/Export?id=${docId}&exportFormat=html`;\n const param = {\n method: \"get\",\n headers: { Authorization: `Bearer ${ScriptApp.getOAuthToken()}` },\n muteHttpExceptions: true,\n };\n return UrlFetchApp.fetch(url, param).getContentText();\n}\n```\n\nExample:\n```text\n</section>\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.479Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":2,"totalLines":143,"estimatedTokens":1278}}1184{"id":"doc-install_the_google_drive_client_libraries_google-3601d973","source":"documentation","title":"Install the Google Drive client libraries | Google for Developers","url":"https://developers.google.com/workspace/drive/api/guides/downloads","text":"Example:\n```text\ndart pub add googleapis\n```\n\nExample:\n```text\nflutter pub add googleapis\n```\n\nExample:\n```text\ngo get google.golang.org/api/urlshortener/v1\n```\n\nExample:\n```text\n<project>\n <dependencies>\n <dependency>\n <groupId>com.google.apis</groupId>\n <artifactId>google-api-services-drive</artifactId>\n <version>v3-rev20240509-2.0.0</version>\n </dependency>\n </dependencies>\n</project>\n```\n\nExample:\n```text\nrepositories {\n mavenCentral()\n}\ndependencies {\n implementation 'com.google.apis:google-api-services-drive:v3-rev20240509-2.0.0'\n}\n```\n\nExample:\n```text\ndotnet add package Google.Apis --version 1.68.0\n```\n\nExample:\n```text\nnpm install @googleapis/drive\n```\n\nExample:\n```text\npod 'GoogleAPIClientForREST/Drive'\n```\n\nExample:\n```text\ncomposer require google/apiclient:^2.15.0\n```\n\nExample:\n```text\nrequire_once '/path/to/google-api-php-client/vendor/autoload.php';\n```\n\nExample:\n```text\npip3 install virtualenv\nvirtualenv <your-env>\nsource <your-env>/bin/activate\n<your-env>/bin/pip install google-api-python-client\n```\n\nExample:\n```text\npip install virtualenv\nvirtualenv <your-env>\n<your-env>\\Scripts\\activate\n<your-env>\\Scripts\\pip.exe install google-api-python-client\n```\n\nExample:\n```text\ngem install google-apis-drive_v3 -v 0.5.0\n```\n\nExample:\n```text\nrequire 'google/apis/drive_v3'\ndrive = Google::Apis::DriveV3::DriveService.new\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.482Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":14,"totalLines":91,"estimatedTokens":348}}1185{"id":"doc-custom_functions_in_google_sheets_apps_script_go-69ab27d9","source":"documentation","title":"Custom Functions in Google Sheets | Apps Script | Google for Developers","url":"https://developers.google.com/apps-script/execution_custom_functions","text":"Example:\n```text\n/**\n * Multiplies an input value by 2.\n * @param {number} input The number to double.\n * @return The input multiplied by 2.\n * @customfunction\n*/\nfunction DOUBLE(input) {\n return input * 2;\n}\n```\n\nExample:\n```text\n/**\n * Multiplies the input value by 2.\n *\n * @param {number} input The value to multiply.\n * @return {number} The input multiplied by 2.\n * @customfunction\n */\nfunction DOUBLE(input) {\n return input * 2;\n}\n```\n\nExample:\n```text\n/**\n * Multiplies the input value by 2.\n *\n * @param {number|Array<Array<number>>} input The value or range of cells\n * to multiply.\n * @return The input multiplied by 2.\n * @customfunction\n */\nfunction DOUBLE(input) {\n return Array.isArray(input) ?\n input.map(row => row.map(cell => cell * 2)) :\n input * 2;\n}\n```\n\nExample:\n```text\n/**\n * Show the title and date for the first page of posts on the\n * Developer blog.\n *\n * @return Two columns of data representing posts on the\n * Developer blog.\n * @customfunction\n */\nfunction getBlogPosts() {\n var array = [];\n var url = 'https://gsuite-developers.googleblog.com/atom.xml';\n var xml = UrlFetchApp.fetch(url).getContentText();\n var document = XmlService.parse(xml);\n var root = document.getRootElement();\n var atom = XmlService.getNamespace('http://www.w3.org/2005/Atom');\n var entries = document.getRootElement().getChildren('entry', atom);\n for (var i = 0; i < entries.length; i++) {\n var title = entries[i].getChild('title', atom).getText();\n var date = entries[i].getChild('published', atom).getValue();\n array.push([title, date]);\n }\n return array;\n}\n```\n\n<|endofdoc|>","metadata":{"transformedAt":"2026-08-18T15:16:56.486Z","totalSectionsIncluded":0,"totalCodeBlocksIncluded":4,"totalLines":72,"estimatedTokens":407}}1186 